Update admin PWA events, branding, and packages
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (push) Has been cancelled
tests / ui (push) Has been cancelled

This commit is contained in:
Codex Agent
2026-01-19 11:35:38 +01:00
parent 926bc7d070
commit fbff2afa3e
43 changed files with 6846 additions and 6323 deletions

View File

@@ -10,7 +10,7 @@ import { Image } from '@tamagui/image';
import { isSameDay, isPast, isFuture, parseISO, differenceInDays, startOfDay } from 'date-fns';
import { MobileShell } from './components/MobileShell';
import { adminPath } from '../constants';
import { ADMIN_EVENTS_PATH, adminPath } from '../constants';
import { useEventContext } from '../context/EventContext';
import { getEventStats, EventStats, TenantEvent, getEventPhotos, TenantPhoto } from '../api';
import { formatEventDate, resolveEventDisplayName } from '../lib/events';
@@ -165,10 +165,21 @@ export default function MobileDashboardPage() {
selectEvent(slugParam);
}, [activeEvent?.slug, selectEvent, slugParam]);
const shouldRedirectToSelector = !isLoading && !activeEvent && !slugParam;
React.useEffect(() => {
if (!shouldRedirectToSelector) return;
navigate(ADMIN_EVENTS_PATH, { replace: true });
}, [navigate, shouldRedirectToSelector]);
const [eventSwitcherOpen, setEventSwitcherOpen] = React.useState(false);
// --- RENDER ---
if (shouldRedirectToSelector) {
return null;
}
if (isLoading) {
return (
<MobileShell activeTab="home" title={t('mobileDashboard.title', 'Dashboard')}>
@@ -188,6 +199,7 @@ export default function MobileDashboardPage() {
// Calculate Readiness
const readiness = useEventReadiness(activeEvent, t as any);
const phase = activeEvent ? getEventPhase(activeEvent) : 'setup';
const isCompleted = phase === 'post';
return (
<MobileShell activeTab="home" title={t('mobileDashboard.title', 'Dashboard')}>
@@ -223,6 +235,7 @@ export default function MobileDashboardPage() {
navigate={navigate}
permissions={memberPermissions}
isMember={isMember}
isCompleted={isCompleted}
/>
{/* 5. RECENT PHOTOS */}
@@ -241,6 +254,7 @@ export default function MobileDashboardPage() {
type EventPhase = 'setup' | 'live' | 'post';
function getEventPhase(event: TenantEvent): EventPhase {
if (event.status === 'archived') return 'post';
if (!event.event_date) return 'setup';
const today = startOfDay(new Date());
const eventDate = parseISO(event.event_date);
@@ -318,17 +332,23 @@ function LifecycleHero({ event, stats, locale, navigate, onSwitch, canSwitch, re
</YStack>
<YStack>
<Text fontSize="$md" fontWeight="800" color={theme.textStrong}>
{t('management:status.archived', 'Event Completed')}
{t('events.recap.completedTitle', 'Event completed')}
</Text>
<Text fontSize="$xs" color={theme.muted}>{t('management:recap.galleryOpen', 'Gallery online')}</Text>
<Text fontSize="$xs" color={theme.muted}>{t('events.recap.galleryOpen', 'Gallery online')}</Text>
</YStack>
</XStack>
<ModernButton
label={t('management:recap.downloadAll', 'Download Photos')}
label={t('events.recap.downloadAll', 'Download photos')}
tone="primary"
icon={<Download size={16} color="white" />}
onPress={() => navigate(adminPath(`/mobile/exports`))}
/>
<ModernButton
label={t('events.recap.openRecap', 'Open recap')}
tone="ghost"
icon={<ArrowRight size={16} color={theme.text} />}
onPress={() => navigate(adminPath(`/mobile/events/${event.slug}/recap`))}
/>
</ModernCard>
</YStack>
);
@@ -413,40 +433,46 @@ function PulseStrip({ event, stats }: any) {
);
}
function UnifiedToolGrid({ event, navigate, permissions, isMember }: any) {
function UnifiedToolGrid({ event, navigate, permissions, isMember, isCompleted }: any) {
const theme = useAdminTheme();
const { t } = useTranslation(['management', 'dashboard']);
const slug = event?.slug;
if (!slug) return null;
const experienceItems = [
{ label: t('management:photos.gallery.title', 'Photos'), icon: ImageIcon, path: `/mobile/events/${slug}/control-room`, color: theme.primary },
!isCompleted ? { label: t('management:events.quick.liveShowSettings', 'Slide Show'), icon: Tv, path: `/mobile/events/${slug}/live-show/settings`, color: '#F59E0B' } : null,
!isCompleted ? { label: t('events.tasks.badge', 'Tasks'), icon: ListTodo, path: `/mobile/events/${slug}/tasks`, color: theme.accent } : null,
!isCompleted ? { label: t('management:events.quick.photobooth', 'Photobooth'), icon: Camera, path: `/mobile/events/${slug}/photobooth`, color: '#8B5CF6' } : null,
].filter((item): item is { label: string; icon: any; path: string; color?: string } => Boolean(item));
const operationsItems = [
!isCompleted ? { label: t('management:invites.badge', 'QR Codes'), icon: QrCode, path: `/mobile/events/${slug}/qr`, color: '#10B981' } : null,
{ label: t('management:events.quick.guests', 'Guests'), icon: Users, path: `/mobile/events/${slug}/members`, color: theme.text },
!isCompleted ? { label: t('management:events.quick.guestMessages', 'Messages'), icon: Megaphone, path: `/mobile/events/${slug}/guest-notifications`, color: theme.text } : null,
!isCompleted ? { label: t('events.branding.titleShort', 'Branding'), icon: Layout, path: `/mobile/events/${slug}/branding`, color: theme.text } : null,
].filter((item): item is { label: string; icon: any; path: string; color?: string } => Boolean(item));
const adminItems = [
{ label: t('management:mobileDashboard.shortcutAnalytics', 'Analytics'), icon: TrendingUp, path: `/mobile/events/${slug}/analytics` },
!isCompleted ? { label: t('events.recap.exportTitleShort', 'Exports'), icon: Download, path: `/mobile/exports` } : null,
{ label: t('management:mobileProfile.settings', 'Settings'), icon: Settings, path: `/mobile/events/${slug}/edit` },
].filter((item): item is { label: string; icon: any; path: string; color?: string } => Boolean(item));
const sections = [
{
title: t('management:branding.badge', 'Experience'),
items: [
{ label: t('management:photos.gallery.title', 'Photos'), icon: ImageIcon, path: `/mobile/events/${slug}/control-room`, color: theme.primary },
{ label: t('management:events.quick.liveShowSettings', 'Slide Show'), icon: Tv, path: `/mobile/events/${slug}/live-show/settings`, color: '#F59E0B' },
{ label: t('management:tasks.badge', 'Tasks'), icon: ListTodo, path: `/mobile/events/${slug}/tasks`, color: theme.accent },
{ label: t('management:events.quick.photobooth', 'Photobooth'), icon: Camera, path: `/mobile/events/${slug}/photobooth`, color: '#8B5CF6' },
]
items: experienceItems,
},
{
title: t('management:workspace.hero.badge', 'Operations'),
items: [
{ label: t('management:invites.badge', 'QR Codes'), icon: QrCode, path: `/mobile/events/${slug}/qr`, color: '#10B981' },
{ label: t('management:events.quick.guests', 'Guests'), icon: Users, path: `/mobile/events/${slug}/members`, color: theme.text },
{ label: t('management:events.quick.guestMessages', 'Messages'), icon: Megaphone, path: `/mobile/events/${slug}/guest-notifications`, color: theme.text },
{ label: t('management:branding.titleShort', 'Branding'), icon: Layout, path: `/mobile/events/${slug}/branding`, color: theme.text },
]
items: operationsItems,
},
{
title: t('management:settings.hero.badge', 'Admin'),
items: [
{ label: t('management:mobileDashboard.shortcutAnalytics', 'Analytics'), icon: TrendingUp, path: `/mobile/events/${slug}/analytics` },
{ label: t('management:recap.exportTitle', 'Exports'), icon: Download, path: `/mobile/exports` },
{ label: t('management:mobileProfile.settings', 'Settings'), icon: Settings, path: `/mobile/events/${slug}/edit` },
]
items: adminItems,
}
];
].filter((section) => section.items.length > 0);
return (
<YStack space="$4">