# SupplierDev — UI Flows

> Defines all screens, navigation, user actions, and states for Web (Next.js) and iOS (SwiftUI).  
> Both platforms must maintain **feature parity** on all core flows.

---

## 1. Navigation Structure

### Web (Next.js)
```
Desktop: Left Sidebar (permanent)
Mobile:  Bottom Tab Bar (fixed)

Sidebar / Bottom Tabs:
  ├── Projects       →  /dashboard
  ├── Analytics      →  /management
  └── Account        →  /account
```

### iOS
```
TabView:
  ├── Visits         →  VisitListView
  ├── Analytics      →  AnalyticsView
  └── Account        →  AccountView
```

---

## 2. Auth Screens

### 2.1 Login
**Route**: `/login` | **iOS**: `LoginView`

| Element | Action | State |
|---------|--------|-------|
| Email input | Type email | Validates on submit |
| Password input | Type password | Validates on submit |
| "Sign in" button | Submit | Loading / Error / Success → Dashboard |
| "Forgot password?" link | Navigate | → Forgot Password |
| "Create one" link | Navigate | → Sign Up |

**States**: idle → loading → error (inline message) | success (redirect)

---

### 2.2 Sign Up
**Route**: `/signup` | **iOS**: `SignupView`

| Element | Action | State |
|---------|--------|-------|
| Email input | Type | |
| Password input | Type (min 6 chars) | |
| "Create account" button | Submit | Loading → Success state |
| "Sign in" link | Navigate | → Login |

**Success state**: Show confirmation card ("Check your email → {email}")

---

### 2.3 Forgot Password
**Route**: `/forgot-password` | **iOS**: `ForgotPasswordView`

Submit email → Success state ("Reset link sent to {email}")

---

### 2.4 Reset Password
**Route**: `/reset-password` | **iOS**: `ResetPasswordView`

New password + confirm → Update → Redirect to Dashboard

---

## 3. Visit List (Dashboard)

**Route**: `/dashboard` | **iOS**: `VisitListView`

### Layout
```
Header: "Supplier Visits"
Action: "+ New Visit" button (top right)

Visit Card (per visit):
  - Supplier name (prominent)
  - Plant location + Product name (subtitle)
  - Visit date
  - Bottleneck indicator (colored badge or CT value)
  - → Navigate to Visit Detail on tap
```

### States
- **Empty**: "No visits yet — create your first visit" + CTA button
- **Loading**: Skeleton cards
- **Populated**: Card list sorted by visit_date desc
- **Pull-to-refresh** (iOS)

### Actions
- Tap card → Visit Detail
- Tap "+ New Visit" → New Visit Form
- Swipe to delete (iOS) / Delete from Edit page (Web)

---

## 4. New Visit

**Route**: `/project/new` | **iOS**: `NewVisitView`

### Form Fields
| Field | Type | Required | Notes |
|-------|------|----------|-------|
| Supplier name | text | ✓ | Auto-focus |
| Plant location | text | — | |
| Product name | text | — | |
| Visit date | date picker | ✓ | Default: today |
| Customer takt time | number (s) | — | |
| Planned OEE | number (%) | — | Default: 85 |
| Target CT | number (s) | — | Auto-calc: takt × OEE/100 |
| Notes | textarea | — | |

**Action**: "Create Visit" → Visit Detail (new visit)

---

## 5. Visit Detail

**Route**: `/project/[id]` | **iOS**: `VisitDetailView`

### Layout
```
Header: Supplier name + "Edit" button

Projektdetails card: Grunddaten / Lieferant / Ausgangslage & Ziele
  (order_reason/order_objective, hidden when both empty) / KPI & Zuordnung / Team

Kennzahlen-Sektion (KAR-983, generic for every project type):
  - Section header "Kennzahlen" (demo badge stays in the sticky project header only)
  - 4 module tiles, each links into its module and shows its own
    "Keine Daten" state when the project has nothing yet:
    - Fabrikanalyse: Erfüllungs-Score /4, erfüllt/teilweise/nicht-Anteile,
      # beantwortete Fragen → /fabrikanalyse/{id}
    - QAF-Vergleich: Summe Herstellkosten Δ% (+ Angebotspreis/Material Δ%)
      from the newest qaf_comparison's persisted qaf_summary_diff
      → /project/{id}/qaf
    - Wertstrom: Durchlaufzeit, VA-Anteil, Engpass-Station from the newest
      value_stream_maps row → /wertstrom/{mapId} (or /wertstrom index if none)
    - Maßnahmen: gesamt/offen/erledigt across lsc_measures + workshop_actions
      → /project/{id}/workshop

KPI Strip (3 cards, LSC project type only):
  - Kundentakt: {customer_takt_time_sec}s
  - Target CT: {target_cycle_time_sec}s  
  - Bottleneck: {station_name} @ {avg_ct}s [color: red/yellow/green]

Quick Nav Grid (5 tiles):
  - Stopwatch   →  Stopwatch
  - Shift        →  Shift Output
  - Actions     →  Workshop Actions
  - QAF         →  QAF Comparison
  - Export      →  Export

Takt Diagram:
  - Yamazumi chart (all stations)
  - Tap bar → Cycle Detail chart

Process Steps:
  - Ordered list of stations
  - Add / Edit / Delete / Reorder
  - Per station: avg CT, # measurements, planned CT
  - Timer icon → Stopwatch for that station
```

### States
- No stations: Show "Add your first process step" CTA
- No measurements: Show "no measurements" per station
- Loading: Skeleton
- Kennzahlen-Sektion (KAR-983): each of the 4 module tiles degrades independently
  to its own "Keine Daten" tile when the project has no assessment / no
  qaf_comparison / no value_stream_maps row / no measures yet — never a crash or
  an empty gap. A QAF comparison that exists but has no persisted summary-diff
  rows (e.g. Multi-QAF) shows a distinct "no core metrics available" note rather
  than a misleading zero.

---

## 6. Edit Visit

**Route**: `/project/[id]/edit` | **iOS**: `EditVisitView`

Same form as New Visit, pre-populated. Includes "Delete Visit" (destructive, confirm dialog).

---

## 7. Stopwatch

**Route**: `/project/[id]/stopwatch` | **iOS**: `StopwatchView`

### Layout
```
Station Selector (pill buttons, horizontal scroll)

Timer Display:
  - Large numeric display: MM:SS.ms
  - State: idle | running | paused

Controls:
  - [Lap / Record]    — Records current time, resets to 0
  - [Start / Stop]    — Toggles timer
  - [Reset]           — Resets timer (confirm if running)

Progress: dots (●○○○○○○○○○) — 10 cycles target

Cycle List (below timer):
  - Cycle #, Time, Outlier flag, Notes icon
  - Edit inline (tap pencil)
  - Delete (swipe iOS / trash button Web)
  - Mark as outlier (toggle)
  - Add notes (tap note icon → modal)

Import / Export:
  - Download Excel template
  - Import from Excel
```

### States
- **Idle**: Timer at 00:00.000, Start button prominent
- **Running**: Timer counting, Lap button is primary action
- **Cycle recorded**: Brief flash/haptic, cycle appears in list
- **10 cycles reached**: Progress bar complete, subtle celebration

### Haptics (iOS)
- Lap tap: `.medium` impact
- 10 cycles complete: `.success` notification

---

## 8. Shift Output

**Route**: `/project/[id]/shift-output` | **iOS**: `ShiftOutputView`

### Layout
```
Date Picker (default: today)
Shift Type Selector: Frühschicht | Spätschicht | Nachtschicht

Hour Table:
  Hour | Soll | Ist | Delta
  06:00 | 52  | 48  | -4  (red)
  07:00 | 52  | 55  | +3  (green)
  ...

  Tap row → Edit row inline
  Add hour → FAB or "Add hour" button

Summary row: Total Soll | Total Ist | Total Delta
```

### Color Coding
- Delta > 0 → green
- Delta = 0 → neutral
- Delta < 0 → red
- Hidden loss warning: all hours ±0 but pattern looks artificially flat

---

## 9. Workshop Actions

**Route**: `/project/[id]/workshop` | **iOS**: `WorkshopView`

### Layout
```
"+ Add Action" button

Action Card (per action):
  - #N  Area  Description
  - Effort/Benefit badges (color-coded)
  - Status badge (open/in_progress/done/rejected)
  - Responsible + Target Date
  - Time/Cost savings (if set)
  - Edit / Delete controls
```

### Effort/Benefit Matrix Colors
- Low: gray  |  Medium: blue  |  High: green (benefit) / red (effort)

### Status Colors
- open: gray  |  in_progress: blue  |  done: green  |  rejected: red/strikethrough

### New/Edit Action Sheet (bottom sheet iOS / modal Web)
All fields from Action entity.

---

## 10. QAF Comparison

**Route**: `/project/[id]/qaf` | **iOS**: `QAFView`

### Layout
```
3 Version Tiles (zur_vergabe | nach_vergabe | aktuell):
  Each tile:
    - Version label
    - File name (if uploaded)
    - Upload date
    - Parsed values: CT, OEE, Costs, Reject Rate
    - "Upload" button (replaces if exists)

Delta Table (shown when ≥2 versions uploaded):
  Metric       | zur Vergabe | nach Vergabe | Aktuell | Δ (latest vs. zur Vergabe)
  Cycle Time   | 52.5s       | 48.2s        | 45.1s   | -7.4s ▼ (green)
  OEE          | 82%         | 85%          | 88%     | +6% ▲ (green)
  Cost/Unit    | €14.20      | €13.50       | €12.80  | -€1.40 ▼ (green)
  Reject Rate  | 2.1%        | 1.8%         | 1.4%    | -0.7% ▼ (green)
```

---

## 10a. QAF-Differences (SupplierPulse)

**Route**: `/qaf-differences` (top-level nav entry "QAF-Vergleiche") | **iOS**: not yet

Distinct from §10: a standalone page that batch-compares full QAF cost documents of
the **same part number** across quotation rounds. Reached from the left sidebar /
bottom tabs, not from a single visit.

### Layout
```
Upload section:
  - Project select (defaults to first owned project)
  - Multi-file picker (.xlsx only, max 20 MB each)
  - "Analysieren" button (disabled while a batch runs)

Result section (after analyze):
  - Stat cards: Vergleiche | Dateien geparst | Review nötig | Datei-Fehler
  - Per-file error list (offending file + reason) when any file fails

Letzte Vergleiche:
  - List of recent comparisons: part number, "Baseline-Review" badge, status
  - Each row links to the comparison detail view (§10b)
  - Refreshes automatically after an analyze run (router.refresh)
```

### States
- No projects: select shows "Keine Projekte"
- Invalid input: inline error ("Bitte ein Projekt wählen." / "…mindestens eine QAF-Datei…")
- Running: button shows "Analysiere…"
- Per-file failure: file is listed under Datei-Fehler, the rest of the batch still completes
- No comparisons yet: "Noch keine Vergleiche."

---

## 10b. QAF-Vergleich Detail

**Route**: `/qaf-differences/[id]` | **iOS**: not yet

On-screen breakdown of a single persisted comparison (read-only, RLS-scoped — a
foreign comparison id returns 404). Reached by clicking a row in "Letzte Vergleiche".

### Layout
```
Header: part number + "Zurück zu QAF-Vergleiche"

Überblick:        baseline-status pill, status, created date,
                  Sachnummer / Teilebenennung / Lieferant / Variante,
                  ALT (Baseline) + NEU (Vergleich) file names
Management-Fazit: root-cause summary + uncertainties
Fertigungskosten-Deltas: per-field cost table (ALT | NEU | Δ abs | Δ % | Status),
                  status colour-coded; empty-state when structure too divergent
Struktur-Änderungen: Neu / Entfallen / Mögliche Umstrukturierung lists
Prozessschritt-Matching: step | status | confidence | method | review
Plausibilität:    severity pill + explanation per issue
```

### States
- Sections with no data are hidden; the cost-delta table shows an explicit empty-state
- Foreign / unknown id → 404 (notFound)

> **Follow-up:** a per-comparison "Export (.xlsx)" download action is planned once the
> export HTTP route is wired (KAR-801).

### Autosave (KAR-985)
The "Hochrechnung & Potenzial in €" section's editable table/Δ-override/Abwehrquote-
Slider, and the separate "Preis-Kalkulator" route (`/qaf-differences/[id]/kalkulator`,
reached via the button in this page's action row), both persist their inputs
automatically per comparison — no Save button. A small inline indicator
("…speichert" / "✓ Gespeichert") is the only feedback; a save/load failure (e.g. the
`user_inputs` migration not yet applied on the target environment) degrades quietly
— the page and its live calculation stay fully usable either way, just without
persistence until the next successful save.

---

## 10c. Multi-QAF-Vergleich öffnen/reviewen (KAR-947)

**Route**: `/qaf-differences/[id]` (same route as §10b, dispatched by
`comparison_mode='multi_qaf'`) | **iOS**: not yet

Reached the same way as §10b (a row in "Letzte Vergleiche"); the page renders this
view instead of §10b's when the persisted comparison is a Multi-QAF container-vs-
container compare.

### Layout
```
Review-Banner: shown when the comparison (or a dropped manual override) needs review

Numbered sections (each with its own expandable "ⓘ Was zeigt diese Sektion?"):
  Überblick:              both sides' container summary + warnings
  Varianten-Matching:     ALT ↔ NEU variant pairs, per-pair confidence/method,
                          "gedroppte manuelle Zuordnung" notice when a prior
                          override no longer applies (e.g. after a file replace)
  Varianten-Zuordnung ändern: inline per-pair override control — confirm a
                          suggested match, mark "kein Gegenstück" (unmatched),
                          or pick a different NEU variant, with an optional note;
                          recomputes the persisted result immediately
  Container-Struktur:     structural diff between the two containers
  Material-Matrix:        per-material diff
  Summary-Profil:         per-variant summary-metrics diff
  Aggregat-Impact:        volume-weighted cost impact, with its own validity gates
```

### States
- Every section is Result-version-tolerant: a comparison computed before a given
  sub-feature shipped shows an explicit "nicht verfügbar für diesen Vergleich"
  notice instead of an empty/zero-diff section
- Setting or clearing a variant-match override re-renders the affected sections
  immediately (no full page reload)

---

## 10d. Variante↔Standard-QAF erstellen (KAR-948)

**Route**: `/qaf-differences` (inline collapsible section on the same page as the
Upload section, project-scoped) → creates a comparison, then navigates to
`/qaf-differences/[id]` | **iOS**: not yet

For the case where a project has only ONE Multi-QAF container on file (no second
container to diff against) — an inline "Starten" / "Schließen" toggle, not a modal
(matches every other interactive flow on this page).

### Layout
```
1. Multi-QAF-Container: <select> of this project's persisted Multi-QAF files
2. Variante: radio list of the chosen container's variants — label, dimensions,
   annual/peak/lifetime volume, parsing confidence, active/inactive/reserved
   badge; only an "Aktiv" variant is selectable (inactive/reserved rows render
   disabled with an explanatory note)
3. Standard-QAF (dieses Projekt): <select> of this project's confirmed
   Standard-QAF files
"Vergleich erstellen" button
```

### States
- No Multi-QAF container / no Standard-QAF file yet in the project: explicit
  "erst hochladen" hint instead of an empty select
- File lists (step 1 + step 3) and the per-container variant list (step 2) each
  load asynchronously with their own loading/error state; a stale, still-in-flight
  file-list fetch can never overwrite a newer one (request-sequence guard), and
  the "Starten/Schließen" toggle is disabled while a fetch is in flight
- Submit validates all three selections client-side before calling the create
  action; a server-side error (e.g. a since-deactivated variant) surfaces inline

---

## 10e. QAF → Wertstrom-Übernahme (QVS-P3, flag-gated: `qafValueStream`)

**Routes**: entry points on `/qaf-differences/[id]` (§10b, `summary`/`g60` modes only)
and on `/project/[id]/qaf`; the dialog itself redirects into `/wertstrom/[id]` |
**iOS**: not yet (web-only until the flag is evaluated for general availability)

Two entry points, one shared dialog — never a per-entry-point copy.

### Entry points
```
A (qaf-differences detail, §10b): "Als Wertstrom übernehmen" button in the
   export-button row, next to the existing Excel-export button(s).
   ALT+NEU comparison → a small ALT/NEU side toggle next to the button,
   default NEU. Hidden entirely when neither side has detectable
   manufacturing steps (no dead button), or when the flag is off.

B (project/[id]/qaf): a "Fertigungsvergleiche (QAF)" list above the existing
   (unchanged) per-visit Comparison Board — this project's QAF-Vergleiche
   (`summary`/`g60` mode) that have an eligible side, each row with the same
   button + ALT/NEU toggle. Most projects have none yet (a project-scoped
   QAF-Vergleich is created from the standalone /qaf-differences page today)
   — the whole section is omitted when the list is empty.
```

### Preview dialog (shared component)
```
Quelle: <Dateiname>

[Duplicate banner — only if this QAF file was already imported before]
  "Aus dieser QAF-Datei existiert bereits ein Wertstrom."
  [Öffnen] [Trotzdem neu erstellen]
[Degraded banner — only if the duplicate check itself failed]
  "Duplikat-Prüfung nicht verfügbar" (never silently "0 Duplikate")

Titel: <editable, pre-filled with a suggested title>
Coverage: "X Schritte · Y mit Zykluszeit · Z mit Kosten [· N abgewählt]"
[Warnings, non-blocking, e.g. "Sequenz aus Zeilenreihenfolge uneindeutig"]
Schritte (N): checkbox list, source order, name + cycle time, all checked
   by default — unchecking a step live-updates the coverage line above
▸ Fehlende Felder (N) — collapsible, one line per field with its reason

[Abbrechen] [Erstellen]
```

### States
- Loading: "Lade Vorschau…"
- Load failure (not found / disabled / unauthorized): message + Schließen,
  no form ever rendered
- Duplicate found: the create form is hidden until "Trotzdem neu erstellen"
  is clicked (never a hard block — Öffnen/Trotzdem neu is always the choice)
- Every step deselected: "Erstellen" is disabled (a 0-step Wertstrom is not
  a real import)
- Create failure (e.g. the source changed since the preview was opened):
  inline error, dialog stays open
- Create success: toast with the partial-import balance ("18 Schritte
  importiert, 3 ohne Zykluszeit"), redirect to the new Wertstrom's editor

### Editor extensions (`/wertstrom/[id]`, additive)
```
Node (canvas): subtle, colourless marker on an imported node (no new colour)

Properties panel (imported node only):
  Name / Zykluszeit / Anzahl Mitarbeiter / Maschinentyp — each shows an
    "importiert" or "geändert" tag once edited after import
  [Quelle anzeigen] → modal: every captured QAF field, its spreadsheet
    cell, original value, unit, and parser confidence
  Wertschöpfung: 4-way select (Wertschöpfend / Notwendig, nicht
    wertschöpfend / Nicht wertschöpfend / Ungeklärt) replacing the old
    binary checkbox; a node with no explicit classification yet shows
    "Ungeklärt" plus the old checkbox's value as a hint, never silently
    promoted to "Wertschöpfend"
  Zykluszeit-Vergleich (only when both a QAF value and a same-named
    measured step exist): "QAF 42,0 s · Gemessen Ø 48,6 s · +15,7 %" —
    display-only, no auto-overwrite

Wertstromanalyse list (/wertstrom): "aus QAF · <Dateiname>" under an
  entry's title when it came from this flow
```

### States
- A node without `qafSource` (manually created, or from the existing
  Excel/LSC import) shows none of the above — same editor as before this
  feature, byte-for-byte
- Empty time-split fields stay visibly empty ("—") — never a placeholder

### QVS-P4 addendum: Multi-QAF Varianten-Sektion (same preview dialog, additive)

Shown ONLY when the source file is the standard (`alt`) side of a
`multi_qaf_variant_vs_standard` comparison AND that comparison's container side
has ≥1 active variant — a plain `summary`/`g60` import (no Multi-QAF
involvement) sees the exact pre-P4 dialog, unchanged.

```
N Varianten mit nachweislich gleichem Fertigungsprofil   [or, when no profile-
  EU · 1.000 Stück                                        binding evidence exists
  US · 500 Stück                                           for the compared variant:
  ( ) Geteilter Fertigungsfluss (empfohlen) — ein Wertstrom, N Varianten als Tag
  ( ) Je Variante ein Wertstrom — N Wertströme mit identischen Schritten
  [only when "Je Variante" selected:]
  "Zykluszeiten sind im QAF nicht variantenspezifisch — alle erzeugten
   Wertströme übernehmen dieselben Schritt-Werte."
```
(degraded binding check → "Nur die verglichene Variante — Profil-Bindung unbekannt", `variants` then contains only that one entry)

- Review-Fix 1 (PR #338 adversarial review): the variant list is evidence-scoped, NOT
  "every active variant in the container" — it is always the ONE variant the underlying
  comparison was actually run against (`engine_version.selectedVariantId`) plus any OTHER
  active variant `variantProfileBindings` provably ties to the SAME manufacturing profile.
  A container's variants may legitimately run on different manufacturing profiles/plants,
  and the comparison itself never checks manufacturing compatibility — tagging the whole
  container would over-claim what the data establishes (see gap-analysis G5).
- Default strategy: "Geteilter Fertigungsfluss" (one `createValueStreamFromQafAction`
  call, unchanged wire shape otherwise — nodes get `variantTags` = the evidence-scoped
  variant-key set above, `value_stream_imports.variant_selector` persisted as
  `{strategy: 'shared_flow', variantKeys}`).
- "Je Variante ein Wertstrom": a dedicated action (`createValueStreamsForVariantsAction`)
  re-derives the variant list server-side (never trusts a client-supplied list) and
  creates N Wertströme sequentially, each with the SAME mapped nodes/connections but a
  single variant tag AND (Review-Fix 3, PR #338) its own `${title} · ${variantLabel}`
  title suffix, so the N resulting Wertströme stay distinguishable in the `/wertstrom`
  list. Toast: "N Wertströme erstellt (je Variante)." (type `'success'`) or, if any
  variant failed, "X von N Wertströmen erstellt — Y fehlgeschlagen." (never hidden) with
  the toast TYPE also reflecting the outcome (Review-Fix 2/10, PR #338 — `'info'` for a
  partial failure, `'error'` for a total failure, never a hardcoded green `'success'`
  regardless of what actually happened); redirect goes to `/wertstrom` (the list) since
  there is no single canonical stream to open. Not one Postgres transaction across all
  N — each variant's own create is transactional, a mid-sequence failure leaves the
  earlier ones persisted (documented, not silently swept under the rug).
- Never shown for a `multi_qaf`-vs-`multi_qaf` comparison — neither side has
  `qaf_manufacturing_step` rows to map from (no step-level data exists to import,
  structurally, not a bug).

---

## 10f. QAF-Reimport & Synchronisation (QVS-P5, flag-gated: `qafValueStream`)

**Routes**: entry point in the `/wertstrom/[id]` editor toolbar; the dialog is a modal
overlay, same chrome as the P3 preview dialog | **iOS**: not yet (web-only until the flag
is evaluated for general availability)

### Entry point
```
Editor toolbar (only when ≥1 node on this Wertstrom carries `qafSource` — an
  old, manually-built, or Excel/LSC-imported Wertstrom never shows this):
  [⟳ Synchronisieren] button, next to "LSC importieren"/"Speichern".
```

### Reimport dialog
```
Quelle für den Abgleich: <Dateiname> [andere Datei wählen ▾]
  (defaults to the file this Wertstrom was created/last synced from; the
   picker lists every other QAF upload in the same project — Spec 21 "A
   newer QAF revision is uploaded")

<N> übernehmen · <N> behalten · <N> Konflikte [· <N> in Quelle entfernt] [· <N> nicht eindeutig zuordenbar]

( ) Alle Quelländerungen übernehmen   ( ) Auswahl übernehmen   ( ) Keine (nur Konflikte/Entfernungen entscheiden)
  (mode selector only — sets which SOURCE_CHANGED fields/NEW_IN_SOURCE steps
   are pre-selected for adoption; does not itself apply anything. Konflikte
   (BOTH_CHANGED) und "In Quelle entfernt"-Entscheidungen werden IMMER
   einzeln entschieden, unabhängig vom Modus.)

Schritte (nur veränderte/neue/entfernte/mehrdeutige — unveränderte werden
  ausgeblendet, kein Formular-Zoo):
  ┌───────────────────────────────────────────────────────────┐
  │ ● Quelle geändert   Montage                                │
  │     Zykluszeit:  Snapshot 30s → Quelle 33s   [✓ übernehmen] │
  │ ● Lokal geändert    Prüfen                                  │
  │     Zykluszeit:  Snapshot 15s, Ihr Wert 18s   (gesperrt —   │
  │     wird nie automatisch überschrieben)                    │
  │ ● Konflikt          Schweißen                               │
  │     Zykluszeit:  Snapshot 42s, Quelle 50s, Ihr Wert 47s      │
  │     ( ) Quelle übernehmen   ( ) Lokal behalten (Standard)   │
  │ ● Neu in Quelle     Verpacken                    [✓ hinzufügen] │
  │ ● In Quelle entfernt  Altprozess     ( ) behalten (Standard) ( ) entfernen │
  │ ● Quelle geändert   Altprozess 2 (lokal bereits entfernt — keine          │
  │     Übernahme möglich — kein Feld-Steuerelement wird angezeigt)          │
  │ ● Nicht eindeutig zuordenbar  Kontrolle (2× in Quelle, 1× im Snapshot) │
  └───────────────────────────────────────────────────────────┘

[Abbrechen] [Separaten Vergleichs-Wertstrom anlegen] [Übernehmen]
```

### States
- Loading: "Lade Abgleich…"
- Load failure (not found / never QAF-importiert / Quelle nicht mehr sichtbar):
  message + Schließen, no form ever rendered. A transient DB error while
  loading is its own, distinguishable message (retry-worthy), never the same
  wording as "kein Import-Verlauf gefunden"
- Switching the source-file dropdown re-loads the comparison; if THAT load
  fails, the previous (now stale) form/dropdown is fully replaced by the
  error state — never shown together with the error
- A step whose live counterpart was already deleted locally is shown (name +
  "lokal bereits entfernt") but renders NO field-adoption checkbox at all for
  its SOURCE_CHANGED fields — nothing to adopt into, same treatment as a
  REMOVED_FROM_SOURCE step's keep/remove radios in this situation
- Apply failure because the underlying state changed since the dialog opened
  (`stale_delta` — live document edited elsewhere, or the source changed
  again): inline error, "bitte neu laden", dialog stays open
- Apply success: toast summarising exactly what happened ("3 Felder
  übernommen, 1 neuer Schritt hinzugefügt, 2 Konflikte zugunsten der Quelle
  gelöst") and the editor's canvas updates in place — no page reload
- "Separaten Vergleichs-Wertstrom anlegen": redirects into the newly created
  Wertstrom's own editor; the ORIGINAL Wertstrom and its import history are
  untouched. If the underlying call reports `idempotentHit: true` (an
  identical comparison request already exists, e.g. a same-day resubmit),
  NOTHING new is created — the dialog shows an honest inline message instead
  of a success toast, and does NOT navigate anywhere

### Never fabricates / never silently overwrites
- A field the delta engine cannot classify with confidence (name present on
  both sides but in a different count) is never forced into "neu"/"entfernt"
  — it is its own, visibly "nicht eindeutig zuordenbar" bucket.
- "Alle Quelländerungen übernehmen" only ever touches fields classified
  **Quelle geändert** and steps classified **Neu in Quelle** — a **Lokal
  geändert** field is never touched by it, and a **Konflikt** field is only
  ever moved by an explicit per-field radio choice, never by the bulk action.
- A step removed from the source defaults to staying in the Wertstrom;
  deleting it requires an explicit choice.

---

## 11. Export

**Route**: `/project/[id]/export` | **iOS**: `ExportView`

### Layout
```
Data Summary (3 stats): Stations | Measurements | Actions

Export Options:
  [Full Report .xlsx]
    Sheets: Cycle Time Summary | Raw Measurements | Workshop Actions | Shift Output
  [Cycle Times .csv]
    Raw measurements only
  [PDF Projektdatenblatt]
  [Gesamtprojekt-Export (Copilot) ▾]       — Word (DOCX) / Markdown (KI-Chat), flag-gated: `copilotExports`, see 11a
  [Gesamtprojekt-Export (Copilot, XLSX)]   — flag-gated: `copilotExports`, see 11a
```

### States
- Generating: spinner in button
- Done: file download triggered / share sheet (iOS)

---

## 11a. Copilot-Exporte (Demo-067 C/E, KAR-982/KAR-984, flag-gated: `copilotExports`)

Four exports (module scope) meant for a consultant to load into an external LLM tool and keep
working with, each available as **DOCX or Markdown** (Demo-067 E, KAR-984 — Markdown for the
"paste the file into an AI chat window" workflow, no Office viewer needed) — plus one XLSX-only
export (Gesamtprojekt). Every export starts with a shared AI-instruction block (what the document
is, how to use it, an LSC/QAF/VSM/FA glossary) and, only for `is_demo` projects, a fictional-data
disclaimer — identical content between the DOCX and Markdown rendering of the same export. Only
visible/callable when `copilotExports` is `true` for the active profile (`true` in `bmw`, `false`
in `default`/`_template`) — the button is hidden client-side AND the server action rejects the
call when the flag is off.

Entry points (one button per module surface, "Copilot-Export" label unless noted — a button
offering more than one format renders as a small dropdown: "Word (DOCX)" / "Markdown
(KI-Chat)"):

```
Fabrikanalyse-Dashboard   /fabrikanalyse/[projectId]/[assessmentId]/dashboard
  → header export-button row, next to the existing "KI-Export" (Markdown) button
  → "Copilot-Export ▾": Word (DOCX) / Markdown (KI-Chat)

QAF-Vergleich-Detail      /qaf-differences/[id]
  → toolbar, next to the existing Excel-export button — standard/summary comparison
    mode only (same restriction the existing XLSX export has; G60/Multi-QAF have their
    own dedicated exports)
  → "Copilot-Export ▾": Word (DOCX) / Markdown (KI-Chat)

Wertstrom-Editor          /wertstrom/[id]
  → top toolbar, next to "Synchronisieren" — net-new, the Wertstrom module had no
    export before this
  → "Copilot-Export ▾": Word (DOCX) / Markdown (KI-Chat)

Gesamtprojekt-Export      /project/[id]/export
  → two buttons, below the existing PDF/CSV/Full-Report buttons:
    "Gesamtprojekt-Export (Copilot) ▾": Word (DOCX) / Markdown (KI-Chat)
    "Gesamtprojekt-Export (Copilot, XLSX)": single format, unchanged
```

### States
- Idle: "Copilot-Export" (plain button) or "Copilot-Export ▾" (format-choice dropdown, 2+
  formats) / busy: spinner + "Erzeuge…" (button disabled, dropdown closed)
- Done: file download triggered (`.docx` / `.md` / `.xlsx`, matching the chosen format)
- Error: inline red text below the button (e.g. flag disabled, no access, unsupported
  QAF comparison mode)

### iOS
Not planned for this PR — Demo-067 is a web-only Top-Management demo feature. Flag stays
`false` in `default`, so no iOS-facing behavior changes.

---

## 12. Management / Analytics

**Route**: `/management` | **iOS**: `AnalyticsView`

### Layout
```
KPI Cards (4):
  - Total Visits
  - Avg Measurements per Visit
  - Visits with Open Actions
  - Total Time Savings Potential (sec)

Bottleneck Chart:
  Bar chart: Visit → bottleneck CT
  Color: green (≤target) / yellow (≤takt) / red (>takt)

Visit Table:
  Supplier | Date | Bottleneck Station | CT | vs Takt | Actions
```

---

## 13. Account / Profile

**Route**: `/account` | **iOS**: `AccountView`

### Sections
1. **Avatar**: Round photo, tap to upload (camera/library on iOS)
2. **Profile**: Full name, Department, Language (en/de)
3. **Email**: Change email (confirmation required)
4. **Password**: Change password (min 6 chars)

---

## 13a. Admin Demo-Verwaltung (KAR-981)

**Component**: `components/demo/demo-control-panel.tsx` (floating "Demo"-Button, admin/masteradmin only) | **iOS**: n/a (web-only ops tool)

Existing flow, unchanged by KAR-981 — documented here only because a new module now
appears in its checkbox list: `top-management-demo` ("Top-Management-Demo 2026_067"),
alongside Projektanlage/Kalender/LSC Workshop/Fabrikanalyse/OEE/Wertstrom/Datenablage/
Auftragseingang. Selecting it and clicking "Laden" seeds the complete, self-contained BMW
top-management demo project 2026_067 (Demo Electronics Systems GmbH, BDC-X3) — one project
spanning Agenda/LSC-Workshop, Fabrikanalyse (202 Antworten), QAF-Vergleich (Award vs.
Current) and Wertstrom in a single click, distinct from the other modules' shared 5-project
demo pool. "Zurücksetzen" removes only this pack's own project and its data — see
`lib/demo/seeder.ts` (`seedPack`/`removePack('top_management_demo')`).

---

## 14. Shared UX Patterns

### Empty States
Every list screen must have an empty state with:
- Explanatory text
- Primary CTA button

### Destructive Actions
Always require confirmation:
- Delete visit ("Delete this visit and all data?")
- Delete station ("Delete this station and all measurements?")
- Delete measurement (no confirm, undo pattern preferred)

### Loading States
- Skeleton screens for initial page loads
- Inline spinner for button actions
- Optimistic UI for quick operations (add measurement, toggle status)

### Error Handling
- Inline form validation before submit
- Toast / alert for network errors
- Specific messages (not generic "Something went wrong")

### Pull to Refresh (iOS)
Available on: Visit List, Visit Detail, Workshop, Shift Output

### Navigation
- Web: Back button in header (← arrow)
- iOS: Standard NavigationStack back swipe
