# 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: <LandingPage /> },
  { path: '/setup', element: <ProfileSetupPage /> },
  {
    path: '/e/:slug',
    element: (
      <EventGuard>
        <PrefetchEvent>
          <HomeLayout />
        </PrefetchEvent>
      </EventGuard>
    ),
    children: [
      { index: true, element: <HomePage /> },
      { path: 'tasks', element: <TaskPickerPage /> },
      { path: 'tasks/:taskId', element: <TaskDetailPage /> },
      { path: 'upload', element: <UploadPage /> },
      { path: 'queue', element: <UploadQueuePage /> },
      { path: 'gallery', element: <GalleryPage /> },
      { path: 'photo/:photoId', element: <PhotoLightbox /> },
      { path: 'achievements', element: <AchievementsPage /> },
      { path: 'slideshow', element: <SlideshowPage /> },
    ],
  },
  // Settings sheet is rendered inside Header; no standalone route.
  { path: '/legal/:page', element: <LegalPage /> },
  { path: '*', element: <NotFoundPage /> },
]);
```

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.
