Fix demo task readiness and gate event creation
This commit is contained in:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user