admin mobile: improve small-screen readability across checklist, tabs, badges, and headers
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-02-08 22:13:01 +01:00
parent 83cf863548
commit e3bb1642db
11 changed files with 131 additions and 82 deletions

View File

@@ -980,8 +980,8 @@ function PackageCard({
backgroundColor={isActive ? accentSoft : undefined} backgroundColor={isActive ? accentSoft : undefined}
gap="$2" gap="$2"
> >
<XStack alignItems="center" justifyContent="space-between"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<Text fontSize="$md" fontWeight="800" color={textStrong}> <Text fontSize="$md" fontWeight="800" color={textStrong} flex={1} minWidth={0}>
{pkg.package_name ?? t('mobileBilling.packageFallback', 'Package')} {pkg.package_name ?? t('mobileBilling.packageFallback', 'Package')}
</Text> </Text>
{label ? <PillBadge tone="success">{label}</PillBadge> : null} {label ? <PillBadge tone="success">{label}</PillBadge> : null}
@@ -1262,8 +1262,8 @@ function AddonRow({
return ( return (
<MobileCard borderColor={border} padding="$3" gap="$1.5"> <MobileCard borderColor={border} padding="$3" gap="$1.5">
<XStack alignItems="center" justifyContent="space-between"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<Text fontSize="$sm" fontWeight="700" color={textStrong}> <Text fontSize="$sm" fontWeight="700" color={textStrong} flex={1} minWidth={0}>
{addon.label ?? addon.addon_key} {addon.label ?? addon.addon_key}
</Text> </Text>
<PillBadge tone={status.tone}>{status.text}</PillBadge> <PillBadge tone={status.tone}>{status.text}</PillBadge>
@@ -1271,11 +1271,11 @@ function AddonRow({
{!hideEventLink && eventName ? ( {!hideEventLink && eventName ? (
eventPath ? ( eventPath ? (
<Pressable onPress={() => navigate(eventPath)}> <Pressable onPress={() => navigate(eventPath)}>
<XStack alignItems="center" justifyContent="space-between"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<Text fontSize="$xs" color={textStrong} fontWeight="600"> <Text fontSize="$xs" color={textStrong} fontWeight="600" flex={1} minWidth={0}>
{eventName} {eventName}
</Text> </Text>
<Text fontSize="$xs" color={primary} fontWeight="700"> <Text fontSize="$xs" color={primary} fontWeight="700" flexShrink={0}>
{t('mobileBilling.openEvent', 'Open event')} {t('mobileBilling.openEvent', 'Open event')}
</Text> </Text>
</XStack> </XStack>
@@ -1316,8 +1316,8 @@ function EventAddonRow({ addon }: { addon: EventAddonSummary }) {
return ( return (
<MobileCard borderColor={border} padding="$3" gap="$1.5"> <MobileCard borderColor={border} padding="$3" gap="$1.5">
<XStack alignItems="center" justifyContent="space-between"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<Text fontSize="$sm" fontWeight="700" color={textStrong}> <Text fontSize="$sm" fontWeight="700" color={textStrong} flex={1} minWidth={0}>
{addon.label ?? addon.key} {addon.label ?? addon.key}
</Text> </Text>
<PillBadge tone={status.tone}>{status.text}</PillBadge> <PillBadge tone={status.tone}>{status.text}</PillBadge>

View File

@@ -629,9 +629,9 @@ function LifecycleHero({
paddingHorizontal="$3" paddingHorizontal="$3"
onPress={() => navigate(adminPath(nextStep.targetPath))} onPress={() => navigate(adminPath(nextStep.targetPath))}
title={ title={
<XStack alignItems="center" gap="$2"> <XStack alignItems="flex-start" gap="$2" flex={1} minWidth={0}>
<Circle size={18} color={theme.primary} strokeWidth={2.5} /> <Circle size={18} color={theme.primary} strokeWidth={2.5} />
<Text fontSize="$sm" fontWeight="700" color={theme.textStrong}> <Text fontSize="$sm" fontWeight="700" color={theme.textStrong} flexShrink={1}>
{nextStep.label} {nextStep.label}
</Text> </Text>
</XStack> </XStack>
@@ -644,8 +644,17 @@ function LifecycleHero({
) : undefined ) : undefined
} }
iconAfter={ iconAfter={
<XStack alignItems="center" gap="$1"> <XStack alignItems="center" gap="$1" maxWidth="52%" flexShrink={1}>
<PillBadge tone="success">{nextStep.ctaLabel}</PillBadge> <Text
fontSize="$xs"
fontWeight="700"
color={theme.primary}
textAlign="right"
numberOfLines={2}
flexShrink={1}
>
{nextStep.ctaLabel}
</Text>
<ChevronRight size={16} color={theme.muted} /> <ChevronRight size={16} color={theme.muted} />
</XStack> </XStack>
} }

View File

@@ -335,11 +335,11 @@ export default function MobileEventGuestNotificationsPage() {
<YStack gap="$2"> <YStack gap="$2">
{history.map((item) => ( {history.map((item) => (
<MobileCard key={item.id} gap="$2" borderColor={border}> <MobileCard key={item.id} gap="$2" borderColor={border}>
<XStack alignItems="center" justifyContent="space-between"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<Text fontSize="$sm" fontWeight="800" color={textStrong}> <Text fontSize="$sm" fontWeight="800" color={textStrong} flex={1} minWidth={0}>
{item.title || t('guestMessages.history.untitled', 'Untitled')} {item.title || t('guestMessages.history.untitled', 'Untitled')}
</Text> </Text>
<XStack gap="$1.5" alignItems="center"> <XStack gap="$1.5" alignItems="center" flexWrap="wrap" justifyContent="flex-end">
<PillBadge tone={item.status === 'active' ? 'success' : 'muted'}> <PillBadge tone={item.status === 'active' ? 'success' : 'muted'}>
{t(`guestMessages.status.${item.status}`, item.status)} {t(`guestMessages.status.${item.status}`, item.status)}
</PillBadge> </PillBadge>
@@ -353,8 +353,8 @@ export default function MobileEventGuestNotificationsPage() {
<Text fontSize="$sm" color={text}> <Text fontSize="$sm" color={text}>
{item.body ?? t('guestMessages.history.noBody', 'No body provided.')} {item.body ?? t('guestMessages.history.noBody', 'No body provided.')}
</Text> </Text>
<XStack alignItems="center" justifyContent="space-between"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<XStack gap="$1.5" alignItems="center"> <XStack gap="$1.5" alignItems="center" flexWrap="wrap" flex={1} minWidth={0}>
<PillBadge tone="muted">{t(`guestMessages.type.${item.type}`, item.type)}</PillBadge> <PillBadge tone="muted">{t(`guestMessages.type.${item.type}`, item.type)}</PillBadge>
{item.target_identifier ? ( {item.target_identifier ? (
<PillBadge tone="muted"> <PillBadge tone="muted">
@@ -376,7 +376,7 @@ export default function MobileEventGuestNotificationsPage() {
</PillBadge> </PillBadge>
)} )}
</XStack> </XStack>
<Text fontSize="$xs" color={mutedText}> <Text fontSize="$xs" color={mutedText} flexShrink={0}>
{formatGuestMessageDate(item.created_at, locale)} {formatGuestMessageDate(item.created_at, locale)}
</Text> </Text>
</XStack> </XStack>

View File

@@ -358,15 +358,15 @@ export default function MobileEventMembersPage() {
const statusInfo = resolveStatus(member.status); const statusInfo = resolveStatus(member.status);
return ( return (
<MobileCard key={member.id} padding="$3" borderColor={border}> <MobileCard key={member.id} padding="$3" borderColor={border}>
<XStack alignItems="center" justifyContent="space-between"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2">
<YStack gap="$1"> <YStack gap="$1" flex={1} minWidth={0}>
<Text fontSize="$sm" fontWeight="700" color={textStrong}> <Text fontSize="$sm" fontWeight="700" color={textStrong} numberOfLines={2}>
{member.name || member.email || t('events.members.fallbackName', 'Guest')} {member.name || member.email || t('events.members.fallbackName', 'Guest')}
</Text> </Text>
<Text fontSize="$xs" color={muted}> <Text fontSize="$xs" color={muted} numberOfLines={2}>
{member.email ?? ''} {member.email ?? ''}
</Text> </Text>
<XStack gap="$1.5" alignItems="center"> <XStack gap="$1.5" alignItems="center" flexWrap="wrap">
<PillBadge tone={statusInfo.tone}> <PillBadge tone={statusInfo.tone}>
{statusInfo.label} {statusInfo.label}
</PillBadge> </PillBadge>

View File

@@ -200,60 +200,72 @@ export default function MobileEventRecapPage() {
orientation="horizontal" orientation="horizontal"
flexDirection="column" flexDirection="column"
> >
<Tabs.List gap="$2"> <Tabs.List gap="$1" flexWrap="wrap">
<Tabs.Tab <Tabs.Tab
value="overview" value="overview"
flex={1} flexGrow={1}
paddingVertical="$2.5" flexShrink={1}
paddingHorizontal="$3" flexBasis="calc(33.333% - 4px)"
minWidth={108}
paddingVertical="$2"
paddingHorizontal="$2"
borderRadius="$4" borderRadius="$4"
hoverStyle={{ backgroundColor: '$backgroundHover' }} hoverStyle={{ backgroundColor: '$backgroundHover' }}
pressStyle={{ backgroundColor: '$backgroundPress' }} pressStyle={{ backgroundColor: '$backgroundPress' }}
activeStyle={{ backgroundColor: '$backgroundPress' }} activeStyle={{ backgroundColor: '$backgroundPress' }}
> >
<Text <Text
fontSize="$sm" fontSize="$xs"
fontWeight={activeTab === 'overview' ? '700' : '600'} fontWeight={activeTab === 'overview' ? '700' : '600'}
color={activeTab === 'overview' ? text : muted} color={activeTab === 'overview' ? text : muted}
textAlign="center" textAlign="center"
numberOfLines={2}
> >
{t('events.recap.tabs.overview', 'Overview')} {t('events.recap.tabs.overview', 'Overview')}
</Text> </Text>
</Tabs.Tab> </Tabs.Tab>
<Tabs.Tab <Tabs.Tab
value="engagement" value="engagement"
flex={1} flexGrow={1}
paddingVertical="$2.5" flexShrink={1}
paddingHorizontal="$3" flexBasis="calc(33.333% - 4px)"
minWidth={108}
paddingVertical="$2"
paddingHorizontal="$2"
borderRadius="$4" borderRadius="$4"
hoverStyle={{ backgroundColor: '$backgroundHover' }} hoverStyle={{ backgroundColor: '$backgroundHover' }}
pressStyle={{ backgroundColor: '$backgroundPress' }} pressStyle={{ backgroundColor: '$backgroundPress' }}
activeStyle={{ backgroundColor: '$backgroundPress' }} activeStyle={{ backgroundColor: '$backgroundPress' }}
> >
<Text <Text
fontSize="$sm" fontSize="$xs"
fontWeight={activeTab === 'engagement' ? '700' : '600'} fontWeight={activeTab === 'engagement' ? '700' : '600'}
color={activeTab === 'engagement' ? text : muted} color={activeTab === 'engagement' ? text : muted}
textAlign="center" textAlign="center"
numberOfLines={2}
> >
{t('events.recap.tabs.engagement', 'Engagement')} {t('events.recap.tabs.engagement', 'Engagement')}
</Text> </Text>
</Tabs.Tab> </Tabs.Tab>
<Tabs.Tab <Tabs.Tab
value="compliance" value="compliance"
flex={1} flexGrow={1}
paddingVertical="$2.5" flexShrink={1}
paddingHorizontal="$3" flexBasis="calc(33.333% - 4px)"
minWidth={108}
paddingVertical="$2"
paddingHorizontal="$2"
borderRadius="$4" borderRadius="$4"
hoverStyle={{ backgroundColor: '$backgroundHover' }} hoverStyle={{ backgroundColor: '$backgroundHover' }}
pressStyle={{ backgroundColor: '$backgroundPress' }} pressStyle={{ backgroundColor: '$backgroundPress' }}
activeStyle={{ backgroundColor: '$backgroundPress' }} activeStyle={{ backgroundColor: '$backgroundPress' }}
> >
<Text <Text
fontSize="$sm" fontSize="$xs"
fontWeight={activeTab === 'compliance' ? '700' : '600'} fontWeight={activeTab === 'compliance' ? '700' : '600'}
color={activeTab === 'compliance' ? text : muted} color={activeTab === 'compliance' ? text : muted}
textAlign="center" textAlign="center"
numberOfLines={2}
> >
{t('events.recap.tabs.compliance', 'Compliance')} {t('events.recap.tabs.compliance', 'Compliance')}
</Text> </Text>
@@ -263,8 +275,8 @@ export default function MobileEventRecapPage() {
<Tabs.Content value="overview" paddingTop="$2"> <Tabs.Content value="overview" paddingTop="$2">
<YStack gap="$4"> <YStack gap="$4">
<MobileCard gap="$3"> <MobileCard gap="$3">
<XStack alignItems="center" justifyContent="space-between"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<YStack gap="$1"> <YStack gap="$1" flex={1} minWidth={0}>
<Text fontSize="$xl" fontWeight="800" color={textStrong}> <Text fontSize="$xl" fontWeight="800" color={textStrong}>
{t('events.recap.completedTitle', 'Event abgeschlossen')} {t('events.recap.completedTitle', 'Event abgeschlossen')}
</Text> </Text>

View File

@@ -1176,31 +1176,45 @@ export default function MobileEventTasksPage() {
borderRadius={16} borderRadius={16}
borderWidth={1} borderWidth={1}
borderColor={border} borderColor={border}
overflow="hidden" overflow="visible"
gap="$0" gap="$1"
flexWrap="wrap"
padding="$1"
> >
{[ {[
{ value: 'assigned', label: t('events.tasks.tabs.tasks', 'Tasks') }, { value: 'assigned', label: t('events.tasks.tabs.tasks', 'Tasks') },
{ value: 'library', label: t('events.tasks.tabs.library', 'Task Library') }, { value: 'library', label: t('events.tasks.tabs.library', 'Task Library') },
{ value: 'emotions', label: t('events.tasks.tabs.emotions', 'Emotions') }, { value: 'emotions', label: t('events.tasks.tabs.emotions', 'Emotions') },
{ value: 'collections', label: t('events.tasks.tabs.collections', 'Collections') }, { value: 'collections', label: t('events.tasks.tabs.collections', 'Collections') },
].map((tab, index, arr) => { ].map((tab) => {
const isActive = activeTab === tab.value; const isActive = activeTab === tab.value;
return ( return (
<Tabs.Tab <Tabs.Tab
key={tab.value} key={tab.value}
value={tab.value} value={tab.value}
flex={1} flexGrow={1}
paddingVertical="$2.5" flexShrink={1}
flexBasis="calc(50% - 4px)"
minWidth={132}
paddingVertical="$2"
paddingHorizontal="$2"
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
borderRightWidth={index === arr.length - 1 ? 0 : 1} borderWidth={1}
borderRightColor={border} borderColor={isActive ? withAlpha(primary, 0.45) : border}
borderRadius={12}
backgroundColor={isActive ? withAlpha(primary, 0.12) : surface}
hoverStyle={{ backgroundColor: '$backgroundHover' }} hoverStyle={{ backgroundColor: '$backgroundHover' }}
pressStyle={{ backgroundColor: '$backgroundPress' }} pressStyle={{ backgroundColor: '$backgroundPress' }}
activeStyle={{ backgroundColor: '$backgroundPress' }} activeStyle={{ backgroundColor: '$backgroundPress' }}
> >
<Text fontSize="$sm" fontWeight={isActive ? '700' : '600'} color={isActive ? text : muted}> <Text
fontSize="$xs"
fontWeight={isActive ? '700' : '600'}
color={isActive ? text : muted}
numberOfLines={2}
textAlign="center"
>
{tab.label} {tab.label}
</Text> </Text>
</Tabs.Tab> </Tabs.Tab>
@@ -1232,6 +1246,7 @@ export default function MobileEventTasksPage() {
<XStack <XStack
alignItems="center" alignItems="center"
gap="$1.5" gap="$1.5"
flexShrink={1}
paddingVertical="$2" paddingVertical="$2"
paddingHorizontal="$3" paddingHorizontal="$3"
borderRadius={14} borderRadius={14}
@@ -1239,14 +1254,16 @@ export default function MobileEventTasksPage() {
borderColor={border} borderColor={border}
backgroundColor={surface} backgroundColor={surface}
> >
<Text fontSize={11} fontWeight="700" color={text}> <YStack flexShrink={1} minWidth={0} maxWidth={128}>
<Text fontSize={11} fontWeight="700" color={text} numberOfLines={1}>
{t('events.tasks.emotionFilterShort', 'Emotion')} {t('events.tasks.emotionFilterShort', 'Emotion')}
</Text> </Text>
<Text fontSize={11} color={muted}> <Text fontSize={11} color={muted} numberOfLines={1}>
{emotionFilter {emotionFilter
? emotions.find((e) => String(e.id) === emotionFilter)?.name ?? t('events.tasks.customEmotion', 'Custom emotion') ? emotions.find((e) => String(e.id) === emotionFilter)?.name ?? t('events.tasks.customEmotion', 'Custom emotion')
: t('events.tasks.allEmotions', 'All')} : t('events.tasks.allEmotions', 'All')}
</Text> </Text>
</YStack>
<ChevronDown size={14} color={muted} /> <ChevronDown size={14} color={muted} />
</XStack> </XStack>
</Pressable> </Pressable>

View File

@@ -449,11 +449,11 @@ function EventListItem({
const stats = buildEventListStats(event); const stats = buildEventListStats(event);
return ( return (
<YStack gap="$1.5"> <YStack gap="$1.5">
<XStack alignItems="center" justifyContent="space-between" gap="$2"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<Text fontSize="$md" fontWeight="800" color={text}> <Text fontSize="$md" fontWeight="800" color={text} flex={1} minWidth={0}>
{renderName(event.name, t)} {renderName(event.name, t)}
</Text> </Text>
<XStack alignItems="center" gap="$1.5"> <XStack alignItems="center" gap="$1.5" flexWrap="wrap" justifyContent="flex-end">
<PillBadge tone={statusTone}>{statusLabel}</PillBadge> <PillBadge tone={statusTone}>{statusLabel}</PillBadge>
{onEdit ? ( {onEdit ? (
<Pressable onPress={() => onEdit(event.slug)}> <Pressable onPress={() => onEdit(event.slug)}>

View File

@@ -239,10 +239,10 @@ function PackageShopCard({
backgroundColor={isActive ? '$green1' : undefined} backgroundColor={isActive ? '$green1' : undefined}
style={{ opacity: isSubdued ? 0.6 : 1 }} style={{ opacity: isSubdued ? 0.6 : 1 }}
> >
<XStack justifyContent="space-between" alignItems="flex-start"> <XStack justifyContent="space-between" alignItems="flex-start" gap="$2" flexWrap="wrap">
<YStack gap="$1"> <YStack gap="$1" flex={1} minWidth={0}>
<XStack gap="$2" alignItems="center"> <XStack gap="$2" alignItems="center" flexWrap="wrap">
<Text fontSize="$lg" fontWeight="800" color={textStrong}> <Text fontSize="$lg" fontWeight="800" color={textStrong} flexShrink={1}>
{pkg.name} {pkg.name}
</Text> </Text>
{isRecommended && <PillBadge tone="warning">{t('shop.badges.recommended', 'Recommended')}</PillBadge>} {isRecommended && <PillBadge tone="warning">{t('shop.badges.recommended', 'Recommended')}</PillBadge>}
@@ -255,12 +255,12 @@ function PackageShopCard({
{!isResellerCatalog && isActive ? <PillBadge tone="success">{t('shop.badges.active', 'Active')}</PillBadge> : null} {!isResellerCatalog && isActive ? <PillBadge tone="success">{t('shop.badges.active', 'Active')}</PillBadge> : null}
</XStack> </XStack>
<XStack gap="$2" alignItems="center"> <XStack gap="$2" alignItems="center" flexWrap="wrap">
<Text fontSize="$md" color={primary} fontWeight="700"> <Text fontSize="$md" color={primary} fontWeight="700">
{new Intl.NumberFormat(undefined, { style: 'currency', currency: 'EUR' }).format(pkg.price)} {new Intl.NumberFormat(undefined, { style: 'currency', currency: 'EUR' }).format(pkg.price)}
</Text> </Text>
{statusLabel && ( {statusLabel && (
<Text fontSize="$xs" color={muted} fontWeight="600"> <Text fontSize="$xs" color={muted} fontWeight="600" flexShrink={1}>
{statusLabel} {statusLabel}
</Text> </Text>
)} )}

View File

@@ -583,41 +583,49 @@ export default function MobileProfileAccountPage() {
orientation="horizontal" orientation="horizontal"
flexDirection="column" flexDirection="column"
> >
<Tabs.List gap="$2"> <Tabs.List gap="$1" flexWrap="wrap">
<Tabs.Tab <Tabs.Tab
value="account" value="account"
flex={1} flexGrow={1}
paddingVertical="$2.5" flexShrink={1}
paddingHorizontal="$3" flexBasis="calc(50% - 4px)"
minWidth={132}
paddingVertical="$2"
paddingHorizontal="$2"
borderRadius="$4" borderRadius="$4"
hoverStyle={{ backgroundColor: '$backgroundHover' }} hoverStyle={{ backgroundColor: '$backgroundHover' }}
pressStyle={{ backgroundColor: '$backgroundPress' }} pressStyle={{ backgroundColor: '$backgroundPress' }}
activeStyle={{ backgroundColor: '$backgroundPress' }} activeStyle={{ backgroundColor: '$backgroundPress' }}
> >
<Text <Text
fontSize="$sm" fontSize="$xs"
fontWeight={activeTab === 'account' ? '700' : '600'} fontWeight={activeTab === 'account' ? '700' : '600'}
color={activeTab === 'account' ? text : muted} color={activeTab === 'account' ? text : muted}
textAlign="center" textAlign="center"
numberOfLines={2}
> >
{t('profile.tabs.account', 'Account')} {t('profile.tabs.account', 'Account')}
</Text> </Text>
</Tabs.Tab> </Tabs.Tab>
<Tabs.Tab <Tabs.Tab
value="branding" value="branding"
flex={1} flexGrow={1}
paddingVertical="$2.5" flexShrink={1}
paddingHorizontal="$3" flexBasis="calc(50% - 4px)"
minWidth={132}
paddingVertical="$2"
paddingHorizontal="$2"
borderRadius="$4" borderRadius="$4"
hoverStyle={{ backgroundColor: '$backgroundHover' }} hoverStyle={{ backgroundColor: '$backgroundHover' }}
pressStyle={{ backgroundColor: '$backgroundPress' }} pressStyle={{ backgroundColor: '$backgroundPress' }}
activeStyle={{ backgroundColor: '$backgroundPress' }} activeStyle={{ backgroundColor: '$backgroundPress' }}
> >
<Text <Text
fontSize="$sm" fontSize="$xs"
fontWeight={activeTab === 'branding' ? '700' : '600'} fontWeight={activeTab === 'branding' ? '700' : '600'}
color={activeTab === 'branding' ? text : muted} color={activeTab === 'branding' ? text : muted}
textAlign="center" textAlign="center"
numberOfLines={2}
> >
{t('profile.tabs.branding', 'Standard-Branding')} {t('profile.tabs.branding', 'Standard-Branding')}
</Text> </Text>

View File

@@ -338,7 +338,7 @@ export default function MobileSettingsPage() {
</Text> </Text>
) : ( ) : (
<YStack gap="$2"> <YStack gap="$2">
<XStack alignItems="center" justifyContent="space-between" gap="$2"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<YStack flex={1} gap="$1"> <YStack flex={1} gap="$1">
<Text fontSize="$sm" fontWeight="700" color={text}> <Text fontSize="$sm" fontWeight="700" color={text}>
{t('mobileSettings.deviceStatus.notifications.label', 'Notifications')} {t('mobileSettings.deviceStatus.notifications.label', 'Notifications')}
@@ -351,7 +351,7 @@ export default function MobileSettingsPage() {
{permissionLabel(devicePermissions.notifications)} {permissionLabel(devicePermissions.notifications)}
</PillBadge> </PillBadge>
</XStack> </XStack>
<XStack alignItems="center" justifyContent="space-between" gap="$2"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<YStack flex={1} gap="$1"> <YStack flex={1} gap="$1">
<Text fontSize="$sm" fontWeight="700" color={text}> <Text fontSize="$sm" fontWeight="700" color={text}>
{t('mobileSettings.deviceStatus.camera.label', 'Camera')} {t('mobileSettings.deviceStatus.camera.label', 'Camera')}
@@ -364,7 +364,7 @@ export default function MobileSettingsPage() {
{permissionLabel(devicePermissions.camera)} {permissionLabel(devicePermissions.camera)}
</PillBadge> </PillBadge>
</XStack> </XStack>
<XStack alignItems="center" justifyContent="space-between" gap="$2"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<YStack flex={1} gap="$1"> <YStack flex={1} gap="$1">
<Text fontSize="$sm" fontWeight="700" color={text}> <Text fontSize="$sm" fontWeight="700" color={text}>
{t('mobileSettings.deviceStatus.storage.label', 'Offline storage')} {t('mobileSettings.deviceStatus.storage.label', 'Offline storage')}
@@ -377,7 +377,7 @@ export default function MobileSettingsPage() {
{storageLabel(devicePermissions.storage)} {storageLabel(devicePermissions.storage)}
</PillBadge> </PillBadge>
</XStack> </XStack>
<XStack alignItems="center" justifyContent="space-between" gap="$2"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<YStack flex={1} gap="$1"> <YStack flex={1} gap="$1">
<Text fontSize="$sm" fontWeight="700" color={text}> <Text fontSize="$sm" fontWeight="700" color={text}>
{t('mobileSettings.deviceStatus.connection.label', 'Connection')} {t('mobileSettings.deviceStatus.connection.label', 'Connection')}

View File

@@ -39,19 +39,21 @@ export function SetupChecklist({
<YStack> <YStack>
<Pressable onPress={() => setCollapsed(!collapsed)}> <Pressable onPress={() => setCollapsed(!collapsed)}>
<YStack padding="$3" paddingVertical="$2.5" gap="$2"> <YStack padding="$3" paddingVertical="$2.5" gap="$2">
<XStack alignItems="center" justifyContent="space-between"> <XStack alignItems="flex-start" justifyContent="space-between" gap="$2" flexWrap="wrap">
<XStack alignItems="center" gap="$2"> <XStack alignItems="center" gap="$2" flex={1} minWidth={0} flexWrap="wrap">
<Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}> <Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}>
{title} {title}
</Text> </Text>
{isAllComplete ? ( {isAllComplete ? (
<CheckCircle2 size={14} color={theme.successText} /> <CheckCircle2 size={14} color={theme.successText} />
) : ( ) : (
<XStack flexShrink={0}>
<PillBadge tone="warning">{t('readiness.pending', 'Noch offen')}</PillBadge> <PillBadge tone="warning">{t('readiness.pending', 'Noch offen')}</PillBadge>
</XStack>
)} )}
</XStack> </XStack>
<XStack alignItems="center" gap="$2"> <XStack alignItems="center" justifyContent="flex-end" gap="$2" flexShrink={0}>
<Text fontSize="$xs" color={theme.muted} fontWeight="600"> <Text fontSize="$xs" color={theme.muted} fontWeight="600">
{completedCount}/{steps.length} {completedCount}/{steps.length}
</Text> </Text>
@@ -89,7 +91,7 @@ export function SetupChecklist({
backgroundColor={isNext ? theme.surfaceMuted : 'transparent'} backgroundColor={isNext ? theme.surfaceMuted : 'transparent'}
onPress={() => navigate(adminPath(step.targetPath))} onPress={() => navigate(adminPath(step.targetPath))}
title={ title={
<XStack alignItems="center" gap="$2.5"> <XStack alignItems="flex-start" gap="$2.5" flex={1} minWidth={0}>
{step.isComplete ? ( {step.isComplete ? (
<CheckCircle2 size={18} color={theme.successText} /> <CheckCircle2 size={18} color={theme.successText} />
) : isNext ? ( ) : isNext ? (
@@ -102,6 +104,7 @@ export function SetupChecklist({
fontWeight={isNext ? '700' : '500'} fontWeight={isNext ? '700' : '500'}
color={step.isComplete ? theme.muted : theme.textStrong} color={step.isComplete ? theme.muted : theme.textStrong}
textDecorationLine={step.isComplete ? 'line-through' : 'none'} textDecorationLine={step.isComplete ? 'line-through' : 'none'}
flexShrink={1}
> >
{step.label} {step.label}
</Text> </Text>