diff --git a/resources/css/app.css b/resources/css/app.css index afb56c1..8b11ec6 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -551,6 +551,12 @@ html.guest-theme.dark { animation: admin-fade-up 220ms cubic-bezier(0.2, 0.7, 0.2, 1) both; } +.admin-sticky-toolbar { + position: sticky; + top: calc(env(safe-area-inset-top, 0px) + 76px); + z-index: 45; +} + @media (prefers-reduced-motion: reduce) { .admin-fade-up { animation: none; diff --git a/resources/js/admin/mobile/EventTasksPage.tsx b/resources/js/admin/mobile/EventTasksPage.tsx index 3a22391..158d7e0 100644 --- a/resources/js/admin/mobile/EventTasksPage.tsx +++ b/resources/js/admin/mobile/EventTasksPage.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { RefreshCcw, Plus, Pencil, Trash2, ChevronDown, ChevronRight, Check } from 'lucide-react'; +import { RefreshCcw, Plus, Pencil, Trash2, ChevronDown, ChevronRight, Check, Info } from 'lucide-react'; import { Card } from '@tamagui/card'; import { YStack, XStack } from '@tamagui/stacks'; import { YGroup } from '@tamagui/group'; @@ -179,6 +179,7 @@ export default function MobileEventTasksPage() { const [emotionForm, setEmotionForm] = React.useState({ name: '', color: String(border) }); const [savingEmotion, setSavingEmotion] = React.useState(false); const [showEmotionFilterSheet, setShowEmotionFilterSheet] = React.useState(false); + const [showTaskDetails, setShowTaskDetails] = React.useState(false); const [quickNavSelection, setQuickNavSelection] = React.useState(''); const [eventRecord, setEventRecord] = React.useState(null); const [tasksToggleBusy, setTasksToggleBusy] = React.useState(false); @@ -645,44 +646,67 @@ export default function MobileEventTasksPage() { ) : null} {!loading ? ( - - - - {t('events.tasks.toggle.title', 'Photo tasks for this event')} - - - {t( - 'events.tasks.toggle.description', - 'Give guests optional photo tasks and prompts.' - )} - - - - - {tasksEnabled - ? t('events.tasks.toggle.active', 'ACTIVE') - : t('events.tasks.toggle.inactive', 'INACTIVE')} - + + + + + + {t('events.tasks.toggle.title', 'Photo tasks for this event')} + + + {tasksEnabled + ? t('events.tasks.toggle.active', 'ACTIVE') + : t('events.tasks.toggle.inactive', 'INACTIVE')} + + + {showTaskDetails ? ( + + {t( + 'events.tasks.toggle.description', + 'Give guests optional photo tasks and prompts.' + )} + + ) : null} + + + setShowTaskDetails((prev) => !prev)} + aria-label={t( + 'events.tasks.toggle.description', + 'Give guests optional photo tasks and prompts.' + )} + > + + + + + + + + + + {showTaskDetails ? ( {tasksEnabled ? t('events.tasks.toggle.onLabel', 'Guests see photo tasks') : t('events.tasks.toggle.offLabel', 'Guest app shows photos only')} - - - - {t('events.tasks.toggle.switchLabel', 'Photo tasks enabled')} - - - - - + ) : null} {isMember && !canManageTasks ? ( {t('events.tasks.toggle.permissionHint', 'You do not have permission to change photo tasks.')} @@ -750,12 +774,7 @@ export default function MobileEventTasksPage() { - + ({ })); vi.mock('@tamagui/react-native-web-lite', () => ({ - Pressable: ({ children, onPress }: { children: React.ReactNode; onPress?: () => void }) => ( - ), @@ -278,6 +286,17 @@ describe('MobileEventTasksPage', () => { ); }); + it('toggles task details in the hero section', async () => { + render(); + + const detailsLabel = 'Give guests optional photo tasks and prompts.'; + expect(await screen.findByText('Photo tasks for this event')).toBeInTheDocument(); + expect(screen.queryByText(detailsLabel)).not.toBeInTheDocument(); + + fireEvent.click(screen.getByLabelText(detailsLabel)); + expect(screen.getByText(detailsLabel)).toBeInTheDocument(); + }); + it('enters selection mode on long press', async () => { render();