Implemented a shared mobile shell and navigation aligned to the new architecture, plus refactored the dashboard and

tab flows.

  - Added a dynamic MobileShell with sticky header (notification bell with badge, quick QR when an event is
    active, event switcher for multi-event users) and stabilized bottom tabs (home, tasks, uploads, profile)
    driven by useMobileNav (resources/js/admin/mobile/components/MobileShell.tsx, components/BottomNav.tsx, hooks/
    useMobileNav.ts).
  - Centralized event handling now supports 0/1/many-event states without auto-selecting in multi-tenant mode and
    exposes helper flags/activeSlug for consumers (resources/js/admin/context/EventContext.tsx).
  - Rebuilt the mobile dashboard into explicit states: onboarding/no-event, single-event focus, and multi-event picker
    with featured/secondary actions, KPI strip, and alerts (resources/js/admin/mobile/DashboardPage.tsx).
  - Introduced tab entry points that respect event context and prompt selection when needed (resources/js/admin/
    mobile/TasksTabPage.tsx, UploadsTabPage.tsx). Refreshed tasks/uploads detail screens to use the new shell and sync
    event selection (resources/js/admin/mobile/EventTasksPage.tsx, EventPhotosPage.tsx).
  - Updated mobile routes and existing screens to the new tab keys and header/footer behavior (resources/js/admin/
    router.tsx, mobile/* pages, i18n nav/header strings).
This commit is contained in:
Codex Agent
2025-12-10 16:13:44 +01:00
parent 9930b272ca
commit 73e550ee87
19 changed files with 840 additions and 249 deletions

View File

@@ -5,21 +5,21 @@ import { Image as ImageIcon, RefreshCcw, Search, Filter } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Pressable } from '@tamagui/react-native-web-lite';
import { MobileScaffold } from './components/Scaffold';
import { MobileShell } from './components/MobileShell';
import { MobileCard, PillBadge, CTAButton } from './components/Primitives';
import { BottomNav } from './components/BottomNav';
import { getEventPhotos, updatePhotoVisibility, featurePhoto, unfeaturePhoto, TenantPhoto } from '../api';
import toast from 'react-hot-toast';
import { isAuthError } from '../auth/tokens';
import { getApiErrorMessage } from '../lib/apiError';
import { useMobileNav } from './hooks/useMobileNav';
import { MobileSheet } from './components/Sheet';
import { useEventContext } from '../context/EventContext';
type FilterKey = 'all' | 'featured' | 'hidden';
export default function MobileEventPhotosPage() {
const { slug: slugParam } = useParams<{ slug?: string }>();
const slug = slugParam ?? null;
const { activeEvent, selectEvent } = useEventContext();
const slug = slugParam ?? activeEvent?.slug ?? null;
const navigate = useNavigate();
const { t } = useTranslation('management');
@@ -31,13 +31,17 @@ export default function MobileEventPhotosPage() {
const [busyId, setBusyId] = React.useState<number | null>(null);
const [totalCount, setTotalCount] = React.useState<number>(0);
const [hasMore, setHasMore] = React.useState(false);
const { go } = useMobileNav(slug);
const [search, setSearch] = React.useState('');
const [showFilters, setShowFilters] = React.useState(false);
const [uploaderFilter, setUploaderFilter] = React.useState('');
const [onlyFeatured, setOnlyFeatured] = React.useState(false);
const [onlyHidden, setOnlyHidden] = React.useState(false);
const [lightbox, setLightbox] = React.useState<TenantPhoto | null>(null);
React.useEffect(() => {
if (slugParam && activeEvent?.slug !== slugParam) {
selectEvent(slugParam);
}
}, [slugParam, activeEvent?.slug, selectEvent]);
const load = React.useCallback(async () => {
if (!slug) return;
@@ -117,10 +121,11 @@ export default function MobileEventPhotosPage() {
}
return (
<MobileScaffold
<MobileShell
activeTab="uploads"
title={t('events.photos.title', 'Photo Moderation')}
onBack={() => navigate(-1)}
rightSlot={
headerActions={
<XStack space="$3">
<Pressable onPress={() => setShowFilters(true)}>
<Filter size={18} color="#0f172a" />
@@ -130,9 +135,6 @@ export default function MobileEventPhotosPage() {
</Pressable>
</XStack>
}
footer={
<BottomNav active="events" onNavigate={go} />
}
>
{error ? (
<MobileCard>
@@ -334,6 +336,6 @@ export default function MobileEventPhotosPage() {
/>
</YStack>
</MobileSheet>
</MobileScaffold>
</MobileShell>
);
}