Clarify photo task wording in admin UI
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-20 08:49:34 +01:00
parent 508c8201fa
commit e1221e0466
25 changed files with 393 additions and 367 deletions

View File

@@ -442,7 +442,7 @@ function UnifiedToolGrid({ event, navigate, permissions, isMember, isCompleted }
const experienceItems = [
{ label: t('management:photos.gallery.title', 'Photos'), icon: ImageIcon, path: `/mobile/events/${slug}/control-room`, color: theme.primary },
!isCompleted ? { label: t('management:events.quick.liveShowSettings', 'Slide Show'), icon: Tv, path: `/mobile/events/${slug}/live-show/settings`, color: '#F59E0B' } : null,
!isCompleted ? { label: t('events.tasks.badge', 'Tasks'), icon: ListTodo, path: `/mobile/events/${slug}/tasks`, color: theme.accent } : null,
!isCompleted ? { label: t('events.tasks.badge', 'Photo tasks'), icon: ListTodo, path: `/mobile/events/${slug}/tasks`, color: theme.accent } : null,
!isCompleted ? { label: t('management:events.quick.photobooth', 'Photobooth'), icon: Camera, path: `/mobile/events/${slug}/photobooth`, color: '#8B5CF6' } : null,
].filter((item): item is { label: string; icon: any; path: string; color?: string } => Boolean(item));

View File

@@ -254,7 +254,7 @@ export default function MobileEventAnalyticsPage() {
<XStack alignItems="center" space="$2">
<ListTodo size={18} color={primary} />
<Text fontSize="$md" fontWeight="800" color={textStrong}>
{t('analytics.tasksTitle', 'Popular Tasks')}
{t('analytics.tasksTitle', 'Popular photo tasks')}
</Text>
</XStack>
@@ -286,8 +286,8 @@ export default function MobileEventAnalyticsPage() {
</YStack>
) : (
<EmptyState
message={t('analytics.noTasks', 'No task activity yet')}
actionLabel={t('analytics.emptyActionOpenTasks', 'Open tasks')}
message={t('analytics.noTasks', 'No photo task activity yet')}
actionLabel={t('analytics.emptyActionOpenTasks', 'Open photo tasks')}
onAction={() => slug && navigate(adminPath(`/mobile/events/${slug}/tasks`))}
/>
)}

View File

@@ -523,7 +523,7 @@ export default function MobileEventFormPage() {
<Text fontSize="$xs" color={muted}>{t('eventForm.fields.publish.help', 'Enable if guests should see the event right away. You can change the status later.')}</Text>
</MobileField>
<MobileField label={t('eventForm.fields.tasksMode.label', 'Tasks & challenges')}>
<MobileField label={t('eventForm.fields.tasksMode.label', 'Photo tasks & challenges')}>
<XStack alignItems="center" space="$2">
<Switch
checked={form.tasksEnabled}
@@ -531,7 +531,7 @@ export default function MobileEventFormPage() {
setForm((prev) => ({ ...prev, tasksEnabled: Boolean(checked) }))
}
size="$3"
aria-label={t('eventForm.fields.tasksMode.label', 'Tasks & challenges')}
aria-label={t('eventForm.fields.tasksMode.label', 'Photo tasks & challenges')}
>
<Switch.Thumb />
</Switch>

View File

@@ -408,7 +408,7 @@ export default function MobileEventRecapPage() {
value={formatCount(engagement.summary.uniqueGuests, locale)}
/>
<Stat
label={t('events.recap.engagement.summary.tasks', 'Tasks solved')}
label={t('events.recap.engagement.summary.tasks', 'Photo tasks solved')}
value={formatCount(engagement.summary.tasksSolved, locale)}
/>
<Stat

View File

@@ -121,7 +121,26 @@ export default function MobileEventTasksPage() {
const navigate = useNavigate();
const { t } = useTranslation('management');
const { user } = useAuth();
const { textStrong, muted, subtle, border, primary, danger, surface, surfaceMuted, dangerBg, dangerText, overlay } = useAdminTheme();
const {
textStrong,
muted,
subtle,
border,
primary,
danger,
surface,
surfaceMuted,
dangerBg,
dangerText,
overlay,
shadow,
glassSurface,
glassBorder,
glassShadow,
} = useAdminTheme();
const stickySurface = glassSurface ?? surface;
const stickyBorder = glassBorder ?? border;
const stickyShadow = glassShadow ?? shadow;
const isMember = user?.role === 'member';
const [assignedTasks, setAssignedTasks] = React.useState<TenantTask[]>([]);
const [library, setLibrary] = React.useState<TenantTask[]>([]);
@@ -731,9 +750,16 @@ export default function MobileEventTasksPage() {
</YStack>
</Card>
<MobileCard
<Card
borderRadius={20}
borderWidth={2}
borderColor={stickyBorder}
backgroundColor={stickySurface}
padding="$3"
space="$0"
shadowColor={stickyShadow}
shadowOpacity={0.16}
shadowRadius={16}
shadowOffset={{ width: 0, height: 10 }}
style={{
position: 'sticky',
top: 'calc(env(safe-area-inset-top, 0px) + 76px)',
@@ -773,7 +799,7 @@ export default function MobileEventTasksPage() {
</XStack>
</Pressable>
</XStack>
</MobileCard>
</Card>
</YStack>
) : null}

View File

@@ -167,7 +167,7 @@ export default function MobileLoginPage() {
{t('login.panel_title', 'Fotospiel.App Event Login')}
</Text>
<Text fontSize="$sm" color="rgba(255,255,255,0.7)" textAlign="center">
{t('login.panel_copy', 'Melde dich an, um Events zu planen, Fotos zu moderieren und Aufgaben anzulegen.')}
{t('login.panel_copy', 'Melde dich an, um Events zu planen, Fotos zu moderieren und Fotoaufgaben anzulegen.')}
</Text>
</YStack>
</YStack>

View File

@@ -24,13 +24,13 @@ export default function MobileTasksTabPage() {
if (activeEvent?.slug && !tasksEnabled) {
return (
<MobileShell activeTab="tasks" title={t('events.tasks.title', 'Tasks')}>
<MobileShell activeTab="tasks" title={t('events.tasks.title', 'Photo tasks')}>
<MobileCard alignItems="flex-start" space="$3">
<Text fontSize="$lg" fontWeight="800" color={text}>
{t('events.tasks.disabledTitle', 'Task mode is off for this event')}
{t('events.tasks.disabledTitle', 'Photo task mode is off for this event')}
</Text>
<Text fontSize="$sm" color={muted}>
{t('events.tasks.disabledBody', 'Guests see only the photo feed. Enable tasks in the event settings to show them again.')}
{t('events.tasks.disabledBody', 'Guests see only the photo feed. Enable photo tasks in the event settings to show them again.')}
</Text>
<CTAButton
label={t('events.actions.settings', 'Event settings')}
@@ -43,13 +43,13 @@ export default function MobileTasksTabPage() {
if (!hasEvents) {
return (
<MobileShell activeTab="tasks" title={t('events.tasks.title', 'Tasks')}>
<MobileShell activeTab="tasks" title={t('events.tasks.title', 'Photo tasks')}>
<MobileCard alignItems="flex-start" space="$3">
<Text fontSize="$lg" fontWeight="800" color={text}>
{t('events.tasks.emptyTitle', 'Create an event first')}
</Text>
<Text fontSize="$sm" color={muted}>
{t('events.tasks.emptyBody', 'Start an event to add tasks, challenges, and checklists.')}
{t('events.tasks.emptyBody', 'Start an event to add photo tasks, challenges, and checklists.')}
</Text>
<CTAButton
label={t('events.actions.create', 'Create Event')}
@@ -63,10 +63,10 @@ export default function MobileTasksTabPage() {
const locale = i18n.language?.startsWith('en') ? 'en-GB' : 'de-DE';
return (
<MobileShell activeTab="tasks" title={t('events.tasks.title', 'Tasks')}>
<MobileShell activeTab="tasks" title={t('events.tasks.title', 'Photo tasks')}>
<YStack space="$2">
<Text fontSize="$sm" color={text} fontWeight="700">
{t('events.tasks.pickEvent', 'Pick an event to manage tasks')}
{t('events.tasks.pickEvent', 'Pick an event to manage photo tasks')}
</Text>
{events.map((event) => (
<Pressable

View File

@@ -285,7 +285,7 @@ describe('MobileDashboardPage', () => {
render(<MobileDashboardPage />);
expect(screen.getByText('Aufgaben hinzufügen')).toBeInTheDocument();
expect(screen.getByText('Fotoaufgaben hinzufügen')).toBeInTheDocument();
});
it('does not redirect endcustomer packages without remaining event quota', () => {

View File

@@ -29,7 +29,7 @@ vi.mock('react-i18next', () => ({
'events.list.empty.filteredHint': 'Try a different status or clear your search.',
'events.list.stats.photos': 'Photos',
'events.list.stats.guests': 'Guests',
'events.list.stats.tasks': 'Tasks',
'events.list.stats.tasks': 'Photo tasks',
'events.workspace.fields.status': 'Status',
'events.detail.pickEvent': 'Select event',
'events.detail.dateTbd': 'Date tbd',

View File

@@ -28,7 +28,7 @@ export function BottomNav({ active, onNavigate }: { active: NavKey; onNavigate:
const items: Array<{ key: NavKey; icon: React.ComponentType<{ size?: number; color?: string; strokeWidth?: number }>; label: string }> = [
{ key: 'home', icon: isDeepHome ? LayoutDashboard : Home, label: t('nav.home', 'Home') },
{ key: 'tasks', icon: CheckSquare, label: t('nav.tasks', 'Tasks') },
{ key: 'tasks', icon: CheckSquare, label: t('nav.tasks', 'Photo tasks') },
{ key: 'uploads', icon: ImageIcon, label: t('nav.uploads', 'Uploads') },
{ key: 'profile', icon: User, label: t('nav.profile', 'Profile') },
];
@@ -112,4 +112,4 @@ export function BottomNav({ active, onNavigate }: { active: NavKey; onNavigate:
</XStack>
</YStack>
);
}
}

View File

@@ -63,10 +63,10 @@ export function useEventReadiness(event: TenantEvent | null, t: (key: string, fa
if (tasksEnabled) {
steps.push({
id: 'tasks',
label: t('management:tasks.badge', 'Aufgaben'),
label: t('management:tasks.badge', 'Fotoaufgaben'),
description: 'Sorgt für 3x mehr Interaktion.',
isComplete: hasTasks,
ctaLabel: t('management:tasks.actions.assign', 'Aufgaben hinzufügen'),
ctaLabel: t('management:tasks.actions.assign', 'Fotoaufgaben hinzufügen'),
targetPath: `/mobile/events/${event.slug}/tasks`,
priority: 2
});

View File

@@ -96,7 +96,7 @@ const LIMIT_LABELS: Array<{ key: string; labelKey: string; fallback: string }> =
{
key: 'max_tasks',
labelKey: 'packageLimits.max_tasks',
fallback: 'Tasks',
fallback: 'Photo tasks',
},
{
key: 'gallery_days',