diff --git a/CHANGELOG.md b/CHANGELOG.md index 21c0b40..56a1ab2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,58 @@ die Versionierung [Semantic Versioning](https://semver.org/lang/de/). ## [Unveröffentlicht] +## [0.3.0] — 2026-09-01 + +Das Kernstück: Bibliothek, Status und Episodenfortschritt. + +### Hinzugefügt + +- **Bibliothek** als Posterraster mit Volltextsuche und Filtern nach Status, + Typ, Genre, Streamingdienst und Tag; Sortierung nach zuletzt aktualisiert, + hinzugefügt, Titel, Bewertung oder Erscheinungsjahr. Der gesamte Filterstand + steht in der URL und ist damit teilbar. +- **Fünf Status** je Eintrag (Schaue gerade, Pausiert, Abgeschlossen, + Abgebrochen, Watchlist). Der Status ergibt sich beim Abhaken von selbst: + die erste Episode startet einen Titel, die letzte schließt ihn ab. + Pausiert und Abgebrochen bleiben unangetastet — wer bewusst pausiert hat, + will das nicht durch eine Sichtung aufgehoben bekommen. +- **Episodenfortschritt**: einzeln abhaken, ganze Staffel auf einmal, + „bis hierhin gesehen" für den Einstieg mitten in einer Staffel, und ein + **„+1 Episode"**-Knopf, der genau die nächste offene Episode nimmt. + Häkchen reagieren sofort und werden zurückgedreht, wenn der Server + widerspricht; beim Abhaken glimmt die Zeile einmal warm auf. +- **„Als Nächstes dran"** mit Episodencode, Titel und Ausstrahlungsdatum; + Fortschritt in Prozent auf Basis der echten Episodenzahl, dazu die + verbleibende Laufzeit. +- **Rewatch-Zählung**: ein neuer Durchlauf setzt den Fortschritt zurück, die + Sichtungen des vorherigen bleiben für die Statistik erhalten. Bei Filmen + zählt jeder Durchlauf einzeln. +- **Rückgängig** für jede Sichtungsaktion — die Rückmeldung am unteren Rand + nennt, was passiert ist, und nimmt es auf Klick zurück. +- **Pro Eintrag**: Bewertung von 1 bis 10, Notizfeld, eigene Tags und das Feld + „wo schaue ich das", das beim Aufnehmen einen Vorschlag aus der + Streaming-Verfügbarkeit erhält und jederzeit überschreibbar ist. +- **Papierkorb** statt hartem Löschen: entfernte Titel bleiben 30 Tage liegen + und behalten dabei ihren Fortschritt. Wird ein Titel erneut aufgenommen, + lebt derselbe Eintrag wieder auf. +- **Titel selbst anlegen** für alles, was TMDB nicht kennt. Solche Einträge + haben keine TMDB-ID und werden vom Abgleich nie angefasst. +- **Seed mit Beispielbibliothek**: erfundener Katalog aus 8 Serien und + 10 Filmen samt lokal erzeugten Postern, dazu 18 Bibliothekseinträge und + 85 Sichtungen über die letzten Monate verteilt. +- **21 Tests** decken Aufnehmen, Abhaken, Fortschritt, Rewatch, Papierkorb und + den Zugriffsschutz zwischen Nutzern ab. + +### Geändert + +- Die Detailseite liegt jetzt unter `/titel/` und hängt an der internen + Titel-ID statt an der TMDB-ID — nur so bekommen manuell angelegte Titel + dieselbe Seite. Treffer aus der Suche laufen über `/tmdb//`, das + den Titel holt und weiterleitet. +- Die Zahl der Build-Worker ist auf vier begrenzt: mit einem Worker je Kern + stürzte der Build auf einer 18-Kern-Maschine reproduzierbar ab, weil jeder + Worker eine eigene Prisma-Engine hochzieht. + ## [0.2.0] — 2026-09-01 Metadaten: Flimmer spricht mit TMDB. @@ -80,6 +132,7 @@ Erste lauffähige Etappe: Gerüst, Marke, Datenmodell und Anmeldung. dass weitere Sprachen als zusätzliche Datei dazukommen können. - **Seed-Skript** mit Demo-Konten sowie README und Design-Dokumentation. -[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/flimmer/compare/v0.2.0...HEAD +[Unveröffentlicht]: https://git.wilde-server.de/florian.wilde/flimmer/compare/v0.3.0...HEAD +[0.3.0]: https://git.wilde-server.de/florian.wilde/flimmer/releases/tag/v0.3.0 [0.2.0]: https://git.wilde-server.de/florian.wilde/flimmer/releases/tag/v0.2.0 [0.1.0]: https://git.wilde-server.de/florian.wilde/flimmer/releases/tag/v0.1.0 diff --git a/DEVLOG.md b/DEVLOG.md index d3820b2..8f4266c 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -34,11 +34,27 @@ Alle Zeiten per `date`-Befehl erfasst (Europe/Berlin), nicht geschätzt. --- +## Etappe 3 — Kern-Tracking: Bibliothek, Status, Episodenfortschritt (v0.3.0) + +| # | Start | Ende | Dauer | Arbeitsschritt | +|---|---|---|---|---| +| 3.1 | 19:01 | 19:06 | 5 min | Domänenlogik: Aufnehmen, Status, Sichtungen, Rewatch-Durchläufe, Fortschritt, nächste Episode | +| 3.2 | 19:06 | 19:10 | 4 min | 14 Tests dazu; dabei ein Fehler im parallelen Testlauf gefunden (Testdateien räumten sich gegenseitig die Fixtures weg) | +| 3.3 | 19:10 | 19:14 | 4 min | Server-Aktionen, Rückmeldungen mit Rückgängig, Bedienelemente (Status, Bewertung, Notizen, Tags, Episodenliste, „+1 Episode") | +| 3.4 | 19:14 | 19:17 | 3 min | Bibliotheksseite mit Filtern und Facetten, Papierkorb, Umbau der Detailseite auf die interne Titel-ID | +| 3.5 | 19:17 | 19:23 | 6 min | Beispieldaten: erfundener Katalog mit lokal erzeugten Postern, gelebte Bibliothek mit Sichtungshistorie; Klickweg im Browser geprüft (+1 Episode, Rückgängig) | +| 3.6 | 19:23 | 19:25 | 2 min | Manuelles Anlegen ohne TMDB-Treffer; Build-Absturz durch zu viele Worker eingegrenzt und behoben | + +**Etappe 3 gesamt: 24 min** (2026-09-01, 19:01 – 19:25) + +--- + ## Gesamtübersicht | Etappe | Version | Dauer | |---|---|---| | 1 — Gerüst, Branding, Docker, Schema, Auth | 0.1.0 | 38 min | | 2 — TMDB-Anbindung | 0.2.0 | 14 min | +| 3 — Kern-Tracking | 0.3.0 | 24 min | -**Bisher gesamt: 52 min** +**Bisher gesamt: 76 min** diff --git a/releases/v0.3.0.md b/releases/v0.3.0.md new file mode 100644 index 0000000..f03aeaf --- /dev/null +++ b/releases/v0.3.0.md @@ -0,0 +1,51 @@ +# Flimmer 0.3.0 + +Das Kernstück: Bibliothek, Status und Episodenfortschritt. + +### Hinzugefügt + +- **Bibliothek** als Posterraster mit Volltextsuche und Filtern nach Status, + Typ, Genre, Streamingdienst und Tag; Sortierung nach zuletzt aktualisiert, + hinzugefügt, Titel, Bewertung oder Erscheinungsjahr. Der gesamte Filterstand + steht in der URL und ist damit teilbar. +- **Fünf Status** je Eintrag (Schaue gerade, Pausiert, Abgeschlossen, + Abgebrochen, Watchlist). Der Status ergibt sich beim Abhaken von selbst: + die erste Episode startet einen Titel, die letzte schließt ihn ab. + Pausiert und Abgebrochen bleiben unangetastet — wer bewusst pausiert hat, + will das nicht durch eine Sichtung aufgehoben bekommen. +- **Episodenfortschritt**: einzeln abhaken, ganze Staffel auf einmal, + „bis hierhin gesehen" für den Einstieg mitten in einer Staffel, und ein + **„+1 Episode"**-Knopf, der genau die nächste offene Episode nimmt. + Häkchen reagieren sofort und werden zurückgedreht, wenn der Server + widerspricht; beim Abhaken glimmt die Zeile einmal warm auf. +- **„Als Nächstes dran"** mit Episodencode, Titel und Ausstrahlungsdatum; + Fortschritt in Prozent auf Basis der echten Episodenzahl, dazu die + verbleibende Laufzeit. +- **Rewatch-Zählung**: ein neuer Durchlauf setzt den Fortschritt zurück, die + Sichtungen des vorherigen bleiben für die Statistik erhalten. Bei Filmen + zählt jeder Durchlauf einzeln. +- **Rückgängig** für jede Sichtungsaktion — die Rückmeldung am unteren Rand + nennt, was passiert ist, und nimmt es auf Klick zurück. +- **Pro Eintrag**: Bewertung von 1 bis 10, Notizfeld, eigene Tags und das Feld + „wo schaue ich das", das beim Aufnehmen einen Vorschlag aus der + Streaming-Verfügbarkeit erhält und jederzeit überschreibbar ist. +- **Papierkorb** statt hartem Löschen: entfernte Titel bleiben 30 Tage liegen + und behalten dabei ihren Fortschritt. Wird ein Titel erneut aufgenommen, + lebt derselbe Eintrag wieder auf. +- **Titel selbst anlegen** für alles, was TMDB nicht kennt. Solche Einträge + haben keine TMDB-ID und werden vom Abgleich nie angefasst. +- **Seed mit Beispielbibliothek**: erfundener Katalog aus 8 Serien und + 10 Filmen samt lokal erzeugten Postern, dazu 18 Bibliothekseinträge und + 85 Sichtungen über die letzten Monate verteilt. +- **21 Tests** decken Aufnehmen, Abhaken, Fortschritt, Rewatch, Papierkorb und + den Zugriffsschutz zwischen Nutzern ab. + +### Geändert + +- Die Detailseite liegt jetzt unter `/titel/` und hängt an der internen + Titel-ID statt an der TMDB-ID — nur so bekommen manuell angelegte Titel + dieselbe Seite. Treffer aus der Suche laufen über `/tmdb//`, das + den Titel holt und weiterleitet. +- Die Zahl der Build-Worker ist auf vier begrenzt: mit einem Worker je Kern + stürzte der Build auf einer 18-Kern-Maschine reproduzierbar ab, weil jeder + Worker eine eigene Prisma-Engine hochzieht. diff --git a/src/components/SeasonList.tsx b/src/components/SeasonList.tsx deleted file mode 100644 index f543ddf..0000000 --- a/src/components/SeasonList.tsx +++ /dev/null @@ -1,137 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { clsx } from "clsx"; - -/** - * Staffel- und Episodenliste. - * - * Staffeln sind eingeklappt, damit eine Serie mit zwanzig Staffeln die Seite - * nicht sprengt; die erste reguläre Staffel steht offen. In Etappe 3 kommen - * hier die Häkchen für den Fortschritt dazu. - */ - -export type EpisodeData = { - id: string; - episodeNumber: number; - name: string; - overview: string | null; - airDate: string | null; - runtime: number | null; -}; - -export type SeasonData = { - id: string; - seasonNumber: number; - name: string; - overview: string | null; - airDate: string | null; - episodes: EpisodeData[]; -}; - -function formatDate(value: string | null): string | null { - if (!value) return null; - return new Date(value).toLocaleDateString("de-DE", { - day: "2-digit", - month: "2-digit", - year: "numeric", - }); -} - -export function SeasonList({ seasons }: { seasons: SeasonData[] }) { - const firstRegular = seasons.find((season) => season.seasonNumber > 0) ?? seasons[0]; - const [open, setOpen] = useState(firstRegular ? [firstRegular.id] : []); - - function toggle(id: string) { - setOpen((current) => - current.includes(id) ? current.filter((entry) => entry !== id) : [...current, id], - ); - } - - if (seasons.length === 0) { - return

Zu dieser Serie liegen keine Staffeln vor.

; - } - - return ( -
- {seasons.map((season) => { - const isOpen = open.includes(season.id); - const air = formatDate(season.airDate); - - return ( -
-

- -

- - {isOpen && ( -
    - {season.episodes.map((episode) => ( -
  1. - - S{String(season.seasonNumber).padStart(2, "0")}E - {String(episode.episodeNumber).padStart(2, "0")} - - - {episode.name} - {episode.overview && ( - - {episode.overview} - - )} - - - {formatDate(episode.airDate) ?? "—"} - {episode.runtime ? {episode.runtime} min : null} - -
  2. - ))} - {season.episodes.length === 0 && ( -
  3. - Für diese Staffel liegen noch keine Episoden vor. -
  4. - )} -
- )} -
- ); - })} -
- ); -} - -function Chevron({ open }: { open: boolean }) { - return ( - - - - ); -}