Files
fotospiel-app/resources/js/admin/mobile/welcome/WelcomeLandingPage.tsx
2026-01-06 11:57:30 +01:00

145 lines
5.1 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, 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>
);
}