Commit Graph
5 Commits
Author SHA1 Message Date
unknownandClaude Opus 5 0d08df0bff feat(erinnerungen): e-mail, wenn ein wunschlisten-termin naeher rueckt
Ruecken Termin und offene Wuensche zusammen, geht eine Nachricht an den
Besitzer der Liste - standardmaessig 14 Tage vorher, hoechstens einmal
pro Woche je Liste. Ist alles reserviert, bleibt es still: eine
Erinnerung ohne Handlungsbedarf ist nur Laerm.

Der Zeitplan haengt an Next' instrumentation-Hook und prueft stuendlich,
nicht taeglich - so kann ein Neustart keinen Tag ueberspringen, und die
Sperrfrist je Liste verhindert trotzdem Mehrfachversand. Der Zeitstempel
wird erst nach erfolgreichem Versand gesetzt, damit ein Fehlversuch
wiederholt wird.

Ohne SMTP_HOST und SMTP_FROM ist die Funktion komplett aus: kein
Zeitplan, kein Versand, und die Verwaltungsseite erklaert stattdessen,
was in die .env gehoert. Mit Konfiguration gibt es dort Knoepfe fuer
Verbindungstest, Testnachricht und sofortige Pruefung.

12 Tests fuer die Entscheidungsregeln. Einer davon hat einen echten
Fehler gefunden: die Sperrfrist rechnete mit umgedrehtem Vorzeichen und
haette jede Erinnerung dauerhaft unterdrueckt.

Refs Phase 3

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 09:22:29 +02:00
unknownandClaude Opus 5 91fb66b13b feat(preise): beobachtete preise und wertentwicklung je set
Auf der Set-Detailseite lassen sich Preise notieren: Betrag, Zustand,
Datum, wo gesehen und eine Notiz. Daraus entsteht eine Reihe statt
eines einzelnen Feldes, denn die interessante Frage ist nicht "was
kostet es", sondern "was hat es seit dem Kauf gemacht".

Als Ausgangswert dient die UVP, sonst die aelteste Beobachtung; der
aktuelle Wert ist die neueste. Fallen am selben Tag zwei Beobachtungen
an, gewinnt der Neupreis - das ist die Zahl, die man zum Ersetzen
zahlen wuerde. Die Statistik-Kachel in der Sammlung zeigt entsprechend
"Aktueller Wert", sobald irgendwo ein Preis notiert ist, sonst
unveraendert "Wert nach UVP".

Bewusst nicht dabei: die Anbindung des BrickLink-Preisfuehrers. Sie war
im Plan als optional vermerkt und braucht OAuth1 mit einer pro Nutzer
registrierten Anwendung - viel Apparat fuer eine Zahl, die sich in zehn
Sekunden eintippen laesst. Die Datenstruktur ist dafuer offen: das Feld
"Wo" nimmt jede Quelle auf.

13 Tests fuer die Rechenwege, inklusive Division durch null, fehlender
UVP und Rundung.

Refs Phase 3

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 09:17:03 +02:00
unknownandClaude Opus 5 c23b754515 feat(sammler): mehrere sammler mit eigener sammlung und wunschliste
Der Katalog bleibt gemeinsam - ein Set existiert einmal, unabhaengig
davon, wer es hat. Besitz und Wunschlisten gehoeren dagegen je einem
Sammler. Damit beantwortet "hat er das schon?" jetzt die Frage "hat *er*
das schon?": ein Set, das nur die Schwester besitzt, faellt fuer den
Bruder wieder unter "fehlt noch" und darf auf seine Wunschliste.

Wichtig fuer den Alltag: Solange es nur einen Sammler gibt, taucht die
Unterscheidung nirgends in der Oberflaeche auf. Kein Umschalter, keine
Abfrage beim Hinzufuegen, keine Badges - die App bleibt exakt so
einfach wie vorher. Erst der zweite Sammler schaltet all das frei.

Die Besitzlogik liegt weiterhin an genau einer Stelle
(src/lib/ownership.ts) und hat nur eine Dimension dazubekommen. Das
automatische "erfuellt" auf Wunschlisten fragt jetzt ebenfalls je Liste:
ein Wunsch auf Max' Liste ist erfuellt, wenn Max das Set bekommt - nicht
wenn Mia es hat. Listen ohne Sammler verhalten sich wie bisher.

Die Migration legt einen Sammler "Sammlung" an und ordnet ihm alles
Vorhandene zu; umbenennen laesst er sich unter Verwaltung -> Sammler.
Der Seed uebernimmt ihn statt einen dritten anzulegen.

Refs Phase 3

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 09:00:35 +02:00
unknownandClaude Opus 5 4c78c5672b feat(auth): better-auth mit passwort, passkeys und app-shell
Registrierung des ersten Nutzers macht ihn zum Administrator, danach
ist die Registrierung geschlossen (Einladungen folgen in Phase 2).
Login per Passwort oder Passkey inklusive Conditional UI, Verwaltung
der Passkeys unter "Konto" mit Umbenennen und Loeschen.

Rate-Limiting liegt in der Datenbank und ueberlebt Neustarts;
Session-Cookies sind httpOnly, sameSite=lax und im Produktivbetrieb
secure. Die Client-IP wird hinter dem Reverse Proxy aus
x-forwarded-for gelesen.

Dazu die mobile-first Grundstruktur: Bottom-Navigation, Header mit
Konto-Menue, Healthcheck unter /api/health und ein Dashboard mit den
vier Einstiegspunkten. Bedienelemente sind auf 44px Touch-Groesse
angehoben.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:40:54 +02:00
unknownandClaude Opus 5 06268bcfd6 feat(db): drizzle-schema, db-client und initiale migration
Vollstaendiges Datenmodell: Katalog (manufacturer, theme, set, tag,
instruction), Besitz (collection_item, location, missing_part, photo),
Wunschlisten (wishlist, wishlist_item) und die Auth-Tabellen von
better-auth inklusive passkey und invite.

Kernregel: ein set ist ein Katalogeintrag, ein collection_item die
Verknuepfung mit dem tatsaechlichen Besitz. Preise liegen als Cent in
INTEGER-Spalten, damit Summen exakt bleiben.

SQLite laeuft mit WAL, foreign_keys=ON und busy_timeout.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 22:25:22 +02:00