<!-- Generated by scripts/build-prompts.mjs — do not edit by hand. -->
# Bau mir dieses Werkzeug: Prüfbuch Betriebsmittel

*Alles Folgende ist die fachliche Anforderung. Halte dich daran; wo sie schweigt, entscheide selbst und sage, was du angenommen hast.*

## Ausgangspunkt

Nutze die Vorlage **openToolbox**: https://github.com/m-dohmen/openToolbox. Lies dort zuerst `AGENTS.md` — sie ist maßgeblich für Schemaform, Feldtypen und die Regeln, an denen ein Einzeldatei-Build zerbricht. Alles Fachliche kommt in eine einzige Datei, `src/domain.js`.

> Ist der openToolbox-Skill installiert (`claude plugin install opentoolbox@opentoolbox`), genügt es, diese Datei einzufügen — er holt sich die Vorlage selbst.

## Das Problem dahinter

Jede Werkstatt, jeder Handwerksbetrieb, jede Kita hat Geräte, die in festen Abständen geprüft werden müssen — Bohrmaschine, Verlängerung, Kaffeemaschine, Heißluftgebläse. Die Prüfung selbst dauert Minuten. Die Verwaltung frisst den Nachmittag, weil das Protokoll im Ordner liegt, die Fristen im Kopf des Meisters, und die Berufsgenossenschaft nach einem Unfall genau eine Frage stellt: **wann wurde dieses Gerät zuletzt geprüft?**

## Was am Ende dastehen muss

Eine einzelne, in sich geschlossene HTML-Datei, per Doppelklick zu öffnen, ohne Server und ohne Installation. Die Datei ist zugleich die Datenbank: Speichern schreibt eine neue HTML-Datei mit den eingebetteten Datensätzen.

## Der Datensatz

Ein Datensatz ist **Betriebsmittel**, mehrere sind **Betriebsmittel**.

**Felder**

| Schlüssel | Beschriftung | Typ | Näheres |
| --- | --- | --- | --- |
| `name` | Gerät | text | Pflicht |
| `kind` | Art | enum | einer von: Handmaschine · Verlängerung/Leitung · Küchengerät · Messgerät · Ladegerät · Ortsfest |
| `serial` | Inventar-/Seriennummer | text | Tabellenkopf `Nr.` |
| `location` | Standort | enum | einer von: Werkstatt · Baustellenwagen · Lager · Büro · Küche · ausgeliehen, Tabellenkopf `Ort` |
| `holder` | In der Hand von | text | Tabellenkopf `Bei` |
| `interval` | Prüfintervall (Monate) | enum | einer von: 6 · 12 · 24, Tabellenkopf `Int.` |
| `lastTest` | Letzte Prüfung | date | Tabellenkopf `Geprüft` |
| `tester` | Prüfer | text | — |
| `result` | Ergebnis | enum | einer von: bestanden · bestanden mit Mangel · nicht bestanden · noch nicht geprüft |
| `defect` | Festgestellter Mangel | text | mehrzeilig, Tabellenkopf `Mangel` |
| `protocol` | Prüfprotokoll | attachment | hochgeladene Datei, im Datensatz abgelegt, Tabellenkopf `Protokoll` |
| `note` | Notiz | text | mehrzeilig |
| `due` | Nächste Prüfung | computed | berechnet, nie gespeichert, Tabellenkopf `Fällig` |
| `daysLeft` | Tage bis zur Prüfung | computed | berechnet, nie gespeichert, Tabellenkopf `Tage` |

**Darstellung**

- Führende Spalte: `name`
- Zweite Zeile darunter: `location`
- Tabellenspalten, in dieser Reihenfolge: `name`, `kind`, `location`, `lastTest`, `due`, `daysLeft`, `result`
- Filter in der Seitenleiste: `kind`, `location`, `result`
- Zählt nicht mehr als offen, wenn: `() => false`
- Rot markiert, wenn: `{ if (r.result === 'nicht bestanden') return true const d = dueDate(r) return !d || d < iso(0) }`

**Berechnete Felder**

Sie werden bei jeder Anzeige gerechnet und nie in den Datensatz geschrieben — eine gespeicherte Ableitung ist in dem Moment falsch, in dem sich eine ihrer Quellen ändert.

- `due` (Nächste Prüfung):

  ```js
  const d = dueDate(r)
  if (!d) return ''
  const [y, m, day] = d.split('-')
  return `${day}.${m}.${y}`
  ```
- `daysLeft` (Tage bis zur Prüfung):

  ```js
  const d = localDateFromIso(dueDate(r))
  if (!d) return ''
  // Beide Seiten als ganze lokale Tage: Date.UTC über die Tages-
  // komponenten hält die Differenz exakt ganzzahlig - ohne die
  // Rundungsrettung, die ab UTC+12 kippt, und ohne den Mix aus
  // UTC-Mitternacht und Lokal-Mitternacht.
  const now = new Date()
  return (
    (Date.UTC(d.getFullYear(), d.getMonth(), d.getDate()) -
      Date.UTC(now.getFullYear(), now.getMonth(), now.getDate())) /
    86400000
  )
  ```

**Prüfregeln**

Bedingungen zwischen Feldern. Sie müssen an einer Stelle greifen, damit Formular, CSV-Import und die Vorschläge der KI durch dieselbe Prüfung laufen.

- **Wenn** `r.result !== 'noch nicht geprüft'` → **Dann** `lastTest`, `tester`
  **Meldung:** „Ein Prüfergebnis ohne Datum und Prüfer ist im Ernstfall wertlos.“
- **Wenn** `r.result === 'bestanden mit Mangel' || r.result === 'nicht bestanden'` → **Dann** `defect`
  **Meldung:** „Zu einem Mangel gehört, worin er besteht.“
- **Wenn** `r.result === 'nicht bestanden'` → **Dann** `note`
  **Meldung:** „Bei „nicht bestanden" gehört in die Notiz, wo das Gerät jetzt ist — es darf nicht weiterlaufen.“

## Dashboard

Kacheln über den gesamten Bestand, nicht über die gefilterte Ansicht.

- Eine Zahl: **Betriebsmittel** — der Anzahl · „im Bestand“
- Eine Zahl: **Fällig oder gesperrt** — der Anzahl (nur Datensätze, die einem Filter entsprechen): `isOverdue(r)` · „sofort anfassen“
- Eine Zahl: **In den nächsten 60 Tagen** — der Anzahl (nur Datensätze, die einem Filter entsprechen): `{ const d = dueDate(r) return d && d >= iso(0) && d <= iso(60) }` · „Prüftermin planen“
- Ein Ring je Ausprägung von `result`
- Balken je Ausprägung von `location`, gemessen an der Anzahl — **Geräte je Standort**
- Balken je Ausprägung von `kind`, gemessen an der Anzahl — **Geräte je Art**

## Geführte Erfassung

Eine kurze Schrittfolge für jemanden, der eine Sache melden soll und das Werkzeug nicht kennt. Geschrieben wird nichts, bevor der letzte Schritt bestätigt ist — ein Abbruch darf nichts hinterlassen.

Titel: **Prüfung eintragen**

> Drei Schritte. Für den ersten Aufbau eines Bestands hilft der Schritt „Liste einlesen".

1. **Schritt 1** — Gerät: Felder: `name`, `kind`, `serial`, `location`, `holder`, `interval`
2. **Schritt 2** — Prüfung: Felder: `lastTest`, `tester`, `result`, `defect`, `note`
3. **Schritt 3** — Liste einlesen: CSV-Upload, zahlt in denselben Durchlauf ein
   nur wenn: `Boolean(drafts.records?.name)`
4. **Schritt 4** — Prüfen: Zusammenfassung, aus dem Schema erzeugt

Abschluss: „Eingetragen. Die nächste Fälligkeit rechnet sich von selbst.“

## Vorgaben

In `DEFAULT_SETTINGS`, `DEFAULT_COLORS` und `DEFAULT_HOME` in `src/app.jsx` setzen.

- Titel: **Prüfbuch Betriebsmittel**
- Untertitel: Wiederkehrende Prüfungen nach DGUV Vorschrift 3
- Dateiname: `pruefbuch`
- Version: `1.0`
- Oberflächensprache: `de`
- Öffnet als: vollständiges Werkzeug
- Farben: `accent` #a8541f · `band` #2a1d14 · `flag` #b3341f · `ok` #3f7a3f · `pending` #c99411

## Startseite

Die Anwendung öffnet auf diesem Text. Er ist ein kleiner Markdown-Teilsatz — Überschriften, Listen, Zitate, fett, kursiv, Inline-Code und Verweise. Wörtlich übernehmen:

```markdown
# Prüfbuch für ortsveränderliche Betriebsmittel

Bohrmaschine, Verlängerung, Kaffeemaschine, Heißluftgebläse: alles muss regelmäßig geprüft werden.
Die Prüfung selbst dauert Minuten. Die Verwaltung frisst den Nachmittag — weil das Protokoll im
Ordner liegt, die Fristen im Kopf des Meisters, und die Berufsgenossenschaft nach einem Unfall
genau eine Frage stellt: **wann wurde dieses Gerät zuletzt geprüft?**

## Was diese Demo zeigt

- **Sortierbare Spalten** — ein Klick auf den Spaltenkopf ordnet: Zahlen nach Größe („Tage"
  reicht von deutlich überfällig bis weit in die Zukunft), Daten chronologisch („Geprüft" spannt
  zwei Jahre), Text alphabetisch; der dritte Klick stellt die Reihenfolge des Datenblocks wieder
  her, leere Werte bleiben unten.
- **Nichts wird summiert, alles gerechnet**: die Fälligkeit ergibt sich aus letzter Prüfung und
  Intervall, die Restzeit daraus, die rote Markierung wieder daraus.
- **Regeln, die dem Ernstfall standhalten** — ein Ergebnis ohne Datum und Prüfer lässt sich nicht
  speichern, ein „nicht bestanden" nicht ohne die Angabe, wo das Gerät jetzt ist.
- **Anhänge** für das Prüfprotokoll am Gerät selbst.

> Erfundene Daten. Veranschaulichung der Struktur, keine Aussage über den Umfang Ihrer Pflichten.
```

## Beispieldaten

Lege 13 realistische Beispieldatensätze an, damit die Datei beim ersten Öffnen nicht leer ist. Erfinde sie im Stil der Felder oben; sie sind Anschauung, nicht die Daten des Nutzers. Sag ihm, dass seine eigenen Daten über **Import CSV → alle ersetzen** hineinkommen.

## Fertig, wenn

- `npm run build` eine einzelne, geschlossene `dist/index.html` erzeugt
- `npm test` durchläuft
- die Datei per Doppelklick öffnet und die Beispieldatensätze zeigt
- die berechneten Felder Werte zeigen und die Regeln einen verstoßenden Datensatz abweisen
- Einstellungen, Farben und Startseite der Vorgabe oben entsprechen

## Vor der Übergabe

Entscheide das selbst, statt es dem Empfänger zu überlassen: `copyright` auf den Eigentümer des Werkzeugs setzen, den Kopfzeilen-Verweis auf das openToolbox-Repository ersetzen und `examplePrompts` abschalten, wenn der Empfänger nur Daten pflegt. Den Aufrufzähler (Einstellungen → Sicherheit) bei der Übergabe ansprechen, statt ihn später in einem Netzwerkprotokoll entdecken zu lassen.

---

*Erzeugt aus `examples/equipment-testing.domain.js` — dem laufenden Quelltext der [Live-Demo](https://m-dohmen.github.io/openToolbox/demos/equipment-testing/). Neu erzeugen mit `npm run prompts`.*

*Alle Daten der Demo sind erfunden. Sie veranschaulicht die Struktur eines solchen Werkzeugs — sie ist keine Rechtsberatung und kein Nachweis von Konformität.*
