docs: readme, schnellstart, bebilderte anleitung und changelog 0.1.0

QUICKSTART fuehrt von null zur laufenden Instanz und enthaelt die
vollstaendige Nginx-Proxy-Manager-Konfiguration - inklusive der beiden
Stellen, an denen es sonst klemmt: client_max_body_size fuer grosse
Anleitungs-PDFs und X-Forwarded-For fuer das Rate-Limit.

Die Anleitung ist bebildert. Die 16 Screenshots entstehen automatisiert
mit `pnpm screenshots` gegen das Produktions-Image im Container, nicht
gegen den Dev-Server: der brach unter Last reproduzierbar mit
ChunkLoadError ab, und die Bilder zeigen so ohnehin genau das, was nach
`docker compose up` zu sehen ist.

Nebenbei behoben: in der Mehrfachauswahl verdeckte die Aktionsleiste die
letzte Kachelreihe.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
unknown
2026-09-07 00:48:32 +02:00
co-authored by Claude Opus 5
parent 61b1249afd
commit b1fc502d55
27 changed files with 573 additions and 87 deletions
+322
View File
@@ -0,0 +1,322 @@
# Bricktrack – Anleitung
So benutzt du Bricktrack im Alltag. Die Bilder sind auf einem Handy
aufgenommen, weil die App genau dafür gebaut ist: für den Laden und das
Kinderzimmer, nicht für den Schreibtisch.
> Installation und Reverse Proxy stehen im [Schnellstart](../QUICKSTART.md).
> Die Bilder hier lassen sich mit `pnpm screenshots` jederzeit neu erzeugen –
> sie stammen aus den Beispieldaten des Seed-Skripts.
---
## Inhalt
1. [Das erste Konto](#1-das-erste-konto)
2. [Der Überblick](#2-der-überblick)
3. [Stammdaten anlegen](#3-stammdaten-anlegen)
4. [Sets in den Katalog](#4-sets-in-den-katalog)
5. [Was fehlt noch? Mehrere Sets auf einmal auswählen](#5-was-fehlt-noch)
6. [Die Set-Detailseite](#6-die-set-detailseite)
7. [Die Sammlung](#7-die-sammlung)
8. [Wunschlisten](#8-wunschlisten)
9. [Teilen – so sehen es Oma und Tante Anne](#9-teilen)
10. [Passkeys einrichten](#10-passkeys-einrichten)
11. [Antworten auf die zwei wichtigen Fragen](#11-die-zwei-fragen)
---
## 1. Das erste Konto
![Registrierung](bilder/01-registrierung.png)
Beim allerersten Aufruf landest du direkt auf der Registrierung. **Wer sich
zuerst registriert, wird Administrator** – danach ist die Registrierung
geschlossen. Melde dich also gleich selbst an, bevor es jemand anderes tut.
Das Passwort braucht mindestens zehn Zeichen. Einen Passkey hinterlegst du
gleich danach unter „Konto" (siehe [Abschnitt 10](#10-passkeys-einrichten)).
---
## 2. Der Überblick
![Startseite](bilder/02-start.png)
Die Startseite ist die Abzweigung zu den vier Bereichen:
| | |
|---|---|
| **Katalog** | Alle bekannten Sets – egal ob vorhanden oder nicht |
| **Fehlende Sets** | Der direkte Weg zu allem, was noch fehlt |
| **Sammlung** | Was tatsächlich da ist |
| **Wunschlisten** | Zum Teilen mit Familie und Freunden |
Unten am Bildschirmrand liegt die Navigation – auf dem Handy mit dem Daumen
erreichbar.
---
## 3. Stammdaten anlegen
![Hersteller verwalten](bilder/03-verwaltung-hersteller.png)
Bevor du Sets anlegst, brauchst du mindestens einen **Hersteller**. Zu finden
über das Konto-Menü oben rechts → **Verwaltung**.
Drei Arten von Stammdaten:
- **Hersteller** – Lego, Playmobil, Cobi, BlueBrixx … beliebig erweiterbar.
Die hinterlegte Website erscheint später als „Zum Shop"-Link auf der
geteilten Wunschliste.
- **Themen** – Serien wie „City", „Technic", „Novelmore". Themen gehören zu
einem Hersteller und können Unterthemen haben („City → Feuerwehr").
- **Standorte** – „Regal Kinderzimmer", „Kiste Keller 3". Auch schachtelbar,
damit „Keller → Kiste 3" möglich ist.
> Ein Hersteller oder Thema lässt sich nicht löschen, solange noch Sets daran
> hängen. Standorte schon – die betroffenen Sammlungseinträge verlieren dann
> nur ihren Standort.
---
## 4. Sets in den Katalog
![Katalog](bilder/04-katalog.png)
Der Katalog ist die Liste **aller Sets, die du kennst** – unabhängig davon, ob
ihr sie besitzt. Das ist der wichtigste Gedanke der App:
> Ein Set im Katalog heißt „das gibt es". Erst ein Eintrag in der Sammlung
> heißt „das haben wir".
Deshalb kannst du unbesorgt Sets anlegen, die ihr euch nur anschaut – genau
die braucht die Wunschliste später.
**Suchen** kannst du nach Setnummer, Name oder EAN. Die vier Filter darunter:
Besitzstatus, Hersteller, Thema und Jahr. Jeder Filterstand steckt in der
URL – ein Lesezeichen auf „alle fehlenden Lego-Technic-Sets" ist damit möglich.
Die Kacheln zeigen auf einen Blick:
- **grüner Haken „Vorhanden"** – ist in der Sammlung (bei mehreren Exemplaren
„2× vorhanden")
- **„Auf Wunschliste"** – steht auf mindestens einer Liste
- Setnummer, Hersteller, Thema, Jahr und UVP
Mit **„Set anlegen"** oben rechts kommt ein neues Set dazu. Pflicht sind nur
Hersteller, Setnummer und Name; alles andere kannst du nachtragen. Ein Bild
lädst du entweder hoch (es wird verkleinert und lokal gespeichert) oder du
hinterlegst eine Bild-URL.
---
## 5. Was fehlt noch?
![Fehlende Sets](bilder/05-fehlende-sets.png)
**Katalog → Filter „Fehlt noch"** (oder direkt `/katalog?besitz=fehlt`) zeigt
ausschließlich Sets, die **nicht** in der Sammlung sind. Das ist der zentrale
Weg zur Wunschliste.
### Mehrere Sets auf einmal
![Mehrfachauswahl](bilder/06-mehrfachauswahl.png)
Eine Kachel **lange drücken** startet den Auswahlmodus – am Handy die
natürliche Geste, das Gerät vibriert kurz. Am Rechner tut es der Knopf
**„Auswählen"**. Danach wählt ein Tippen jede weitere Kachel an oder ab.
Unten erscheint die Aktionsleiste mit der Anzahl und dem Knopf **„Zur
Wunschliste hinzufügen"**.
![Zieldialog](bilder/07-wunschliste-dialog.png)
Im Dialog wählst du die Ziel-Wunschliste oder legst gleich eine neue an, dazu
eine Priorität für alle ausgewählten Sets:
| Priorität | Bedeutung |
|---|---|
| Großer Wunsch | Das steht ganz oben |
| Gerne | Der Normalfall |
| Irgendwann | Nice to have |
Die Priorität lässt sich später je Eintrag ändern.
**Doppelte werden übersprungen.** Sets, die schon auf der Liste stehen oder
bereits in der Sammlung sind, werden nicht noch einmal hinzugefügt – die
Meldung danach sagt dir genau, welche das waren und warum. Das ist keine
UI-Höflichkeit, sondern in der Datenbank abgesichert.
---
## 6. Die Set-Detailseite
![Set-Detailseite](bilder/08-set-detail.png)
Hier steht alles zu einem Set:
- **Zur Sammlung hinzufügen** – ein Tipp, und das Set gilt als vorhanden.
Details wie Standort, Zustand oder Kaufpreis trägst du danach nach.
- **Auf Wunschliste** – derselbe Zieldialog wie oben, nur für dieses eine Set.
Ist das Set bereits in der Sammlung, ist der Knopf ausgegraut: Was da ist,
gehört nicht auf einen Wunschzettel.
- **Anleitungen** – PDFs hochladen (mehrere je Set, etwa „Buch 1 von 3") und
direkt im Browser durchblättern, ohne Download und ohne fremde Dienste.
Alternativ verlinkst du die Anleitung beim Hersteller.
- **Fotos** – eigene Bilder, am Handy direkt aus der Kamera.
- **In der Sammlung** – jedes Exemplar mit Status, Vollständigkeit, Zustand,
Standort, Kaufdatum, Preis und „geschenkt von".
### Die fünf Status eines Exemplars
| Status | Bedeutung |
|---|---|
| Vorhanden | Da, aber weder auf- noch abgebaut vermerkt |
| Aufgebaut | Steht zusammengebaut im Regal |
| Zerlegt | In Einzelteilen in der Kiste |
| Verliehen | Gerade bei jemand anderem |
| **Verkauft** | Zählt **nicht** mehr als Besitz |
Der letzte Punkt ist wichtig: Ein verkauftes Set taucht wieder unter „Fehlt
noch" auf und darf zurück auf eine Wunschliste.
---
## 7. Die Sammlung
![Sammlung](bilder/09-sammlung.png)
Oben vier Kacheln: Anzahl Sets, Teile insgesamt, Wert nach UVP und wie viele
Sets im Katalog noch fehlen. Darunter die Aufteilung nach Hersteller.
Gefiltert wird nach Status, Vollständigkeit, Standort und Hersteller – nützlich
für die Frage „Was steht eigentlich in Kiste 3?".
Verkaufte Exemplare zählen in keiner Kachel mit.
---
## 8. Wunschlisten
![Wunschlisten](bilder/10-wunschlisten.png)
Jede Liste hat einen Titel, einen Anlass und optional einen Termin. Die Karte
zeigt, wie viele Wünsche drauf sind und wie viele davon noch niemand
übernommen hat.
![Wunschliste bearbeiten](bilder/11-wunschliste-bearbeiten.png)
Auf der Liste selbst kannst du:
- die **Reihenfolge** mit den Pfeilen ändern – so stehen die wichtigsten
Wünsche oben,
- die **Priorität** je Eintrag anpassen,
- Einträge entfernen,
- unten im Bereich **„Aus fehlenden Sets hinzufügen"** direkt weitere Sets
dazunehmen. Dort steckt dieselbe Auswahlansicht wie im Katalog, schon
vorgefiltert auf Sets, die weder in der Sammlung noch auf dieser Liste sind.
### Zwei Einstellungen, die den Unterschied machen
- **Über Link teilbar** – ohne diesen Haken ist der öffentliche Link tot.
Praktisch, wenn du eine Liste erst fertig bauen willst.
- **Überraschungsmodus** – dann siehst *du* nur „Reserviert", nicht von wem.
Die Schenkenden untereinander sehen die Namen weiterhin, damit niemand
doppelt kauft.
---
## 9. Teilen
Auf der Wunschliste steht oben der **Link zum Teilen**. Kopieren, oder am
Handy über den Teilen-Knopf direkt in WhatsApp, Signal oder eine Mail geben.
> Der Link ist der einzige Schlüssel. Wer ihn hat, sieht die Liste – mehr
> nicht: keine Sammlung, keine anderen Listen, keine Namen von Konten. Für
> Suchmaschinen ist die Seite gesperrt.
>
> Ist ein Link in falsche Hände geraten, erzeugst du mit **„Neuen Link
> erzeugen"** einen frischen. Der alte funktioniert sofort nicht mehr.
### So sieht es für die Beschenkenden aus
![Öffentliche Wunschliste](bilder/12-oeffentliche-wunschliste.png)
Kein Konto, keine Anmeldung, keine App. Nur die Liste, mit Bild, Setnummer,
Preis, Priorität und – falls hinterlegt – einem Link zum Shop des Herstellers.
![Reservieren](bilder/13-reservieren.png)
Ein Tipp auf **„Das schenke ich"**, Name eintragen, fertig.
![Reserviert](bilder/14-reserviert.png)
Danach sehen alle anderen, dass dieser Wunsch vergeben ist. Die Person selbst
sieht „Das schenkst du" und kann die Reservierung jederzeit wieder aufheben –
auf demselben Gerät mit einem Tipp, von einem anderen Gerät über den
Storno-Link, den sie beim Reservieren bekommt.
![Reservierung in der Übersicht](bilder/15-wunschliste-reserviert.png)
Auf deiner Seite siehst du, wer was übernimmt – oder im Überraschungsmodus
eben nur, *dass* etwas übernommen ist.
**Was passiert, wenn das Geschenk ankommt?** Sobald du das Set zur Sammlung
hinzufügst, markiert Bricktrack den Wunsch automatisch als **erfüllt** und
blendet ihn aus der öffentlichen Ansicht aus. Niemand kauft es dann noch ein
zweites Mal.
---
## 10. Passkeys einrichten
![Konto und Passkeys](bilder/16-konto-passkeys.png)
Unter **Konto → Passkeys → Passkey hinzufügen** hinterlegst du Fingerabdruck,
Gesichtserkennung oder die Geräte-PIN als Anmeldung. Danach reicht auf der
Anmeldeseite ein Tipp auf „Mit Passkey anmelden" – auf vielen Geräten schlägt
der Browser den Passkey sogar von selbst vor.
Ein Passkey gilt pro Gerät. Leg ruhig mehrere an – Handy, Tablet, Laptop – und
gib ihnen sprechende Namen.
> **Zwei Dinge, die man wissen muss:**
> 1. Passkeys brauchen HTTPS. Über eine reine `http://`-Adresse (außer
> `localhost`) bietet der Browser sie gar nicht erst an.
> 2. Passkeys sind an die Domain gebunden. Ziehst du von
> `bricktrack.example.de` auf eine andere Adresse um, musst du alle
> Passkeys neu anlegen. Das Passwort funktioniert weiterhin – deshalb ist
> es sinnvoll, es nicht zu vergessen.
---
## 11. Die zwei Fragen
Dafür ist die ganze App gebaut:
**„Welche Sets hat er?"**
→ Sammlung. Oder im Katalog der Filter „In der Sammlung".
**„Welche Sets hat er noch nicht?"**
→ Katalog, Filter „Fehlt noch". Von dort in zwei Handgriffen auf eine
Wunschliste und per Link zu Oma.
Im Laden vor dem Regal: Katalog öffnen, Setnummer von der Packung eintippen.
Steht ein grüner Haken dran, habt ihr es schon.
---
## Kleinigkeiten, die den Alltag leichter machen
- **Filter stecken in der URL.** Ein Lesezeichen auf
`/katalog?besitz=fehlt&hersteller=…` ist eine gespeicherte Suche.
- **Preise** kannst du als `12,99` oder `12.99` eintippen – beides wird
verstanden, gespeichert wird auf den Cent genau.
- **Dasselbe Set zweimal** ist kein Problem: Zwei Exemplare sind zwei Einträge
in der Sammlung, mit eigenem Standort und Zustand.
- **Anleitungs-PDFs** können groß sein. Kommt beim Hochladen ein Fehler
„413", muss im Reverse Proxy `client_max_body_size` hoch – siehe
[QUICKSTART, Schritt 5](../QUICKSTART.md#5-reverse-proxy-einrichten-nginx-proxy-manager).
- **Alles Wichtige liegt in `./data`.** Dieses eine Verzeichnis sichern
genügt – Datenbank, Bilder, Fotos und Anleitungen sind darin.
+82 -16
View File
@@ -1,32 +1,98 @@
# Devlog
Zeiterfassung und Nachvollziehbarkeit der Entwicklung. Eine Zeile je
abgeschlossenem Arbeitsschritt, Zeiten in `HH:mm` (Europe/Berlin).
abgeschlossenem Arbeitsschritt, Zeiten in `HH:mm` (Europe/Berlin). Die
Commit-Spalte verweist auf den Stand am Ende des Schritts.
## Phase 1 - MVP
## Phase 1 – MVP (v0.1.0)
### 2026-09-06
### 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; 5 Entscheidungsfragen geklaert (Cent statt Float, Migrator statt drizzle-kit im Image, Bilder oeffentlich / PDFs geschuetzt, Token gehasht, Branch pro Phase) | - |
| 22:12 | 22:20 | 0:08 | Scaffold | pnpm via npm global installiert (corepack scheiterte an Rechten in `C:\Program Files\nodejs`), Next 15.5.25 + Tailwind 4 + shadcn/ui (radix-nova) mit 20 Komponenten | `0de49a9` |
| 22:20 | 22:26 | 0:06 | Datenmodell | 19 Tabellen in Drizzle, CHECK-Constraints fuer `instruction`, `photo` und `priority`, Unique-Index `(wishlist_id, set_id)`, Migration erzeugt und angewendet, WAL verifiziert | `06268bc` |
| 22:26 | 22:34 | 0:08 | Dokumentation | CLAUDE.md (Stack, Konventionen, Datenmodell, Sicherheitsregeln), Roadmap, Changelog, Devlog | `folgt` |
| 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` |
**Summe Phase 1 bisher: 0:28**
### 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**
---
## 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\pnpm.ps1` und ist im PATH.
- **shadcn-CLI**: Die aktuelle Version (4.21) arbeitet mit Presets
(`-p nova`) und Base-Libraries (`-b radix`) statt der frueheren
Style-Auswahl. Sie zieht `cn` und `radix-ui` als Pakete; `clsx` und
`tailwind-merge` werden dadurch nicht mehr direkt gebraucht.
`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` (Exports `.` und `./client`). Das erwartete
Schema deckt sich exakt mit der Tabelle `passkey`.
`@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.
### 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.
+16 -16
View File
@@ -2,36 +2,36 @@
Legende: `[ ]` offen · `[~]` in Arbeit · `[x]` fertig
## Phase 1 - MVP (Ziel: v0.1.0)
## Phase 1 - MVP (v0.1.0, abgeschlossen am 07.09.2026)
Alles laeuft vollstaendig im Docker-Stack.
Laeuft vollstaendig im Docker-Stack.
- [x] Projekt-Scaffold (Next 15, Tailwind 4, shadcn/ui, pnpm)
- [x] Datenmodell in Drizzle, initiale Migration
- [x] Projektdokumentation (CLAUDE.md, Roadmap, Changelog, Devlog)
- [ ] Auth: Registrierung des ersten Users (wird Admin), Login mit
- [x] Auth: Registrierung des ersten Users (wird Admin), Login mit
Passwort, Passkey anlegen und damit einloggen (Conditional UI),
Passkey-Verwaltung unter "Konto", Rate-Limit auf Login
- [ ] Mobile-first Layout-Shell mit Bottom-Navigation
- [ ] Stammdaten: Hersteller, Themen (mit Unterthemen), Standorte
- [ ] Set-Katalog: Kachelliste, Suche, Filter (Hersteller, Thema, Jahr,
- [x] Mobile-first Layout-Shell mit Bottom-Navigation
- [x] Stammdaten: Hersteller, Themen (mit Unterthemen), Standorte
- [x] Set-Katalog: Kachelliste, Suche, Filter (Hersteller, Thema, Jahr,
Besitzstatus), Anlegen und Bearbeiten
- [ ] Upload-Pipeline (PDF/JPEG/PNG/WebP, Magic-Byte-Pruefung,
- [x] Upload-Pipeline (PDF/JPEG/PNG/WebP, Magic-Byte-Pruefung,
serverseitige Dateinamen, Thumbnails)
- [ ] Set-Detailseite: Metadaten, Bild, Anleitungen mit pdf.js-Viewer,
- [x] Set-Detailseite: Metadaten, Bild, Anleitungen mit pdf.js-Viewer,
Fotos, Sammlungseintraege
- [ ] Sammlung: Uebersicht, Filter, Statistik-Kacheln
- [ ] Fehlende Sets (`/katalog?besitz=fehlt`) mit Mehrfachauswahl und
- [x] Sammlung: Uebersicht, Filter, Statistik-Kacheln
- [x] Fehlende Sets (`/katalog?besitz=fehlt`) mit Mehrfachauswahl und
Bulk-Aktion "Zur Wunschliste hinzufuegen"
- [ ] Wunschlisten: Verwaltung, Prioritaet, Sortierung, Teilen,
- [x] Wunschlisten: Verwaltung, Prioritaet, Sortierung, Teilen,
Ueberraschungsmodus, automatisches "erfuellt"
- [ ] Oeffentliche Wunschliste `/w/[shareToken]` mit Reservierung ohne
- [x] Oeffentliche Wunschliste `/w/[shareToken]` mit Reservierung ohne
Anmeldung und Storno-Link
- [ ] Seed-Skript (3 Hersteller, 10 Sets, 4 in Sammlung, 1 Wunschliste)
- [ ] Unit-Tests (Besitzstatus, Bulk-Add inkl. Dubletten, Reservierung,
- [x] Seed-Skript (3 Hersteller, 10 Sets, 4 in Sammlung, 1 Wunschliste)
- [x] Unit-Tests (Besitzstatus, Bulk-Add inkl. Dubletten, Reservierung,
Token-Erzeugung) und Playwright-Smoke-Test
- [ ] Docker: Multi-Stage-Build, Compose-Stack, Healthcheck, Entrypoint
- [ ] Dokumentation: README, QUICKSTART, bebilderte Anleitung,
- [x] Docker: Multi-Stage-Build, Compose-Stack, Healthcheck, Entrypoint
- [x] Dokumentation: README, QUICKSTART, bebilderte Anleitung,
update.sh, Nginx-Proxy-Manager-Konfiguration
## Phase 2 - Komfort (Ziel: v0.2.0)
Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB