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

@@ -1,5 +1,6 @@
import React from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { ChevronLeft, Bell, QrCode, ChevronsUpDown, Search } from 'lucide-react';
import { YStack, XStack, SizableText as Text, Image } from 'tamagui';
import { Pressable } from '@tamagui/react-native-web-lite';
@@ -12,7 +13,7 @@ import { MobileCard, CTAButton } from './Primitives';
import { useNotificationsBadge } from '../hooks/useNotificationsBadge';
import { useOnlineStatus } from '../hooks/useOnlineStatus';
import { resolveEventDisplayName } from '../../lib/events';
import { TenantEvent, getEvents } from '../../api';
import { TenantEvent, getEvents, getTenantPackagesOverview, type TenantPackageSummary } from '../../api';
import { setTabHistory } from '../lib/tabHistory';
import { loadPhotoQueue } from '../lib/photoModerationQueue';
import { countQueuedPhotoActions } from '../lib/queueStatus';
@@ -31,6 +32,59 @@ type MobileShellProps = {
headerActions?: React.ReactNode;
};
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;
}
export function MobileShell({ title, subtitle, children, activeTab, onBack, headerActions }: MobileShellProps) {
const { events, activeEvent, selectEvent } = useEventContext();
const { user } = useAuth();
@@ -40,6 +94,7 @@ export function MobileShell({ title, subtitle, children, activeTab, onBack, head
const { t } = useTranslation('mobile');
const { count: notificationCount } = useNotificationsBadge();
const online = useOnlineStatus();
const isSuperAdmin = user?.role === 'super_admin' || user?.role === 'superadmin';
useDocumentTitle(title);
@@ -144,6 +199,29 @@ export function MobileShell({ title, subtitle, children, activeTab, onBack, head
};
const showQr = Boolean(effectiveActive?.slug) && allowPermission('join-tokens:manage');
const { data: packagesOverview, isLoading: packagesLoading } = useQuery({
queryKey: ['mobile', 'header', 'packages-overview'],
enabled: !isMember && !isSuperAdmin,
queryFn: () => getTenantPackagesOverview({ force: true }),
});
const canCreateEvent = React.useMemo(() => {
if (isMember) {
return false;
}
if (isSuperAdmin) {
return true;
}
if (packagesLoading) {
return false;
}
const activePackages = collectActivePackages(packagesOverview ?? null);
return activePackages.some((pkg) => resellerHasRemainingEvents(pkg));
}, [isMember, isSuperAdmin, packagesLoading, packagesOverview]);
// --- CONTEXT PILL ---
const EventContextPill = () => {
if (!effectiveActive || isEventsIndex || isCompactHeader) {
@@ -387,10 +465,11 @@ export function MobileShell({ title, subtitle, children, activeTab, onBack, head
<BottomNav active={activeTab} onNavigate={go} />
<EventSwitcherSheet
open={switcherOpen}
onClose={() => setSwitcherOpen(false)}
events={effectiveEvents}
activeSlug={effectiveActive?.slug ?? null}
open={switcherOpen}
onClose={() => setSwitcherOpen(false)}
events={effectiveEvents}
activeSlug={effectiveActive?.slug ?? null}
canCreateEvent={canCreateEvent}
/>
<UserMenuSheet
open={userMenuOpen}