# Web-Sync-Notizen (rakku-v2 → Mitarbeiter-App)

Diese Datei listet alle Änderungen an der Web-App (`/var/www/html/rakku-v2`), die für die Mitarbeiter-App relevant sein können — neue Server-Actions/API-Felder, geänderte Datenmodelle, neues Verhalten. Gedacht als Abgleichspunkt zwischen den beiden Claude-Sessions, die parallel an Web und Desktop-App arbeiten. Bitte beim Nachziehen einer Änderung den jeweiligen Eintrag mit `[x] erledigt in Desktop-App` markieren oder ergänzen, falls sich beim Nachbauen Abweichungen ergeben.

Stand: 13.09.2026.

---

## ✅ Punkte 17, 20, 22, 23, 25 in der Desktop-App nachgezogen (13.09.2026, App v2.43.0) — siehe Punkt 28 für Details.

## 🔴 Los geht's hier: Punkte 17–27 (neu, 13.09.2026)

Zwischen dem letzten Sync (Punkt 16, 01.09., App v2.42.0) und heute lag eine
lange Web-Session mit zwei sehr unterschiedlichen Teilen:

1. **Ein kompletter visueller Redesign-Durchlauf** der öffentlichen Seiten
   (Homepage, Portfolio, Über mich, Angebote, Gutscheine, Conventions, Blog,
   FAQ, Header-Nav) — **null Desktop-Bezug**, reines CSS/Layout auf
   Kundenseiten. Kurz zusammengefasst unter Punkt 26, nicht im Detail
   aufgeführt (>100 einzelne Feintuning-Commits).
2. **Ein großer Ausbau des ToDo-Systems** (Struktur/Ansicht/Zusammenarbeit/
   Automatik, Punkt 17) plus mehrere kleinere Admin-Features (Conventions,
   Rabattcodes, Kalender, Gutschein-Schalter, Charakter-Wunsch-Status,
   Systemprotokoll) — **das ist der eigentlich relevante Teil für den
   "Umbau der Mitarbeiter App"**.

**Empfohlene Reihenfolge für diese Runde:** zuerst Punkt 17+20 (ToDo-System —
mit Abstand größte Lücke, Team-API hat die neuen Felder teils schon, die
App-UI noch gar nicht), danach 22–25 nach Bedarf/Priorität des Nutzers,
Punkt 21 (Systemprotokoll) ist ein optionaler Neuzugang, kein Nachzieh-Zwang.

---

## 1. Datumsformatierung site-weit (zweistellig)

`Date.toLocaleDateString("de-DE")` ohne explizite Optionen füllt Tag/Monat nicht mit führender Null (ergab "23.8.2026" statt "23.08.2026").

- Neue Helper in `src/lib/orderConstants.ts`: `formatDateDE(date)`, `formatDateTimeDE(date)`, `formatDateLocale(date, locale)` — alle mit explizitem `{ day: "2-digit", month: "2-digit", year: "numeric" }`.
- **Für die Desktop-App relevant, falls dort eigene Datumsformatierung existiert** — gleiches Muster prüfen/übernehmen.

## 2. Checkbox/Radio-Styling (nur Web)

Rein kosmetisch, `src/app/globals.css`. Desktop-App hat eigenes Stylesheet — nicht dringend, aber ggf. später angleichen (leicht abgerundete Ecken bei Checkboxen, rund bei Radios, helle Füllung mit dunklem Haken/Punkt).

## [Desktop-App-Status: 25.08.2026, Session claude-e8]

Punkte 1–7 alle geprüft/nachgezogen, App bei v2.35.0. Details je Eintrag unten markiert. Punkt 2 bewusst offen (kosmetisch, geringe Priorität).

## 3. Foto-Ordner-Sichtbarkeit — drei Bugs behoben

**Modell:** `src/models/ClientPhotoFolder.ts` — Felder `clientId`, `orderId` (nullable), `released` (bool).

- **Admin-Auftragsseite zeigte alle Ordner des Kunden, nicht nur die des Auftrags.**
  `src/app/[locale]/admin/orders/[orderId]/page.tsx`: Query von `ClientPhotoFolder.find({ clientId })` → `ClientPhotoFolder.find({ orderId: order._id })` geändert.
  **Falls die Desktop-App denselben Auftrags-Foto-Panel-Endpunkt nutzt (`/api/team/v1/...`), dort dieselbe Filterung prüfen** — laut Peer-Session (claude-e8) betraf das auch `src/lib/orderDetail.ts` (intern von `/api/team/v1/orders/[orderId]` genutzt), wurde dort bereits nachgezogen.
  **[x] erledigt in Desktop-App** — `orderDetail.ts` auf `orderId`-Filter umgestellt (25.08., vor diesem Sync-Notes-Eintrag).

- **Kundenseite zeigte gar keine Fotos zum jeweiligen Auftrag.**
  Neue Sektion in `src/app/[locale]/account/orders/[orderId]/page.tsx`: Query `ClientPhotoFolder.find({ orderId: order._id, released: true })`, verlinkt zu `/account/photos/[folderId]`. Rein Web-Kundenseite, für die Mitarbeiter-App vermutlich nicht relevant.

- **Ordner waren automatisch freigegeben, sobald sie über einen Auftrag zugewiesen wurden** (ohne dass jemand "Ordner freigeben" geklickt hat).
  `src/lib/actions/admin.ts` (`assignPhotoFolderAction`): `released: Boolean(orderId)` → `released: false` (immer). Schema-Default in `ClientPhotoFolder.ts` ebenfalls `true` → `false` geändert.
  **Wichtig, falls die Desktop-App eigene Ordner-Zuweisungs-Logik hat statt dieselbe `assignPhotoFolderAction` zu nutzen** — dann dort denselben Fix nachziehen. Falls sie dieselbe Server-Action aufruft: nichts zu tun.
  **[x] nichts zu tun** — Desktop-App ruft `assignPhotoFolderAction` direkt über `/api/team/v1/photo-folders` auf, Fix automatisch geerbt.

## 4. Auftrag abschließen + Feedback-Anfrage

- **Neue Server-Action** `markOrderCompletedAction(orderId)` in `src/lib/actions/admin.ts`:
  - Setzt `tag` auf `"done_phase1"` (nur wenn noch nicht `done_*`)
  - Loggt `OrderActivity` (`tag_changed`)
  - Löst die Feedback-Anfrage-Mail aus (`OrderFeedback`-Dokument + `notifyClientFeedbackRequest`), **genau einmal pro Auftrag** (eindeutiger Index auf `orderId` in `OrderFeedback`)
- **Neue UI:** `src/components/admin/MarkOrderCompletedButton.tsx` — grüner "✓ Auftrag abschließen"-Button neben Status-Pill/Archivieren auf der Admin-Auftragsseite; zeigt "✓ Auftrag abgeschlossen" sobald der Tag `done_*` ist.
- **`archiveOrderAction` teilt sich jetzt dieselbe Logik** (neuer interner Helper `markCompletedIfNeeded`) — Archivieren markiert den Auftrag jetzt IMMER auch als abgeschlossen (tag → `done_phase1`), falls er nicht schon `done_*` war. Vorher konnte ein archivierter Auftrag beim Kunden noch einen alten Zwischenstatus zeigen, obwohl schon die Feedback-Mail raus war — das ist jetzt konsistent.
- **Kundenseite** (`account/orders/[orderId]/page.tsx`): grünes Abschluss-Banner bei `tag === "done_phase1" || "done_posted"` (nicht bei `"done_dropped"`, das ist eine Absage) mit "Bewertung abgeben"-Button (Link zu `/feedback/{token}`, Token aus `OrderFeedback`), wechselt zu Dank-Text sobald `submitted: true`.
- **Neue i18n-Keys** in `messages/de.json` / `messages/en.json` unter `orders`: `photosTitle`, `photosCount`, `completedTitle`, `completedText`, `leaveFeedback`, `feedbackThanks`.

**Für die Desktop-App:** Falls dort ein eigener "Archivieren"-Button existiert, der NICHT über `archiveOrderAction` läuft, sondern eigenes API-Handling hat — sollte er idealerweise ebenfalls den Tag auf `done_phase1` setzen (außer bei bestehendem `done_*`-Tag), damit Web- und Kunden-Ansicht konsistent bleiben. Ein eigener "Auftrag abschließen"-Button (Pendant zu `MarkOrderCompletedButton`) wäre ebenfalls sinnvoll, damit Personal nicht zwischen App und Web wechseln muss.
**[x] erledigt in Desktop-App** — Archivieren-Button ruft `archiveOrderAction` direkt auf (Fix automatisch geerbt); eigener "✓ Auftrag abschließen"-Button (Pendant zu `MarkOrderCompletedButton`) existiert in `OrderDetailScreen.tsx`, ruft `markOrderCompletedAction` über `/api/team/v1/orders/[orderId]/complete`.

## 5. Auftragstitel umbenennbar

- **Neue Server-Action** `updateOrderTitleAction(orderId, title)` in `src/lib/actions/admin.ts` — trimmt, kappt bei 300 Zeichen (Schema-Limit von `characterOrConcept`), braucht `orders_edit`-Berechtigung.
- **Neue UI:** `src/components/admin/OrderTitleEditor.tsx` — Titel auf der Admin-Auftragsseite ist jetzt per ✎-Stift inline editierbar (Eingabefeld + Speichern/Abbrechen statt statischem `<h1>`).
- **Für die Desktop-App:** Falls der Auftragstitel dort angezeigt wird, wäre ein Pendant-Button/Dialog zum Umbenennen sinnvoll, der dieselbe Action aufruft.
**[x] erledigt in Desktop-App** — `OrderTitleEditor`-Pendant in `OrderDetailScreen.tsx`, ruft `updateOrderTitleAction` über `/api/team/v1/orders/[orderId]/title`.

## 6. Status-Wechsel-Mail nur noch bei Phasenwechsel

- `updateOrderTagAction` in `src/lib/actions/admin.ts`: die generische "Status geändert"-Mail an den Kunden wird jetzt **nur noch bei echtem Phasenwechsel** verschickt (Shoot → Edit → Done, erkannt am Tag-Präfix vor dem ersten `_`), nicht mehr bei jedem kleineren Statuswechsel innerhalb derselben Phase (z. B. "Warten auf Termin" → "Termin geplant" verschickt jetzt keine Mail mehr, "Fotos sichten" → "Shooting abgeschlossen"-artige Phasenwechsel weiterhin schon).
- Neuer interner Helper `tagPhase(tag)` (Präfix vor `_`).
- **Für die Desktop-App:** Falls dort eine eigene Tag-Änderungs-Logik mit eigenem Mail-Trigger existiert (statt `updateOrderTagAction` zu nutzen) — dieselbe Phasenwechsel-Prüfung nachziehen, sonst verschickt die App bei jedem Klick weiterhin unnötige Mails, während die Website das nicht mehr tut.
  **[x] nichts zu tun** — Status-Dropdown in `OrderDetailScreen.tsx` ruft `updateOrderTagAction` direkt über `/api/team/v1/orders/[orderId]/tag` auf, Fix automatisch geerbt.

## 7. Foto-Ordner nachträglich einem Auftrag zuordnen

- `updatePhotoFolderAction` in `src/lib/actions/admin.ts` akzeptiert jetzt zusätzlich ein `orderId`-Feld im FormData — leer = keinem Auftrag zugeordnet, sonst muss der Zielauftrag zum selben Kunden gehören (sonst wird die Änderung ignoriert).
- `src/components/admin/AdminPhotoPanel.tsx`: neue `orders`-Prop (Liste `{id, label}` der Aufträge des Kunden) + neues Auswahlfeld "Zugeordneter Auftrag" im Bearbeiten-Formular jedes Ordners.
- Beide Seiten, die `AdminPhotoPanel` nutzen (`admin/orders/[orderId]/page.tsx`, `admin/clients/[clientId]/page.tsx`), laden jetzt zusätzlich die Aufträge des Kunden (`Order.find({ clientId })`) für dieses Dropdown.
- **Hintergrund:** Durch Punkt 3 (Ordner nur noch auftrags-scoped sichtbar) gab es vorher keine Möglichkeit mehr, einen allgemein zugewiesenen Ordner (`orderId: null`) nachträglich einem bestimmten Auftrag zuzuordnen — das schließt diese Lücke.
- **Für die Desktop-App:** Falls dort ebenfalls Foto-Ordner verwaltet werden, wäre ein Pendant-Dropdown sinnvoll — nutzt dieselbe Server-Action, falls die App sie direkt aufruft.
  **[x] erledigt in Desktop-App (nur Auftragsseite)** — `orderDetail.ts` liefert jetzt zusätzlich `clientOrders` ({id,label} der Aufträge des Kunden), `/api/team/v1/photo-folders/[folderId]` PATCH reicht `orderId` an `updatePhotoFolderAction` durch, `PhotoFolderPanel` in `OrderDetailScreen.tsx` hat das "Zugeordneter Auftrag"-Dropdown im Bearbeiten-Formular. **Offen:** Die Desktop-`ClientDetailScreen` hat noch gar kein Foto-Ordner-Panel (anders als `admin/clients/[clientId]/page.tsx`, das `AdminPhotoPanel` inkl. neuem Dropdown nutzt) — wurde in dieser Session bewusst nicht gebaut, siehe "Bekannte offene Punkte" unten.

---

## 8. E-Mail-Zustellbarkeit (Plain-Text-Alternative + async `emailShell`)

- `emailShell` (Layout-Wrapper für alle Mails) ist jetzt async, holt die Firmenadresse für die Fußzeile.
- `mail.ts`: alle ausgehenden Mails bekommen zusätzlich eine Plain-Text-Alternative (bessere Zustellbarkeit/Spam-Score).
- Betroffen: ~10 Dateien unter `src/lib/` (`accountDeletion.ts`, `auth.ts`, `conventions.ts`, `feedback.ts`, `loyalty.ts`, `orderEmails.ts`, `preshootReminder.ts`, `probationReminders.ts`, `reengagement.ts`, `voucherEmails.ts`, `voucherExpiry.ts`, `weatherCheck.ts`, `yearlyRecap.ts`, `emailTemplate.ts`) — durchgehend nur `await` an bestehenden `emailShell(...)`-Aufrufstellen ergänzt.
- Öffentliche Signaturen von `sendMail`/`sendMailIfEnabled` in `mail.ts` unverändert.
- **[x] nichts zu tun** — rein interne Mail-Rendering-Änderung, keine Server-Action-Signaturen geändert, die die App aufruft. Geprüft (25.08., Session claude-e8).

## 9. Kunden-Feedback → öffentliches Sterne-System (mit Team-Freigabe)

Bisher floss eine `OrderFeedback`-Bewertung (1-5 Sterne + optionaler Kommentar, siehe Punkt 4) nie in die öffentlich sichtbaren Bewertungen ein — auch nicht nach der bestehenden "Als Kundenstimme übernehmen"-Konvertierung, die das `rating`-Feld gar nicht mit kopiert hat. Jetzt gibt es einen zweiten, unabhängigen Freigabe-Weg speziell für die reine Sternebewertung (auch ohne Kommentar nutzbar), plus eine dauerhafte Durchschnitts-Anzeige auf der Startseite.

- **Modell:** `src/models/OrderFeedback.ts` — neues Feld `usedForQuickReview: boolean` (Default `false`), analog zu `usedForTestimonial`.
- **Neue Server-Action** `createQuickReviewFromFeedbackAction(feedbackId)` in `src/lib/actions/feedback.ts` (Berechtigung `testimonials_create`, wie die bestehende Testimonial-Konvertierung): erstellt aus `feedback.rating` + `feedback.clientName` einen `QuickReview`-Eintrag, **sofort `visible: true`** (der Klick selbst ist die Freigabe-Entscheidung des Teams — kein zweiter Gegenlese-Schritt wie bei Kundenstimmen, weil hier kein Zitat-Text redigiert werden muss). Setzt danach `feedback.usedForQuickReview = true`.
- **Bugfix in `createTestimonialFromFeedbackAction`:** kopiert jetzt zusätzlich `rating: feedback.rating` in die neu erstellte `Testimonial` (vorher blieb das Rating-Feld dort immer `0`, wodurch konvertierte Kundenstimmen nie in den Durchschnitt eingerechnet wurden).
- **Admin-UI:** `src/components/admin/FeedbackRow.tsx` + `src/app/[locale]/admin/feedback/page.tsx` — neuer Button "Als Sterne-Bewertung freigeben" neben dem bestehenden "Als Kundenstimme übernehmen"-Button, beide hinter demselben `item.allowTestimonial`-Consent-Flag (der Text dieser Checkbox im Kunden-Formular deckt "mein Feedback darf verwendet werden" generisch ab, nicht nur den Kommentar — daher bewusst dasselbe Consent-Flag für beide Wege, keine neue Einwilligung nötig). Nach Klick: "✓ Sterne-Bewertung übernommen".
- **Startseite** (`src/app/[locale]/page.tsx`): neue dauerhafte Sektion direkt unter dem Hero — "Unsere Kunden sind sehr zufrieden" + die bereits bestehende (aber bisher auf der Startseite ungenutzte) `CombinedReviewsBadge`-Komponente aus `ReviewsWidget.tsx` (zeigt gerundeten Sterne-Durchschnitt + Anzahl). Zeigt sich automatisch gar nicht, solange es keine bewertete Kundenstimme/Sterne-Bewertung gibt.
- **Wichtig — warum das die "Fake-Bewertungen"-Anforderung erfüllt:** Der Durchschnitt auf der Startseite berechnet sich ausschließlich aus `Testimonial`/`QuickReview`-Einträgen mit `visible: true`. Eine rohe `OrderFeedback`-Bewertung fließt NIEMALS automatisch ein — nur nachdem ein Teammitglied bewusst einen der beiden Freigabe-Buttons geklickt hat. Der Kunde selbst bekommt von dieser Freigabe/Nicht-Freigabe nichts mit (kein Status, keine Benachrichtigung) — exakt wie beim bestehenden Kundenstimme-Prinzip.
- **Neue i18n-Keys** in `messages/de.json`/`messages/en.json` unter `home`: `happyCustomersText`, `reviewsLabel`, `reviewsCountLabel`.

**Für die Desktop-App:** Falls dort ein eigenes Feedback-Übersichts-Screen existiert (Pendant zu `admin/feedback`) — ein Pendant-Button "Als Sterne-Bewertung freigeben" wäre sinnvoll, der `createQuickReviewFromFeedbackAction` aufruft. Die Startseiten-Änderung ist rein Web (kein Desktop-App-Bezug).
**[ ] offen** — Desktop-App hat aktuell gar kein Feedback-Screen (Pendant zu `admin/feedback`, bereits vor dieser Session als fehlender Bereich bekannt) — Freigabe-Button kann erst dazu, wenn der Screen selbst gebaut wird. Nicht Teil der 2FA-Bugfix-Runde (25.08., claude-ba).

## 10. Fehlende Feedback-Anfrage bei direktem Tag-Wechsel auf "abgeschlossen"

Lücke gefunden: Wenn ein Auftrag NICHT über "Auftrag abschließen"/Archivieren, sondern direkt über das generische Status-Dropdown auf `done_phase1`/`done_posted` gesetzt wurde, zeigte die Kundenseite zwar das grüne "Auftrag abgeschlossen"-Banner (Punkt 4), aber **kein** "Bewertung abgeben"-Button — weil dafür ein `OrderFeedback`-Datensatz mit Token nötig ist, den bis dahin nur `markOrderCompletedAction`/`archiveOrderAction` erzeugt haben, nicht das einfache Tag-Ändern.

- `updateOrderTagAction` in `src/lib/actions/admin.ts` ruft jetzt ebenfalls `sendFeedbackRequestIfNeeded(orderId)` auf, sobald der NEUE Tag `done_phase1` oder `done_posted` ist — unabhängig vom bisherigen Tag. Dedupliziert wie gehabt über den eindeutigen `orderId`-Index auf `OrderFeedback`, also gefahrlos auch bei mehrfachem Setzen desselben Tags.
- Betraf mindestens einen echten, laufenden Auftrag (dessen Tag schon vor dieser Session-Änderung auf `done_phase1` stand) — dafür wurde die fehlende Feedback-Anfrage nachträglich über `archiveOrderAction` ausgelöst (Auftrag ist jetzt archiviert **und** die Kundin/der Kunde hat die echte Bewertungs-Mail bekommen).
- **Für die Desktop-App:** Falls dort eine eigene Tag-Änderungs-Logik existiert statt `updateOrderTagAction` direkt aufzurufen (siehe schon Punkt 6) — dieselbe Ergänzung nachziehen, sonst bleibt genau diese Lücke dort bestehen.
  **[x] nichts zu tun** — Status-Dropdown ruft `updateOrderTagAction` direkt auf (siehe Punkt 6), Fix automatisch geerbt. Geprüft 25.08., claude-ba.

## 11. [Desktop-App] 2FA-Login schlug in der App fehl ("Anmeldung abgelaufen")

Bug gefunden über Nutzer-Meldung ("2FA geht in der App nicht, Anmeldung fehlgeschlagen"), nicht von Web-Seite verursacht — trotzdem hier dokumentiert, weil der Fix in `src/lib/auth.ts` liegt (geteilte Datei).

- Ursache: `createPending2faToken`/`createPendingPasswordChangeToken` in `src/lib/auth.ts` setzten die Zwischenschritt-Cookie (`rakku_2fa_pending`/`rakku_pwchange_pending`) immer mit `SameSite=Lax` — derselbe Bug wie beim ursprünglichen Session-Cookie (siehe `createSession`/`crossOrigin`-Kommentar weiter oben in der Datei), nur beim 2FA-Zwischenschritt statt beim eigentlichen Login. Cookie kam beim Login-Request an, aber der zweite Request (Code-Eingabe) aus der App ist Cross-Origin — `SameSite=Lax` wird da vom Browser nicht mitgeschickt, `getPending2faPayload()` liefert `null`, App zeigt "Die Anmeldung ist abgelaufen — bitte erneut einloggen."
- Fix: beide Funktionen haben jetzt einen `crossOrigin`-Parameter (Default `false`, wie bei `createSession`). `src/app/api/team/v1/login/route.ts` und `src/app/api/team/v1/2fa/route.ts` rufen sie jetzt mit `crossOrigin=true` auf. Web-Login (`src/lib/actions/auth.ts`) unverändert bei `SameSite=Lax`.
- **Rein serverseitiger Fix, kein Desktop-App-Update nötig** — mit dem nächsten Login-Versuch funktioniert 2FA sofort.
- Behoben, gebaut, deployt (25.08., Session claude-ba — vormals u. a. claude-e8, Sessionwechsel durch Kontext-Kompaktierung).

## 12. Testimonial-Modell: neues Feld `realName`

Nutzer-Wunsch: bei Kundenstimmen mit Handle/Spitzname (z. B. Cosplay-Namen) soll optional der echte Name davor stehen, das Handle bleibt in Anführungszeichen — Anzeige-Format `Max „Zoneks"`. Zusätzlich Layout-Umbau der Testimonial-Karte auf der Startseite (Profilbild jetzt oben, vergrößert, ragt zur Hälfte über den Kartenrand; Name+Sterne direkt darunter als Zeile).

- **Modell:** `src/models/Testimonial.ts` — neues Feld `realName: string` (Default `""`, optional, max. 100 Zeichen). Rein additiv, kein Migrations-Schritt nötig — bestehende Dokumente haben das Feld einfach nicht gesetzt (Mongoose liefert dafür automatisch den Default).
- **Server-Actions** (`src/lib/actions/testimonials.ts`): `createTestimonialAction` und `updateTestimonialAction` lesen/speichern jetzt zusätzlich `formData.get("realName")`.
- **Web-Admin-UI:** `NewTestimonialForm.tsx` + `TestimonialRow.tsx` haben ein neues Eingabefeld "Echter Name (optional)" neben dem bestehenden Name/Handle-Feld.
- **Anzeige:** `ReviewsWidget.tsx` (`TestimonialCard`) — `displayName = realName ? \`${realName} „${authorName}"\` : authorName`. Gilt für Startseite (`src/app/[locale]/page.tsx`) und überall sonst, wo `TestimonialItem` durchgereicht wird (`src/lib/reviewList.ts`).
- **API für die Desktop-App:** `/api/team/v1/testimonials` und `/api/team/v1/testimonials/[itemId]` geben `.lean()`-Dokumente direkt als JSON zurück — `realName` kommt dort automatisch mit, keine Route-Änderung nötig.
- **Für die Desktop-App:** Testimonial-Typ + Create/Edit-Formulare dort müssten `realName` ergänzen, wenn Kundenstimmen auch über die App gepflegt werden sollen. claude-ba übernimmt das laut eigener Ankündigung.
  **[x] teilweise erledigt in Desktop-App** — `Testimonial`-Typ (`api.ts`) hat `realName`; Erstellen-Formular in `ReviewsScreen.tsx` (`TestimonialsTab`) hat das neue Feld "Echter Name (optional)" + schickt es mit; Listenanzeige nutzt dasselbe `realName „authorName"`-Format. **Nicht ergänzt:** die Desktop-App hat kein Bearbeiten-Formular für bestehende Kundenstimmen (nur Anlegen/Löschen/Sichtbarkeit) — das Karten-Layout auf der Startseite ist ohnehin rein Web. Gebaut, App v2.36.0, veröffentlicht (01.09., claude-ba).
- Getestet: die eine bestehende Kundenstimme ("Zoneks") war seit Erstellung nie tatsächlich auf `visible: true` gesetzt worden (Nutzer hatte "Als Kundenstimme übernehmen" mit der separaten Sichtbarkeits-Freigabe verwechselt) — dabei direkt live auf sichtbar gesetzt, Rendering auf der Startseite bestätigt.
- Gebaut (`tsc --noEmit` + `next build` sauber), `pm2 restart rakku-v2 --update-env`, live verifiziert (01.09., diese Session).

## 13. [Desktop-App] Kundenstimmen & Kurzbewertungen: Bearbeiten + Reihenfolge

Teil der großen Paritäts-Runde ("alles was im Web-Admin geht, soll auch in der App gehen") — Audit hatte für Punkt 9 (`Reviews`) festgestellt: weder Kundenstimmen noch Kurzbewertungen ließen sich in der App bearbeiten oder umsortieren (nur Anlegen/Löschen/Sichtbarkeit).

- **API** `src/app/api/team/v1/testimonials/[itemId]/route.ts` — neuer `PATCH`-Handler, unterscheidet per `Content-Type`: JSON → nur `{direction}`-Reorder (`moveTestimonialAction`); Multipart → vollständiger Edit-Durchreich an `updateTestimonialAction` (inkl. optionalem neuem Bild, `rating`/`reviewUrl` nur bei `source === "google"`, exakt wie die Server-Action selbst filtert).
- **API** `src/app/api/team/v1/quick-reviews/[itemId]/route.ts` — neuer `PATCH`-Handler, ebenfalls JSON-Reorder oder Edit (`authorName`/`rating`) via `updateQuickReviewAction`.
- **Desktop-UI** `ReviewsScreen.tsx` komplett umgebaut: neue `TestimonialRow`/`QuickReviewRow`-Komponenten (Muster wie `FaqScreen.tsx`s `EditableItem`) mit Auf/Ab-Reorder-Pfeilen + aufklappbarem Bearbeiten-Formular (Kundenstimmen: authorName/realName/quote/quoteEn/context/contextEn, bei Google-Quelle zusätzlich rating/reviewUrl, optional neues Profilbild; Kurzbewertungen: authorName/rating). Anlege-Formular für Kundenstimmen hat jetzt auch `quoteEn`/`contextEn`-Felder (fehlten vorher).
- Getypt (`tsc -b` sauber), Web gebaut+deployt, App-Version 2.39.0 gebaut und veröffentlicht (01.09., claude-ba).
- **[x] erledigt** — vollständige Parität zum Web für beide Tabs.

## 14. [Desktop-App] Neuer Screen: Feedback

Teil der großen Paritäts-Runde — war laut Audit der höchstwertige komplett fehlende Bereich (kein Screen, keine API überhaupt). Volle Parität zu `admin/feedback/page.tsx` inkl. der beiden Konvertierungs-Buttons aus Punkt 9.

- **API** `src/app/api/team/v1/feedback/route.ts` (neu, GET) — Berechtigung `orders_view` (wie die Web-Seite), liefert dieselben Felder wie `FeedbackRow` sie braucht plus `sentCount`.
- **API** `src/app/api/team/v1/feedback/[feedbackId]/convert-testimonial/route.ts` und `.../convert-quick-review/route.ts` (beide neu, POST) — rufen `createTestimonialFromFeedbackAction`/`createQuickReviewFromFeedbackAction` direkt auf (Berechtigungsprüfung `testimonials_create` steckt schon in der Action selbst).
- **Desktop-UI** `FeedbackScreen.tsx` (neu) — Stat-Kacheln (Antworten/Ø Bewertung/Für Kundenstimme freigegeben) + Liste mit denselben zwei Freigabe-Buttons wie im Web, Link zum jeweiligen Auftrag. Registriert in `App.tsx` (`/feedback`) und `AppShell.tsx`-Nav (Kategorie „Aufträge", Berechtigung `orders_view`, an derselben Position wie im Web zwischen Kunden und Ablehnungsgründe).
- Getypt (`tsc -b` sauber), Web gebaut+deployt, App-Version 2.40.0 gebaut und veröffentlicht (01.09., claude-ba).
- **[x] erledigt** — vollständige Parität zum Web.

## 15. [Desktop-App] Intranet: von reinem Lesen auf volle CRUD-Parität

War laut Audit einer der schwersten Lücken — die App-Seite (`TeamScreen.tsx`, Tab "Intranet") konnte bisher nur lesen, keine Beiträge anlegen/bearbeiten/verschieben/löschen, obwohl die API dafür komplett fehlte (`/api/team/v1/intranet` war reines GET).

- **API** `src/app/api/team/v1/intranet/route.ts` — GET liefert jetzt zusätzlich `departments` (für die Abteilungs-Auswahl im Formular) sowie `canCreate`/`canEdit`/`canDelete`-Flags; neues POST (`createIntranetArticleAction`).
- **API** `src/app/api/team/v1/intranet/[articleId]/route.ts` (neu) — PATCH (Reorder via `direction`, oder voller Edit inkl. `departmentIds`/`pinned` via `updateIntranetArticleAction`) + DELETE (`deleteIntranetArticleAction`).
- **API** `src/app/api/team/v1/intranet/[articleId]/visible/route.ts` (neu) — POST (`setIntranetArticleVisibleAction`).
- **Lib** `src/lib/intranetList.ts` — `getVisibleIntranetArticles` gibt jetzt zusätzlich `departmentIds` mit zurück (additiv, für die Bearbeiten-Vorbefüllung in der App nötig; Web-Admin-Seite hatte das schon über einen eigenen Query, jetzt auch im geteilten Helfer).
- **Desktop-UI** `TeamScreen.tsx`s `IntranetTab` komplett umgebaut: Anlege-Formular (Titel/Inhalt/Kategorie/Abteilungs-Mehrfachauswahl/Angepinnt-Checkbox, nur sichtbar bei `canCreate`), neue `IntranetArticleRow`-Komponente mit Auf/Ab-Reorder + Sichtbarkeits-Toggle + aufklappbarem Bearbeiten-Formular + Löschen (alle nur bei entsprechender Berechtigung sichtbar) sowie dem bestehenden Aufklapp-zum-Lesen-Verhalten für alle.
- Getypt (`tsc -b` sauber), Web gebaut+deployt, App-Version 2.41.0 gebaut und veröffentlicht (01.09., claude-ba).
- **[x] erledigt** — volle Parität zum Web (bewusst ohne die reine Freitextsuche aus `IntranetBrowser.tsx`, da bei üblicher Artikelanzahl in der App-Liste verzichtbar; kann bei Bedarf leicht ergänzt werden).

## 16. [Desktop-App] ToDos: von Mini-Checkliste auf volle Parität

War laut Audit die schwerste ToDo-Lücke — die App hatte nur Titel/Erledigt-Haken/Löschen, die API selbst konnte kein Bearbeiten und keine Notizen. Jetzt komplette Parität zu `TodoBoard.tsx`/`TodoItem.tsx`/`NewTodoForm.tsx`.

- **Lib** `src/lib/todoList.ts` — `getTodoList` gibt jetzt zusätzlich `orderId`/`notes`/`recurrence` zurück (vorher nur die reduzierte Teilmenge, die die alte simple App-Liste brauchte). **Bonus-Aufräumarbeit:** `admin/todos/page.tsx` hatte bisher eine eigene, abweichende Inline-Query mit demselben vollen Feldsatz dupliziert — jetzt nutzt die Web-Seite selbst auch `getTodoList`, eine einzige Quelle statt zweier, die hätten auseinanderlaufen können.
- **API** `src/app/api/team/v1/todos/route.ts` — GET liefert jetzt zusätzlich `staffList`/`orderOptions`/`viewerId`/`canViewAll`/`canCreateForOthers`/`canEditOthers`/`canDeleteOthers` (fürs Zuständig-Dropdown, Auftragsbezug, Sichtbarkeits-Scope in der App); POST forwarded jetzt auch `assignedToId`/`orderId`/`recurrence` (vorher stillschweigend nur title/description/dueDate/priority — Fremdzuweisung & Auftragsbezug & Wiederholung gingen über die App also gar nicht).
- **API** `src/app/api/team/v1/todos/[todoId]/route.ts` — neues PATCH (voller Edit via `updateTodoAction`, inkl. derselben Fremdzuweisungs-Berechtigungsprüfung).
- **API** `src/app/api/team/v1/todos/[todoId]/notes/route.ts` (neu) — POST (`addTodoNoteAction`).
- **Desktop-UI** `TeamScreen.tsx`s `TodosTab` komplett neu gebaut nach dem Web-Vorbild: Stat-Kacheln (Offen/In Bearbeitung/Überfällig/Erledigt), Mein-vs-Alle-Scope, Status-/Zuständigen-Filter, volles Anlege-Formular (Beschreibung/Zuständig/Priorität/Fälligkeitsdatum/Wiederholung/Auftragsbezug), pro ToDo: Status-Dropdown, Prioritäts-Farbmarkierung, Überfällig-Hervorhebung, aufklappbare Details mit Bearbeiten-Formular + Notizen-Verlauf + Notiz hinzufügen + Löschen — jeweils rechte-gesteuert (eigene ToDos immer, fremde nur mit `todos_edit`/`todos_delete`).
- Getypt (`tsc -b` sauber), Web gebaut+deployt, App-Version 2.42.0 gebaut und veröffentlicht (01.09., claude-ba).
- **[x] erledigt** — volle Parität zum Web.

---

## 17. [GROSS] ToDo-System: Struktur, Ansicht, Zusammenarbeit, Automatik

Nutzer-Wunsch: "muss um Weiten vergrößert und erweitert werden". Umgesetzt in
sechs Web-Commits (`547fea8` Unteraufgaben/Labels/Projekte → `995f104`
Vorlagen → `bd4346b` Ansichten → `36a2671` Mehrfachauswahl → `7f507d6`
Beobachter/weitere Zuständige/@Erwähnungen/Verlauf → `307625b` Anhänge →
`e2a83c6` Erinnerung+Automatik-ToDos → `0031670` Wiederholung ausgebaut).
Das `Todo`-Dokument hat sich dabei fast verdoppelt.

**Modell `src/models/Todo.ts` — vollständige aktuelle Feldliste:**

```
title, description, status, priority, dueDate, orderId (optional),
assignedToId, additionalAssigneeIds[] (dürfen wie Haupt-Zuständige bearbeiten),
watcherIds[] (nur Benachrichtigungen),
labels: string[] (Freitext, Farbe wird CLIENTSEITIG aus dem Namen abgeleitet,
  siehe labelColor() in todoConstants.ts — deterministisches HSL, kein
  Farbfeld in der DB),
project: string (Freitext-Bündelung, z. B. "DoKomi 2027"),
subtasks: [{ text, done, doneAt }] (eigene _id je Subtask),
attachments: [{ filename, storedName, size, mimeType, uploadedAt }],
activity: [{ type, actorId, text, createdAt }] (Audit-Verlauf: "created",
  "edited", "status", "note", "attachment" — automatisch geschrieben),
notes: [...] (die eigentlichen Diskussions-Notizen inkl. @Erwähnungen,
  getrennt von activity),
completedAt,
recurrence: "none"|"daily"|"weekly"|... , recurrenceInterval (alle N Einheiten),
recurrenceWeekdays: number[] (0=So…6=Sa, nur bei weekly),
recurrenceEndDate,
dueReminderSentAt (verhindert doppelte Erinnerungsmails),
autoKey: string (Idempotenz-Schlüssel für automatisch erzeugte ToDos, leer
  bei manuellen — siehe Punkt 18)
```

**Neues Modell `src/models/TodoTemplate.ts`:**
```
name, defaultProject,
items: [{ title, description, priority, dueOffsetDays (Tage relativ zu einem
  Basisdatum, oder null), labels[], subtasks[] (als Text-Zeilen) }],
createdById
```

**Neue/erweiterte Server-Actions in `src/lib/actions/todos.ts`:**
- `createTodoAction`, `updateTodoAction` — jetzt inkl. labels/project/subtasks/
  additionalAssigneeIds/watcherIds/recurrence(Interval/Weekdays/EndDate).
  **Wichtig:** `updateTodoAction` überschreibt additionalAssigneeIds/
  watcherIds NUR, wenn im FormData ein Feld `peopleEdited=1` mitgeschickt
  wird — sonst bleiben bestehende Werte unangetastet (Schutz gegen
  versehentliches Leeren bei Teil-Updates).
- `addTodoSubtaskAction(todoId, text)`, `toggleTodoSubtaskAction(todoId, subtaskId, done)`,
  `deleteTodoSubtaskAction(todoId, subtaskId)` — neu, einzeln pro Unteraufgabe.
- `addTodoAttachmentsAction(todoId, _prev, formData)` (Dateien aus `formData.getAll("files")`),
  `deleteTodoAttachmentAction(todoId, storedName)` — neu. Dateien liegen unter
  `private-uploads/todos/<todoId>/`, Serving über die NEUE Route
  `src/app/api/files/todos/[todoId]/[filename]/route.ts` (staff-only, prüft
  Teilnehmerschaft ODER `todos_view`-Berechtigung).
- `createTodoTemplateAction`, `updateTodoTemplateAction`, `deleteTodoTemplateAction`,
  `createTodosFromTemplateAction(templateId, _prev, formData)` — neu, Vorlagen-CRUD
  + "aus Vorlage anlegen" (erzeugt mehrere ToDos auf einmal per `insertMany`).
- `bulkUpdateTodosAction(ids, patch: {status?, assigneeId?, dueDate?})`,
  `bulkDeleteTodosAction(ids)` — neu, für Mehrfachauswahl im Web-Board.
- `addTodoNoteAction` — jetzt mit `resolveMentions()` (erkennt `@Vorname`/
  `@Vollername`, case-insensitive gegen alle Staff-Namen) und benachrichtigt
  erwähnte Personen separat ("Erwähnt in: …") von der restlichen Audience.

**Neue Web-UI-Komponenten** (Referenz für den App-Nachbau, alle unter
`src/components/admin/`): `TodoBoard.tsx` (Listen-/Board-/Fälligkeits-Ansicht,
"☀ Mein Tag"-Filter, Label-/Projekt-Filter, "nach Projekt gruppieren",
Mehrfachauswahl + Sammel-Aktions-Leiste, Drag&Drop im Board), `TodoItem.tsx`
(Zeile + aufklappbare Details: Bearbeiten-Formular, Unteraufgaben-Checkliste,
Anhänge-Liste mit Upload, Verlauf, Notizen mit @Erwähnung-Hinweis),
`TodoPeoplePicker.tsx` (Checkbox-Chips für weitere Zuständige/Beobachter),
`TodoRecurrenceFields.tsx` (Intervall/Wochentage/Enddatum, nur bei aktiver
Wiederholung sichtbar), `TodoTemplateManager.tsx` (Vorlagen anlegen/anwenden/
bearbeiten/löschen), `NewTodoForm.tsx` (jetzt inkl. Labels/Projekt/
Unteraufgaben/Empfänger-Auswahl in einem `<details>`), `TagInput.tsx`
(Chip-Editor: tippen+Enter → Chip, generisch, auch anderswo genutzt).

**Für die Desktop-App:** `TeamScreen.tsx`s `TodosTab` (v2.42.0, Punkt 16) ist
gegen die ALTE, einfachere Form gebaut — braucht praktisch einen Neubau nach
diesem Vorbild. Siehe Punkt 20 für den genauen API-Stand (was die Team-API
schon liefert vs. was komplett fehlt).
**[ ] offen** — größte Baustelle dieser Runde.

## 18. Automatische ToDo-Verknüpfungen (neu)

Neuer Helper `src/lib/todoAutomation.ts`: `ensureAutoTodo(opts)` — idempotent
über `Todo.findOne({ autoKey })`, legt sonst ein neues ToDo mit
`activity: [{ type: "created", text: "Automatisch angelegt" }]` an und
benachrichtigt den/die Zuständige:n; `removeAutoTodo(autoKey)` löscht es
wieder, außer es wurde schon erledigt (`status !== "done"`). `daysBefore(date, n)`.

Ausgelöst von drei Stellen (alle in `src/lib/actions/`):
- **Convention-Beitritt/-Austritt eines Mitarbeiters** (`conventions.ts`:
  `joinConventionAction`/`assignStaffToConventionAction` legen das ToDo an,
  `leaveConventionAction`/`removeStaffFromConventionAction` entfernen es
  wieder): `autoKey = "conv:<convId>:<staffId>"`, fällig 3 Tage vor
  Convention-Start.
- **Warteliste gewährt** (`conventions.ts` `grantWaitlistEntryAction`):
  `autoKey = "shoot-prep:<orderId>"`, fällig 2 Tage vor Shooting-Termin,
  Unteraufgaben vorbefüllt ("Referenzbilder sammeln", "Equipment prüfen",
  "Ablauf mit Kunde klären").
- **Shooting-Termin manuell gesetzt** (`admin.ts` `setOrderShootDateAction`):
  derselbe `shoot-prep:<orderId>`-Key, damit beide Wege (Warteliste ODER
  direkte Terminvergabe) zum selben Vorbereitungs-ToDo führen.

**Für die Desktop-App:** Nichts zu bauen — diese ToDos laufen einfach durch
die normale ToDo-Liste/API wie jedes andere. Höchstens interessant als
Kontext, falls die App-Liste mal nach `autoKey`/Ursprung filtern soll.
**[ ] optional, kein Muss**

## 19. ToDo-Fälligkeits-Erinnerungen (Cron, rein Backend)

`src/lib/todoReminders.ts` (`runTodoDueReminderSweep`) + neue Cron-Route
`src/app/api/cron/todo-reminders/route.ts`, täglich 07:00 Uhr im System-
Crontab. Benachrichtigt `assignedToId` + `additionalAssigneeIds` für ToDos,
die in <24h fällig sind und noch nicht gemeldet wurden (`dueReminderSentAt`).
**Für die Desktop-App: nichts zu tun**, rein serverseitig.

## 20. Team-API: aktueller Stand für die neuen ToDo-Felder

Bereits **nachgezogen** (Web-Session, Commit `018fbfb`, 09.09.–10.09.2026):
- `src/lib/teamTodoBody.ts` — baut aus dem JSON-Body der App eine FormData
  für `createTodoAction`/`updateTodoAction`, jetzt inkl. `labels` (Array→
  comma-joined), `project`, `subtasks` (Array→newline-joined Text),
  `additionalAssigneeIds`/`watcherIds` (setzt automatisch `peopleEdited=1`,
  s. o.), `recurrenceInterval`, `recurrenceWeekdays[]`, `recurrenceEndDate`.
- `GET /api/team/v1/todos` (`src/app/api/team/v1/todos/route.ts`) — liefert
  über `getTodoList()` (`src/lib/todoList.ts`) jetzt JEDES neue Feld pro
  ToDo mit: `additionalAssigneeIds`, `watcherIds`, `labels`, `project`,
  `subtasks` (id/text/done), `attachments` (filename/storedName/size/
  mimeType), `activity` (letzte 20), `recurrenceInterval`,
  `recurrenceWeekdays`, `recurrenceEndDate`. Die Rohdaten kommen also schon
  vollständig an — nur die App-UI zeigt/bearbeitet sie noch nicht.
- `PATCH /api/team/v1/todos/[todoId]` — voller Edit inkl. aller neuen Felder
  über `updateTodoAction`.
- `POST /api/team/v1/todos/[todoId]/notes` — bereits vorhanden (Punkt 16),
  unverändert, nutzt `addTodoNoteAction` inkl. @Erwähnungen.

**Komplett fehlt in der Team-API** (keine Route existiert):
- Einzelne Unteraufgaben-Operationen (`addTodoSubtaskAction`/
  `toggleTodoSubtaskAction`/`deleteTodoSubtaskAction`) — aktuell nur über
  ein komplettes `subtasks`-Text-Replace beim normalen Update möglich, kein
  Haken-Toggle für eine einzelne Zeile.
- Anhänge (`addTodoAttachmentsAction`/`deleteTodoAttachmentAction`) — keine
  Upload/Download/Delete-Route; auch `/api/files/todos/[todoId]/[filename]`
  (Web-Serving-Route) hat kein Team-API-Pendant.
- Vorlagen (`createTodoTemplateAction` & Co., `createTodosFromTemplateAction`)
  — keine `/todos/templates`-Routen.
- Mehrfachauswahl (`bulkUpdateTodosAction`/`bulkDeleteTodosAction`) — keine
  `/todos/bulk`-Route.

**Für die Desktop-App:** Vor dem UI-Neubau (Punkt 17) erst diese vier
Routen-Gruppen ergänzen (Muster: bestehende `/todos/[todoId]/notes/route.ts`
als Vorlage — schlanker Wrapper, der direkt die Server-Action aufruft, deren
Berechtigungsprüfung schon eingebaut ist).
**[ ] offen**

## 21. Neues Systemprotokoll (`/admin/logs`) — kein Team-API-Pendant

Komplett neuer Bereich (Commit `da8cfb9`), kein "Nachziehen" einer
bestehenden Funktion, sondern optionaler Neuzugang:
- Neue Collection `SystemLog` (90 Tage TTL) + `src/instrumentation.ts`
  (Next.js `onRequestError`-Hook fängt JEDEN Serverfehler automatisch ab)
  + `POST /api/client-error` (meldet clientseitige Fehler).
- `src/lib/logFeed.ts` führt SystemLog mit TeamActivity/OrderActivity/
  Todo-Verlauf/Notifications/QM-Meldungen zu einer nach Kategorie+Zeitraum
  filterbaren Ansicht zusammen.
- Neues Recht `logs` (einfaches Single-Action-Recht wie `statistics`/`qm`).
- **Für die Desktop-App:** Kein Nachzieh-Zwang — falls gewünscht, wäre ein
  reiner Lese-Screen (Fehler + Aktivität der letzten Tage) ein sinnvoller,
  aber komplett neuer Bereich, keine Team-API existiert dafür bisher.
**[ ] optional, niedrige Priorität**

## 22. Conventions: Foto/Logo, Warteliste-Verwaltung als 2 Tabs, Kollisions-Check

- **Modell** `src/models/Convention.ts`: `image` (admin-hochgeladenes Foto,
  Commit `25f0189`) und `logo` (Commit `d9b5ced`), beide `string` (Pfad),
  Upload-Kategorie `"conventions"` in `ALLOWED_CATEGORIES`
  (`api/uploads/[category]/[filename]/route.ts`). Neue Action
  `removeConventionMediaAction(conventionId, kind: "image"|"logo")`.
- **Admin-UI** `admin/conventions/page.tsx` jetzt 2 Tabs ("Conventions" /
  "Warteliste", Commit `d8db714`) statt einer langen Liste.
  `GrantWaitlistForm.tsx` (neu ausgelagert): Termin-Datum als Dropdown der
  festen Convention-Tage statt Freitext, PLUS ein Kollisions-Check (prüft
  clientseitig gegen `Order.find({shootDate in range})`, zeigt Warnung bei
  Überschneidung — **nicht blockierend**, nur Hinweis).
- **Kalender-Systemansicht "Conventions"** (Commit `7b4b055`): neue
  `Calendar.kind = "conventions"` (+ `"orders"`/`"absences"`/`"custom"`
  bereits vorher), auto-erzeugt via `getOrCreateConventionsCalendar()`
  (`src/lib/actions/calendars.ts`), zeigt Conventions, denen Mitarbeiter
  zugewiesen sind. Nicht editierbar (`src/lib/calendars.ts`
  `canEditCalendar` gibt für `kind==="conventions"` immer `false`), aber für
  jeden Staff sichtbar (`canViewCalendar` immer `true`).
- **Team-API:** `GET/PATCH /api/team/v1/conventions[/[id]]` und
  `/calendar`-Routen existieren schon (Basis-CRUD) — ob `image`/`logo`
  bereits mit rausgereicht werden, bitte kurz prüfen (wahrscheinlich ja,
  falls die Route einfach `.lean()`-Dokumente durchreicht, analog zu
  `realName` bei Testimonials, Punkt 12). **Keine** Team-API-Route für
  Warteliste-Verwaltung (ansehen/gewähren) — kompletter Neubau nötig, falls
  gewünscht.
- **Für die Desktop-App:** Foto/Logo-Anzeige vermutlich kleine Ergänzung
  (falls Conventions-Screen existiert); Warteliste-Verwaltung wäre ein
  neuer Bereich (bisher offenbar noch gar nicht in der App).
**[ ] offen**

## 23. Rabattcodes: convention-gebunden

- **Modell** `src/models/DiscountCode.ts`: neues Feld `conventionId`
  (`ObjectId | null`, ref Convention). `redeemDiscountCodeAction`
  (`src/lib/actions/discounts.ts`) lehnt die Einlösung ab, wenn
  `discount.conventionId` gesetzt ist und nicht zur `conventionId` des
  Auftrags passt.
- Admin-Formular (`NewDiscountCodeForm`/`DiscountCodeRow`) hat ein neues
  "Convention"-Auswahlfeld.
- **Für die Desktop-App:** Falls Rabattcodes dort verwaltet werden (Team-API
  `/discounts` existiert bereits) — Create/Edit-Formular um dieselbe
  Convention-Auswahl ergänzen, sonst lässt sich das Feld aus der App nicht
  setzen (Anzeige kommt vermutlich automatisch mit, wie bei `realName`).
**[ ] offen**

## 24. Charakter-Wunsch-Status (Wunschliste)

- **Modell** `src/models/CharacterWish.ts`: neues Feld `status`
  (`"offen"|"angenommen"|"abgelehnt"|"umgesetzt"`, Default `"offen"`).
  `AdminWishlistRow.tsx` hat ein Status-Dropdown.
- **Für die Desktop-App:** Es gibt aktuell **gar keine Team-API für die
  Wunschliste** (kein `/api/team/v1/wishlist`, vorbestehende Lücke, nicht
  durch diese Änderung verursacht) — falls die App das je bekommen soll,
  gleich mit Status-Feld von Anfang an bauen.
**[ ] optional, vorbestehende Lücke**

## 25. Einstellungen: Gutschein-Käufer deaktivieren

- **Modell** `src/models/Settings.ts`: neues `voucherSettings.enabled`
  (Default `true`). Neue Action `setVoucherPurchasesEnabledAction`;
  `requestVoucherAction` verweigert bei `enabled === false`.
  `VoucherPurchaseSettingsPanel.tsx` (neu) auf der Web-Einstellungsseite.
- **Team-API:** `/settings` + diverse `/settings/*`-Subrouten existieren
  (banner/invoice/maintenance/mileage-rate/tax-reserve) — **keine**
  `/settings/voucher`-Route bisher.
- **Für die Desktop-App:** Kleiner, schneller Ergänzung wert, falls die
  App-Einstellungsseite Feature-Schalter wie den Wartungsmodus schon zeigt.
**[ ] offen, klein**

## 26. Rein visuelles Redesign öffentlicher Seiten (kein Desktop-Bezug)

Sehr viele Commits (>100) zwischen `320ead0` und `e1fa398`: Homepage-Hero,
Portfolio, Über mich, Angebote, Gutscheine, Conventions (öffentliche Seite),
Blog, FAQ — alle auf einen einheitlichen "Page Header Banner" umgestellt
(Foto-Banner + Eyebrow/Titel/Untertitel), plus Header-Nav-Fixes (aktiver
Menüpunkt in Kupfer/Amber, per `usePathname()` clientseitig statt
serverseitig ermittelt — server-berechnete Werte "hingen" beim Wechsel
zwischen Layout-Geschwister-Seiten fest; falls die Desktop-App eine eigene
Web-Ansicht einbettet, ist dieses Next.js-Partial-Rendering-Verhalten evtl.
relevant, sonst nicht), Lesbarkeits-Fixes (Verlauf/Vignette hinter Text auf
Fotos). **Alles reines Kunden-Frontend, keine Modelle/Actions/APIs
geändert — nichts davon ist für die Mitarbeiter-App relevant.**

## 27. Performance & Infrastruktur (kein Desktop-Bezug, nur Kontext)

Web-App läuft jetzt spürbar stabiler/schneller (`getSettings()` mit
`React.cache()` memoisiert statt 4× Upsert pro Seitenaufruf, pm2 im
Cluster-Modus mit 2 Instanzen + Heap-Limit, nginx mit Static-Asset-Offload/
Keepalive/HTTP2/gzip). Reine Deploy-/Server-Konfiguration, nichts, das die
Mitarbeiter-App betrifft oder das dort nachgebaut werden müsste.

---

## 28. [Desktop-App] Punkte 17/20/22/23/25 nachgezogen — großer Sync-Durchlauf

Reaktion auf Nutzer-Wunsch ("Webseite neu designed, neue Features, alles zusammenführen") — die komplette Runde 17-27 durchgearbeitet, in der von Punkt 9 (Zeile 25-28) empfohlenen Reihenfolge: erst ToDo-System (größte Lücke), dann Conventions/Rabattcodes/Einstellungen. Punkt 21 (Systemprotokoll) und Punkt 24 (Wunschliste) bewusst ausgelassen (siehe unten, "Bekannte offene Punkte").

**ToDo-System (Punkt 17+20) — Team-API zuerst vervollständigt, dann App-UI komplett neu gebaut:**
- **Web-seitig ergänzt** (fehlte laut Punkt 20 komplett): `src/lib/todoList.ts` bekam `getTodoTemplateList()` (Vorlagen-Liste, jetzt von `admin/todos/page.tsx` UND der Team-API gemeinsam genutzt statt einer eigenen Inline-Query). Neue Team-API-Routen: `/todos/[todoId]/subtasks[/[subtaskId]]` (add/toggle/delete), `/todos/[todoId]/attachments[/[storedName]]` (Upload via Multipart-Durchreiche, Download mit CORS-Headern fürs App-eigene `fetch()`+`window.rakku.openFile()`, Delete), `/todos/templates[/[templateId]][/apply]` (CRUD + Anwenden), `/todos/bulk` (PATCH für Sammel-Status/Zuständigkeit/Fälligkeit, DELETE für Sammel-Löschen). `GET /todos` liefert jetzt zusätzlich `templates`, `allLabels`, `allProjects`.
- **Desktop-UI**: `TodosTab` aus `TeamScreen.tsx` in eine eigene Datei `screens/TodosTab.tsx` ausgelagert (wurde zu groß für eine gemeinsame Datei mit Intranet/Abwesenheiten) und komplett neu gebaut: Labels (farbige Chips, deterministische Farbe wie `labelColor()` im Web), Projekt-Feld + Projekt-Filter, Unteraufgaben-Checkliste (einzeln togglebar/löschbar/hinzufügbar), Anhänge (Mehrfach-Upload, Download via IPC wie bei PDFs, Löschen), Verlauf (letzte Aktivitäten), weitere Zuständige + Beobachter (Checkbox-Chips, nur mit `todos_create`-Berechtigung sichtbar wie im Web), erweiterte Wiederholung (Intervall/Wochentage/Enddatum), "☀ Mein Tag"-Filter, Mehrfachauswahl + Sammel-Aktionsleiste (Status setzen/Löschen), Vorlagen-Verwaltung (anlegen/bearbeiten/löschen/anwenden mit derselben Text-Zeilen-Syntax wie im Web: `Titel | +Tage | #Label | Priorität`).
- **Bewusst nicht nachgebaut**: die Board-/Drag&Drop-Ansicht aus `TodoBoard.tsx` (App bleibt bei einer Listenansicht wie alle anderen Bereiche — reine Sortierung/Filterung deckt denselben Bedarf ab) und die `@Erwähnung`-Autovervollständigung beim Tippen (Server löst `@Name` weiterhin serverseitig auf, das funktioniert unverändert auch ohne UI-Vorschlagsliste — nur ein Hinweistext beim Notizfeld ergänzt).

**Conventions (Punkt 22) — von Anlegen/Sichtbarkeit/Löschen auf volle Parität + neuer Warteliste-Tab:**
- **Bugfix beim Nachziehen gefunden**: `src/lib/conventionList.ts` war in einer früheren Session schon mal umgebaut worden (auf `{items, staffOptions}` statt eines flachen Arrays), aber `src/app/api/team/v1/conventions/route.ts` (GET) war dabei nicht mitgezogen worden — hätte der App ein falsch verschachteltes `{conventions: {items, staffOptions}}` statt `{conventions: [...], staffOptions: [...]}` geliefert. Jetzt korrigiert, plus `viewerId` in der Antwort ergänzt (fehlte komplett, aber nötig fürs "Ich bin dabei"/Team-Zuteilung in der App).
- **Neue Team-API-Routen**: `PATCH /conventions/[id]` (Multipart-Durchreiche für Foto/Logo-Upload beim Bearbeiten), `POST /conventions` jetzt ebenfalls Multipart statt JSON (Foto/Logo gleich beim Anlegen), `POST /conventions/[id]/media` (Foto/Logo entfernen), `/join`, `/leave`, `/assign` (POST=zuteilen, DELETE=entfernen), `/reopen-slots`. Neuer Helfer `getConventionWaitlistList()` in `conventionList.ts` (Kollisions-Check-Daten + Warteliste über alle Conventions, aus `admin/conventions/page.tsx` extrahiert, Seite nutzt jetzt denselben Helfer) + Route `GET /conventions/waitlist` + `POST /conventions/waitlist/[entryId]/grant`.
- **Desktop-UI**: `ConventionsScreen.tsx` komplett neu, jetzt 2 Tabs wie im Web ("Conventions" / "Warteliste"). Conventions-Tab: Foto/Logo-Upload+Anzeige+Entfernen bei Anlegen und Bearbeiten, Team-vor-Ort-Verwaltung (Ich bin dabei/Verlassen/Zuteilen/Entfernen), "Warteliste benachrichtigen"-Button. Warteliste-Tab: offene/erledigte Anfragen, Genehmigen-Formular (Tag-Dropdown der festen Convention-Tage, Start-/Endzeit, Ort) mit Kollisions-Hinweis (nicht blockierend, wie im Web).
- **Bewusst nicht nachgebaut**: der Warteliste-Chat (`WaitlistMessage`/`adminSendWaitlistMessageAction`) — die Web-Seite selbst zeigt den seit dem Redesign auch nicht mehr an (nur noch das Genehmigen-Formular), also keine Lücke zur aktuellen Admin-Oberfläche.

**Rabattcodes (Punkt 23):** `getDiscountCodes()` liefert jetzt `conventionId`/`conventionName` mit; Team-API-Routen (`POST /discounts`, `PATCH /discounts/[id]`) forwarden `conventionId`. Desktop: Anlege- und Bearbeiten-Formular in `PricingScreen.tsx`s `DiscountsTab` haben ein neues Convention-Auswahlfeld ("Keine Convention-Bindung" als Default), Zeilenanzeige zeigt die gebundene Convention.

**Einstellungen (Punkt 25):** `GET /api/team/v1/settings` liefert jetzt zusätzlich `voucherSettings`; neue Route `POST /api/team/v1/settings/voucher`. Desktop: neues Panel "Gutscheine" in `SettingsScreen.tsx` (Schalter "Neue Gutschein-Käufe erlauben").

Alles getypt (`tsc -b`/`tsc --noEmit` sauber), Web gebaut+deployt (inkl. Bugfix-Nachbau), App-Version 2.43.0 gebaut und veröffentlicht (13.09., diese Session).
**[x] erledigt** — Punkte 17/20/22/23/25 vollständig nachgezogen.

## 29. [Desktop-App] Punkte 21 (Systemprotokoll) + 24 (Wunschliste) doch nachgezogen

Nutzer hat nach Punkt 28 nachgefragt, warum diese beiden fehlen — waren nur als "niedrige Priorität" eingestuft, nicht als "nicht gewollt". Beide jetzt nachgebaut.

**Wunschliste (Punkt 24):**
- **Web:** neuer Helfer `src/lib/wishlistList.ts` (`getWishlistGroups()`, aus `admin/wishlist/page.tsx` extrahiert — nach Charakter gruppiert, meistgewünschte zuerst). Seite selbst nutzt den Helfer jetzt auch.
- **Team-API (komplett neu, vorher gar nicht angebunden):** `GET /api/team/v1/wishlist` (Berechtigung `orders_view`, liefert `canDelete` via `orders_edit`), `PATCH /api/team/v1/wishlist/[wishId]` (Status setzen), `DELETE /api/team/v1/wishlist/[wishId]`.
- **Desktop:** neuer Screen `WishlistScreen.tsx` — nach Charakter gruppierte, aufklappbare Liste, Status-Dropdown (offen/angenommen/abgelehnt/umgesetzt, farbcodiert wie im Web) + Löschen. Registriert in `App.tsx` (`/wishlist`) und Nav (Kategorie „Aufträge", zwischen Feedback und Ablehnungsgründe, Berechtigung `orders_view`).

**Systemprotokoll (Punkt 21):**
- **Team-API (neu):** `GET /api/team/v1/logs` — dünner Wrapper um das bestehende `getLogFeed()` (das war schon als eigenständige, wiederverwendbare Funktion gebaut, keine Web-seitige Änderung nötig), Query-Parameter `cat`/`range`/`page`, Berechtigung `logs`.
- **Desktop:** neuer Screen `LogsScreen.tsx` — Kategorie-Filter-Chips mit Live-Zählern (Fehler/Warnungen/System-Info/Team/Aufträge/ToDos/Benachrichtigungen/QM), Zeitraum-Auswahl (24h/7d/30d/90d), aufklappbare Einträge (Meta-Felder + Stacktrace bei Fehlern), Seitennavigation. Rein lesend, wie im Web. Registriert in `App.tsx` (`/logs`) und Nav (Kategorie „Verwaltung", letzter Eintrag, Berechtigung `logs`).

Getypt (`tsc -b`/`tsc --noEmit` sauber), Web gebaut+deployt, App-Version 2.44.0 gebaut und veröffentlicht (13.09., diese Session).
**[x] erledigt** — beide Bereiche vollständig nachgezogen, keine bekannten offenen Punkte mehr aus der 17-27-Runde außer den bewusst dauerhaft ausgelassenen (ToDo-Board-Ansicht, Warteliste-Chat — siehe unten).

## 30. [Desktop-App] ToDos: Board-Ansicht + Fälligkeits-Ansicht + Projekt-Gruppierung nachgezogen

Nutzer-Regel nach Punkt 29 präzisiert: "wenn ein System nicht mehr existiert auf der Seite brauchst du es nicht, es sollte aber alles gehen was auch auf der Seite geht" — die ToDo-Board/Drag&Drop-Ansicht existiert auf der Web-Seite (`TodoBoard.tsx`) weiterhin aktiv, war in Punkt 28 nur als "bewusst nicht nachgebaut" eingestuft. Jetzt vollständig nachgezogen, rein clientseitig in `TodosTab.tsx` — keine neuen Team-API-Routen nötig, alles nutzt die bestehenden ToDo-Endpunkte.

- **Ansichts-Umschalter** (Liste/Board/Fälligkeit), 1:1 wie `TodoBoard.tsx`:
  - **Board**: 3 Spalten (Offen/In Bearbeitung/Erledigt), Drag & Drop zwischen Spalten setzt den Status (`api.setTodoStatus`) — HTML5 Drag-and-Drop (`draggable`/`onDragStart`/`onDragOver`/`onDrop`), Status-Filter wird in dieser Ansicht ignoriert (zeigt immer alle 3 Spalten, wie im Web).
  - **Fälligkeit**: nach Fälligkeits-Eimern gruppiert (Überfällig/Heute/Diese Woche/Später/Kein Datum), exakt dieselbe `dueBucket()`-Logik wie im Web.
  - **Nach Projekt gruppieren**-Checkbox (nur in der Listenansicht, nur wenn Projekte existieren).
- **Auswählen-Modus als Umschalter** statt dauerhaft sichtbarer Checkboxen — Button "Auswählen"/"Auswahl beenden" wie im Web, Checkboxen erscheinen nur während des Auswahlmodus. Sammel-Aktionen (Status/Zuständigkeit/Fälligkeit) wenden jetzt sofort bei Auswahl im Dropdown an (kein separater "Anwenden"-Klick mehr), genau wie `TodoBoard.tsx`.
- **"Meine ToDos"/"☀ Mein Tag"-Filterlogik korrigiert**, um exakt dem Web zu entsprechen: filtert nur auf Haupt-Zuständigkeit ODER Ersteller (nicht mehr zusätzlich auf "weitere Zuständige", das war vorher eine kleine Abweichung vom Web-Verhalten).

Getypt (`tsc -b` sauber, ein `function`→`const`-Fix wegen TS-Closure-Narrowing bei `renderItem`), App-Version 2.45.0 gebaut und veröffentlicht (13.09., diese Session). Kein Web-Build nötig (reine Client-Logik, bestehende API-Endpunkte).
**[x] erledigt** — Board-Ansicht, Fälligkeits-Ansicht, Projekt-Gruppierung und Auswahlmodus vollständig nachgezogen.

## 31. [Desktop-App] Kundenkonto-Verwaltung: Anlegen, Passwort/Unternehmen, Löschen/Anonymisieren, Zum Mitarbeiter machen

Nutzer-Meldung: "Ich kann in der App keine Kunden Konten etc. erstellen" — dieser Bereich war seit der allerersten Parität-Audit als "Phase-1-Umfang, Peripheriefunktionen bleiben der Web-Ansicht vorbehalten" dokumentiert (Kommentar in `src/lib/clientDetail.ts`), jetzt vollständig nachgezogen.

- **Web:** `getClientDetail()` (`src/lib/clientDetail.ts`) liefert jetzt zusätzlich `hasInvoices` (für die Lösch-vs-Anonymisieren-Entscheidung); `role`/`deletionRequestedAt` kamen schon im `client`-Objekt mit, waren nur im Desktop-Typ nicht freigelegt.
- **Team-API:**
  - `POST /api/team/v1/clients` (neu) — "Konto vor Ort anlegen" via `adminCreateClientAction` (Berechtigung `client_accounts`, steckt in der Action selbst).
  - `PATCH /api/team/v1/clients/[clientId]` (neu) — Unternehmen/Passwort ändern via `updateClientAccountAction`.
  - `DELETE /api/team/v1/clients/[clientId]` (neu) — Löschen/Anonymisieren via `adminDeleteClientAction` (anonymisiert automatisch statt hart zu löschen, wenn Rechnungen vorhanden sind — Aufbewahrungspflicht § 147 AO).
  - Mitarbeiter-Beförderung brauchte gar keine neue Route — `/api/team/v1/team/staff/[clientId]/role` (`setStaffRoleAction`) existierte bereits aus einer früheren Phase (Team-&-Rechte-Bereich), war nur auf der Kundenseite der App noch nicht verlinkt.
- **Desktop-UI:**
  - `ClientsListScreen.tsx` — neuer Button "+ Kunde anlegen" (nur mit `client_accounts`-Berechtigung sichtbar) öffnet ein Formular mit allen Pflichtfeldern (Vor-/Nachname, Spitzname optional, E-Mail, Übergangspasswort, Geburtsdatum, Telefon, Anschrift) — exakt wie `NewConventionForm`-Muster.
  - `ClientDetailScreen.tsx` — neue Kachel "Kundenkonto verwalten" (Unternehmen + Passwort setzen), neuer Lösch-Bereich (rot umrandet, Text wechselt automatisch zwischen "Kunde löschen"/"Kunde anonymisieren" je nach `hasInvoices`, JS-`confirm()` wie im Web), "Zum Mitarbeiter machen"-Button im Kopfbereich (nur wenn noch kein Mitarbeiter) mit demselben `ConfirmPasswordDialog`-Schritt wie beim Entziehen des Mitarbeiterstatus in `TeamManagementScreen.tsx` — dort wird `api.setStaffRole` bereits verwendet, jetzt auch zum Befördern statt nur zum Entziehen.
- Getypt (`tsc -b`/`tsc --noEmit` sauber), Web gebaut+deployt, App-Version 2.46.0 gebaut und veröffentlicht (15.09., diese Session).
- **[x] erledigt** — Kundenkonto-Erstellung war der konkrete Auslöser; Passwort/Unternehmen, Löschen/Anonymisieren und Mitarbeiter-Beförderung gleich mit erledigt, da alle vier zusammen als eine Gruppe ("Client detail missing") in der ursprünglichen Audit standen.

## 32. [Desktop-App] Fehlende Akzentfarbe des Redesigns nachgezogen

Nutzer-Meldung: "Die Akzentfarben wie sie sind auch nicht drin, also das neue Design." Ursache gefunden: Das Redesign (Punkt 26) hatte am 03.09.2026 die erste echte Akzentfarbe der Seite eingeführt — Kupfer/Amber `--rk-accent: #c98a4b` (Kommentar in `globals.css`: "ersetzt keine bestehende Farbe, ist die erste echte Akzentfarbe der Seite"). Diese Variable fehlte in der Desktop-App komplett (`renderer/src/index.css` hatte nur `--rk-bg`/`--rk-surface`/`--rk-ink`/`--rk-ink-muted`/`--rk-line`), obwohl sie inzwischen quer durchs Web-Admin verwendet wird (u. a. `TodoBoard.tsx`, `TodoPeoplePicker.tsx`, `TodoRecurrenceFields.tsx`, `TodoTemplateManager.tsx`, `GrantWaitlistForm.tsx`, `CalendarDay.tsx`, `DiscountCodeRow.tsx`, `LogList.tsx`).

- **`renderer/src/index.css`**: `--rk-accent: #c98a4b` ergänzt, 1:1 aus `globals.css` übernommen.
- **Stellen korrigiert, die stattdessen Weiß/Smaragd verwendet hatten** (per Quellabgleich mit den o. g. Web-Dateien):
  - `TodosTab.tsx`: "☀ Mein Tag"-Button, "Auswählen"-Umschalter, ausgewählte Personen-Chips (weitere Zuständige/Beobachter), ausgewählte Wochentag-Chips (Wiederholung), "+ Neue Vorlage"-Button (Akzent-Outline) und die primären "Speichern"/"Anlegen"-Buttons der Vorlagen-Verwaltung (jetzt volltonig Akzent-Hintergrund mit dunklem Text, wie `bg-[var(--rk-accent)] ... text-[#141413]` im Web).
  - `ConventionsScreen.tsx`: "Genehmigen"-Button im Warteliste-Formular jetzt volltonig Akzent statt Weiß-Outline.
  - `CalendarScreen.tsx`: "+N mehr"-Überlauf-Text jetzt Akzentfarbe statt gedämpftem Grau.
  - `PricingScreen.tsx`: Rabattcode-Zeile zeigte `stammkundenOnly`/`neukundenOnly`/`ownerName` bisher gar nicht an (nur im Bearbeiten-Formular editierbar) und `conventionName` nur als Fließtext — jetzt alle vier als farbige Pillen wie im Web (Smaragd/Himmelblau/Akzent/Fuchsia).
- **Bewusst unverändert gelassen**: die Segmented-Control-Umschalter (Tabs wie "Meine ToDos"/"Alle ToDos", "Liste/Board/Fälligkeit", Kategorie-Reiter in Reviews/Pricing/Offers) — die nutzen im Web durchgehend `bg-white/10` für den aktiven Zustand, NICHT die Akzentfarbe (per Quellabgleich mit `TodoBoard.tsx`/`AdminNavLinks.tsx` bestätigt) — hier war die App schon korrekt.
- **Nicht nachgezogen (kein Desktop-Äquivalent vorhanden):** `ActiveStatePanel.tsx` ("Was ist gerade aktiv?", Settings-Panel, gehört zu den bewusst browser-only gelassenen 13 Settings-Panels) und das "⏳ Geplant"-Badge in `BlogPostRow.tsx`/`PortfolioItemRow.tsx` (setzt `publishedAt`-Terminplanung voraus, die die App noch gar nicht hat — bereits als offener Punkt bekannt).

Getypt (`tsc -b` sauber), kein Web-Build nötig (reines Desktop-Styling), App-Version 2.47.0 gebaut und veröffentlicht (15.09., diese Session).
**[x] erledigt** — Akzentfarbe eingeführt und an allen per Quellabgleich gefundenen Stellen korrigiert.

## 33. [Desktop-App] Kunde-anlegen: strukturierte Anschrift statt einem Freitextfeld

Nutzer-Meldung mit zwei Screenshots: das "Anschrift"-Feld beim Kunden-Anlegen war ein einzelnes Freitextfeld statt Straße/PLZ/Ort/Land wie sonst im System üblich (zweiter Screenshot zeigte das Muster aus der Kunden-Selbstregistrierung).

- **Ursache**: `adminCreateClientAction` (`src/lib/actions/clientAccounts.ts`) war der einzige Ort im System, der noch ein flaches `address`-Freitextfeld erwartete — überall sonst (Selbstregistrierung `registerAction`, Profil, TFP-Vertrag, Rechnungseinstellungen) werden strukturierte `billingStreet`/`billingPostCode`/`billingCity`/`billingCountryCode`-Felder verwendet und daraus bei Bedarf ein Anzeige-String über `formatAddress()` erzeugt.
- **Web:** `adminCreateClientAction` nimmt jetzt dieselben vier strukturierten Felder entgegen (Pflicht: Straße/PLZ/Ort, Land default "DE"), speichert sie strukturiert UND leitet `address` per `formatAddress()` ab (identisches Muster wie `registerAction`). `NewClientForm.tsx` (Web-Admin-Formular für "Konto vor Ort anlegen") entsprechend auf die 4 Felder umgebaut (Straße volle Breite, PLZ+Ort nebeneinander, Land darunter) — vorher hatte selbst das Web-Formular nur ein Freitext-Textarea, das war also eine echte, vorbestehende Inkonsistenz im System selbst, nicht nur ein App-Nachzieh-Rückstand.
- **Team-API** `POST /api/team/v1/clients` reicht jetzt die 4 Felder durch statt eines einzelnen `address`-Strings.
- **Desktop:** `api.createClient()`-Typ und `ClientsListScreen.tsx`s Anlege-Formular auf dieselben 4 Felder umgebaut, exakt gleiches Layout wie `NewClientForm.tsx`.
- Getypt (`tsc -b`/`tsc --noEmit` sauber), Web gebaut+deployt, App-Version 2.48.0 gebaut und veröffentlicht (15.09., diese Session).
- **[x] erledigt** — betrifft jetzt Web-Admin-Formular UND App gleichermaßen, keine Diskrepanz mehr zwischen beiden.

## 34. Kunden-Auswahl: durchsuchbar statt langer Liste, jetzt auch nach Geburtsdatum (Web + App gemeinsam umgesetzt, claude-64 + claude-5d)

Nutzer-Meldung mit Screenshot: das `<select>` beim "Neuen Auftrag für Kunden anlegen" war bei vielen Kunden unbrauchbar (lange Liste, kein Suchen). Wunsch: Suchleiste statt Dropdown, Geburtsdatum mit anzeigen/durchsuchbar machen — auch bei der normalen Kundensuche. Arbeitsteilung zwischen zwei parallel laufenden Sessions: Web-Seite von claude-64, Desktop-App von dieser Session.

- **Web** (`src/lib/clientList.ts`, geteilt von `admin/clients/page.tsx` UND der Team-API): `ClientRow` hat jetzt `birthDate`; die Such-Query durchsucht per `$expr`/`$dateToString` jetzt zusätzlich das als "TT.MM.JJJJ" formatierte Geburtsdatum (z. B. "12.05" oder "1998" findet Treffer). `admin/clients/page.tsx` zeigt das Geburtsdatum jetzt mit an. `NewOrderForClientForm.tsx`s `<select>` wurde durch eine neue `ClientSearchField`-Suchkomponente ersetzt. `GET /api/team/v1/orders/new` liefert `birthDate` jetzt mit (vorher eigene, separate `Client.find()`-Query ohne dieses Feld).
- **Team-API** `GET /api/team/v1/clients` bekommt `birthDate` automatisch mit, da es denselben `getClientList()`-Helfer nutzt.
- **Desktop:** neue wiederverwendbare Komponente `renderer/src/components/ClientPicker.tsx` — Texteingabe filtert live nach Name/E-Mail/Geburtsdatum, Dropdown-Liste mit Pfeiltasten-Navigation + Enter/Escape, zeigt gewähltes Ergebnis als "Name (E-Mail) · geb. TT.MM.JJJJ" mit ✕-Button zum Ändern. Ersetzt das `<select>` in `NewOrderScreen.tsx`. `ClientsListScreen.tsx` zeigt Geburtsdatum jetzt in jeder Zeile an; die bestehende Suche durchsucht es serverseitig automatisch mit (kein App-Code nötig, kommt über denselben `q`-Parameter).
- Getypt (`tsc -b`/`tsc --noEmit` sauber), Web gebaut+deployt (claude-64, Commit `737110e`), App-Version 2.49.0 gebaut und veröffentlicht (15.09., diese Session).
- **[x] erledigt**

## 35. [Desktop-App] Alle Dropdowns app-weit: dunkles statt helles Aufklapp-Menü

Nutzer-Screenshot: die Aufklappliste von `<select>`-Feldern erschien mit hellem/weißem Hintergrund und kaum lesbarem hellgrauem Text — passierte bei praktisch jedem Dropdown in der App.

- **Ursache**: Chromium/Electron rendert die aufgeklappte `<option>`-Liste eines `<select>` in einer eigenen nativen UI-Schicht, die weder Tailwind-Klassen noch CSS-Variablen vom Element selbst übernimmt — nur das geschlossene Feld folgt dem eigenen Styling, die Liste fällt auf den hellen System-Standard zurück, wenn nichts explizit dagegen gesetzt wird.
- Die Webseite kennt denselben Effekt und flickt ihn bisher nur punktuell mit `[color-scheme:dark]` auf einzelnen Elementen (u. a. `TodoBoard.tsx`, `GrantWaitlistForm.tsx`, `TodoTemplateManager.tsx`, `AdminWishlistRow.tsx`) — deckt dort also selbst auch nicht alle `<select>`/Datum-Felder ab.
- **Fix in der App**: `color-scheme: dark` einmal global auf `:root` in `renderer/src/index.css` gesetzt, statt es an jeder einzelnen Stelle nachzutragen — betrifft dadurch wirklich jedes `<select>`, `<option>`, `<input type="date">`/`type="time"` und unstyled Checkbox/Radio in der ganzen App auf einen Schlag, auch zukünftig neu hinzukommende, ohne Einzelnachtrag.
- Kein Web-Build nötig (reines Desktop-CSS), App-Version 2.50.0 gebaut und veröffentlicht (15.09., diese Session).
- **Nachtrag (15.09.2026):** `color-scheme: dark` allein reichte in der Praxis doch nicht überall — Nutzer meldete das Dropdown weiterhin unleserlich. Web-Session (claude-64) fand denselben Fund unabhängig beim selben Bug auf der Webseite und ergänzte dort zusätzlich eine explizite `select option { background-color: var(--rk-surface); color: var(--rk-ink); }`-Regel (+ `:disabled`-Variante) statt sich nur auf `color-scheme` zu verlassen. Identische Regel jetzt auch in `index.css` ergänzt — App-Version 2.51.0 gebaut, im gepackten Build via `asar extract` verifiziert, veröffentlicht.
- **[x] erledigt** — geht hier über die Web-eigene (unvollständige) Einzelfall-Lösung hinaus, da global statt punktuell gelöst; beide Seiten nutzen jetzt dieselbe zweistufige Absicherung (`color-scheme` + explizite `select option`-Regel).

## 36. Termin-Treffpunkt: interne Notiz (`Order.eventAddressNote`)

Web-Session (claude-64) hat `Order.eventAddressNote` ergänzt (String, max. 500, rein intern — bewusst NICHT Teil von `effectiveEventAddress()`, erreicht also nie Kundenseite/Kalender-Export). `setOrderShootDateAction` nimmt jetzt optional `note` in `eventAddress` entgegen; `POST /api/team/v1/orders/[orderId]/shootdate` als `addressNote` im Body.

- **Kein Web-Änderungsbedarf bei `orderDetail.ts`** — anfängliche Sorge des Peers war unbegründet: `getOrderDetail()` gibt das komplette `order`-Dokument roh zurück (`Order.findById().lean()`, keine kuratierte Feldliste), also kommt `eventAddressNote` automatisch mit, sobald es im Schema existiert — genau wie `eventStreet`/`eventPostCode`/`eventCity`/`eventCountryCode` das schon vorher taten, ohne dass `orderDetail.ts` sie einzeln aufzählt.
- **Desktop:** `OrderDetailResponse.order` um `eventAddressNote` ergänzt, `api.setShootDate()` nimmt jetzt `addressNote` entgegen, `ShootDatePanel` in `OrderDetailScreen.tsx` hat ein neues Textarea-Feld "Notiz zum Treffpunkt (nur intern, Team-Ansicht)" direkt unter den Adressfeldern (gleicher Text/Platzhalter/Maxlänge wie `ShootDatePanel.tsx` im Web), wird zusammen mit Datum/Adresse über denselben "Speichern"-Klick gesichert.
- Getypt (`tsc -b` sauber), kein Web-Build nötig, App-Version 2.52.0 gebaut und veröffentlicht (15.09., diese Session).
- **[x] erledigt**

## 37. Kundenkonto-Stammdaten: Anrede/Pronomen + vollständige Bearbeitung (Regression gefunden & behoben)

Web-Session (claude-ee) hat `updateClientAccountAction` deutlich erweitert (Anlass: neues `Client.salutation`/`pronouns`-Feld, Punkt vorher). Vorher nur Unternehmen+Passwort, jetzt zusätzlich firstName/lastName (Pflicht), email (Format+Eindeutigkeit), und für `role === "client"` zusätzlich salutation (Pflicht)/pronouns (optional, nur bei "divers"), plus nickname/phone/billing-Adresse — alles in einem Rutsch im selben "Kundenkonto verwalten"-Formular.

- **Gefundene Regression:** Die Team-API-Route (`PATCH /api/team/v1/clients/[clientId]`) hatte nur `company`/`password` an die Action weitergereicht — nachdem die Action firstName/lastName (und für Kunden salutation) zu Pflichtfeldern gemacht hat, wäre **jedes** Speichern aus der Desktop-App heraus fehlgeschlagen (leere Pflichtfelder → Validierungsfehler), auch wenn man nur das Passwort ändern wollte. Rechtzeitig vor Veröffentlichung gefunden und gefixt, bevor es Mitarbeitern aufgefallen wäre.
- **Web-Route** reicht jetzt alle Felder durch: firstName/lastName/salutation/pronouns/nickname/email/phone/billingStreet/billingPostCode/billingCity/billingCountryCode/company/password.
- **Desktop:** `ClientDetailResponse.client`-Typ um `lastName`/`salutation`/`pronouns`/`billingCountryCode` ergänzt; `ClientAccountPanel` in `ClientDetailScreen.tsx` komplett neu — exakt dieselben Felder/Pflicht-Sternchen/Reihenfolge wie `ClientAccountPanel.tsx` im Web, inkl. Anrede-Dropdown (nur sichtbar für `role === "client"`) mit Pronomen-Feld bei "Divers".
- Getypt (`tsc -b`/`tsc --noEmit` sauber), Web gebaut+deployt, App-Version 2.54.0 (zusammen mit den signierten Mac-Builds aus Punkt "Mac-Signierung", s. u.) gebaut und veröffentlicht (22.09., diese Session).
- **[x] erledigt**

## 38. Vertrags-Erinnerung: "nicht unterschrieben" gut erkennbar + Erinnerungs-Button

Web-Session (claude-9d) hat auf Tills Wunsch Vertragsstatus besser sichtbar gemacht: `Contract.status "sent"` gab's schon (= wartet auf Unterschrift), neu ist `Contract.reminders[] = {sentAt, sentByClientId}` als Audit-Log, `sendContractReminderAction(contractId)` (48h-Cooldown, E-Mail + In-App-Notification `"contract_reminder"`, Activity-Log), sowie eine neue Web-Seite `/admin/contracts` (alle Verträge auftragsübergreifend — **nicht** in der App nachgebaut, siehe unten).

- **Web:** `getOrderDetail()`s `contract`-Mapping (`src/lib/orderDetail.ts`, bisher eine kuratierte Teilmenge ohne Reminder-Info) um `lastReminderAt` ergänzt (letzter Eintrag aus `reminders[]`, oder `null`). `POST /api/team/v1/orders/[orderId]/contract` um `action: "remind"` erweitert (ruft `sendContractReminderAction` auf, gibt den Cooldown-Fehlertext 1:1 durch) — bewusst als neuer Zweig der bestehenden Route statt einer eigenen, gleiches Muster wie `"send"`/`"delete"`.
- **Desktop:** `OrderDetailResponse.contract` um `lastReminderAt` ergänzt; `api.sendContractReminder()` neu. `ContractPanel` in `OrderDetailScreen.tsx`: Status jetzt als farbige Pille (Grau/Orange/Grün/Rot für draft/sent/signed/declined, wie `StatusBadge` im Web) statt reinem Text; neue `ContractReminderControl`-Komponente (nur bei `status === "sent"`) — zeigt entweder den "Erinnerung senden"/"Erneut erinnern"-Button oder, falls Cooldown aktiv, "Nächste Erinnerung ab …" (identische 48h-Berechnung wie `ReminderControl`/`ContractReminderButton` im Web).
- **Bewusst nicht nachgebaut:** die neue `/admin/contracts`-Übersichtsseite (alle Verträge auftragsübergreifend) — in der App bleibt Vertragsstatus vorerst nur auftragsbezogen sichtbar (Auftragsdetailseite), passend zum bisherigen Muster (Statistiken/Systemprotokoll sind die einzigen "über alle Aufträge hinweg"-Ansichten, die die App hat). Kann bei Bedarf als eigener Screen nachgezogen werden.
- Getypt (`tsc -b`/`tsc --noEmit` sauber), Web gebaut+deployt, App-Version 2.55.0 gebaut und veröffentlicht (22.09., diese Session).
- **[x] erledigt** (Kernstück — Übersichtsseite bewusst offen, siehe oben)

## 39. Mac-Update-Mechanismus umgebaut (reine Desktop-Infra, kein Web-Bezug)

Till meldete: Mac-App lässt sich nicht aktualisieren, Deinstallation unklar. Ursache: `electron-updater`s Mac-Weg läuft über das in Electron eingebaute Squirrel.Mac, das für den Download+Ersetzen-Schritt eine ECHTE, stabile Apple-Signatur braucht — unsere Ad-hoc-Signatur (Punkt "Mac-Signierung"/`rcodesign`, s. Punkt 37-Umfeld) ist bewusst nur ein Gatekeeper-"beschädigt"-Fix und liefert keine über Versionen hinweg stabile Identität (jede Version hat einen anderen Ad-hoc-Hash). Squirrel.Mac kann ein Update damit nicht verifizieren → Update-Check lief ins Leere. Genau das Risiko, das schon in der Mac-Build-Notiz vorab vermerkt war ("Auto-Update auf ungesigntem Mac-Build nicht verifiziert") — jetzt live bestätigt.

- **Fix (`main.js`):** auf `process.platform === 'darwin'` läuft nicht mehr `autoUpdater.checkForUpdates()`, sondern eine eigene `checkForMacUpdateManually()` — holt `latest-mac.yml`, vergleicht Version gegen `app.getVersion()`, zeigt bei neuerer Version einen Dialog mit "Herunterladen"-Button, der die Web-Downloadseite (`/de/admin/mitarbeiter-app`, aus Tills eigener Idee weiter oben) öffnet, statt eine still scheiternde Auto-Installation zu versuchen. Windows bleibt komplett unverändert beim bisherigen `electron-updater`/NSIS-Weg (funktioniert dort zuverlässig ohne Signatur-Abhängigkeit).
- **Deinstallation:** kein Installer/Uninstaller bei ZIP-Vertrieb — `.app` einfach in den Papierkorb ziehen, wie bei jeder anderen manuell installierten Mac-App.
- Kein Web-Änderungsbedarf (reine Electron-`main.js`-Änderung). `node -c main.js` sauber, App-Version 2.56.0 (Mac + Windows) gebaut und veröffentlicht (24.09., diese Session).
- **Echte Lösung weiterhin offen:** nur ein echtes Apple-Entwicklerzertifikat gibt Squirrel.Mac eine stabile Identität für automatische Updates — der manuelle Download-Hinweis ist eine dauerhafte, ehrliche Übergangslösung für den unsignierten Weg, kein Workaround, der noch verbessert werden kann.
- **[x] erledigt** — Update-Mechanismus behoben (Download-Hinweis statt stillem Fehlschlag), Deinstallation ist auf Nutzerseite jetzt bekannt.

## 40. Personalsystem-Parität: Zeiterfassung, Urlaub, Personalakte, sensible Daten, Dokumenten-Autofill

Größter Nachbau bisher — das komplette, am 24.-25.09.2026 auf der Webseite gebaute Personalsystem (siehe Memory `project_rakku_v2_personalsystem.md`) existierte in der App bis dahin überhaupt nicht (nicht mal Zeiterfassung/Ein-Ausstempeln). Till hat den kompletten Nachbau inkl. sensibler Daten in der App direkt freigegeben (25.09.2026), nachdem die Website-Seite sich beruhigt hatte (Urlaubssystem + Vertrags-Autofill fertig).

- **Sicherheitslücke gefunden+gefixt (Web):** `GET /api/team/v1/clients/[clientId]` (`getClientDetail()`, roher `Client`-Doc) prüfte nur die breite `clients_view`-Berechtigung — bei einem Mitarbeiter (`role: "staff"`) wären damit Gehalt/Gehaltshistorie/Urlaubskontingent/Personalakte-Einträge für JEDEN mit `clients_view` abrufbar gewesen, unabhängig von der eigentlich zuständigen, viel engeren `team_management_view`-Berechtigung. Route bestand ausschließlich fürs Team-API/App (kein Web-UI-Konsument), hatte den Rollen-Check schlicht vergessen. Gefixt: zusätzlicher Rollen-Check + `encryptedIban`/`encryptedSocialSecurityNumber`/`encryptedTaxId` werden jetzt grundsätzlich nie mehr als Ciphertext-Blob mitgeschickt (dafür gibt's die dedizierte `sensitive`-Route mit eigenem Audit-Log).
- **Neue Web-Routen** (alle dünne FormData-Wrapper um bestehende Server-Actions, gleiches Muster wie überall im Team-API): `lib/staffFile.ts` (`getStaffFileData`, Pendant zu `/admin/team/[clientId]/page.tsx`) + `team/staff/[clientId]/file` (GET), `/birthdate`, `/salary`, `/vacation-allowance`, `/employment-records` (+ `[recordId]` DELETE), `/onboarding` (+ `[itemId]` PATCH/DELETE), `/sensitive` (GET entschlüsselt on-demand + POST, Audit-Log-Verhalten 1:1 wie Web), `/timeentries` (GET eigener Status + Team-Ansicht für `hr` in einem Request, POST clock in/out), `/vacation-requests` (GET eigene Anträge + `pending`-Liste für `hr` in einem Request, POST erstellen) + `/vacation-requests/[requestId]` (POST mit `action: cancel/approve/reject`, gleiches Mehrzweig-Muster wie beim Vertrags-Erinnerungs-Endpoint aus Punkt 38).
- **`documentsList.ts` erweitert:** liefert jetzt dieselben Autofill-Felder pro Mitarbeiter wie `admin/documents/page.tsx` (departmentName/positionName/address/employmentStartDate/birthDate/probezeit/stundenlohn/gehalt/urlaubstage) — vorher bewusst weggelassen (siehe alter Kommentar im File), jetzt Parität, weil die App das für ihr eigenes Autofill braucht.
- **Desktop, neue Screens:** `ZeiterfassungScreen.tsx` (ursprünglich: Ein-/Ausstempeln, Heute/Woche-Summen, eigene ArbZG-Hinweise, Team-Ansicht+Hinweise für `hr`, Urlaubs-Selbstbedienung eingebettet — exakt wie `/admin/zeiterfassung` zu dem Zeitpunkt; **seit Punkt 41 nur noch reine HR-Teamübersicht, Selbstbedienung zog in einen eigenen Screen um**, siehe dort), `UrlaubsantraegeScreen.tsx` (HR-Genehmigungs-Eingang, wie `/admin/urlaubsantraege`), `StaffFileScreen.tsx` (komplett neue Personalakte — Abteilung/Position/Teams/Berechtigungs-Summary/Probezeit-Warnung, Geburtsdatum/Gehalt/Urlaubskontingent inline editierbar, Gehalts-/Urlaubshistorie, sensible Daten (nur `payroll_sensitive_data`), Beschäftigungsverlauf mit Hinzufügen/Löschen, Einarbeitungs-Checkliste, Dokumentenliste mit PDF-Öffnen). Verlinkt von `TeamManagementScreen.tsx`'s Mitarbeiterliste ("Personalakte"-Link pro Zeile).
- **Nav-Umbau:** neue Kategorie "Personalsystem" in `AppShell.tsx` (Zeiterfassung [alwaysVisible] / Urlaubsanträge [hr] / Team & Rechte / Dokumente / QM & Personal), identisch zur Web-Umgruppierung — "Team & Rechte"/"Dokumente"/"QM & Personal" dafür aus "Verwaltung" entfernt. `NavLinkDef` um `alwaysVisible` pro Link erweitert (vorher nur pro Kategorie möglich).
- **`DocumentsScreen.tsx`:** `abmahnung`/`gehaltsanpassung` als Kategorien ergänzt; komplett neues Autofill-System (vorher lud die App Vorlagentext 1:1 ohne jede Substitution, stand sogar explizit als Hinweis im UI) — `extractTokens`/`fillTemplate`/`TOKEN_DESCRIPTIONS`/`AUTO_FIELDS` von der Web-Seite dupliziert (kein Codeaustausch zwischen den Repos möglich), automatisch ausgefüllte vs. noch offene Platzhalter werden wie im Web separat angezeigt, offene bekommen eigene Eingabefelder statt nur Text-Hinweis.
- **Bewusst nicht nachgebaut:** `Department.defaultHourlyWage`/`Position.defaultHourlyWage`-Bearbeitung in `TeamManagementScreen.tsx` (Standard-Stundenlöhne) — sekundäres Feature, im Web über `updateDepartmentSalaryAction`/`updatePositionSalaryAction`, hier aus Zeitgründen zurückgestellt. Kann bei Bedarf nachgezogen werden.
- Getypt (`tsc -b` Desktop sauber, `npx tsc --noEmit` Web sauber), `vite build` sauber, Web gebaut (Deploy-Zeitpunkt siehe Git/pm2-Log), App-Version 2.57.0 gebaut und veröffentlicht (25.09., diese Session).
- **[x] erledigt** — Standard-Stundenlohn-Bearbeitung bewusst offen, siehe oben.

## 41. Mitarbeiter-Selbstbedienung zieht ins Konto um + Quick-Tips-Onboarding

Direkt im Anschluss an Punkt 40 hat die Webseite noch draufgesattelt (claude-f5 meldete das): Till hatte ursprünglich mehrfach explizit gesagt, die Selbstbedienung solle im Konto-Bereich (`/account`) leben, nicht im Admin-Bereich — das wurde bei Punkt 40 zunächst nicht korrekt umgesetzt (Zeiterfassung-Selbstbedienung lag auf `/admin/zeiterfassung`). Web-Session hat das jetzt korrigiert: neue Seite `/account/mitarbeiter` bündelt Ein-/Ausstempeln+ArbZG-Hinweise+Heute/Woche+letzte Einträge, Urlaubs-Selbstbedienung, rein lesende Gehaltsübersicht+Historie (`MySalaryPanel.tsx`, neu) und sensible Selbstangaben (`SensitiveDataPanel.tsx` ohne Audit-Log-Anzeige) — für JEDEN Mitarbeiter sichtbar, unabhängig von Admin-Berechtigungen. `/admin/zeiterfassung` ist seitdem `hr`-gated und zeigt NUR NOCH die Team-Übersicht (aktuell eingestempelt + teamweite ArbZG-Hinweise), redirectet Nicht-HR nach `/account/mitarbeiter`.

Zusätzlich neu: **Quick-Tips-Onboarding** (`QuickTipsModal.tsx`, Lightroom-Vorbild) — Ersteinrichtungs-Tour im Konto-Dashboard, 4 Tipps für Mitarbeiter (Ein-/Ausstempeln, Urlaub beantragen, Gehalt & sensible Daten, Mitarbeiterhandbuch), überspringbar, mit Fortschrittsanzeige, einmal pro Konto (`Client.onboardingTipsSeenAt`, `dismissOnboardingTipsAction`).

- **Web-Verifikation vor dem Nachbau:** claude-f5s Meldung erst gegen den echten Code/git-Stand geprüft (Lehre aus dem `claude-c2`-Vorfall in Punkt-40-Umfeld, Memory `project_rakku_v2_personalsystem.md`) — beide Features existierten tatsächlich, committed (`9811bbd`, `5a1dd1e`).
- **Neue Web-Routen:** `GET /api/team/v1/session` liefert jetzt zusätzlich `clientId` und `onboardingTipsSeenAt`; neue `POST /api/team/v1/dismiss-onboarding-tips`; neue `GET /api/team/v1/account/salary` (eigene, rein lesende Gehaltsdaten — bewusst OHNE `team_management_view`-Gate, da es die eigenen Daten sind, gleiche Query wie `AccountMitarbeiterPage`, kein Umweg über `getStaffFileData`).
- **Desktop, umstrukturiert:** `ZeiterfassungScreen.tsx` ist jetzt reine HR-Teamübersicht (zeigt Hinweis + Verweis auf "Mein Arbeitsbereich", falls `hr`-Berechtigung fehlt). Neuer Screen `MeinArbeitsbereichScreen.tsx` bündelt exakt wie `/account/mitarbeiter` alles Selbstbedienungs-Zeug. `ClockInOutPanel`/`VacationSelfServicePanel`/`SensitiveDataPanel`/`SalaryHistoryPanel` dafür aus den Screens in eigene, geteilte Komponenten (`renderer/src/components/`) extrahiert statt dupliziert — gleiches Prinzip wie im Web (dort werden dieselben Komponenten zwischen Admin-Personalakte und Konto-Tab geteilt).
- **Nav:** "Zeiterfassung" jetzt `hr`-gated statt `alwaysVisible`; neuer, für alle sichtbarer Link "Mein Arbeitsbereich" (`alwaysVisible`) an erster Stelle der Personalsystem-Kategorie.
- **`QuickTipsModal.tsx`** 1:1 nachgebaut (nur die Mitarbeiter-Tipps, die App hat keine Kunden-Accounts) — teilt `Client.onboardingTipsSeenAt` mit dem Web, einmal auf irgendeiner Oberfläche weggeklickt gilt überall als gesehen. Erscheint beim App-Start, falls `onboardingTipsSeenAt` noch leer ist (Prüfung über die erweiterte `/session`-Antwort).
- Getypt (`tsc -b` Desktop sauber, `npx tsc --noEmit` Web sauber), `vite build` sauber. App-Version weiterhin 2.57.0 (im selben Veröffentlichungsschritt wie Punkt 40 enthalten, kein separater Versionssprung nötig, da beide Runden vor dem ersten Build zusammenkamen).
- **[x] erledigt**

## 42. Auftrags-Chat wird reines Archiv (Vorbereitung Mail-App)

Web-Session hat den In-App-Chat auf der Kunden-Auftragsseite durch einen "E-Mail verfassen"-Button ersetzt (Commit `92af31b`, 01.10.2026) — Kommunikation läuft jetzt über echte E-Mails, Grundlage für die separate Mail-App (ordnet eingehende Mails per Auftragsnummer im Betreff zu). Die Web-Admin-Seite wurde aus demselben Grund ebenfalls auf ein reines Lese-Archiv umgestellt (kein Senden mehr), die Team-API-Route (`api/team/v1/orders/[orderId]/chat`) blieb dabei bewusst unangetastet funktionsfähig — claude-da wies uns proaktiv (kein Zeitdruck, reine Konsistenzfrage) darauf hin, dass ein Senden von der App aus seitdem ins Leere läuft, weil die Kundenseite nicht mehr antworten kann.

- **Verifiziert vor dem Nachbau** (Lehre aus früheren Vorfällen, nie Peer-Behauptungen ungeprüft übernehmen): `git show 92af31b` bestätigte den exakten Wortlaut/Umfang der Web-Admin-Änderung.
- **Desktop:** `ChatPanel` in `OrderDetailScreen.tsx` von sendefähig auf reines Archiv umgestellt — Eingabefeld/Senden-Button entfernt, Titel/Leer-Text 1:1 wie im Web-Admin übernommen ("Nachrichten (Archiv) — Kommunikation läuft jetzt per E-Mail" / "Keine Nachrichten vorhanden."). `api.sendOrderChatMessage()` bewusst nicht aus `api.ts` entfernt (Team-API-Route bleibt ja absichtlich bestehen) — nur die UI nutzt sie nicht mehr.
- **Bewusst nicht nachgebaut:** kein `EmailOrderButton`-Äquivalent für die App — den gibt's im Web auch nur auf der Kundenseite, Mitarbeiter haben bereits echte E-Mail-Programme, brauchen keinen mailto-Hilfsbutton.
- Getypt (`tsc -b` sauber), `vite build` sauber, App-Version 2.58.0 gebaut und veröffentlicht (02.10., diese Session).
- **[x] erledigt**

## Bekannte offene Punkte (nicht umgesetzt, nur erwähnt)

- `archiveOrderAction` verschickt die Feedback-Mail unverändert auch bei zuvor bereits `done_dropped` (abgesagten) Aufträgen — vorbestehendes Verhalten, nicht Teil der heutigen Änderungen, aber ggf. erwähnenswert falls das mal angepasst werden soll.
- **Desktop-App, offen:** `ClientDetailScreen.tsx` hat kein Foto-Ordner-Panel (Zuweisen/Freigeben/Bearbeiten) — auf der Kundenseite im Web geht das über `AdminPhotoPanel`, in der App bisher nur über die Auftragsseite möglich. Wäre eine sinnvolle Erweiterung, aber bewusst nicht Teil der heutigen Sync-Runde (Scope-Grund: Nutzer-Anfrage betraf konkrete Web-Änderungen, nicht Neubau).
- **Desktop-App, offen (kosmetisch):** Checkbox/Radio-Styling aus Punkt 2 nicht auf die Desktop-App übertragen — eigenes Stylesheet, niedrige Priorität.
- **Desktop-App, offen (kosmetisch, 13.09.2026):** die `@Erwähnung`-Tipp-Autovervollständigung beim Notiz-Schreiben nicht nachgebaut — nur ein Hinweistext im Feld, Server-Logik (Mentions-Auflösung) funktioniert davon unabhängig unverändert. Die ToDo-Board/Drag&Drop-Ansicht selbst ist inzwischen NICHT mehr offen — siehe Punkt 30, nachgezogen.
- **Desktop-App, bewusst ausgelassen (13.09.2026):** Warteliste-Chat (`WaitlistMessage`) — Web-Admin-UI zeigt den seit dem Redesign selbst nicht mehr an, siehe Punkt 28. Wunschliste (Punkt 24) und Systemprotokoll (Punkt 21) sind NICHT mehr offen — siehe Punkt 29, auf Nutzer-Nachfrage nachgezogen.
