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

@@ -6,9 +6,8 @@ import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { ListItem } from '@tamagui/list-item';
import { Pressable } from '@tamagui/react-native-web-lite';
import { MobileScaffold } from './components/Scaffold';
import { MobileShell } from './components/MobileShell';
import { MobileCard, CTAButton } from './components/Primitives';
import { BottomNav } from './components/BottomNav';
import {
getEvent,
getEventTasks,
@@ -33,7 +32,7 @@ import { getApiErrorMessage } from '../lib/apiError';
import toast from 'react-hot-toast';
import { MobileSheet } from './components/Sheet';
import { Tag } from './components/Tag';
import { useMobileNav } from './hooks/useMobileNav';
import { useEventContext } from '../context/EventContext';
const inputStyle: React.CSSProperties = {
width: '100%',
@@ -51,7 +50,8 @@ function InlineSeparator() {
export default function MobileEventTasksPage() {
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');
@@ -67,7 +67,6 @@ export default function MobileEventTasksPage() {
const [busyId, setBusyId] = React.useState<number | null>(null);
const [assigningId, setAssigningId] = React.useState<number | null>(null);
const [eventId, setEventId] = React.useState<number | null>(null);
const { go } = useMobileNav(slug);
const [searchTerm, setSearchTerm] = React.useState('');
const [emotionFilter, setEmotionFilter] = React.useState<string>('');
const [expandedLibrary, setExpandedLibrary] = React.useState(false);
@@ -79,6 +78,11 @@ export default function MobileEventTasksPage() {
const [editingEmotion, setEditingEmotion] = React.useState<TenantEmotion | null>(null);
const [emotionForm, setEmotionForm] = React.useState({ name: '', color: '#e5e7eb' });
const [savingEmotion, setSavingEmotion] = React.useState(false);
React.useEffect(() => {
if (slugParam && activeEvent?.slug !== slugParam) {
selectEvent(slugParam);
}
}, [slugParam, activeEvent?.slug, selectEvent]);
const load = React.useCallback(async () => {
if (!slug) {
@@ -273,10 +277,11 @@ export default function MobileEventTasksPage() {
}
return (
<MobileScaffold
<MobileShell
activeTab="tasks"
title={t('events.tasks.title', 'Tasks & Checklists')}
onBack={() => navigate(-1)}
rightSlot={
headerActions={
<XStack space="$2">
<Pressable onPress={() => load()}>
<RefreshCcw size={18} color="#0f172a" />
@@ -286,9 +291,6 @@ export default function MobileEventTasksPage() {
</Pressable>
</XStack>
}
footer={
<BottomNav active="tasks" onNavigate={go} />
}
>
{error ? (
<MobileCard>
@@ -734,7 +736,7 @@ export default function MobileEventTasksPage() {
>
<Plus size={20} color="#ffffff" />
</Pressable>
</MobileScaffold>
</MobileShell>
);
}