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:
@@ -11,6 +11,9 @@ export interface EventContextValue {
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
activeEvent: TenantEvent | null;
|
||||
activeSlug: string | null;
|
||||
hasEvents: boolean;
|
||||
hasMultipleEvents: boolean;
|
||||
selectEvent: (slug: string | null) => void;
|
||||
refetch: () => void;
|
||||
}
|
||||
@@ -58,11 +61,16 @@ export function EventProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const hasStored = Boolean(storedSlug);
|
||||
const slugExists = hasStored && events.some((event) => event.slug === storedSlug);
|
||||
const fallbackSlug = events[0]?.slug;
|
||||
|
||||
if (!slugExists && fallbackSlug) {
|
||||
if (!slugExists) {
|
||||
const shouldAutoselect = events.length === 1;
|
||||
const fallbackSlug = shouldAutoselect ? events[0]?.slug : null;
|
||||
setStoredSlug(fallbackSlug);
|
||||
window.localStorage.setItem(STORAGE_KEY, fallbackSlug);
|
||||
if (fallbackSlug) {
|
||||
window.localStorage.setItem(STORAGE_KEY, fallbackSlug);
|
||||
} else {
|
||||
window.localStorage.removeItem(STORAGE_KEY);
|
||||
}
|
||||
}
|
||||
}, [events, storedSlug]);
|
||||
|
||||
@@ -76,10 +84,20 @@ export function EventProvider({ children }: { children: React.ReactNode }) {
|
||||
return matched;
|
||||
}
|
||||
|
||||
// Fallback to the first event if the stored slug is missing or stale.
|
||||
return events[0];
|
||||
// Only auto-select the single available event. When multiple events exist and
|
||||
// no stored slug is present we intentionally return null to let the UI prompt
|
||||
// for a selection.
|
||||
if (events.length === 1) {
|
||||
return events[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [events, storedSlug]);
|
||||
|
||||
const hasEvents = events.length > 0;
|
||||
const hasMultipleEvents = events.length > 1;
|
||||
const activeSlug = activeEvent?.slug ?? null;
|
||||
|
||||
const selectEvent = React.useCallback((slug: string | null) => {
|
||||
setStoredSlug(slug);
|
||||
if (typeof window !== 'undefined') {
|
||||
@@ -97,10 +115,13 @@ export function EventProvider({ children }: { children: React.ReactNode }) {
|
||||
isLoading,
|
||||
isError,
|
||||
activeEvent,
|
||||
activeSlug,
|
||||
hasEvents,
|
||||
hasMultipleEvents,
|
||||
selectEvent,
|
||||
refetch,
|
||||
}),
|
||||
[events, isLoading, isError, activeEvent, selectEvent, refetch]
|
||||
[events, isLoading, isError, activeEvent, activeSlug, hasEvents, hasMultipleEvents, selectEvent, refetch]
|
||||
);
|
||||
|
||||
return <EventContext.Provider value={value}>{children}</EventContext.Provider>;
|
||||
|
||||
Reference in New Issue
Block a user