145 lines
5.1 KiB
TypeScript
145 lines
5.1 KiB
TypeScript
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, trackOnboarding } from '../../api';
|
||
import { useEventContext } from '../../context/EventContext';
|
||
import {
|
||
ADMIN_HOME_PATH,
|
||
ADMIN_WELCOME_EVENT_PATH,
|
||
ADMIN_WELCOME_PACKAGES_PATH,
|
||
adminPath,
|
||
} from '../../constants';
|
||
import { ADMIN_COLORS } from '../theme';
|
||
|
||
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));
|
||
const remainingEvents = packagesData?.activePackage?.remaining_events ?? null;
|
||
const shouldGoBilling = !hasActivePackage || (remainingEvents !== null && remainingEvents <= 0);
|
||
const handleSkip = React.useCallback(() => {
|
||
void trackOnboarding('dismissed');
|
||
navigate(ADMIN_HOME_PATH);
|
||
}, [navigate]);
|
||
|
||
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={handleSkip}
|
||
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={ADMIN_COLORS.textMuted}>
|
||
{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={
|
||
shouldGoBilling
|
||
? t('hero.primary.billing', 'Open billing')
|
||
: t('ctaList.createEvent.button', 'Go to event manager')
|
||
}
|
||
onPress={() => navigate(shouldGoBilling ? adminPath('/mobile/billing#packages') : ADMIN_WELCOME_EVENT_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={ADMIN_COLORS.accentSoft}
|
||
alignItems="center"
|
||
justifyContent="center"
|
||
>
|
||
<Icon size={18} color={ADMIN_COLORS.primary} />
|
||
</XStack>
|
||
<Text fontSize="$sm" fontWeight="800">
|
||
{title}
|
||
</Text>
|
||
</XStack>
|
||
{badge ? <PillBadge tone="muted">{badge}</PillBadge> : null}
|
||
</XStack>
|
||
<Text fontSize="$sm" color={ADMIN_COLORS.textMuted}>
|
||
{body}
|
||
</Text>
|
||
</MobileCard>
|
||
);
|
||
}
|