Let the same choice open notes and drafts
All checks were successful
CI / Lint, Typen, Tests, Build (push) Successful in 11m47s
CI / Migrationen auf leerer Datenbank (push) Successful in 10m10s

The picker in the bell now governs both lists, so note_subscriptions is
renamed to colleague_subscriptions -- a name that only mentions notes would
mislead the next reader.

Reading and writing a draft now reach differently far. hire_drafts_owner
(for all) is split into four policies: select lets in your own drafts and
those of the people you added, while insert/update/delete stay with the
owner. A draft is unfinished work with no lock and no history; two people
writing into the same row would overwrite each other silently.

That split forces a change in the actions: a policy does not reject a write,
it lets it hit no rows. saveHireDraft and deleteHireDraft now read the row
count instead of reporting success over a row that never changed.

The card shows a foreign draft with its author and without Fortsetzen or
Loeschen -- offering a button that reliably ends in a database error is a
promise without cover.

check-schema-types.mjs learns `alter table ... rename to`; without it the
drift check reports one rename as two errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-10 16:05:40 +02:00
parent e8e675fd07
commit eeaf210e78
17 changed files with 650 additions and 54 deletions

View File

@@ -268,8 +268,30 @@ ist ein bewusster zweiter Schritt.
### `hire_drafts`, `saved_reports`
Zwischenstand des Einstellungsassistenten (`payload` JSONB, `step`) und
gespeicherte Berichtskonfigurationen. Beide sind auf die anlegende Person
eingeschränkt.
gespeicherte Berichtskonfigurationen.
`saved_reports` gehört ganz der anlegenden Person. Bei `hire_drafts` gehen
Lesen und Schreiben seit September 2026 verschieden weit: **lesen** darf, wem
der Entwurf gehört, und wer die anlegende Person in seiner Glocke hinzugewählt
hat (`colleague_subscriptions`); **anlegen, ändern und löschen** darf nur die
anlegende Person selbst. Ein Entwurf ist unfertige Arbeit ohne Sperre und ohne
Historie — zwei Personen, die abwechselnd hineinschreiben, überschreiben
einander lautlos.
Die Anwendung liest deshalb bei `update` und `delete` die Zahl der
betroffenen Zeilen: eine Policy weist ein Schreiben nicht mit einem Fehler ab,
sie lässt es ins Leere laufen (`actions/hireDrafts.ts`).
### `colleague_subscriptions`
Wen jemand hinzugewählt hat, je eine Zeile aus `user_id` und
`author_user_id`. Die Vorgabe ist die eigene Person: **ohne** Zeile sieht man
nur die eigenen Notizen und Entwürfe, mit Zeile zusätzlich die der genannten
Person. Die Auswahl sitzt in der Glocke in der Kopfzeile und steuert beides.
Ein Abo auf sich selbst lässt `chk_kollegen_abos_nicht_selbst` nicht zu — die
eigenen Zeilen sind ohnehin immer dabei. Die Tabelle hiess bis September 2026
`note_subscriptions`, als die Auswahl nur für die Notizen galt.
---
@@ -390,8 +412,12 @@ Wo das Muster abweicht:
ohne Freischaltung. Sonst könnte niemand erfahren, warum er nicht
hineinkommt.
- `app_users` ebenso: die eigene Zeile oder HR.
- `hire_drafts` und `saved_reports` verlangen zusätzlich, dass die Zeile der
anfragenden Person gehört.
- `saved_reports` und `colleague_subscriptions` verlangen zusätzlich, dass
die Zeile der anfragenden Person gehört.
- `hire_drafts` hat vier — `select`, `insert`, `update`, `delete` getrennt,
weil das Lesen seit September 2026 weiter reicht als das Schreiben (siehe
oben). Die alte Sammelregel `for all` musste dafür weichen: Policies
addieren sich, eine stehengebliebene hätte die vier neuen ausgehebelt.
- `locations` trennt Lesen und Schreiben in zwei Policies, prüft aber beide
Male dasselbe.