feat(archiv): Uebersicht, Volltextsuche und Druckansicht
Der Einstieg am Schreibtisch und das Gedaechtnis der Anwendung. Die Uebersicht
sortiert nach Dringlichkeit — ueberfaellig zuerst, und das ist das Einzige in
Warnfarbe; alles andere auf der Seite ist Auskunft.
Das Archiv sucht ueber Titel, Notizen, erfasste Werte und Kommentare in einer
SQL-Anweisung: `Schrittergebnis.wert` ist ein Json, und nur `("wert")::text`
trifft den Trigramm-Index aus #006. Vor allem aber gehoert die Fundstelle zum
Treffer — ein Treffer ohne sichtbaren Grund ist fuer den Suchenden wertlos.
Die Druckansicht ist ein eigenes Lesemodell und kein Schalter an
`laufZumAbarbeiten`: dort entsteht beim Fuellen der Befehle ein Passwort, und auf
einem Blatt, das jemand abheftet, darf keines stehen. Vom Mitarbeiter holt sie
nur Name, UPN und Abteilung — nicht alles und dann gefiltert.
Der unabhaengige Review fand zwei Blocker, beide im Dashboard: „ueberfaellig“
verglich mit dem Augenblick statt mit dem Tagesbeginn, und der Schnellstart
zaehlte Laeufe fremder Kunden mit. Was „ueberfaellig“ heisst, steht seitdem in
`src/lib/fristen.ts` — eine Regel fuer Lesemodell und Anzeige.
Closes #15
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit was merged in pull request #29.
This commit is contained in:
@@ -240,6 +240,42 @@ ein `'wasm-unsafe-eval'` — die schmale Erlaubnis, WebAssembly zu uebersetzen,
|
||||
an derselben Stelle ein Feld fuer den abgetippten Code: der steht lesbar unter
|
||||
jedem QR, und ein Scanner, der nicht startet, darf keine Sackgasse sein.
|
||||
|
||||
## 5f. Archiv und Ausdruck: das Gedaechtnis
|
||||
|
||||
Die **Archivsuche** geht ueber drei Tabellen — `Lauf.titel`, `Schrittergebnis.notiz`,
|
||||
`Schrittergebnis.wert` und `Kommentar.rumpf` — und steht deshalb als **eine**
|
||||
SQL-Anweisung in `src/server/queries/archiv.ts` statt als vier Prisma-Abfragen.
|
||||
Zwei Gruende, beide handfest:
|
||||
|
||||
1. `Schrittergebnis.wert` ist ein Json. Nur `("wert")::text` trifft den
|
||||
`pg_trgm`-GIN-Index aus #006; eine ganze Json-Spalte als Text zu durchsuchen
|
||||
kann Prisma nicht.
|
||||
2. Die Fundstelle gehoert zum Treffer. Wer im Archiv sucht, will nicht wissen
|
||||
**dass** etwas passt, sondern **wo** — Titel, Notiz, Wert oder Kommentar, mit
|
||||
dem Ausschnitt drumherum. Vier getrennte Abfragen muessten das hinterher
|
||||
wieder zusammenlegen.
|
||||
|
||||
Jeder Wert geht als Parameter in die Anweisung (`Prisma.sql`), und `%`, `_` und
|
||||
`\` werden fuer `ILIKE` maskiert: die Suche nach „50 %“ trifft sonst „5099“.
|
||||
`tsvector` bleibt die spaetere Optimierung, so wie in §5a festgehalten.
|
||||
|
||||
**Abgelegt heisst `COALESCE(archiviertAm, abgeschlossenAm, erstelltAm)`** — derselbe
|
||||
Ausdruck fuer die Sortierung **und** den Zeitraumfilter. Zwei verschiedene
|
||||
Ausdruecke hiessen: ein Lauf steht ausserhalb des Zeitraums, den die Liste an
|
||||
seiner Position anzeigt.
|
||||
|
||||
Die **Druckansicht** (`/drucken/lauf/<id>`) ist ein eigenes Lesemodell und kein
|
||||
Schalter an `laufZumAbarbeiten`. Dort entsteht beim Fuellen der Befehle ein
|
||||
`{{generiertes_passwort}}`; auf einem Blatt, das jemand abheftet, darf keines
|
||||
stehen — dort erscheint `[Passwort – nicht gespeichert]`, und erzeugt wird
|
||||
keines. Der Beweis dafuer ist ein Vergleich: zwei Ausdrucke desselben Laufs sind
|
||||
Zeichen fuer Zeichen gleich, ein erzeugtes Passwort waere bei jedem Aufbau ein
|
||||
anderes.
|
||||
|
||||
Vom Mitarbeiter holt dieses Lesemodell **nur Name, UPN und Abteilung** — nicht
|
||||
alles und dann gefiltert. Ein Ausdruck liegt herum, wird kopiert und weitergegeben;
|
||||
was darauf steht, ist aus der Hand gegeben.
|
||||
|
||||
## 6. Betrieb: zwei Wege, eine Regel
|
||||
|
||||
Die Datenbank ist auf beiden Wegen von außen nicht erreichbar:
|
||||
|
||||
+57
-2
@@ -17,11 +17,12 @@ als Unterpunkte mit Aufgabe und Dauer.
|
||||
| #012 | M7 – Platzhalter, Namensregeln, Escaping | 2 h 30 min |
|
||||
| #013 | M8 – Läufe: Service und Abarbeitung | 3 h 05 min |
|
||||
| #014 | M9 – QR-Tags, Geräteseite, Etiketten, Scanner | 3 h 35 min |
|
||||
| | **Summe** | **20 h 25 min** |
|
||||
| #015 | M10 – Dashboard und Archiv | 2 h 20 min |
|
||||
| | **Summe** | **22 h 45 min** |
|
||||
|
||||
| Meilenstein | Tatsächlich | Status |
|
||||
|---|---|---|
|
||||
| v0.1.0 | 20 h 25 min | in Arbeit |
|
||||
| v0.1.0 | 22 h 45 min | in Arbeit |
|
||||
|
||||
---
|
||||
|
||||
@@ -625,3 +626,57 @@ als Unterpunkte mit Aufgabe und Dauer.
|
||||
|
||||
- #015 Dashboard und Archiv — die Übersicht für den Alltag und die Volltextsuche über alles, was
|
||||
abgearbeitet wurde.
|
||||
|
||||
---
|
||||
|
||||
## 2026-09-25 · Sitzung 11 · 21:35–23:00
|
||||
|
||||
**Issue:** #015 (M10)
|
||||
**Version:** 0.1.0
|
||||
|
||||
### Erledigt
|
||||
|
||||
- **Drei Teile parallel**: Archiv (Sub-Agent), Druckansicht (Sub-Agent), Übersicht (selbst)
|
||||
- Archiv mit Volltextsuche über Titel, Notizen, Werte und Kommentare, Fundstellen im Treffer,
|
||||
sechs Filtern und Mandantensicht in jeder Abfrage
|
||||
- Druckansicht eines Laufs: hell, ohne Navigation, A4 — ersetzt im MVP den PDF-Export
|
||||
- Übersicht mit Überfälligem, eigenen Zuweisungen, zuletzt Bearbeitetem und Schnellstart
|
||||
- Unabhängiger Review: **zwei Blocker**, fünf Befunde, neun Anmerkungen — alle abgearbeitet
|
||||
- **980 Tests** (vorher 930) und **67 Ende-zu-Ende-Tests** grün
|
||||
|
||||
### Entscheidungen
|
||||
|
||||
- **`ABGESCHLOSSEN` gehört ins Archiv.** „Abgeschlossen ist nicht archiviert" betrifft den
|
||||
Schreibschutz, nicht das Suchen: wer die Seriennummer von letzter Woche sucht, weiß nicht, ob
|
||||
schon jemand archiviert hat.
|
||||
- **Eine SQL-Anweisung für drei Tabellen.** `Schrittergebnis.wert` ist ein Json — nur
|
||||
`("wert")::text` trifft den Trigramm-Index, und über Prisma lässt sich eine Json-Spalte nicht als
|
||||
Text durchsuchen. Vor allem aber gehört die **Fundstelle** zum Treffer: ein Treffer ohne
|
||||
sichtbaren Grund ist für den Suchenden wertlos.
|
||||
- **Die Druckansicht ist ein eigenes Lesemodell**, kein Schalter an `laufZumAbarbeiten`. Dort
|
||||
entsteht beim Füllen der Befehle ein Passwort; auf einem Blatt, das jemand abheftet, darf keines
|
||||
stehen.
|
||||
- **Was „überfällig" heißt, steht jetzt in `src/lib/fristen.ts`** — eine Regel, zwei Benutzer
|
||||
(Lesemodell und Anzeige). Zwei Fassungen derselben Regel laufen auseinander.
|
||||
|
||||
### Probleme
|
||||
|
||||
- **Beide Blocker steckten in meinem eigenen Teil.** „Überfällig" verglich mit dem Augenblick
|
||||
statt mit dem Tagesbeginn — ein heute fälliger Lauf stand ab zwei Uhr morgens in Warnfarbe. Und
|
||||
der Schnellstart zählte Läufe fremder Kunden mit: eine Vorlage ohne Kundenbezug gilt für alle,
|
||||
ihr Zähler nicht.
|
||||
- **Mein Test ließ genau die Grenze aus, an der der Fehler saß.** Er kannte „gestern" und
|
||||
„morgen", aber nicht „heute". Seitdem steht die Regel in einer reinen Funktion mit eigenen
|
||||
Grenztests.
|
||||
- **Ein E2E-Test, der nicht scheitern konnte:** „trägt kein Passwort" lief gegen eine Vorlage ohne
|
||||
Passwortschritt. Er hätte auch bestanden, wenn die Druckansicht Passwörter erzeugte.
|
||||
- **Der Trigramm-Index auf `Lauf.titel` war strukturell unerreichbar**, weil der Titeltreffer aus
|
||||
der sechsmal gelesenen und deshalb materialisierten CTE las. Der Review hat das mit 40 000
|
||||
Läufen gemessen, nicht vermutet — das ist der Unterschied zwischen „sollte passen" und „passt".
|
||||
- **Siebter Meilenstein in Folge mit einer Zusage ohne Deckung:** „kein
|
||||
`{{generiertes_passwort}}` im Druck" galt nur für Vorlagentexte.
|
||||
|
||||
### Nächster Schritt
|
||||
|
||||
- #016 Verzeichnis und LDAP-Abgleich — der größte verbleibende Brocken: `ldapts`, Attribut-Mapping,
|
||||
Zeitplan, verschlüsseltes Bind-Passwort und ein Integrationstest gegen einen OpenLDAP-Container.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# #015 M10 – Dashboard und Archiv
|
||||
|
||||
**Status:** offen
|
||||
**Status:** erledigt
|
||||
**Meilenstein:** v0.1.0
|
||||
**Labels:** phase-1, frontend, backend
|
||||
**Geschätzt:** 8 h **Tatsächlich:** –
|
||||
**Geschätzt:** 8 h **Tatsächlich:** 2 h 20 min
|
||||
|
||||
## Ziel
|
||||
|
||||
@@ -11,18 +11,51 @@ Der Einstieg am Schreibtisch und das Gedächtnis der Anwendung.
|
||||
|
||||
## Aufgaben
|
||||
|
||||
- [ ] Dashboard: meine offenen Läufe, überfällige Läufe, zuletzt bearbeitet, Schnellstart der häufigsten Vorlagen
|
||||
- [ ] Archiv-Volltextsuche über Titel, Notizen, Werte und Kommentare in einem gemeinsamen Query-Modul mit `pg_trgm` und `ILIKE`
|
||||
- [ ] Filter nach Vorlage, Mitarbeiter, Abteilung, Gerät, Zeitraum und Bearbeiter; im Modus `DIENSTLEISTUNG` zusätzlich nach Kunde
|
||||
- [ ] Druckansicht eines Laufs: Print-CSS, Kopfdaten, alle Abschnitte und Schritte, Werte, Notizen, Namen, Zeitstempel; ersetzt im MVP den PDF-Export
|
||||
- [ ] In der Druckansicht erscheinen vom Mitarbeiter nur Name, UPN und Abteilung, sonst nichts
|
||||
- [ ] Screenshots Übersicht, Archiv, Druckansicht in `docs/screenshots/m10/`
|
||||
- [x] Dashboard: meine offenen Läufe, überfällige Läufe, zuletzt bearbeitet, Schnellstart der häufigsten Vorlagen
|
||||
- [x] Archiv-Volltextsuche über Titel, Notizen, Werte und Kommentare in einem gemeinsamen Query-Modul mit `pg_trgm` und `ILIKE`
|
||||
- [x] Filter nach Vorlage, Mitarbeiter, Abteilung, Gerät, Zeitraum und Bearbeiter; im Modus `DIENSTLEISTUNG` zusätzlich nach Kunde
|
||||
- [x] Druckansicht eines Laufs: Print-CSS, Kopfdaten, alle Abschnitte und Schritte, Werte, Notizen, Namen, Zeitstempel; ersetzt im MVP den PDF-Export
|
||||
- [x] In der Druckansicht erscheinen vom Mitarbeiter nur Name, UPN und Abteilung, sonst nichts
|
||||
- [x] Screenshots Übersicht, Archiv, Druckansicht in `docs/screenshots/m10/`
|
||||
|
||||
## Akzeptanzkriterien
|
||||
|
||||
- [ ] Test: ein archivierter Lauf wird über ein Wort aus einer Schritt-Notiz gefunden
|
||||
- [ ] Test: ein archivierter Lauf wird über ein Wort aus einem Kommentar gefunden
|
||||
- [ ] Test: die Filter kombinieren sich (Vorlage und Zeitraum und Bearbeiter)
|
||||
- [ ] Test: `{{generiertes_passwort}}` erscheint nicht in der Druckansicht
|
||||
- [ ] Test: überfällige Läufe sind genau die mit `faelligAm < heute` und Status `OFFEN` oder `IN_ARBEIT`
|
||||
- [ ] Die Druckansicht ist hell, ohne Navigation, und passt auf A4
|
||||
- [x] Test: ein archivierter Lauf wird über ein Wort aus einer Schritt-Notiz gefunden
|
||||
- [x] Test: ein archivierter Lauf wird über ein Wort aus einem Kommentar gefunden
|
||||
- [x] Test: die Filter kombinieren sich (Vorlage und Zeitraum und Bearbeiter)
|
||||
- [x] Test: `{{generiertes_passwort}}` erscheint nicht in der Druckansicht
|
||||
- [x] Test: überfällige Läufe sind genau die mit `faelligAm < heute` und Status `OFFEN` oder `IN_ARBEIT`
|
||||
- [x] Die Druckansicht ist hell, ohne Navigation, und passt auf A4
|
||||
|
||||
## Ergebnis
|
||||
|
||||
Erledigt am 2026-09-25. **980 Tests** (vorher 930) und **67 Ende-zu-Ende-Tests** grün, Screenshots in
|
||||
`docs/screenshots/m10/`.
|
||||
|
||||
Drei Teile entstanden parallel: Archiv, Druckansicht und Übersicht. Eine Entscheidung des
|
||||
Archiv-Teils ist nicht offensichtlich und darum hier festgehalten: **auch `ABGESCHLOSSEN` gehört
|
||||
ins Archiv.** „Abgeschlossen ist nicht archiviert" (architektur.md §4) betrifft den Schreibschutz,
|
||||
nicht das Suchen — wer die Seriennummer von letzter Woche sucht, weiß nicht, ob schon jemand
|
||||
archiviert hat.
|
||||
|
||||
### Der unabhängige Review
|
||||
|
||||
Zwei Blocker, fünf Befunde, neun Anmerkungen — beide Blocker steckten im Dashboard, also in meinem
|
||||
eigenen Teil:
|
||||
|
||||
- **„Überfällig" traf auch, was heute fällig ist.** Verglichen wurde mit dem Augenblick statt mit
|
||||
dem Tagesbeginn; ein Datumsfeld speichert Mitternacht UTC, und damit stand ein heute fälliger
|
||||
Lauf hierzulande ab zwei Uhr morgens in Warnfarbe — an genau dem Tag, an dem er erledigt werden
|
||||
soll. Die Regel steht jetzt als `fristVerstrichen()` in `src/lib/fristen.ts`, Lesemodell und
|
||||
Anzeige benutzen dieselbe, und ein Test prüft die Grenze selbst. Der alte Test kannte nur
|
||||
„gestern" und „morgen".
|
||||
- **Der Schnellstart zählte Läufe fremder Kunden mit.** Eine Vorlage ohne Kundenbezug gilt für alle
|
||||
(#009); ihr Zähler sammelte die Läufe sämtlicher Kunden, und „4× gestartet" war damit eine
|
||||
Auskunft über fremde Betriebe — dazu eine, die die Reihenfolge nach fremder Arbeit sortierte.
|
||||
|
||||
Dazu die Befunde: der E2E-Test „trägt kein Passwort" lief gegen eine Vorlage **ohne** Passwortschritt
|
||||
und konnte gar nicht scheitern; der Trigramm-Index auf `Lauf.titel` war strukturell unerreichbar,
|
||||
weil der Titeltreffer aus der materialisierten CTE las; `pflichtangaben` wurde berechnet,
|
||||
dokumentiert und nie benutzt; „N weitere in der Liste" führte bei Überfälligen in die ungefilterte
|
||||
Liste; und die Zusage „kein `{{generiertes_passwort}}` im Druck" galt nur für Vorlagentexte —
|
||||
wörtlich in eine Notiz geschriebene Zeichenfolgen gibt der Beleg wieder, wie es ein Beleg tun muss.
|
||||
|
||||
@@ -19,9 +19,9 @@ Diese Dateien sind die Quelle; `pnpm issues:sync` spiegelt sie in den Gitea-Trac
|
||||
| #012 | [M7 – Platzhalter, Namensregeln, Escaping](012-platzhalter-namensregeln.md) | erledigt | v0.1.0 | 8 h | 2 h 30 min |
|
||||
| #013 | [M8 – Läufe: Service und Abarbeitung](013-laeufe-abarbeitung.md) | erledigt | v0.1.0 | 14 h | 3 h 05 min |
|
||||
| #014 | [M9 – QR-Tags, Geräteseite, Etiketten, Scanner](014-qr-tags-geraeteseite.md) | erledigt | v0.1.0 | 12 h | 3 h 35 min |
|
||||
| #015 | [M10 – Dashboard und Archiv](015-dashboard-archiv.md) | offen | v0.1.0 | 8 h | – |
|
||||
| #015 | [M10 – Dashboard und Archiv](015-dashboard-archiv.md) | erledigt | v0.1.0 | 8 h | 2 h 20 min |
|
||||
| #016 | [M11 – Verzeichnis und LDAP-Abgleich](016-verzeichnis-ldap.md) | offen | v0.1.0 | 12 h | – |
|
||||
| #017 | [M12 – Seed der sieben Vorlagen](017-seed-sieben-vorlagen.md) | offen | v0.1.0 | 8 h | – |
|
||||
| #018 | [M13 – E2E-Tests, Anleitung, PDF](018-e2e-anleitung-pdf.md) | offen | v0.1.0 | 14 h | – |
|
||||
| #019 | [M14 – Betriebswege und Release v0.1.0](019-betrieb-release.md) | offen | v0.1.0 | 10 h | – |
|
||||
| | **Summe** | | | **136 h** | **20 h 25 min** |
|
||||
| | **Summe** | | | **136 h** | **22 h 45 min** |
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 165 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 127 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 259 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 192 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
Reference in New Issue
Block a user