<!-- Generated by scripts/build-prompts.mjs — do not edit by hand. -->
# 请帮我做这个工具: Prüfbuch Betriebsmittel

*下面是完整的功能需求。请照此实现；需求没说到的地方由你判断，并说明你的假设。*

## 从哪里开始

使用 **openToolbox** 模板：https://github.com/m-dohmen/openToolbox。先读该仓库里的 `AGENTS.md` —— 它是 schema 形态、字段类型以及「哪些做法会破坏单文件构建」的权威说明。所有业务相关的内容只写进一个文件：`src/domain.js`。

> 如果已安装 openToolbox skill（`claude plugin install opentoolbox@opentoolbox`），直接粘贴本文件即可，它会自己去取模板。

## 它解决的问题

每一间车间、每一家手工业企业、每一所托儿所，都有必须按固定周期检验的设备 —— 电钻、接线板、咖啡机、热风枪。检验本身只要几分钟，管理却要耗掉一个下午：检验记录在文件夹里，周期在师傅脑子里。而事故发生后，工伤保险机构只问一个问题：**这台设备上一次是什么时候检验的？**

## 最终必须交付什么

一个自包含的 HTML 文件，双击即可打开，无需服务器、无需安装。文件本身就是数据库：保存会写出一个新的 HTML 文件，数据嵌在其中。

## 记录

一条记录是 **Betriebsmittel**，多条是 **Betriebsmittel**。

**字段**

| 键 | 标签 | 类型 | 说明 |
| --- | --- | --- | --- |
| `name` | Gerät | text | 必填 |
| `kind` | Art | enum | 取值之一：Handmaschine · Verlängerung/Leitung · Küchengerät · Messgerät · Ladegerät · Ortsfest |
| `serial` | Inventar-/Seriennummer | text | 表头 `Nr.` |
| `location` | Standort | enum | 取值之一：Werkstatt · Baustellenwagen · Lager · Büro · Küche · ausgeliehen, 表头 `Ort` |
| `holder` | In der Hand von | text | 表头 `Bei` |
| `interval` | Prüfintervall (Monate) | enum | 取值之一：6 · 12 · 24, 表头 `Int.` |
| `lastTest` | Letzte Prüfung | date | 表头 `Geprüft` |
| `tester` | Prüfer | text | — |
| `result` | Ergebnis | enum | 取值之一：bestanden · bestanden mit Mangel · nicht bestanden · noch nicht geprüft |
| `defect` | Festgestellter Mangel | text | 多行, 表头 `Mangel` |
| `protocol` | Prüfprotokoll | attachment | 上传的文件，存放在记录内, 表头 `Protokoll` |
| `note` | Notiz | text | 多行 |
| `due` | Nächste Prüfung | computed | 计算得出，从不存储, 表头 `Fällig` |
| `daysLeft` | Tage bis zur Prüfung | computed | 计算得出，从不存储, 表头 `Tage` |

**呈现方式**

- 主列：`name`
- 主列下方的第二行：`location`
- 表格列，按此顺序：`name`, `kind`, `location`, `lastTest`, `due`, `daysLeft`, `result`
- 侧栏筛选：`kind`, `location`, `result`
- 不再计为未完成的条件：`() => false`
- 标红的条件：`{ if (r.result === 'nicht bestanden') return true const d = dueDate(r) return !d || d < iso(0) }`

**计算字段**

每次渲染时求值，绝不写回记录 —— 一旦某个输入变化，存下来的派生值当场就是错的。

- `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
  )
  ```

**校验规则**

跨字段的条件。必须只在一处生效，使表单、CSV 导入和 AI 提出的改动都走同一套检查。

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

## 仪表板

统计整个记录集，而不是筛选后的视图。

- 一个数字：**Betriebsmittel** — 记录条数 · „im Bestand“
- 一个数字：**Fällig oder gesperrt** — 记录条数（仅符合筛选条件的记录）: `isOverdue(r)` · „sofort anfassen“
- 一个数字：**In den nächsten 60 Tagen** — 记录条数（仅符合筛选条件的记录）: `{ const d = dueDate(r) return d && d >= iso(0) && d <= iso(60) }` · „Prüftermin planen“
- 按 `result` 的取值分组的环形图
- 按 `location` 的取值分组的条形，度量 记录条数 — **Geräte je Standort**
- 按 `kind` 的取值分组的条形，度量 记录条数 — **Geräte je Art**

## 引导式录入

给「只需报告一件事、并不熟悉这个工具」的人用的一串短步骤。在最后一步确认之前不写入任何数据 —— 中途放弃必须不留痕迹。

标题：**Prüfung eintragen**

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

1. **步骤 1** — Gerät: 字段：`name`, `kind`, `serial`, `location`, `holder`, `interval`
2. **步骤 2** — Prüfung: 字段：`lastTest`, `tester`, `result`, `defect`, `note`
3. **步骤 3** — Liste einlesen: CSV 上传，并入同一次录入
   仅当：`Boolean(drafts.records?.name)`
4. **步骤 4** — Prüfen: 由 schema 自动生成的摘要

结束页：“Eingetragen. Die nächste Fälligkeit rechnet sich von selbst.”

## 默认设置

在 `src/app.jsx` 的 `DEFAULT_SETTINGS`、`DEFAULT_COLORS` 和 `DEFAULT_HOME` 中设置。

- 标题: **Prüfbuch Betriebsmittel**
- 副标题: Wiederkehrende Prüfungen nach DGUV Vorschrift 3
- 文件名: `pruefbuch`
- 版本: `1.0`
- 界面语言: `de`
- 打开方式: 完整工具
- 配色: `accent` #a8541f · `band` #2a1d14 · `flag` #b3341f · `ok` #3f7a3f · `pending` #c99411

## 起始页

应用以这段文字作为起始页。它是一个很小的 Markdown 子集：标题、列表、引用、加粗、斜体、行内代码和链接。请原样使用：

```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.
```

## 示例数据

添加 13 条真实感的示例记录，使文件初次打开时不为空。按上面的字段风格自行编写；它们只是示例，不是用户的数据。告诉用户，他自己的数据通过 **Import CSV → replace all** 导入。

## 完成的标准

- `npm run build` 产出单个自包含的 `dist/index.html`
- `npm test` 通过
- 双击可打开，并显示示例记录
- 计算字段有值，规则会拒绝违反它们的记录
- 设置、配色和起始页与上面的规格一致

## 交付之前

这些由你决定，不要留给接收方：把 `copyright` 设为工具的归属方，替换指向 openToolbox 仓库的顶栏链接，若接收方只录入数据就关闭 `examplePrompts`。交付时主动说明使用计数器（设置 → 安全）。

---

*由 `examples/equipment-testing.domain.js` 生成 —— 即[在线演示](https://m-dohmen.github.io/openToolbox/demos/equipment-testing/)的真实源码。重新生成：`npm run prompts`。*

*演示中的数据均为虚构。它展示的是这类工具的结构，不构成法律建议，也不能作为合规证明。*
