Files
fotospiel-app/resources/js/admin/mobile/welcome/WelcomeEventPage.tsx
Codex Agent cf73f408b2 Navigation now feels more “app‑like” with
stateful tabs and reliable back behavior, and a full onboarding flow is wired in with conditional package selection
  (skips when an active package exists).

  What changed

  - Added per‑tab history + back navigation fallback to make tab switching/Back feel native (resources/js/admin/mobile/
    lib/tabHistory.ts, resources/js/admin/mobile/hooks/useBackNavigation.ts, resources/js/admin/mobile/hooks/
    useMobileNav.ts, resources/js/admin/mobile/components/MobileShell.tsx + updates across mobile pages).
  - Implemented onboarding flow pages + shared shell, and wired new routes/prefetch (resources/js/admin/mobile/welcome/
    WelcomeLandingPage.tsx, resources/js/admin/mobile/welcome/WelcomePackagesPage.tsx, resources/js/admin/mobile/
    welcome/WelcomeSummaryPage.tsx, resources/js/admin/mobile/welcome/WelcomeEventPage.tsx, resources/js/admin/mobile/
    components/OnboardingShell.tsx, resources/js/admin/router.tsx, resources/js/admin/mobile/prefetch.ts).
  - Conditional package step: packages page redirects to event setup if activePackage exists; selection stored locally
    for summary (resources/js/admin/mobile/lib/onboardingSelection.ts, resources/js/admin/mobile/welcome/
    WelcomePackagesPage.tsx).
  - Added a “Start welcome journey” CTA in the empty dashboard state (resources/js/admin/mobile/DashboardPage.tsx).
  - Added translations for onboarding shell + selected package + dashboard CTA (resources/js/admin/i18n/locales/en/
    onboarding.json, resources/js/admin/i18n/locales/de/onboarding.json, resources/js/admin/i18n/locales/en/
    management.json, resources/js/admin/i18n/locales/de/management.json).
  - Tests for new helpers/hooks (resources/js/admin/mobile/lib/tabHistory.test.ts, resources/js/admin/mobile/lib/
    onboardingSelection.test.ts, resources/js/admin/mobile/hooks/useBackNavigation.test.tsx).
2025-12-28 19:51:57 +01:00

129 lines
4.6 KiB
TypeScript

import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { CalendarDays, Sparkles, Users } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { OnboardingShell } from '../components/OnboardingShell';
import { MobileCard, CTAButton } from '../components/Primitives';
import { ADMIN_HOME_PATH, ADMIN_WELCOME_BASE_PATH, ADMIN_WELCOME_PACKAGES_PATH, ADMIN_WELCOME_SUMMARY_PATH, adminPath } from '../../constants';
import { getTenantPackagesOverview } from '../../api';
import { getSelectedPackageId } from '../lib/onboardingSelection';
export default function WelcomeEventPage() {
const navigate = useNavigate();
const { t } = useTranslation('onboarding');
const selectedId = getSelectedPackageId();
const { data: overview } = useQuery({
queryKey: ['mobile', 'onboarding', 'packages-overview'],
queryFn: () => getTenantPackagesOverview({ force: true }),
staleTime: 60_000,
});
const hasActivePackage =
Boolean(overview?.activePackage) || Boolean(overview?.packages?.some((pkg) => pkg.active));
const backTarget = selectedId
? ADMIN_WELCOME_SUMMARY_PATH
: hasActivePackage
? ADMIN_WELCOME_BASE_PATH
: ADMIN_WELCOME_PACKAGES_PATH;
return (
<OnboardingShell
eyebrow={t('eventSetup.layout.eyebrow', 'Step 4')}
title={t('eventSetup.layout.title', 'Prepare your first event')}
subtitle={t(
'eventSetup.layout.subtitle',
'Fill in a few details, invite co-hosts, and open your guest gallery for the big day.',
)}
onBack={() => navigate(backTarget)}
onSkip={() => navigate(ADMIN_HOME_PATH)}
skipLabel={t('layout.jumpToDashboard', 'Jump to dashboard')}
>
<MobileCard space="$3">
<Text fontSize="$sm" fontWeight="800">
{t('eventSetup.step.title', 'Event setup in minutes')}
</Text>
<Text fontSize="$sm" color="#6b7280">
{t(
'eventSetup.step.description',
'We guide you through name, date, mood, and tasks. Afterwards you can moderate photos and support guests live.',
)}
</Text>
<YStack space="$2">
<FeatureRow
icon={Sparkles}
title={t('eventSetup.tiles.story.title', 'Story & mood')}
body={t('eventSetup.tiles.story.copy', 'Pick imagery, colours, and emotion cards for your event.')}
/>
<FeatureRow
icon={Users}
title={t('eventSetup.tiles.team.title', 'Organise your team')}
body={t('eventSetup.tiles.team.copy', 'Invite moderators or photographers and assign roles.')}
/>
<FeatureRow
icon={CalendarDays}
title={t('eventSetup.tiles.launch.title', 'Prepare go-live')}
body={t('eventSetup.tiles.launch.copy', 'Create QR codes, test the gallery, and align the run of show.')}
/>
</YStack>
</MobileCard>
<MobileCard space="$2">
<Text fontSize="$sm" fontWeight="800">
{t('eventSetup.cta.heading', 'Ready for your first event?')}
</Text>
<Text fontSize="$sm" color="#6b7280">
{t(
'eventSetup.cta.description',
"You're switching to the event manager. Assign tasks, invite members, and test the gallery. You can always return to the welcome journey.",
)}
</Text>
<CTAButton label={t('eventSetup.cta.button', 'Create event')} onPress={() => navigate(adminPath('/mobile/events/new'))} />
</MobileCard>
<YStack space="$2">
<CTAButton
label={t('eventSetup.actions.dashboard.button', 'Open dashboard')}
tone="ghost"
onPress={() => navigate(ADMIN_HOME_PATH)}
/>
<CTAButton
label={t('eventSetup.actions.events.button', 'Open event list')}
tone="ghost"
onPress={() => navigate(adminPath('/mobile/events'))}
/>
</YStack>
</OnboardingShell>
);
}
function FeatureRow({
icon: Icon,
title,
body,
}: {
icon: React.ComponentType<{ size?: number; color?: string }>;
title: string;
body: string;
}) {
return (
<XStack alignItems="center" space="$2">
<XStack width={34} height={34} borderRadius={12} backgroundColor="#e0f2fe" alignItems="center" justifyContent="center">
<Icon size={16} color="#0284c7" />
</XStack>
<YStack>
<Text fontSize="$sm" fontWeight="700">
{title}
</Text>
<Text fontSize="$xs" color="#6b7280">
{body}
</Text>
</YStack>
</XStack>
);
}