# 07a — Guest PWA Routes & Components This scaffold describes recommended routes, guards, directories, and components for the Guest PWA. It is framework-leaning (React Router v6 + Vite), but adaptable. Routing Principles - Event routes require an event token (from QR/PIN). Guard redirects to Landing when missing/invalid. - Use route-level code splitting for camera/lightbox/slideshow. - Prefer modal routes (photo detail) layered over the gallery. Route Map (proposed) - `/` — Landing (QR/PIN input; deep-link handler) - `/setup` — Profile Setup (name/avatar; skippable) - `/e/:slug` — Home/Feed (default gallery view + info bar) - `/e/:slug/tasks` — Task Picker (random/emotion) - `/e/:slug/tasks/:taskId` — Task Detail (card) - `/e/:slug/upload` — Upload Picker (camera/library + tagging) - `/e/:slug/queue` — Upload Queue (progress/retry) - `/e/:slug/gallery` — Gallery index (alias of Home or dedicated page) - `/e/:slug/photo/:photoId` — Photo Lightbox (modal over gallery) - `/e/:slug/achievements` — Achievements (optional) - `/e/:slug/slideshow` — Slideshow (optional, read-only) - `/legal/:page` — Legal pages (imprint/privacy/terms) - `*` — NotFound Note: The settings experience is handled via the header sheet (no dedicated route; legal pages stay routable under /legal/:page). Guards & Loaders - `EventGuard` — verifies event token in storage; attempts refresh; otherwise redirects to `/`. - `PrefetchEvent` — loads event metadata/theme on `:slug` routes. - `OfflineFallback` — surfaces offline banner and queues mutations. Suggested Directory Structure ``` apps/guest-pwa/ src/ routes/ index.tsx // router config + guards pages/ LandingPage.tsx ProfileSetupPage.tsx HomePage.tsx TaskPickerPage.tsx TaskDetailPage.tsx UploadPage.tsx UploadQueuePage.tsx GalleryPage.tsx PhotoLightbox.tsx // modal route AchievementsPage.tsx SlideshowPage.tsx SettingsSheet.tsx LegalPage.tsx NotFoundPage.tsx components/ Header.tsx InfoBar.tsx BottomNav.tsx QRPinForm.tsx CTAButtons.tsx EmotionPickerGrid.tsx TaskCard.tsx CameraPicker.tsx // photos only; no video capture UploadPreviewList.tsx UploadQueueList.tsx GalleryMasonry.tsx PhotoCard.tsx FiltersBar.tsx Toast.tsx stores/ useEventStore.ts // tenant/event token, theme useProfileStore.ts // name/avatar (local) useUploadQueue.ts // IndexedDB-backed queue services/ apiClient.ts // fetch wrapper + trace id eventsApi.ts // GET event meta photosApi.ts // list/finalize/like uploadService.ts // request signed URL, do PUT, finalize; photo only sw.ts // service worker register helpers hooks/ useOnline.ts useA2HS.ts usePollStats.ts // polls /events/:slug/stats every 10s usePollGalleryDelta.ts // polls /events/:slug/photos?since=... i18n/ config.ts // i18next init with react-i18next, backend loadPath '/lang/\{\{lng\}\}/guest.json' de.json // Namespace: guest (e.g., { "gallery": { "title": "Galerie" } }) en.json main.tsx App.tsx ``` Router Sketch (React Router v6) ```tsx import { createBrowserRouter } from 'react-router-dom'; import EventGuard from './routes/EventGuard'; import PrefetchEvent from './routes/PrefetchEvent'; export const router = createBrowserRouter([ { path: '/', element: }, { path: '/setup', element: }, { path: '/e/:slug', element: ( ), children: [ { index: true, element: }, { path: 'tasks', element: }, { path: 'tasks/:taskId', element: }, { path: 'upload', element: }, { path: 'queue', element: }, { path: 'gallery', element: }, { path: 'photo/:photoId', element: }, { path: 'achievements', element: }, { path: 'slideshow', element: }, ], }, // Settings sheet is rendered inside Header; no standalone route. { path: '/legal/:page', element: }, { path: '*', element: }, ]); ``` Component Checklist - Layout - `Header`, `InfoBar` (X Gäste online • Y Aufgaben gelöst), `BottomNav`, `Toast`. - Entry - `QRPinForm` (QR deep link or PIN fallback), `ProfileForm` (name/avatar). - Home/Feed - `HeroCard` (Willkommensgruess + Eventtitel) und `StatTiles` (online Gaeste, geloeste Aufgaben, letztes Upload). - `CTAButtons` (Aufgabe ziehen, Direkt-Upload, Galerie) + `UploadQueueLink` fuer Warteschlange. - `EmotionPickerGrid` und `GalleryPreview` als inhaltlicher Einstieg. - Tasks - `EmotionPickerGrid`, `TaskCard` (shows duration, group size, actions). - Capture/Upload (photos only) - `CameraPicker`, `UploadPreviewList`, `UploadQueueList`. - Photo View - `PhotoLightbox` (modal), like/share controls, emotion tags. - Settings & Legal - `SettingsSheet` (Header-Overlay mit Namenseditor, eingebetteten Rechtsdokumenten, Cache-Leeren), `LegalPage` Renderer. State & Data - TanStack Query for server data (events, photos); optimistic updates for likes. - Zustand store for local-only state (profile, queue, banners). - IndexedDB for upload queue; CacheStorage for shell/assets. - i18n: react-i18next; load 'guest' namespace JSON from /lang/\{locale\}/guest.json; path-based detection for /de/e/:slug, /en/e/:slug; useTranslation('guest') in components. - Polling: focus-aware intervals (10s stats, 30s gallery); use document visibility to pause; backoff on failures. Accessibility & Performance - Focus management on modal open/close; trap focus. - Color contrast and minimum tap target sizes (44px). - Code-split camera/lightbox/slideshow; prefetch next gallery page. Out of Scope - Video capture/upload is not supported.