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:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user