further improvements for the mobile admin
This commit is contained in:
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { CalendarDays, Image as ImageIcon, ListTodo, QrCode, Settings, Users, Sparkles } from 'lucide-react';
|
||||
import { CheckCircle2, Image as ImageIcon, ListTodo, MessageCircle, QrCode, Settings, Users, Sparkles } from 'lucide-react';
|
||||
import { YStack, XStack } from '@tamagui/stacks';
|
||||
import { SizableText as Text } from '@tamagui/text';
|
||||
import { Pressable } from '@tamagui/react-native-web-lite';
|
||||
@@ -133,35 +133,178 @@ function OnboardingEmptyState() {
|
||||
const theme = useTheme();
|
||||
const text = String(theme.color12?.val ?? theme.color?.val ?? '#f8fafc');
|
||||
const muted = String(theme.gray11?.val ?? theme.gray?.val ?? '#cbd5e1');
|
||||
const border = String(theme.borderColor?.val ?? '#cbd5e1');
|
||||
const accent = String(theme.primary?.val ?? '#2563eb');
|
||||
const accentSoft = String(theme.blue3?.val ?? '#e0f2fe');
|
||||
const accentStrong = String(theme.blue10?.val ?? '#1d4ed8');
|
||||
const stepBg = String(theme.gray2?.val ?? '#f8fafc');
|
||||
const stepBorder = String(theme.gray5?.val ?? '#e2e8f0');
|
||||
const supportBg = String(theme.gray2?.val ?? '#f8fafc');
|
||||
const supportBorder = String(theme.gray5?.val ?? '#e2e8f0');
|
||||
|
||||
const steps = [
|
||||
t('mobileDashboard.emptyStepDetails', 'Add name & date'),
|
||||
t('mobileDashboard.emptyStepQr', 'Share your QR poster'),
|
||||
t('mobileDashboard.emptyStepReview', 'Review first uploads'),
|
||||
];
|
||||
|
||||
const previews = [
|
||||
{
|
||||
icon: QrCode,
|
||||
title: t('mobileDashboard.emptyPreviewQr', 'Share QR poster'),
|
||||
desc: t('mobileDashboard.emptyPreviewQrDesc', 'Print-ready codes for guests and crew.'),
|
||||
},
|
||||
{
|
||||
icon: ImageIcon,
|
||||
title: t('mobileDashboard.emptyPreviewGallery', 'Gallery & highlights'),
|
||||
desc: t('mobileDashboard.emptyPreviewGalleryDesc', 'Moderate uploads, feature the best moments.'),
|
||||
},
|
||||
{
|
||||
icon: ListTodo,
|
||||
title: t('mobileDashboard.emptyPreviewTasks', 'Tasks & challenges'),
|
||||
desc: t('mobileDashboard.emptyPreviewTasksDesc', 'Guide guests with playful prompts.'),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<YStack space="$3">
|
||||
<MobileCard alignItems="flex-start" space="$3">
|
||||
<Text fontSize="$lg" fontWeight="800" color={text}>
|
||||
{t('mobileDashboard.emptyTitle', 'Create your first event')}
|
||||
</Text>
|
||||
<Text fontSize="$sm" color={muted}>
|
||||
{t('mobileDashboard.emptyBody', 'Start an event to manage tasks, QR posters and uploads.')}
|
||||
</Text>
|
||||
<CTAButton label={t('mobileDashboard.ctaCreate', 'Create event')} onPress={() => navigate(adminPath('/mobile/events/new'))} />
|
||||
<CTAButton label={t('mobileDashboard.ctaDemo', 'View demo')} tone="ghost" onPress={() => navigate(adminPath('/mobile/events'))} />
|
||||
<MobileCard
|
||||
padding="$4"
|
||||
borderColor="transparent"
|
||||
overflow="hidden"
|
||||
backgroundColor="linear-gradient(140deg, rgba(14,165,233,0.16), rgba(79,70,229,0.22))"
|
||||
>
|
||||
<YStack position="absolute" top={-10} right={-10} opacity={0.16} scale={1.2}>
|
||||
<Sparkles size={72} color={accentStrong} />
|
||||
</YStack>
|
||||
<YStack position="absolute" bottom={-14} left={-8} opacity={0.14}>
|
||||
<QrCode size={96} color={accentStrong} />
|
||||
</YStack>
|
||||
<YStack space="$2" zIndex={1}>
|
||||
<PillBadge tone="muted">{t('mobileDashboard.emptyBadge', 'Welcome aboard')}</PillBadge>
|
||||
<Text fontSize="$xl" fontWeight="900" color={text}>
|
||||
{t('mobileDashboard.emptyTitle', "Welcome! Let's launch your first event")}
|
||||
</Text>
|
||||
<Text fontSize="$sm" color={text} opacity={0.9}>
|
||||
{t('mobileDashboard.emptyBody', 'Print a QR, collect uploads, and start moderating in minutes.')}
|
||||
</Text>
|
||||
<CTAButton label={t('mobileDashboard.ctaCreate', 'Create event')} onPress={() => navigate(adminPath('/mobile/events/new'))} />
|
||||
</YStack>
|
||||
</MobileCard>
|
||||
<MobileCard space="$2">
|
||||
<Text fontSize="$sm" fontWeight="800" color={text}>
|
||||
{t('mobileDashboard.highlightsTitle', 'What you can do')}
|
||||
</Text>
|
||||
<YStack space="$1.5">
|
||||
{[
|
||||
t('mobileDashboard.highlightImages', 'Review photos & uploads'),
|
||||
t('mobileDashboard.highlightTasks', 'Assign tasks & challenges'),
|
||||
t('mobileDashboard.highlightQr', 'Share QR posters'),
|
||||
t('mobileDashboard.highlightGuests', 'Invite helpers & guests'),
|
||||
].map((item) => (
|
||||
<XStack key={item} alignItems="center" space="$2">
|
||||
<PillBadge tone="muted">{item}</PillBadge>
|
||||
|
||||
<MobileCard space="$2.5" borderColor={border} backgroundColor={stepBg}>
|
||||
<XStack alignItems="center" justifyContent="space-between">
|
||||
<Text fontSize="$sm" fontWeight="800" color={text}>
|
||||
{t('mobileDashboard.emptyChecklistTitle', 'Quick steps to go live')}
|
||||
</Text>
|
||||
<PillBadge tone="muted">
|
||||
{t('mobileDashboard.emptyChecklistProgress', '{{done}}/{{total}} steps', { done: 0, total: steps.length })}
|
||||
</PillBadge>
|
||||
</XStack>
|
||||
<YStack space="$2">
|
||||
{steps.map((label) => (
|
||||
<XStack
|
||||
key={label}
|
||||
alignItems="center"
|
||||
space="$2"
|
||||
padding="$2"
|
||||
borderRadius={12}
|
||||
backgroundColor="rgba(255,255,255,0.5)"
|
||||
borderWidth={1}
|
||||
borderColor={stepBorder}
|
||||
>
|
||||
<XStack
|
||||
width={34}
|
||||
height={34}
|
||||
borderRadius={12}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
backgroundColor={accentSoft}
|
||||
borderWidth={1}
|
||||
borderColor={`${accentStrong}33`}
|
||||
>
|
||||
<CheckCircle2 size={18} color={accent} />
|
||||
</XStack>
|
||||
<Text fontSize="$sm" color={text} flex={1}>
|
||||
{label}
|
||||
</Text>
|
||||
</XStack>
|
||||
))}
|
||||
</YStack>
|
||||
</MobileCard>
|
||||
|
||||
<MobileCard space="$2" borderColor={border}>
|
||||
<Text fontSize="$sm" fontWeight="800" color={text}>
|
||||
{t('mobileDashboard.emptyPreviewTitle', "Here's what awaits")}
|
||||
</Text>
|
||||
<XStack space="$2" flexWrap="wrap">
|
||||
{previews.map(({ icon: Icon, title, desc }) => (
|
||||
<YStack
|
||||
key={title}
|
||||
width="48%"
|
||||
minWidth={160}
|
||||
space="$1.5"
|
||||
padding="$3"
|
||||
borderRadius={14}
|
||||
borderWidth={1}
|
||||
borderColor={`${border}aa`}
|
||||
backgroundColor="rgba(255,255,255,0.6)"
|
||||
shadowColor="#0f172a"
|
||||
shadowOpacity={0.04}
|
||||
shadowRadius={10}
|
||||
shadowOffset={{ width: 0, height: 6 }}
|
||||
>
|
||||
<XStack
|
||||
width={36}
|
||||
height={36}
|
||||
borderRadius={12}
|
||||
backgroundColor={accentSoft}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<Icon size={18} color={accent} />
|
||||
</XStack>
|
||||
<Text fontSize="$sm" fontWeight="700" color={text}>
|
||||
{title}
|
||||
</Text>
|
||||
<Text fontSize="$xs" color={muted}>
|
||||
{desc}
|
||||
</Text>
|
||||
</YStack>
|
||||
))}
|
||||
</XStack>
|
||||
</MobileCard>
|
||||
|
||||
<MobileCard space="$2" backgroundColor={supportBg} borderColor={supportBorder}>
|
||||
<XStack alignItems="center" space="$2">
|
||||
<XStack
|
||||
width={36}
|
||||
height={36}
|
||||
borderRadius={12}
|
||||
backgroundColor={accentSoft}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<MessageCircle size={18} color={accent} />
|
||||
</XStack>
|
||||
<YStack space="$0.5">
|
||||
<Text fontSize="$sm" fontWeight="800" color={text}>
|
||||
{t('mobileDashboard.emptySupportTitle', 'Need help?')}
|
||||
</Text>
|
||||
<Text fontSize="$xs" color={muted}>
|
||||
{t('mobileDashboard.emptySupportBody', 'We are here if you need a hand getting started.')}
|
||||
</Text>
|
||||
</YStack>
|
||||
</XStack>
|
||||
<XStack space="$3">
|
||||
<Text fontSize="$xs" color={accent} textDecorationLine="underline">
|
||||
{t('mobileDashboard.emptySupportDocs', 'Docs: Getting started')}
|
||||
</Text>
|
||||
<Text fontSize="$xs" color={accent} textDecorationLine="underline">
|
||||
{t('mobileDashboard.emptySupportEmail', 'Email support')}
|
||||
</Text>
|
||||
</XStack>
|
||||
</MobileCard>
|
||||
</YStack>
|
||||
);
|
||||
}
|
||||
@@ -313,6 +456,7 @@ function SecondaryGrid({
|
||||
const muted = String(theme.gray11?.val ?? theme.gray?.val ?? '#cbd5e1');
|
||||
const border = String(theme.borderColor?.val ?? '#334155');
|
||||
const surface = String(theme.surface?.val ?? '#0b1220');
|
||||
const brandingAllowed = Boolean((event?.package as any)?.branding_allowed ?? true);
|
||||
const tiles = [
|
||||
{
|
||||
icon: Users,
|
||||
@@ -338,6 +482,13 @@ function SecondaryGrid({
|
||||
color: '#10b981',
|
||||
action: onSettings,
|
||||
},
|
||||
{
|
||||
icon: Sparkles,
|
||||
label: t('mobileDashboard.shortcutBranding', 'Branding & moderation'),
|
||||
color: '#22d3ee',
|
||||
action: brandingAllowed ? onSettings : undefined,
|
||||
disabled: !brandingAllowed,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -347,7 +498,14 @@ function SecondaryGrid({
|
||||
</Text>
|
||||
<XStack flexWrap="wrap" space="$2">
|
||||
{tiles.map((tile) => (
|
||||
<ActionTile key={tile.label} icon={tile.icon} label={tile.label} color={tile.color} onPress={tile.action} />
|
||||
<ActionTile
|
||||
key={tile.label}
|
||||
icon={tile.icon}
|
||||
label={tile.label}
|
||||
color={tile.color}
|
||||
onPress={tile.action}
|
||||
disabled={tile.disabled}
|
||||
/>
|
||||
))}
|
||||
</XStack>
|
||||
{event ? (
|
||||
|
||||
Reference in New Issue
Block a user