upgrade to tamagui v2 and guest pwa overhaul

This commit is contained in:
Codex Agent
2026-02-02 13:01:20 +01:00
parent 2e78f3ab8d
commit 7c6e14ffe2
168 changed files with 47462 additions and 8914 deletions

View File

@@ -89,7 +89,7 @@ function SectionHeader({
const subtitleSize = compact ? '$xs' : '$sm';
const spacing = compact ? '$1' : '$1.5';
return (
<YStack space={spacing}>
<YStack gap={spacing}>
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize={titleSize} fontWeight="800" color={theme.textStrong}>
{title}
@@ -222,7 +222,7 @@ export default function MobileDashboardPage() {
return (
<MobileShell activeTab="home" title={t('mobileDashboard.title', 'Dashboard')}>
<DashboardCard padding="$0">
<YStack padding="$3" space="$2">
<YStack padding="$3" gap="$2">
<SectionHeader
title={t('dashboard:overview.title', 'At a glance')}
showSeparator={false}
@@ -231,7 +231,7 @@ export default function MobileDashboardPage() {
/>
</YStack>
<Separator backgroundColor={theme.border} opacity={0.6} />
<YStack padding="$3" space="$2.5">
<YStack padding="$3" gap="$2.5">
{/* 1. LIFECYCLE HERO */}
<LifecycleHero
event={activeEvent}
@@ -326,7 +326,7 @@ function LifecycleHero({
if (phase === 'live') {
return (
<YStack space="$2">
<YStack gap="$2">
<Header />
<DashboardCard
variant={cardVariant}
@@ -335,10 +335,10 @@ function LifecycleHero({
borderColor="transparent"
style={{ backgroundImage: 'linear-gradient(135deg, #4F46E5 0%, #4338CA 100%)' }}
>
<YStack space={isEmbedded ? '$2.5' : '$3'}>
<YStack gap={isEmbedded ? '$2.5' : '$3'}>
<XStack alignItems="center" justifyContent="space-between">
<YStack space="$1">
<XStack alignItems="center" space="$2">
<YStack gap="$1">
<XStack alignItems="center" gap="$2">
<YStack width={8} height={8} borderRadius={4} backgroundColor="#22C55E" />
<Text color="white" fontWeight="700" fontSize="$xs" textTransform="uppercase" letterSpacing={1}>
{t('dashboard:liveNow.status', 'Happening Now')}
@@ -404,11 +404,11 @@ function LifecycleHero({
if (phase === 'post') {
return (
<YStack space="$2">
<YStack gap="$2">
<Header />
<DashboardCard variant={cardVariant} padding={cardPadding}>
<YStack space={isEmbedded ? '$2.5' : '$3'}>
<XStack alignItems="center" space="$2.5">
<YStack gap={isEmbedded ? '$2.5' : '$3'}>
<XStack alignItems="center" gap="$2.5">
<YStack width={40} height={40} borderRadius={20} backgroundColor={theme.successText} alignItems="center" justifyContent="center">
<CheckCircle2 size={20} color="white" />
</YStack>
@@ -427,7 +427,7 @@ function LifecycleHero({
height={48}
borderRadius={16}
>
<XStack alignItems="center" space="$2">
<XStack alignItems="center" gap="$2">
<Download size={16} color="white" />
<Text fontSize="$sm" fontWeight="800" color="white">
{t('events.recap.downloadAll', 'Download photos')}
@@ -443,7 +443,7 @@ function LifecycleHero({
height={48}
borderRadius={16}
>
<XStack alignItems="center" space="$2">
<XStack alignItems="center" gap="$2">
<Text fontSize="$sm" fontWeight="800" color={theme.textStrong}>
{t('events.recap.openRecap', 'Open recap')}
</Text>
@@ -458,7 +458,7 @@ function LifecycleHero({
// SETUP
return (
<YStack space="$2">
<YStack gap="$2">
<Header />
{showQuickControls ? (
<XStack
@@ -472,7 +472,7 @@ function LifecycleHero({
paddingVertical="$2"
>
<Pressable onPress={() => navigate(adminPath(`/mobile/events/${event.slug}/edit`))}>
<XStack alignItems="center" space="$1.5">
<XStack alignItems="center" gap="$1.5">
<Settings size={16} color={theme.primary} />
<Text fontSize="$sm" fontWeight="700" color={theme.textStrong}>
{t('dashboard:readiness.quickSettings', 'Event settings')}
@@ -480,8 +480,8 @@ function LifecycleHero({
</XStack>
</Pressable>
<XStack alignItems="center" space="$3">
<YStack alignItems="center" space="$1">
<XStack alignItems="center" gap="$3">
<YStack alignItems="center" gap="$1">
<Text fontSize="$xs" color={theme.muted} textTransform="uppercase" letterSpacing={0.8}>
{t('dashboard:readiness.publishToggle', 'Live')}
</Text>
@@ -499,13 +499,13 @@ function LifecycleHero({
</XStack>
) : null}
<DashboardCard variant={cardVariant} padding={cardPadding}>
<YStack space={isEmbedded ? '$2.5' : '$3'}>
<YStack gap={isEmbedded ? '$2.5' : '$3'}>
<XStack alignItems="center" justifyContent="space-between">
<YStack>
<Text fontSize="$xs" color={theme.muted} fontWeight="700" textTransform="uppercase">
{t('dashboard:upcoming.status.planning', 'Countdown')}
</Text>
<Text fontSize="$2xl" fontWeight="900" color={theme.primary}>
<Text fontSize="$xxl" fontWeight="900" color={theme.primary}>
{daysToGo}{' '}
<Text fontSize="$sm" color={theme.muted} fontWeight="500">
{t('management:galleryStatus.daysLabel', 'days')}
@@ -518,7 +518,7 @@ function LifecycleHero({
</XStack>
{showNextStep && nextStep ? (
<YStack space="$2">
<YStack gap="$2">
<Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}>
{t('dashboard:readiness.nextStepTitle', 'Next step')}
</Text>
@@ -531,7 +531,7 @@ function LifecycleHero({
paddingHorizontal="$3"
onPress={() => navigate(adminPath(nextStep.targetPath))}
title={
<XStack alignItems="center" space="$2">
<XStack alignItems="center" gap="$2">
<Circle size={18} color={theme.primary} strokeWidth={2.5} />
<Text fontSize="$sm" fontWeight="700" color={theme.textStrong}>
{nextStep.label}
@@ -546,7 +546,7 @@ function LifecycleHero({
) : undefined
}
iconAfter={
<XStack alignItems="center" space="$1">
<XStack alignItems="center" gap="$1">
<PillBadge tone="success">{nextStep.ctaLabel}</PillBadge>
<ChevronRight size={16} color={theme.muted} />
</XStack>
@@ -665,7 +665,7 @@ function UnifiedToolGrid({ event, navigate, permissions, isMember, isCompleted }
return (
<DashboardCard padding="$0">
<YStack padding="$3.5" space="$2">
<YStack padding="$3.5" gap="$2">
<SectionHeader
title={t('dashboard:quickActions.title', 'Quick actions')}
subtitle={t('dashboard:quickActions.description', 'Jump straight to the most important actions.')}
@@ -674,9 +674,9 @@ function UnifiedToolGrid({ event, navigate, permissions, isMember, isCompleted }
/>
</YStack>
<Separator backgroundColor={theme.border} opacity={0.6} />
<YStack padding="$3.5" space="$3">
<YStack padding="$3.5" gap="$3">
{sections.map((section) => (
<YStack key={section.title} space="$2">
<YStack key={section.title} gap="$2">
<Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}>
{section.title}
</Text>
@@ -692,7 +692,7 @@ function UnifiedToolGrid({ event, navigate, permissions, isMember, isCompleted }
paddingHorizontal="$3"
onPress={() => navigate(adminPath(item.path))}
title={
<XStack alignItems="center" space="$2.5">
<XStack alignItems="center" gap="$2.5">
<XStack
width={32}
height={32}
@@ -729,7 +729,7 @@ function RecentPhotosSection({ photos, navigate, slug }: { photos: TenantPhoto[]
return (
<DashboardCard>
<YStack space="$2">
<YStack gap="$2">
<XStack alignItems="center" justifyContent="space-between">
<Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}>
{t('photos.recentTitle', 'Latest Uploads')}
@@ -749,7 +749,7 @@ function RecentPhotosSection({ photos, navigate, slug }: { photos: TenantPhoto[]
<Separator backgroundColor={theme.border} opacity={0.6} />
<XStack space="$2" overflow="scroll" paddingVertical="$1">
<XStack gap="$2" overflow="scroll" paddingVertical="$1">
{photos.map((photo) => (
<Pressable key={photo.id} onPress={() => navigate(adminPath(`/mobile/events/${slug}/control-room`))}>
<YStack
@@ -762,7 +762,7 @@ function RecentPhotosSection({ photos, navigate, slug }: { photos: TenantPhoto[]
borderColor={theme.border}
>
{photo.thumbnail_url ? (
<Image source={{ uri: photo.thumbnail_url }} width={80} height={80} resizeMode="cover" />
<Image src={photo.thumbnail_url} width={80} height={80} objectFit="cover" />
) : (
<YStack flex={1} alignItems="center" justifyContent="center">
<ImageIcon size={20} color={theme.muted} />
@@ -785,12 +785,12 @@ function AlertsSection({ event, stats, t }: any) {
return (
<DashboardCard>
<YStack space="$2">
<YStack gap="$2">
<Text fontSize="$xs" fontWeight="700" color={theme.muted} textTransform="uppercase" letterSpacing={1}>
{t('management:alertsTitle', 'Alerts')}
</Text>
<Separator backgroundColor={theme.border} opacity={0.6} />
<YStack space="$2">
<YStack gap="$2">
{limitWarnings.map((w: any, idx: number) => {
const isDanger = w.tone === 'danger';
const bg = isDanger ? theme.dangerBg : theme.warningBg;
@@ -807,7 +807,7 @@ function AlertsSection({ event, stats, t }: any) {
borderWidth={1}
borderColor={border}
alignItems="center"
space="$2"
gap="$2"
>
<Icon size={16} color={text} />
<Text fontSize="$sm" color={text} fontWeight="600">
@@ -826,7 +826,7 @@ function EmptyState({ canManage, onCreate }: any) {
const theme = useAdminTheme();
const { t } = useTranslation(['management', 'mobile']);
return (
<YStack flex={1} alignItems="center" justifyContent="center" space="$4">
<YStack flex={1} alignItems="center" justifyContent="center" gap="$4">
<Sparkles size={48} color={theme.primary} />
<Text fontSize="$lg" fontWeight="800" color={theme.textStrong} textAlign="center">
{t('mobile:header.appName', 'Event Admin')}