<!-- Generated by scripts/build-prompts.mjs — do not edit by hand. -->
# Construis-moi cet outil: Verarbeitungsverzeichnis

*Tout ce qui suit est la spécification fonctionnelle. Suis-la telle quelle ; là où elle se tait, tranche toi-même et dis ce que tu as supposé.*

## Point de départ

Utilise le modèle **openToolbox** : https://github.com/m-dohmen/openToolbox. Lis d’abord `AGENTS.md` dans ce dépôt — il fait autorité sur la forme du schéma, les types de champs et les règles qui cassent une construction en fichier unique. Tout le métier tient dans un seul fichier, `src/domain.js`.

> Si le skill openToolbox est installé (`claude plugin install opentoolbox@opentoolbox`), il suffit de coller ce fichier — il récupère le modèle lui-même.

## Le problème traité

Selon l’art. 30 du RGPD, presque toute entreprise doit tenir un registre des activités de traitement. Presque aucune n’en tient un digne de ce nom, et la raison est rarement la mauvaise volonté : c’est la collecte. Les réponses sont chez douze personnes différentes : les candidatures aux RH, l’outil de newsletter au marketing, la vidéosurveillance chez le gardien. Qui veut le remplir depuis le centre écrit douze courriels et en reçoit neuf. L’outil doit donc fonctionner comme quelque chose qu’on envoie : chaque service déclare son propre traitement, enregistre, renvoie.

## Ce qui doit exister à la fin

Un seul fichier HTML autonome, ouvert par double-clic, sans serveur ni installation. Le fichier est aussi la base de données : enregistrer écrit un nouveau HTML avec les enregistrements intégrés.

## L’enregistrement

Un enregistrement est **Verarbeitung**, plusieurs sont **Verarbeitungen**.

**Champs**

| Clé | Libellé | Type | Détail |
| --- | --- | --- | --- |
| `name` | Verarbeitungstätigkeit | text | obligatoire |
| `area` | Bereich | enum | l’une de : Personal · Vertrieb · Marketing · IT · Buchhaltung · Empfang |
| `contact` | Ansprechperson | text | en-tête de tableau `Kontakt` |
| `purpose` | Zweck der Verarbeitung | text | multiligne, en-tête de tableau `Zweck` |
| `subjects` | Betroffene | enum | l’une de : Beschäftigte · Bewerberinnen und Bewerber · Kundinnen und Kunden · Interessenten · Lieferanten · Besucher |
| `categories` | Datenkategorien | text | multiligne, en-tête de tableau `Daten` |
| `basis` | Rechtsgrundlage | enum | l’une de : Vertrag (Art. 6 I b) · Rechtliche Pflicht (Art. 6 I c) · Berechtigtes Interesse (Art. 6 I f) · Einwilligung (Art. 6 I a) · noch zu klären, en-tête de tableau `Grundlage` |
| `system` | System / Ablage | text | en-tête de tableau `System` |
| `processor` | Auftragsverarbeiter | text | en-tête de tableau `AV` |
| `thirdCountry` | Drittlandtransfer | enum | l’une de : nein · ja · unklar, en-tête de tableau `Drittland` |
| `retention` | Löschfrist | text | en-tête de tableau `Frist` |
| `risk` | Risiko | enum | l’une de : gering · mittel · hoch |
| `status` | Status | enum | l’une de : gemeldet · in Prüfung · freigegeben · nachzubessern |
| `reviewed` | Zuletzt geprüft | date | en-tête de tableau `Geprüft` |
| `note` | Notiz | text | multiligne |
| `age` | Tage seit Prüfung | computed | calculé, jamais stocké, en-tête de tableau `Alter` |

**Présentation**

- Colonne principale : `name`
- Deuxième ligne en dessous : `area`
- Colonnes du tableau, dans cet ordre : `name`, `area`, `basis`, `subjects`, `retention`, `age`, `status`
- Filtres de la barre latérale : `area`, `status`, `risk`
- Ne compte plus comme ouvert quand : `r.status === 'freigegeben'`
- Signalé en rouge quand : `!r.reviewed || r.reviewed < iso(-365)`

**Champs calculés**

Ils sont dérivés à chaque affichage et jamais écrits dans l’enregistrement : une dérivation stockée devient fausse dès qu’une de ses sources change.

- `age` (Tage seit Prüfung):

  ```js
  const reviewed = localDateFromIso(r.reviewed)
  if (!reviewed) return ''
  /* Ganze lokale Tage über die Date.UTC-Differenz der Tageskompo-
     nenten - exakt ganzzahlig, ohne Rundungsrettung und ohne den
     Mix aus UTC-Mitternacht und Lokal-Mitternacht. */
  const now = new Date()
  return (
    (Date.UTC(now.getFullYear(), now.getMonth(), now.getDate()) -
      Date.UTC(reviewed.getFullYear(), reviewed.getMonth(), reviewed.getDate())) /
    86400000
  )
  ```

**Règles de validation**

Conditions entre champs. Elles doivent s’appliquer en un seul endroit, afin que le formulaire, l’import CSV et les propositions de l’IA passent par le même contrôle.

- **Quand** `r.basis === 'Einwilligung (Art. 6 I a)'` → **Alors** `note`
  **Message:** „Bei einer Einwilligung gehört in die Notiz, wie und wo sie eingeholt wird.“
- **Quand** `r.thirdCountry === 'ja'` → **Alors** `processor`
  **Message:** „Bei einem Drittlandtransfer muss stehen, wer dort verarbeitet.“
- **Quand** `r.status === 'freigegeben'` → **Alors** `purpose`, `categories`, `retention`, `reviewed`
  **Message:** „Freigeben lässt sich nur, was Zweck, Datenkategorien, Löschfrist und Prüfdatum hat.“
- **Quand** `r.risk === 'hoch'` → **Alors** `note`
  **Message:** „Ein hohes Risiko braucht eine Begründung — es kann eine Folgenabschätzung auslösen.“

## Tableau de bord

Tuiles sur l’ensemble des enregistrements, pas sur la vue filtrée.

- Un nombre : **Verarbeitungen** — le nombre d’enregistrements · „im Verzeichnis“
- Un nombre : **Prüfung überfällig** — le nombre d’enregistrements (uniquement les enregistrements correspondant à un filtre): `isOverdue(r)` · „nie oder vor über einem Jahr“
- Un nombre : **Offene Fragen** — le nombre d’enregistrements (uniquement les enregistrements correspondant à un filtre): `r.basis === 'noch zu klären' || r.thirdCountry === 'unklar'` · „Rechtsgrundlage oder Drittland unklar“
- Un anneau par valeur de `status`
- Barres par valeur de `area`, mesurant le nombre d’enregistrements — **Verarbeitungen je Bereich**
- Barres par valeur de `risk`, mesurant le nombre d’enregistrements — **Verteilung nach Risiko**

## Saisie guidée

Une courte séquence d’étapes pour quelqu’un qui doit signaler une chose et ne connaît pas l’outil. Rien n’est écrit avant la validation de la dernière étape : abandonner ne doit rien laisser.

Titre : **Eine Verarbeitung melden**

> Fünf kurze Schritte. Wenn Sie etwas nicht wissen, lassen Sie es leer — ein unvollständiger Eintrag ist besser als keiner. Gespeichert wird erst am Ende.

1. **Étape 1** — Was und wer: champs : `name`, `area`, `contact`
2. **Étape 2** — Zweck und Daten: champs : `purpose`, `subjects`, `categories`
3. **Étape 3** — Rechtsgrundlage: champs : `basis`, `note`
4. **Étape 4** — Wo liegt es: champs : `system`, `processor`, `thirdCountry`, `retention`
5. **Étape 5** — Risiko: champs : `risk`
   uniquement si : `drafts.records?.subjects === 'Beschäftigte' || drafts.records?.subjects === 'Bewerberinnen und Bewerber' || drafts.records?.thirdCountry === 'ja'`
6. **Étape 6** — Prüfen: récapitulatif généré à partir du schéma

Écran final : « Danke — das ist aufgenommen. Bitte die Datei speichern und zurücksenden. »

## Valeurs par défaut

À définir dans `DEFAULT_SETTINGS`, `DEFAULT_COLORS` et `DEFAULT_HOME` dans `src/app.jsx`.

- Titre: **Verarbeitungsverzeichnis**
- Sous-titre: Art. 30 DSGVO — gemeldet von den Fachbereichen
- Nom de fichier: `verarbeitungsverzeichnis`
- Version: `1.0`
- Langue de l’interface: `de`
- S’ouvre en: saisie guidée (la liste reste masquée)
- Couleurs: `accent` #3a5a8c · `band` #1b2333 · `flag` #b0472e · `ok` #3a7a5e · `pending` #c08a12

## Page d’accueil

L’application s’ouvre sur ce texte. C’est un sous-ensemble de Markdown : titres, listes, citations, gras, italique, code en ligne et liens. À reprendre tel quel :

```markdown
# Verzeichnis von Verarbeitungstätigkeiten

Nach **Art. 30 DSGVO** muss fast jedes Unternehmen eines führen. Fast keines führt eines, das den
Namen verdient — und der Grund ist selten Unwille.

Es ist die Erhebung. Die Angaben liegen bei zwölf verschiedenen Leuten: die Bewerbungsmappen bei
der Personalstelle, das Newsletter-Werkzeug im Marketing, die Videoanlage beim Hausmeister. Wer das
zentral ausfüllen will, schreibt zwölf Mails und bekommt neun Antworten.

## Was diese Demo zeigt

Diese Datei ist im **Erfassungsmodus** ausgeliefert: sie öffnet direkt im Meldeformular und zeigt
die Liste gar nicht. Gedacht ist sie zum Weiterschicken — jeder Fachbereich meldet seine eigene
Verarbeitung, speichert und schickt zurück. Die Rückläufer führt man mit *Datei abgleichen*
zusammen.

Umschalten auf das vollständige Werkzeug: **Einstellungen → Anwendung → Öffnet als**.

> Erfundene Einträge, Veranschaulichung der Struktur. Keine Rechtsberatung.
```

## Données de démonstration

Ajoute 10 enregistrements de démonstration réalistes pour que le fichier ne soit pas vide à l’ouverture. Invente-les dans l’esprit des champs ci-dessus ; ce sont des illustrations, pas les données de l’utilisateur. Dis-lui que ses propres données entrent par **Import CSV → replace all**.

## Terminé quand

- `npm run build` produit un seul `dist/index.html` autonome
- `npm test` passe
- le fichier s’ouvre par double-clic et affiche les enregistrements de démonstration
- les champs calculés affichent des valeurs et les règles refusent un enregistrement fautif
- les réglages, les couleurs et la page d’accueil correspondent à la spécification ci-dessus

## Avant de la livrer

Tranche toi-même plutôt que de laisser le destinataire décider : mets `copyright` au nom du propriétaire de l’outil, remplace le lien d’en-tête pointant vers le dépôt openToolbox, et désactive `examplePrompts` si le destinataire ne fait que saisir des données. Mentionne le compteur d’ouvertures (Réglages → Sécurité) à la livraison.

---

*Généré à partir de `examples/gdpr-processing.domain.js`, le code réel de la [démo en ligne](https://m-dohmen.github.io/openToolbox/demos/gdpr-processing/). Régénérer avec `npm run prompts`.*

*Toutes les données de la démo sont inventées. Elle illustre la structure d’un tel outil — ce n’est ni un conseil juridique ni une preuve de conformité.*
