Restructure event tasks layout
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-01-22 20:50:38 +01:00
parent db5fea9f2a
commit 32644eb41e
3 changed files with 200 additions and 140 deletions

View File

@@ -584,6 +584,7 @@
"imported": "Fotoaufgabenpaket importiert", "imported": "Fotoaufgabenpaket importiert",
"saveTask": "Fotoaufgabe speichern", "saveTask": "Fotoaufgabe speichern",
"add": "Hinzufügen", "add": "Hinzufügen",
"assignedTitle": "Aufgabenliste",
"emptyHint": "Lege jetzt Fotoaufgaben an oder importiere ein Paket.", "emptyHint": "Lege jetzt Fotoaufgaben an oder importiere ein Paket.",
"emptyTitle": "Noch keine Fotoaufgaben", "emptyTitle": "Noch keine Fotoaufgaben",
"emptyBody": "Lege Fotoaufgaben an oder importiere ein Paket für dein Event.", "emptyBody": "Lege Fotoaufgaben an oder importiere ein Paket für dein Event.",

View File

@@ -580,6 +580,7 @@
"imported": "Photo task pack imported", "imported": "Photo task pack imported",
"saveTask": "Save photo task", "saveTask": "Save photo task",
"add": "Add", "add": "Add",
"assignedTitle": "Task list",
"emptyHint": "Add photo tasks or import a pack.", "emptyHint": "Add photo tasks or import a pack.",
"emptyTitle": "No photo tasks yet", "emptyTitle": "No photo tasks yet",
"emptyBody": "Create photo tasks or import a pack for your event.", "emptyBody": "Create photo tasks or import a pack for your event.",

View File

@@ -701,10 +701,15 @@ export default function MobileEventTasksPage() {
</YStack> </YStack>
) : ( ) : (
<YStack space="$2"> <YStack space="$2">
<XStack alignItems="center" flexWrap="wrap" space="$2"> <XStack alignItems="baseline" justifyContent="space-between" flexWrap="wrap" space="$2">
<Text fontSize="$sm" color={muted}> <XStack alignItems="baseline" space="$2" flexWrap="wrap">
<Text fontSize="$sm" fontWeight="800" color={text}>
{t('events.tasks.assignedTitle', 'Task list')}
</Text>
<Text fontSize="$xs" color={muted}>
{t('events.tasks.count', '{{count}} photo tasks', { count: filteredTasks.length })} {t('events.tasks.count', '{{count}} photo tasks', { count: filteredTasks.length })}
</Text> </Text>
</XStack>
{typeof remainingTasks === 'number' && typeof maxTasks === 'number' ? ( {typeof remainingTasks === 'number' && typeof maxTasks === 'number' ? (
<Tag <Tag
label={t('events.tasks.remainingIndicator', '{{count}} / {{total}} tasks remaining', { label={t('events.tasks.remainingIndicator', '{{count}} / {{total}} tasks remaining', {
@@ -1041,22 +1046,41 @@ export default function MobileEventTasksPage() {
</MobileCard> </MobileCard>
) : null} ) : null}
{!loading ? ( {loading ? (
<MobileCard space="$2" padding="$3"> <YStack space="$2">
<XStack alignItems="center" justifyContent="space-between" space="$2" flexWrap="wrap"> {Array.from({ length: 4 }).map((_, idx) => (
<YStack space="$1" flex={1} minWidth={180}> <SkeletonCard key={`tsk-${idx}`} height={70} />
<XStack alignItems="center" space="$2" flexWrap="wrap"> ))}
</YStack>
) : (
<Card
borderRadius={24}
borderWidth={2}
borderColor={border}
backgroundColor={surface}
padding="$3"
>
<YStack space="$3">
<Card
borderRadius={18}
borderWidth={1}
borderColor={border}
backgroundColor={surfaceMuted}
padding="$3"
>
<YStack space="$2">
<XStack alignItems="center" justifyContent="space-between" space="$2">
<YStack space="$1" flex={1}>
<Text fontSize="$sm" fontWeight="800" color={text}> <Text fontSize="$sm" fontWeight="800" color={text}>
{t('events.tasks.toggle.title', 'Photo task mode')} {t('events.tasks.toggle.title', 'Photo task mode')}
</Text> </Text>
</YStack>
<XStack alignItems="center" space="$2">
<PillBadge tone={tasksEnabled ? 'success' : 'warning'}> <PillBadge tone={tasksEnabled ? 'success' : 'warning'}>
{tasksEnabled {tasksEnabled
? t('events.tasks.toggle.active', 'ACTIVE') ? t('events.tasks.toggle.active', 'ACTIVE')
: t('events.tasks.toggle.inactive', 'INACTIVE')} : t('events.tasks.toggle.inactive', 'INACTIVE')}
</PillBadge> </PillBadge>
</XStack>
</YStack>
<XStack alignItems="center" space="$2">
<Pressable <Pressable
onPress={() => setShowTaskDetails((prev) => !prev)} onPress={() => setShowTaskDetails((prev) => !prev)}
aria-label={t( aria-label={t(
@@ -1065,28 +1089,20 @@ export default function MobileEventTasksPage() {
)} )}
> >
<XStack <XStack
width={32} width={30}
height={32} height={30}
borderRadius={10} borderRadius={10}
alignItems="center" alignItems="center"
justifyContent="center" justifyContent="center"
borderWidth={1} borderWidth={1}
borderColor={showTaskDetails ? withAlpha(primary, 0.45) : border} borderColor={showTaskDetails ? withAlpha(primary, 0.45) : border}
backgroundColor={showTaskDetails ? withAlpha(primary, 0.12) : surfaceMuted} backgroundColor={showTaskDetails ? withAlpha(primary, 0.12) : surface}
> >
<Info size={14} color={showTaskDetails ? primary : muted} /> <Info size={14} color={showTaskDetails ? primary : muted} />
</XStack> </XStack>
</Pressable> </Pressable>
</XStack> </XStack>
</XStack> </XStack>
{showTaskDetails ? (
<YStack space="$2">
<Text fontSize="$xs" color={muted}>
{t(
'events.tasks.toggle.description',
'Control whether guests see mission cards and prompts.'
)}
</Text>
<XStack alignItems="center" justifyContent="space-between" space="$2"> <XStack alignItems="center" justifyContent="space-between" space="$2">
<Text fontSize="$sm" fontWeight="700" color={text}> <Text fontSize="$sm" fontWeight="700" color={text}>
{t('events.tasks.toggle.switchLabel', 'Photo tasks for guests')} {t('events.tasks.toggle.switchLabel', 'Photo tasks for guests')}
@@ -1106,44 +1122,77 @@ export default function MobileEventTasksPage() {
? t('events.tasks.toggle.onLabel', 'Mission cards active') ? t('events.tasks.toggle.onLabel', 'Mission cards active')
: t('events.tasks.toggle.offLabel', 'Photo feed only')} : t('events.tasks.toggle.offLabel', 'Photo feed only')}
</Text> </Text>
</YStack> {showTaskDetails ? (
<Text fontSize="$xs" color={muted}>
{t('events.tasks.toggle.description', 'Control whether guests see mission cards and prompts.')}
</Text>
) : null} ) : null}
{isMember && !canManageTasks ? ( {isMember && !canManageTasks ? (
<Text fontSize="$xs" color={muted}> <Text fontSize="$xs" color={muted}>
{t('events.tasks.toggle.permissionHint', 'You do not have permission to change photo tasks.')} {t('events.tasks.toggle.permissionHint', 'You do not have permission to change photo tasks.')}
</Text> </Text>
) : null} ) : null}
</MobileCard>
) : null}
{loading ? (
<YStack space="$2">
{Array.from({ length: 4 }).map((_, idx) => (
<SkeletonCard key={`tsk-${idx}`} height={70} />
))}
</YStack> </YStack>
) : ( </Card>
<Tabs value={activeTab} onValueChange={(value) => setActiveTab(value as TaskSectionKey)}>
<Tabs.List> <Tabs
<Tabs.Tab value="assigned">{t('events.tasks.tabs.tasks', 'Tasks')}</Tabs.Tab> value={activeTab}
<Tabs.Tab value="library">{t('events.tasks.tabs.library', 'Task Library')}</Tabs.Tab> onValueChange={(value) => setActiveTab(value as TaskSectionKey)}
<Tabs.Tab value="emotions">{t('events.tasks.tabs.emotions', 'Emotions')}</Tabs.Tab> flexDirection="column"
<Tabs.Tab value="collections">{t('events.tasks.tabs.collections', 'Collections')}</Tabs.Tab> alignItems="stretch"
width="100%"
>
<Tabs.List
borderRadius={16}
borderWidth={1}
borderColor={border}
backgroundColor={surfaceMuted}
overflow="hidden"
gap="$0"
>
{[
{ value: 'assigned', label: t('events.tasks.tabs.tasks', 'Tasks') },
{ value: 'library', label: t('events.tasks.tabs.library', 'Task Library') },
{ value: 'emotions', label: t('events.tasks.tabs.emotions', 'Emotions') },
{ value: 'collections', label: t('events.tasks.tabs.collections', 'Collections') },
].map((tab, index, arr) => {
const isActive = activeTab === tab.value;
return (
<Tabs.Tab
key={tab.value}
value={tab.value}
flex={1}
unstyled
paddingVertical="$2.5"
alignItems="center"
justifyContent="center"
backgroundColor={isActive ? primary : 'transparent'}
borderRightWidth={index === arr.length - 1 ? 0 : 1}
borderRightColor={border}
pressStyle={{ backgroundColor: isActive ? primary : surface }}
>
<Text fontSize="$sm" fontWeight={isActive ? '700' : '500'} color={isActive ? 'white' : text}>
{tab.label}
</Text>
</Tabs.Tab>
);
})}
</Tabs.List> </Tabs.List>
<Tabs.Content value="assigned" paddingTop="$2"> <Tabs.Content value="assigned" paddingTop="$2">
<Card borderRadius={18} borderWidth={1} borderColor={border} backgroundColor={surface} padding="$3">
<YStack space="$2"> <YStack space="$2">
<YStack className="admin-sticky-toolbar"> <YStack className="admin-sticky-toolbar">
<Card <Card
borderRadius={20} borderRadius={16}
borderWidth={2} borderWidth={1}
borderColor={stickyBorder} borderColor={stickyBorder}
backgroundColor={stickySurface} backgroundColor={stickySurface}
padding="$3" padding="$2.5"
shadowColor={stickyShadow} shadowColor={stickyShadow}
shadowOpacity={0.16} shadowOpacity={0.12}
shadowRadius={16} shadowRadius={12}
shadowOffset={{ width: 0, height: 10 }} shadowOffset={{ width: 0, height: 8 }}
> >
<XStack alignItems="center" space="$2"> <XStack alignItems="center" space="$2">
<XStack flex={1}> <XStack flex={1}>
@@ -1182,20 +1231,29 @@ export default function MobileEventTasksPage() {
</YStack> </YStack>
{taskPanel} {taskPanel}
</YStack> </YStack>
</Card>
</Tabs.Content> </Tabs.Content>
<Tabs.Content value="library" paddingTop="$2"> <Tabs.Content value="library" paddingTop="$2">
<Card borderRadius={18} borderWidth={1} borderColor={border} backgroundColor={surface} padding="$3">
{libraryPanel} {libraryPanel}
</Card>
</Tabs.Content> </Tabs.Content>
<Tabs.Content value="emotions" paddingTop="$2"> <Tabs.Content value="emotions" paddingTop="$2">
<Card borderRadius={18} borderWidth={1} borderColor={border} backgroundColor={surface} padding="$3">
{emotionsPanel} {emotionsPanel}
</Card>
</Tabs.Content> </Tabs.Content>
<Tabs.Content value="collections" paddingTop="$2"> <Tabs.Content value="collections" paddingTop="$2">
<Card borderRadius={18} borderWidth={1} borderColor={border} backgroundColor={surface} padding="$3">
{collectionsPanel} {collectionsPanel}
</Card>
</Tabs.Content> </Tabs.Content>
</Tabs> </Tabs>
</YStack>
</Card>
)} )}
<MobileSheet <MobileSheet