# Mobile Testing Guide — EventSnap v0.15.0

## Setup

### Dev Servers

| Service | URL | Notes |
|---------|-----|-------|
| Frontend | `http://localhost:5173` | Vite dev server, hot-reload |
| Backend API | `http://localhost:3000` | Rust/Axum |
| Database | `localhost:5432` | PostgreSQL (Docker) |

The frontend dev server proxies `/api` and `/media` to the backend automatically.

**Mobile device access:** Connect your phone to the same Wi-Fi network.
Find your machine's local IP (`ip a | grep 192.168` or `hostname -I`), then open
`http://<your-ip>:5173` on your phone.

### Browser DevTools Mobile Emulation (quick testing without a phone)

1. Open Chrome → DevTools (`F12`) → Toggle device toolbar (`Ctrl+Shift+M`)
2. Select **iPhone 14 Pro** or **Pixel 7** from the device dropdown
3. Reload the page — safe-area insets and viewport are emulated
4. To test touch gestures: enable "Touch" in the three-dot menu inside the device toolbar

---

## Test Accounts

Use the following to get all three roles:

| Role | How to get it |
|------|--------------|
| Guest | Join at `/join` with any name |
| Host | Promote a guest via Host Dashboard, or set role in DB |
| Admin | POST to `/api/v1/admin/login` or navigate to `/admin/login` |

Admin password: `admin123` (set in `.env`)

---

## Section 1 — Bottom Navigation Bar

**Goal:** Verify the tab bar is present, thumb-accessible, and correct per role.

### 1.1 Bar Presence & Safe Area
- [ ] Open `/feed` — a bottom tab bar with **Galerie**, a blue circle FAB, and **Konto** appears
- [ ] On a real iPhone/Safari: the bar does **not** overlap the home indicator (safe-area padding)
- [ ] On Chrome DevTools with an iPhone device: the bar is above the viewport bottom
- [ ] Scroll down on a long feed — the bar stays **fixed** at the bottom at all times
- [ ] The bar has a frosted-glass blur effect (`bg-white/90 backdrop-blur-md`)

### 1.2 Active Tab Indicator
- [ ] On `/feed` — the Galerie icon is **blue**; Konto icon is gray
- [ ] Tap **Konto** — navigates to `/account`; Konto icon turns blue, Galerie goes gray
- [ ] Tap **Galerie** — navigates back to `/feed`

### 1.3 Role Gating
- [ ] Log in as a **guest** — bar shows Galerie · FAB · Konto (3 items)
- [ ] Log in as a **host** — same 3 items (dashboard links are inside Account, not the bar)
- [ ] Log in as **admin** — same 3 items

### 1.4 Auth Pages Hide the Bar
- [ ] Visit `/join` — **no** bottom bar
- [ ] Visit `/recover` — **no** bottom bar
- [ ] Visit `/admin/login` — **no** bottom bar

---

## Section 2 — Upload FAB & Bottom Sheet

**Goal:** Verify the FAB opens the upload sheet and both source options work.

### 2.1 FAB Appearance
- [ ] The FAB is a blue circle elevated ~12 px above the tab bar
- [ ] A camera icon with an implicit "+" meaning is shown
- [ ] When uploads are in the queue (pending or uploading), a **red badge number** appears on the FAB

### 2.2 Sheet Opens & Closes
- [ ] Tap the FAB — a bottom sheet slides up smoothly (~300 ms) from below
- [ ] Sheet shows: **Galerie** (blue icon), **Kamera** (purple icon), **Abbrechen**
- [ ] Tap the gray backdrop — sheet slides **back down** and closes
- [ ] Tap **Abbrechen** — sheet closes
- [ ] Swipe the drag handle downward — sheet closes *(if touch gestures are enabled)*

### 2.3 Gallery Source
- [ ] Tap **Galerie** — the native file picker opens
- [ ] Select 1–3 images or videos
- [ ] Sheet closes; you are navigated to `/upload` (the composer page)
- [ ] Thumbnail strip at the top shows your selected files
- [ ] **Bottom nav is gone** on this page (immersive full-screen)

### 2.4 Camera Source
- [ ] Tap the FAB → **Kamera**
- [ ] Browser asks for camera permission — grant it
- [ ] Full-screen camera UI appears (existing CameraCapture component)
- [ ] Take a photo
- [ ] Camera closes; you are navigated to `/upload` with the captured image in the strip

### 2.5 Upload Composer Page
- [ ] Back `×` button top-left → returns to `/feed`, clears pending files
- [ ] Thumbnail strip scrolls horizontally when >3 files
- [ ] Each thumbnail has a small `×` to remove it — tapping removes that file only
- [ ] Caption `<textarea>` is **auto-focused** (keyboard opens on mobile)
- [ ] Type `#party #spaß` in the caption — **quick-tag chips** appear below the textarea in real time
- [ ] Quick-tag chips are read-only (they reflect what's already in the caption)
- [ ] The **"Hochladen"** sticky button at the bottom shows the file count: "2 Dateien hochladen"
- [ ] Button is **disabled** when the strip is empty
- [ ] Also a smaller "Hochladen" button in the header (convenient on desktop/landscape)
- [ ] Tap **Hochladen** — files are queued, you are returned to `/feed`
- [ ] A **slim blue progress bar** appears just above the bottom tab bar while uploading
- [ ] FAB shows a **red badge** during upload; badge disappears when done
- [ ] A brief "Fertig" / completed state appears in the UploadQueue (check queue store)

---

## Section 3 — Feed: List View

**Goal:** Verify the default chronological list view.

### 3.1 Default State
- [ ] Open `/feed` — **list view** is active by default (≡ icon highlighted in the toggle)
- [ ] Posts appear as full-width cards in reverse-chronological order (newest first)

### 3.2 List Card Anatomy
For each card, verify:
- [ ] **Avatar circle** with the uploader's initial letter and a deterministic color
- [ ] **Display name** + **relative timestamp** ("vor 2 Min.", "vor 1 Std.", etc.)
- [ ] **Media**: full-width image, or video with a play button overlay
- [ ] **Caption** below the media (truncated to 3 lines with `...` if long)
- [ ] **Like count** (❤️) and **Comment count** (💬) action buttons
- [ ] Tapping the ❤️ toggles the like optimistically (count changes immediately)
- [ ] Tapping 💬 or the media opens the **Lightbox Modal** (existing behavior, unchanged)

### 3.3 Hashtag Chips (List View Only)
- [ ] Below the main header, hashtag filter chips are visible in **list view**
- [ ] Tap a hashtag chip — feed re-fetches filtered by that tag
- [ ] Tap **Alle** — returns to unfiltered feed
- [ ] Chips are **not visible** when grid view is active

### 3.4 Infinite Scroll
- [ ] Scroll to the bottom — more posts load automatically
- [ ] A spinner appears briefly while loading more
- [ ] Scroll sentinel triggers ~200 px before the actual bottom

### 3.5 Real-Time Updates (SSE)
- [ ] Open the feed on two devices/tabs simultaneously
- [ ] Upload a photo on one — it appears at the **top** of the other's list view in real time
- [ ] Like a post on one — the count updates on the other

---

## Section 4 — Feed: Grid View & Search

**Goal:** Verify the 3-column grid, search bar, autocomplete, and filter chips.

### 4.1 Switching to Grid View
- [ ] Tap the ⊞ grid icon in the header — view switches to a 3-column grid
- [ ] The ≡/⊞ toggle shows ⊞ as active (white background, shadow)
- [ ] Hashtag chips **disappear**; a **search bar** slides in below the header

### 4.2 Grid Layout
- [ ] Grid is **3 columns** with equal square cells (no 2-column fallback on mobile)
- [ ] Videos show a ▶ play button overlay
- [ ] Tapping a cell opens the Lightbox Modal
- [ ] Grid background is seamless (0.5px gap between cells)

### 4.3 Search Bar
- [ ] Search bar shows: 🔍 icon, placeholder "Nutzer oder #Tag suchen…", × clear button
- [ ] Tapping the bar focuses it and opens the keyboard
- [ ] × button appears only when there is text in the input; tapping it clears the query

### 4.4 Autocomplete — On Focus (Empty)
- [ ] Focus the search bar with no text — a dropdown appears with:
  - Up to 3 uploader names (person icon)
  - Up to 3 popular tags (#)
- [ ] The dropdown disappears when the input loses focus (150 ms delay)

### 4.5 Autocomplete — Tag Suggestions
- [ ] Type `#` — only **tag suggestions** appear (no users), sorted by frequency
- [ ] Type `#par` — only tags starting with "par" remain (e.g. `#party`, `#parade`)
- [ ] Tap a suggestion — it's added as a **blue filter chip** below the search bar; input clears

### 4.6 Autocomplete — User Suggestions
- [ ] Type a partial name (e.g. `max`) — users matching "max" appear first, then tags containing "max"
- [ ] Tap a user suggestion — chip added: shows the name without `#` prefix

### 4.7 Filter Chips
- [ ] After selecting a tag filter — grid shows only posts with that tag in the caption
- [ ] Select a second tag — grid shows posts with **either** tag (OR logic)
- [ ] Select a user **and** a tag — grid shows posts by that user **that also** have that tag (AND across types)
- [ ] Each chip has an **× remove button**; tapping it removes only that chip
- [ ] When 2+ chips are active: **"Alle löschen"** link appears; tapping clears all filters
- [ ] When no results match: "Keine Treffer für die gewählten Filter." + "Filter zurücksetzen" button

### 4.8 Switching Back to List View
- [ ] Tap ≡ — list view returns; search bar gone; hashtag chips reappear
- [ ] Active grid filters are **reset** when switching back to list (no stale state)

---

## Section 5 — Account Page

**Goal:** Verify the profile card, dashboard links, and leave-confirm flow.

### 5.1 Profile Card
- [ ] Open `/account` via the Konto tab
- [ ] **Avatar circle** shows your initial letter in a deterministic color
- [ ] **Display name** and **role badge** (Gast / Gastgeber / Admin) shown
- [ ] Session expiry date shown in small text below

### 5.2 Dashboard Links (Host/Admin Only)
- [ ] Log in as a **guest** — no "Dashboards" section visible at all
- [ ] Log in as a **host** — "Dashboards" section shows ⭐ **Host-Dashboard** → chevron
  - [ ] Tapping it navigates to `/host`
  - [ ] No Admin-Dashboard link visible
- [ ] Log in as **admin** — both links appear:
  - [ ] ⭐ Host-Dashboard → `/host`
  - [ ] 🛡 Admin-Dashboard → `/admin`

### 5.3 PIN Card
- [ ] Amber card shows the 4-digit PIN in large monospace font
- [ ] **Kopieren** button copies to clipboard; label changes to "Kopiert!" for 2 seconds
- [ ] If no PIN is stored: fallback message shown

### 5.4 Konto Section
- [ ] **Gerät wechseln / PIN nutzen** → navigates to `/recover`
- [ ] **Event verlassen** (red text) → tapping opens a **leave-confirm bottom sheet**
  - [ ] Sheet shows: "Event verlassen?", "Du wirst abgemeldet…", red "Abmelden" + "Abbrechen"
  - [ ] Tap backdrop — sheet closes, you remain logged in
  - [ ] Tap **Abbrechen** — same
  - [ ] Tap **Abmelden** — you are logged out and redirected to `/join`

### 5.5 No Stale Nav Links
- [ ] **No** "Zur Galerie" link in the header (navigation is via the bottom bar)

---

## Section 6 — Host Dashboard

**Goal:** Verify the back arrow, collapsible sections, and all existing host actions still work.

### 6.1 Navigation
- [ ] Open Host Dashboard via Account → ⭐ Host-Dashboard
- [ ] Page shows a **← back arrow** in the top-left header
- [ ] Tapping it navigates to `/account`
- [ ] **No** shield/gallery header icons (removed)
- [ ] Bottom tab bar is still visible

### 6.2 Statistiken Section (Collapsible)
- [ ] Section is **expanded** by default with a downward chevron
- [ ] Shows a 2×2 grid of stat cards: Gäste, Uploads, Uploads status (Offen/Gesperrt), Freigegeben (Ja/Nein)
- [ ] Numbers are large and readable on mobile
- [ ] Tap the **Statistiken** header button — section collapses (smooth max-height animation)
- [ ] Chevron rotates 180° to point upward when collapsed
- [ ] Tap again — section expands

### 6.3 Event-Einstellungen Section (Collapsible)
- [ ] Collapse/expand works same as above
- [ ] Shows **"Uploads sperren"** (amber) / **"Uploads wieder öffnen"** (green) button
- [ ] Shows **"Galerie freigeben"** (blue) / "Galerie bereits freigegeben" (disabled gray)
- [ ] Tap "Uploads sperren" — toast confirms, button switches to "Uploads wieder öffnen"
- [ ] Existing functionality unchanged

### 6.4 Nutzerverwaltung Section (Collapsible)
- [ ] **Search bar** at top of section filters the user list in real time (client-side)
- [ ] Each user row shows name, role badge, banned badge (if applicable), upload count/bytes
- [ ] **Sperren** button triggers the existing ban modal (confirm + hide-uploads checkbox)
- [ ] **Entsperren** appears for banned users
- [ ] **Host** button promotes a guest to host role
- [ ] **Degradieren** appears for hosts (admin only)
- [ ] Toast notifications appear above the bottom bar (not obscured by it)

---

## Section 7 — Admin Dashboard

**Goal:** Verify the inner tab bar, all 4 tabs, and the new Nutzer tab.

### 7.1 Navigation
- [ ] Open Admin Dashboard via Account → 🛡 Admin-Dashboard
- [ ] Page shows **← back arrow** → `/account`
- [ ] **No** star/gallery header icons
- [ ] Bottom tab bar visible

### 7.2 Inner Tab Bar
- [ ] A second tab bar appears **below the main header**, sticky on scroll
- [ ] 4 tabs: **Stats · Config · Export · Nutzer**
- [ ] Active tab has a blue bottom border and blue text
- [ ] Inactive tabs are gray
- [ ] Tabs are scrollable horizontally (try narrowing viewport)
- [ ] Switching tabs is instant with no page reload

### 7.3 Stats Tab
- [ ] Shows a **2×2 grid** of metric cards: Gäste, Uploads, Kommentare, Speicher %
- [ ] Values are large (`text-3xl`)
- [ ] Below the grid: a full-width disk usage bar with color coding
  - Blue ≤ 74%, Amber 75–89%, Red ≥ 90%
- [ ] Exact used/total/free values shown

### 7.4 Config Tab
- [ ] Shows stacked label + full-width input for each of the 8 config keys
- [ ] Inputs are `type="number"` with large touch targets
- [ ] A **"Speichern"** button is **sticky at the bottom** of the tab (always visible, even on long scroll)
- [ ] Edit a value → tap Speichern → toast "Konfiguration gespeichert."
- [ ] Tap Speichern with no changes → toast "Keine Änderungen."

### 7.5 Export Tab
- [ ] **"Galerie freigeben"** button triggers gallery release
- [ ] **"Aktualisieren"** button refreshes the jobs list only (no full page flash)
- [ ] Export jobs listed with status chips: Ausstehend (gray) / Läuft (blue) / Fertig (green) / Fehlgeschlagen (red)
- [ ] Running jobs show a progress bar
- [ ] Failed jobs show the error message in red

### 7.6 Nutzer Tab (New)
- [ ] Users are loaded from `/host/users` (admin shares host permissions)
- [ ] **Search bar** filters list in real time
- [ ] Same ban/unban/promote/demote actions as Host dashboard
- [ ] After an action (e.g. ban) only the users list refreshes, not the whole page

---

## Section 8 — Toast Position

- [ ] On host/admin pages, toasts appear at `bottom-24` (above the bottom nav bar)
- [ ] Toasts are **not** obscured by the nav bar

---

## Section 9 — Desktop Usability (Second Citizen)

**Goal:** Confirm all pages are still usable on a wide viewport.

### 9.1 Layout Centering
- [ ] On a 1280px+ viewport, all pages center their content at `max-w-2xl` or `max-w-3xl`
- [ ] Bottom tab bar spans full width but content columns remain centered
- [ ] No content is clipped or overflows horizontally

### 9.2 Feed Desktop
- [ ] List view: cards are centered, readable at 672px max width
- [ ] Grid view: 3 columns at max-width — cells are larger and look good
- [ ] Search bar is full-width within the max-width container

### 9.3 Upload Composer Desktop
- [ ] Upload page is full-height, centered column
- [ ] Both the header "Hochladen" button AND the sticky bottom button are present
- [ ] Desktop users can click the header button (more convenient without reaching to bottom)

### 9.4 Host / Admin Desktop
- [ ] Host collapsible sections work with mouse clicks
- [ ] Admin inner tabs work with mouse clicks; all 4 tabs visible without scrolling at 1280px
- [ ] Config tab sticky save is visible on desktop scroll

---

## Section 10 — Edge Cases

### 10.1 Upload with No Files Selected
- [ ] Navigate directly to `/upload` in the browser
- [ ] No files pending → "Keine Dateien ausgewählt" screen shown with "Zurück" button
- [ ] "Hochladen" button is disabled

### 10.2 Rate Limiting
- [ ] Upload rapidly beyond the configured limit (default: 10/hour)
- [ ] A `429` response is received
- [ ] The countdown banner appears above the bottom nav: "Upload-Limit erreicht. Wird in X Sek. automatisch fortgesetzt."
- [ ] After the countdown, the queue resumes automatically

### 10.3 SSE Reconnect
- [ ] Stop the backend briefly and restart
- [ ] The feed reconnects (SSE) — new uploads appear once the backend is back

### 10.4 Back Navigation from Upload
- [ ] Pick files → navigate to `/upload`
- [ ] Tap `×` → files are discarded (`clearPending()` runs, object URLs are revoked)
- [ ] Navigate back to `/upload` directly — "Keine Dateien ausgewählt" shown (not stale files)

### 10.5 Grid Filter Persistence
- [ ] Set a filter chip in grid view
- [ ] Switch to list view — filter is cleared (list always shows full unfiltered feed)
- [ ] Switch back to grid — search bar is empty, no stale chips

---

## Known Limitations (Not Bugs)

| Item | Status |
|------|--------|
| "Anzeigename ändern" in Account | Deferred — shown as disabled; requires `/me` PATCH endpoint |
| Upload count in Account profile card | Deferred — requires `/me` GET endpoint |
| CSS collapse animation on host sections | Uses `max-h` trick; may be slightly sluggish for very large user lists |
| Autocomplete results | Derived from currently-loaded posts only; new posts via SSE update the pool automatically |
