feat(archiv): Uebersicht, Volltextsuche und Druckansicht
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s

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:
2026-09-25 20:59:38 +00:00
co-authored by Claude Opus 5
parent e7a2e2d5c7
commit ab5777a57e
32 changed files with 4202 additions and 38 deletions
+45
View File
@@ -0,0 +1,45 @@
import { describe, expect, it } from "vitest";
import { fristVerstrichen, tagesbeginn } from "@/lib/fristen";
/**
* Die Grenze zwischen „heute faellig“ und „ueberfaellig“ (#015).
*
* Der Review hat genau hier zugeschlagen: verglichen wurde mit dem Augenblick
* statt mit dem Tagesbeginn, und ein heute faelliger Lauf stand ab zwei Uhr
* morgens in Warnfarbe. Der alte Test kannte nur „gestern“ und „morgen“ — die
* Grenze selbst blieb ungeprueft, und genau dort sass der Fehler.
*/
describe("Fristen", () => {
const HEUTE_FRUEH = new Date(2026, 8, 25, 2, 0, 0);
const HEUTE_SPAET = new Date(2026, 8, 25, 23, 30, 0);
it("zählt den heutigen Tag nicht als verstrichen", () => {
const heuteMitternacht = new Date(2026, 8, 25, 0, 0, 0);
expect(fristVerstrichen(heuteMitternacht, HEUTE_FRUEH)).toBe(false);
expect(fristVerstrichen(heuteMitternacht, HEUTE_SPAET)).toBe(false);
});
it("zählt gestern als verstrichen, egal zu welcher Uhrzeit", () => {
const gestern = new Date(2026, 8, 24, 23, 59, 0);
expect(fristVerstrichen(gestern, HEUTE_FRUEH)).toBe(true);
expect(fristVerstrichen(gestern, HEUTE_SPAET)).toBe(true);
});
it("zählt morgen nicht als verstrichen", () => {
expect(fristVerstrichen(new Date(2026, 8, 26, 0, 0, 0), HEUTE_SPAET)).toBe(false);
});
it("nimmt eine fehlende Frist als nicht verstrichen", () => {
expect(fristVerstrichen(null, HEUTE_FRUEH)).toBe(false);
expect(fristVerstrichen(undefined, HEUTE_FRUEH)).toBe(false);
});
it("gibt den Tagesbeginn in lokaler Zeit zurück", () => {
const beginn = tagesbeginn(HEUTE_SPAET);
expect(beginn.getFullYear()).toBe(2026);
expect(beginn.getMonth()).toBe(8);
expect(beginn.getDate()).toBe(25);
expect(beginn.getHours()).toBe(0);
expect(beginn.getMinutes()).toBe(0);
});
});