Der Dev-Server verlor unter der Last eines ganzen Durchlaufs reproduzierbar Chunks (ChunkLoadError beim Laden von app/layout), was sich weder durch Aufwaermen noch durch laengere Zeitlimits stabilisieren liess. Der Smoke-Test faehrt jetzt - wie der Screenshot-Lauf schon vorher - das Produktions-Image hoch. Ergebnis: 29 Sekunden statt fuenf Minuten, keine Aussetzer, und getestet wird genau das Artefakt, das spaeter deployt wird. Dabei behoben: Der Fehlteile-Dialog hielt eine Kopie der Liste aus dem Moment des Oeffnens und zeigte ein gerade hinzugefuegtes Teil nicht an. Er liest jetzt den aktuellen Eintrag aus den Props statt aus dem Zustand. Gefunden hat das der erweiterte Screenshot-Lauf. Dokumentation zu Phase 2: Changelog 0.2.0, Roadmap abgehakt, Devlog mit Zeiten, CLAUDE.md um die neuen Module und Testkonventionen ergaenzt und die Anleitung um fuenf Kapitel (Import, Scanner, Fehlteile, Einladungen, Daten, Startbildschirm) - mit vier neuen, automatisch erzeugten Bildern. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
133 lines
8.2 KiB
Markdown
133 lines
8.2 KiB
Markdown
# Devlog
|
||
|
||
Zeiterfassung und Nachvollziehbarkeit der Entwicklung. Eine Zeile je
|
||
abgeschlossenem Arbeitsschritt, Zeiten in `HH:mm` (Europe/Berlin). Die
|
||
Commit-Spalte verweist auf den Stand am Ende des Schritts.
|
||
|
||
## Phase 1 – MVP (v0.1.0)
|
||
|
||
### 06.09.2026
|
||
|
||
| Von | Bis | Dauer | Schritt | Ergebnis | Commit |
|
||
|---|---|---|---|---|---|
|
||
| 22:06 | 22:12 | 0:06 | Planung | Phase-1-Plan und Drizzle-Schema zur Freigabe vorgelegt; fünf Entscheidungsfragen geklärt (Cent statt Float, Migrator statt drizzle-kit im Image, Bilder öffentlich / PDFs geschützt, Token gehasht, Branch pro Phase) | – |
|
||
| 22:12 | 22:20 | 0:08 | Scaffold | pnpm installiert, Next 15.5.25 + Tailwind 4 + shadcn/ui (radix-nova) mit 20 Komponenten, Touch-Größen auf 44 px angehoben | `0de49a9` |
|
||
| 22:20 | 22:25 | 0:05 | Datenmodell | 19 Tabellen in Drizzle, CHECK-Constraints, Unique-Index `(wishlist_id, set_id)`, Migration erzeugt und angewendet, WAL verifiziert | `06268bc` |
|
||
| 22:25 | 22:26 | 0:01 | Dokumentation | CLAUDE.md, Roadmap, Changelog, Devlog | `b74cecb` |
|
||
| 22:26 | 22:40 | 0:14 | Auth und App-Shell | better-auth mit Passwort und Passkeys, erster Nutzer wird Admin, Rate-Limit in der Datenbank, Konto-Seite, Bottom-Navigation, `/api/health` | `4c78c56` |
|
||
| 22:40 | 22:46 | 0:06 | Stammdaten | CRUD für Hersteller, Themen und Standorte über eine gemeinsame CrudSection; zod-Schemata, ActionResult, Slug, Geld- und Datumshelfer | `af0ae03` |
|
||
| 22:46 | 22:59 | 0:13 | Katalog | Kachelliste mit Suche und vier Filtern, Besitzlogik in `ownership.ts`, Set-Formular, Detailseite, Upload-Pipeline mit Magic-Byte-Prüfung, pdf.js-Viewer, Seed-Skript | `856800b` |
|
||
| 22:59 | 23:06 | 0:07 | Wunschlisten und Sammlung | Mehrfachauswahl mit Dubletten-Skip, Wunschlisten-Verwaltung, Teilen, Überraschungsmodus, öffentliche Ansicht mit Reservierung und Storno, Sammlungsübersicht mit Statistik | `79b0133` |
|
||
| 23:06 | 23:31 | 0:25 | Tests | 50 Vitest-Unit-Tests, Playwright-Smoke-Test über den ganzen Weg bis zur Reservierung durch einen Besucher ohne Konto | `b0152b8` |
|
||
|
||
### 07.09.2026
|
||
|
||
| Von | Bis | Dauer | Schritt | Ergebnis | Commit |
|
||
|---|---|---|---|---|---|
|
||
| 23:31 | 00:09 | 0:38 | Docker | Multi-Stage-Build, Entrypoint mit Rechteprüfung und Migration, Compose-Stack, Healthcheck, `update.sh` mit Rollback; Image gebaut und Container verifiziert | `61b1249` |
|
||
| 00:09 | 00:50 | 0:41 | Dokumentation und Screenshots | README, QUICKSTART mit Nginx-Proxy-Manager-Konfiguration, bebilderte Anleitung, 16 automatisiert erzeugte Screenshots, Changelog 0.1.0 | folgt |
|
||
|
||
**Summe Phase 1: 2:44**
|
||
|
||
## Phase 2 – Komfort (v0.2.0)
|
||
|
||
### 07.09.2026
|
||
|
||
| Von | Bis | Dauer | Schritt | Ergebnis | Commit |
|
||
|---|---|---|---|---|---|
|
||
| 06:50 | 07:01 | 0:11 | Einladungen | Admin erzeugt einmal gültige Registrierungslinks; Freigabe über AsyncLocalStorage, damit die Rolle nicht aus dem Formular kommen kann. Smoke-Test deckt den ganzen Weg ab | `52d69e5` |
|
||
| 07:01 | 07:12 | 0:11 | Rebrickable-Import | Setnummer genügt: Metadaten, Thema und lokal gespeichertes Bild; 14 Tests gegen gestubbtes `fetch` inklusive aller Fehlerfälle | `4e3cbb8` |
|
||
| 07:12 | 07:15 | 0:03 | Barcode-Scanner | EAN mit der Handykamera, Prüfziffer-Kontrolle, zxing wird erst beim Öffnen geladen | `baa94bf` |
|
||
| 07:15 | 07:18 | 0:03 | Fehlteile | Liste je Sammlungseintrag; Vollständigkeit pflegt sich daraus selbst | `5c5c521` |
|
||
| 07:18 | 07:23 | 0:05 | Export und Import | ZIP, JSON und CSV; Import ersetzt in einer Transaktion, 10 Tests für den Rundweg | `45aa8f1` |
|
||
| 07:23 | 07:27 | 0:04 | PWA | Manifest, Icons aus SVG-Quelle, Service Worker mit Offline-Katalog | `39271f8` |
|
||
| 07:27 | 07:45 | 0:18 | Verifikation und Dokumentation | Smoke-Test, Docker-Build, Changelog 0.2.0, Anleitung und Roadmap | folgt |
|
||
|
||
**Summe Phase 2: 0:55**
|
||
|
||
---
|
||
|
||
## Notizen und Entscheidungen
|
||
|
||
### Umgebung
|
||
|
||
- **pnpm**: `corepack enable` scheitert auf diesem Rechner an fehlenden
|
||
Schreibrechten in `C:\Program Files\nodejs`. Stattdessen
|
||
`npm install -g pnpm@10`; pnpm liegt unter `%APPDATA%\npm` und ist im PATH.
|
||
- **Port 3000** ist auf dem Entwicklungsrechner von einer anderen Anwendung
|
||
belegt. Der Dev-Server läuft deshalb lokal auf 3100, die Testläufe auf
|
||
3210 und 3220.
|
||
- **`pnpm build` unter Windows** scheitert mit `EPERM … symlink`: Der
|
||
Standalone-Output legt Symlinks in `.next/standalone` an, wofür Windows den
|
||
Entwicklermodus oder administrative Rechte braucht. Der Docker-Build unter
|
||
Linux ist davon nicht betroffen; lokal prüft man mit
|
||
`docker build -t bricktrack:test .`.
|
||
|
||
### Bibliotheken
|
||
|
||
- **shadcn-CLI 4.21** arbeitet mit Presets (`-p nova`) und Base-Libraries
|
||
(`-b radix`) statt der früheren Style-Auswahl. Sie zieht `cn` und `radix-ui`
|
||
als Pakete; `clsx` und `tailwind-merge` werden dadurch nicht mehr direkt
|
||
gebraucht und wurden wieder entfernt. Die Voreinstellungen sind auf
|
||
Desktop-Dichte ausgelegt (Buttons `h-8`) – für die Bedienung am Handy auf
|
||
44 px angehoben.
|
||
- **better-auth 1.7.3**: Das Passkey-Plugin liegt nicht mehr unter
|
||
`better-auth/plugins/passkey`, sondern im eigenen Paket
|
||
`@better-auth/passkey`. Der Drizzle-Adapter löst Modelle über die
|
||
TypeScript-Property-Namen auf, nicht über die Spaltennamen – deshalb können
|
||
auch die Auth-Tabellen durchgehend `snake_case` verwenden.
|
||
- **Drizzle**: Selbstbezügliche Fremdschlüssel (`parent_theme_id`) brauchen
|
||
`AnySQLiteColumn` als Rückgabetyp, nicht `AnyColumn`.
|
||
- **pdf.js** wird lokal gebündelt, inklusive Worker. Kein CDN, damit der
|
||
Browser des Nutzers keine fremden Server kontaktiert.
|
||
|
||
### Stolpersteine
|
||
|
||
- **CRLF im Entrypoint**: Eine unter Windows geschriebene `entrypoint.sh` mit
|
||
CRLF scheitert im Container mit dem irreführenden
|
||
`exec /app/entrypoint.sh: no such file or directory` – der Kernel sucht
|
||
dann `/bin/sh\r`. Behoben mit einer `.gitattributes`, die für Shell-Skripte
|
||
LF erzwingt.
|
||
- **Migrations-Bundle**: Der mit esbuild als ESM gebündelte Migrations-Runner
|
||
scheiterte an `Dynamic require of "fs" is not supported` – mitgebündelte
|
||
CJS-Abhängigkeiten (dotenv) rufen `require()` für Node-Builtins auf. Als
|
||
CommonJS gebündelt läuft es.
|
||
- **Playwright und Reihenfolge**: Playwright startet den Web-Server **vor**
|
||
dem `globalSetup`. Das Löschen des Testdatenverzeichnisses im globalSetup
|
||
scheiterte deshalb unter Windows an der bereits geöffneten SQLite-Datei;
|
||
das Aufräumen läuft jetzt im npm-Skript davor.
|
||
- **Screenshots gegen den Dev-Server** waren nicht reproduzierbar: unter Last
|
||
brach der Stream mit `ChunkLoadError` beziehungsweise
|
||
`Unexpected end of JSON input` ab. Die Bilder entstehen jetzt gegen das
|
||
Produktions-Image im Container – verlässlich und zugleich näher an dem, was
|
||
Nutzer wirklich sehen.
|
||
|
||
### Phase 2
|
||
|
||
- **`server-only` in Tests**: Das Paket wirft beim Import ausserhalb einer
|
||
Server-Komponente, wodurch sich Servermodule nicht testen liessen. In
|
||
`vitest.config.mts` ist es jetzt auf einen leeren Stub gemappt – der Import
|
||
ist eine Build-Zeit-Absicherung, kein Verhalten.
|
||
- **Namenskonflikt `BASE_URL`**: Vite benutzt genau diesen Namen für seinen
|
||
Public-Base-Path und setzt ihn auf `/`. Die Tests überschreiben ihn deshalb
|
||
ausdrücklich. Dabei ist aufgefallen, dass `src/env.ts` bei einem leeren Wert
|
||
abbrach statt auf den Standard zurückzufallen – behoben, denn ein
|
||
`BASE_URL=` in der `.env` ist ein realistischer Tippfehler.
|
||
- **Rebrickable und Anleitungen**: Die API v3 stellt keine Anleitungsdateien
|
||
bereit. Statt das zu verschleiern, legt der Import einen externen Link auf
|
||
die LEGO-Serviceseite zur Setnummer an.
|
||
- **Import statt Zusammenführen**: Zwei Sammlungen zu mischen bräuchte
|
||
Konfliktregeln für jede Tabelle und wäre im Ergebnis nicht vorhersagbar.
|
||
Der Import ersetzt deshalb – dafür mit getipptem `ERSETZEN` als Bremse.
|
||
|
||
### Beim Testen gefunden und behoben
|
||
|
||
- Eine spontan aus dem Katalog angelegte Wunschliste war **nicht teilbar**:
|
||
Das Feld `isPublic` kommt aus einer Checkbox und fiel beim programmatischen
|
||
Anlegen auf `false`. Die Schnellanlage setzt es jetzt ausdrücklich.
|
||
- Das Sternchen an Pflichtfeldern gehörte zum zugänglichen Namen des Feldes
|
||
(„Name *"). Jetzt `aria-hidden` – der Pflichtstatus steckt ohnehin im
|
||
`required` des Eingabefeldes.
|
||
- In der Mehrfachauswahl verdeckte die Aktionsleiste die letzte Kachelreihe;
|
||
das Raster bekommt im Auswahlmodus zusätzliches Polster nach unten.
|