Fix demo task readiness and gate event creation
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-02-05 11:26:07 +01:00
parent 7262617897
commit 04c399aeb6
14 changed files with 318 additions and 36 deletions

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { AlertCircle, Bell, CalendarDays, Camera, CheckCircle2, ChevronRight, Circle, Download, Heart, Image as ImageIcon, Layout, ListTodo, Megaphone, QrCode, Settings, ShieldCheck, Sparkles, TrendingUp, Tv, Users } from 'lucide-react';
import { AlertCircle, Bell, CalendarDays, Camera, CheckCircle2, ChevronRight, Circle, Download, Heart, Image as ImageIcon, Layout, ListTodo, Megaphone, Plus, QrCode, Settings, ShieldCheck, Sparkles, TrendingUp, Tv, Users } from 'lucide-react';
import { Button } from '@tamagui/button';
import { Card } from '@tamagui/card';
import { YGroup } from '@tamagui/group';
@@ -19,7 +19,7 @@ import toast from 'react-hot-toast';
import { MobileShell } from './components/MobileShell';
import { ADMIN_EVENTS_PATH, adminPath } from '../constants';
import { useEventContext } from '../context/EventContext';
import { getEventStats, EventStats, TenantEvent, getEventPhotos, TenantPhoto, updateEvent, getLiveShowQueue } from '../api';
import { getEventStats, EventStats, TenantEvent, getEventPhotos, TenantPhoto, updateEvent, getLiveShowQueue, getTenantPackagesOverview, type TenantPackageSummary } from '../api';
import { formatEventDate } from '../lib/events';
import { useAuth } from '../auth/context';
import { ADMIN_ACTION_COLORS, useAdminTheme } from './theme';
@@ -37,6 +37,59 @@ function translateLimits(t: any) {
return (key: string, options?: any) => t(`management:limits.${key}`, key, options);
}
function collectActivePackages(
overview: { packages: TenantPackageSummary[]; activePackage: TenantPackageSummary | null } | null
): TenantPackageSummary[] {
if (!overview) {
return [];
}
const packages = overview.packages ?? [];
const activePackage = overview.activePackage;
const activeList = packages.filter((pkg) => pkg.active);
if (activePackage && !activeList.some((pkg) => pkg.id === activePackage.id) && activePackage.active) {
return [activePackage, ...activeList];
}
return activeList;
}
function toNumber(value: unknown): number | null {
if (typeof value === 'number' && Number.isFinite(value)) {
return value;
}
if (typeof value === 'string' && value.trim() !== '') {
const parsed = Number(value);
if (Number.isFinite(parsed)) {
return parsed;
}
}
return null;
}
function resellerHasRemainingEvents(pkg: TenantPackageSummary): boolean {
if (pkg.package_type !== 'reseller') {
return false;
}
const remaining = toNumber(pkg.remaining_events);
if (remaining !== null) {
return remaining > 0;
}
const limits = (pkg.package_limits ?? {}) as Record<string, unknown>;
const limitMaxEvents = toNumber(limits.max_events_per_year);
if (limitMaxEvents === null) {
return false;
}
const usedEvents = toNumber(pkg.used_events) ?? 0;
return limitMaxEvents > usedEvents;
}
// --- TAMAGUI-ALIGNED PRIMITIVES ---
function DashboardCard({
@@ -129,6 +182,7 @@ export default function MobileDashboardPage() {
const { user } = useAuth();
const theme = useAdminTheme();
const isMember = user?.role === 'member';
const isSuperAdmin = user?.role === 'super_admin' || user?.role === 'superadmin';
// --- LOGIC ---
const memberPermissions = React.useMemo(() => {
@@ -178,6 +232,29 @@ export default function MobileDashboardPage() {
},
});
const { data: packagesOverview, isLoading: packagesLoading } = useQuery({
queryKey: ['mobile', 'dashboard', 'packages-overview'],
enabled: !isMember && !isSuperAdmin,
queryFn: () => getTenantPackagesOverview({ force: true }),
});
const canCreateEvent = React.useMemo(() => {
if (!canManageEvents) {
return false;
}
if (isSuperAdmin) {
return true;
}
if (isMember || packagesLoading) {
return false;
}
const activePackages = collectActivePackages(packagesOverview ?? null);
return activePackages.some((pkg) => resellerHasRemainingEvents(pkg));
}, [canManageEvents, isSuperAdmin, isMember, packagesLoading, packagesOverview]);
const locale = i18n.language?.startsWith('en') ? 'en-GB' : 'de-DE';
React.useEffect(() => {
@@ -265,6 +342,7 @@ export default function MobileDashboardPage() {
permissions={memberPermissions}
isMember={isMember}
isCompleted={isCompleted}
canCreateEvent={canCreateEvent}
/>
{/* 5. RECENT PHOTOS */}
@@ -627,7 +705,7 @@ function PulseStrip({ event, stats, liveShowApprovedCount }: any) {
return <KpiStrip items={items} />;
}
function UnifiedToolGrid({ event, navigate, permissions, isMember, isCompleted }: any) {
function UnifiedToolGrid({ event, navigate, permissions, isMember, isCompleted, canCreateEvent }: any) {
const theme = useAdminTheme();
const { t } = useTranslation(['management', 'dashboard']);
const slug = event?.slug;
@@ -649,6 +727,7 @@ function UnifiedToolGrid({ event, navigate, permissions, isMember, isCompleted }
].filter(Boolean) as ToolItem[];
const adminItems: ToolItem[] = [
canCreateEvent ? { label: t('management:events.list.actions.create', 'Create Event'), icon: Plus, path: `/mobile/events/new`, color: theme.primary } : null,
{ label: t('management:mobileDashboard.shortcutAnalytics', 'Analytics'), icon: TrendingUp, path: `/mobile/events/${slug}/analytics`, color: ADMIN_ACTION_COLORS.analytics },
!isCompleted ? { label: t('events.recap.exportTitleShort', 'Exports'), icon: Download, path: `/mobile/exports`, color: ADMIN_ACTION_COLORS.recap } : null,
{ label: t('management:mobileProfile.settings', 'Settings'), icon: Settings, path: `/mobile/events/${slug}/edit`, color: ADMIN_ACTION_COLORS.settings },