Phase 2: Wartungs- und Pruefmanagement
Wartungsvorlagen mit Checklisten, Prueftermine aus einem naechtlichen Lauf, Prueffristen-Dashboard, Pruefwizard mit Foto und Unterschrift, unveraenderliche Protokolle als PDF, Maengel-Workflow, Rueckrufe und Lebensdauerueberwachung. Elf neue Tabellen, alle mit organisationId und Row Level Security. Die Unveraenderbarkeit abgeschlossener Protokolle setzt ein Datenbanktrigger durch, nicht der Anwendungscode: eine Korrektur erzeugt eine neue Pruefung mit Verweis auf die alte, beide bleiben lesbar. 79 neue Tests (47 Unit, 10 gegen eine echte Datenbank, 22 E2E). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7f866ac35c
commit
e503aafc18
+74
-3
@@ -80,6 +80,44 @@ const AUFNAHMEN: Aufnahme[] = [
|
||||
beschreibung: "Formular für ein neues Gerät",
|
||||
angemeldet: true,
|
||||
},
|
||||
|
||||
// Phase 2 — Wartung und Prüfung
|
||||
{
|
||||
name: "11-fristen",
|
||||
pfad: "/pruefungen",
|
||||
beschreibung: "Fristen-Dashboard mit Zeitfenstern und Bündelung",
|
||||
angemeldet: true,
|
||||
},
|
||||
{
|
||||
name: "12-vorlagen",
|
||||
pfad: "/pruefungen/vorlagen",
|
||||
beschreibung: "Wartungsvorlagen mit Intervall und Prüfvorschrift",
|
||||
angemeldet: true,
|
||||
},
|
||||
{
|
||||
name: "13-maengel",
|
||||
pfad: "/maengel",
|
||||
beschreibung: "Mängel von der Meldung bis zur Behebung",
|
||||
angemeldet: true,
|
||||
},
|
||||
{
|
||||
name: "14-rueckrufe",
|
||||
pfad: "/rueckrufe",
|
||||
beschreibung: "Rückrufe des Herstellers",
|
||||
angemeldet: true,
|
||||
},
|
||||
{
|
||||
name: "15-lebensdauer",
|
||||
pfad: "/pruefungen/lebensdauer",
|
||||
beschreibung: "Geräte am Ende ihrer Nutzungsdauer",
|
||||
angemeldet: true,
|
||||
},
|
||||
{
|
||||
name: "16-protokolle",
|
||||
pfad: "/pruefungen/verlauf",
|
||||
beschreibung: "Abgeschlossene Prüfprotokolle",
|
||||
angemeldet: true,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -92,6 +130,12 @@ const DYNAMISCHE_AUFNAHMEN: Array<{
|
||||
/** Findet den Link auf der Übersichtsseite. */
|
||||
von: string;
|
||||
linkMuster: RegExp;
|
||||
/**
|
||||
* Zusätzliche Bedingung an die Beschriftung des Links. Nötig bei den
|
||||
* Protokollen: dort stehen auch Entwürfe in der Liste, und die zeigen ein
|
||||
* halb leeres Bild.
|
||||
*/
|
||||
textMuster?: RegExp;
|
||||
}> = [
|
||||
{
|
||||
name: "04-geraet",
|
||||
@@ -105,6 +149,20 @@ const DYNAMISCHE_AUFNAHMEN: Array<{
|
||||
von: "/fahrzeuge",
|
||||
linkMuster: /^\/fahrzeuge\/[0-9a-f-]{36}$/,
|
||||
},
|
||||
{
|
||||
name: "17-checkliste",
|
||||
beschreibung: "Checkliste einer Wartungsvorlage",
|
||||
von: "/pruefungen/vorlagen",
|
||||
linkMuster: /^\/pruefungen\/vorlagen\/[0-9a-f-]{36}$/,
|
||||
},
|
||||
{
|
||||
name: "18-pruefprotokoll",
|
||||
beschreibung: "Abgeschlossenes Prüfprotokoll mit Checkliste und Unterschrift",
|
||||
von: "/pruefungen/verlauf",
|
||||
linkMuster: /^\/pruefungen\/[0-9a-f-]{36}$/,
|
||||
// Nur fertige Protokolle: die tragen eine Nummer, Entwürfe nicht.
|
||||
textMuster: /^\d{4}-\d{4}$/,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -116,6 +174,12 @@ const DYNAMISCHE_AUFNAHMEN: Array<{
|
||||
*/
|
||||
async function fertigGeladen(seite: Page): Promise<void> {
|
||||
await seite.waitForLoadState("networkidle");
|
||||
|
||||
// Die Entwicklungsanzeige von Next.js gehört nicht in eine Anleitung.
|
||||
await seite
|
||||
.addStyleTag({ content: "nextjs-portal { display: none !important; }" })
|
||||
.catch(() => undefined);
|
||||
|
||||
try {
|
||||
await seite.waitForFunction(() => !document.querySelector(".animate-pulse"), undefined, {
|
||||
timeout: 15_000,
|
||||
@@ -177,11 +241,18 @@ async function main(): Promise<void> {
|
||||
.locator("a[href]")
|
||||
.evaluateAll((elemente) =>
|
||||
elemente
|
||||
.map((element) => element.getAttribute("href") ?? "")
|
||||
.filter((href) => href.startsWith("/")),
|
||||
.map((element) => ({
|
||||
href: element.getAttribute("href") ?? "",
|
||||
text: (element.textContent ?? "").trim(),
|
||||
}))
|
||||
.filter((eintrag) => eintrag.href.startsWith("/")),
|
||||
);
|
||||
|
||||
const treffer = ziele.find((href) => aufnahme.linkMuster.test(href));
|
||||
const treffer = ziele.find(
|
||||
(eintrag) =>
|
||||
aufnahme.linkMuster.test(eintrag.href) &&
|
||||
(!aufnahme.textMuster || aufnahme.textMuster.test(eintrag.text)),
|
||||
)?.href;
|
||||
if (!treffer) {
|
||||
console.warn(`[screenshots] Kein Ziel für ${aufnahme.name} gefunden — übersprungen.`);
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user