Files
fotospiel-app/resources/js/admin/mobile/welcome/WelcomeLandingPage.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

131 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { Image as ImageIcon, Sparkles, Users } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { MobileCard, CTAButton, PillBadge } from '../components/Primitives';
import { OnboardingShell } from '../components/OnboardingShell';
import { getTenantPackagesOverview } from '../../api';
import { useEventContext } from '../../context/EventContext';
import {
ADMIN_HOME_PATH,
ADMIN_WELCOME_EVENT_PATH,
ADMIN_WELCOME_PACKAGES_PATH,
adminPath,
} from '../../constants';
export default function WelcomeLandingPage() {
const navigate = useNavigate();
const { t } = useTranslation('onboarding');
const { hasEvents } = useEventContext();
const { data: packagesData } = useQuery({
queryKey: ['mobile', 'onboarding', 'packages-overview'],
queryFn: () => getTenantPackagesOverview({ force: true }),
staleTime: 60_000,
});
const hasActivePackage =
Boolean(packagesData?.activePackage) || Boolean(packagesData?.packages?.some((pkg) => pkg.active));
return (
<OnboardingShell
eyebrow={t('layout.eyebrow', 'Fotospiel Customer Admin')}
title={t('layout.title', 'Welcome to your event studio')}
subtitle={t(
'layout.subtitle',
'Begin with an inspired introduction, secure your package, and craft the perfect guest gallery all optimised for mobile hosts.',
)}
onSkip={() => navigate(ADMIN_HOME_PATH)}
skipLabel={t('layout.jumpToDashboard', 'Jump to dashboard')}
>
<MobileCard space="$3">
<PillBadge tone="muted">{t('hero.eyebrow', 'Your event, your stage')}</PillBadge>
<Text fontSize="$lg" fontWeight="900">
{t('hero.title', 'Design the next Fotospiel experience')}
</Text>
<Text fontSize="$sm" color="#6b7280">
{t(
'hero.description',
'In just a few steps you guide guests through a magical photo journey complete with storytelling, tasks, and a moderated gallery.',
)}
</Text>
<XStack space="$2" flexWrap="wrap">
<CTAButton
label={
hasActivePackage
? t('ctaList.createEvent.button', 'Go to event manager')
: t('hero.primary.button', 'Explore packages')
}
onPress={() => navigate(hasActivePackage ? ADMIN_WELCOME_EVENT_PATH : ADMIN_WELCOME_PACKAGES_PATH)}
fullWidth={false}
/>
{hasEvents ? (
<CTAButton
label={t('hero.secondary.button', 'View existing events')}
tone="ghost"
onPress={() => navigate(adminPath('/mobile/events'))}
fullWidth={false}
/>
) : null}
</XStack>
</MobileCard>
<YStack space="$3">
<FeatureCard
icon={ImageIcon}
title={t('highlights.gallery.title', 'Premium guest gallery')}
body={t('highlights.gallery.description', 'Curate photos in real time, highlight favourites, and share QR codes in a tap.')}
badge={t('highlights.gallery.badge', 'New')}
/>
<FeatureCard
icon={Users}
title={t('highlights.team.title', 'Flexible team onboarding')}
body={t('highlights.team.description', 'Invite co-hosts, assign roles, and stay on top of moderation and tasks.')}
/>
<FeatureCard
icon={Sparkles}
title={t('highlights.story.title', 'Storytelling in chapters')}
body={t(
'highlights.story.description',
'Guided tasks and emotion cards turn every event into a memorable journey.',
)}
/>
</YStack>
</OnboardingShell>
);
}
function FeatureCard({
icon: Icon,
title,
body,
badge,
}: {
icon: React.ComponentType<{ size?: number; color?: string }>;
title: string;
body: string;
badge?: string;
}) {
return (
<MobileCard space="$2">
<XStack alignItems="center" justifyContent="space-between">
<XStack alignItems="center" space="$2">
<XStack width={36} height={36} borderRadius={12} backgroundColor="#e0f2fe" alignItems="center" justifyContent="center">
<Icon size={18} color="#0284c7" />
</XStack>
<Text fontSize="$sm" fontWeight="800">
{title}
</Text>
</XStack>
{badge ? <PillBadge tone="muted">{badge}</PillBadge> : null}
</XStack>
<Text fontSize="$sm" color="#6b7280">
{body}
</Text>
</MobileCard>
);
}