From 364178496a286763de96e88fc08d9ec8de6db1dd Mon Sep 17 00:00:00 2001 From: Till JS Date: Fri, 17 Apr 2026 03:57:55 +0200 Subject: [PATCH] =?UTF-8?q?feat(library):=20M2=20=E2=80=94=20CRUD=20form,?= =?UTF-8?q?=20grid=20view,=20detail=20view?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the user-facing surface for the Bibliothek module: Components (components/): - KindTabs — Alle / Bücher / Filme / Serien / Comics with counts - StatusFilter — Geplant / Läuft / Fertig / Pausiert / Abgebrochen chips - RatingStars — 0–5 stars, click to set, click again to clear - StatusBadge — colored status pill - CoverImage — lazy with kind-emoji fallback - EntryCard — grid card (cover + title + rating + status) - EntryForm — create/edit with kind selector + typ-spezifische Felder (pages, runtime, seasons, issues, format) Views (views/): - GridView — responsive grid of EntryCards + empty state - DetailView — cover, metadata, status picker, rating, delete/fav actions, review display, typ-spezifische details (book pages/format, movie runtime/director, series seasons/episodes, comic issues/publisher). Edit toggles in-place to EntryForm. Route: - /library — rewritten ListView (tabs + filters + grid + FAB) - /library/entry/[id] — DetailView Progress features (episode tracker for series, page slider for books, times-counter button for re-reads/re-watches) are deferred to M3 since they need dedicated interaction patterns. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/lib/modules/library/ListView.svelte | 261 ++++++--- .../library/components/CoverImage.svelte | 66 +++ .../library/components/EntryCard.svelte | 105 ++++ .../library/components/EntryForm.svelte | 534 ++++++++++++++++++ .../library/components/KindTabs.svelte | 91 +++ .../library/components/RatingStars.svelte | 90 +++ .../library/components/StatusBadge.svelte | 27 + .../library/components/StatusFilter.svelte | 56 ++ .../modules/library/views/DetailView.svelte | 351 ++++++++++++ .../lib/modules/library/views/GridView.svelte | 46 ++ .../(app)/library/entry/[id]/+page.svelte | 35 ++ 11 files changed, 1585 insertions(+), 77 deletions(-) create mode 100644 apps/mana/apps/web/src/lib/modules/library/components/CoverImage.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/library/components/EntryCard.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/library/components/EntryForm.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/library/components/KindTabs.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/library/components/RatingStars.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/library/components/StatusBadge.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/library/components/StatusFilter.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/library/views/DetailView.svelte create mode 100644 apps/mana/apps/web/src/lib/modules/library/views/GridView.svelte create mode 100644 apps/mana/apps/web/src/routes/(app)/library/entry/[id]/+page.svelte diff --git a/apps/mana/apps/web/src/lib/modules/library/ListView.svelte b/apps/mana/apps/web/src/lib/modules/library/ListView.svelte index eee048a27..390a22d5d 100644 --- a/apps/mana/apps/web/src/lib/modules/library/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/library/ListView.svelte @@ -1,68 +1,156 @@
-

Bibliothek

-

Bücher, Filme, Serien und Comics — alles an einem Ort.

+
+
+

Bibliothek

+

Bücher, Filme, Serien, Comics — was du liest und schaust.

+
+ +
+
+ (activeKind = k)} /> + +
+ (activeStatus = s)} /> + +
+ + +
+ {#if entries$.loading} -

Lädt…

+

Lädt…

{:else} - {#each KIND_ORDER as kind (kind)} - {@const list = grouped[kind]} -
-

- {KIND_LABELS[kind].emoji} - {KIND_LABELS[kind].de} - ({list.length}) -

- {#if list.length === 0} -

Noch keine Einträge.

- {:else} -
    - {#each list as entry (entry.id)} -
  • - {entry.title} - {#if entry.year} - · {entry.year} - {/if} - {STATUS_LABELS[entry.status].de} - {#if entry.rating != null} - {entry.rating.toFixed(1)} ★ - {/if} -
  • - {/each} -
- {/if} -
- {/each} + {/if}
+{#if showCreate} + +{/if} + diff --git a/apps/mana/apps/web/src/lib/modules/library/components/CoverImage.svelte b/apps/mana/apps/web/src/lib/modules/library/components/CoverImage.svelte new file mode 100644 index 000000000..81b472e71 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/library/components/CoverImage.svelte @@ -0,0 +1,66 @@ + + +
+ {#if url && !errored} + (errored = true)} loading="lazy" /> + {:else} + {KIND_LABELS[kind].emoji} + {/if} +
+ + diff --git a/apps/mana/apps/web/src/lib/modules/library/components/EntryCard.svelte b/apps/mana/apps/web/src/lib/modules/library/components/EntryCard.svelte new file mode 100644 index 000000000..9738f10c4 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/library/components/EntryCard.svelte @@ -0,0 +1,105 @@ + + + + + diff --git a/apps/mana/apps/web/src/lib/modules/library/components/EntryForm.svelte b/apps/mana/apps/web/src/lib/modules/library/components/EntryForm.svelte new file mode 100644 index 000000000..477c8c3c2 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/library/components/EntryForm.svelte @@ -0,0 +1,534 @@ + + +
+
+

{mode === 'edit' ? 'Eintrag bearbeiten' : 'Neuer Eintrag'}

+ +
+ + {#if mode === 'create'} +
+ Typ +
+ {#each KIND_ORDER as k (k)} + + {/each} +
+
+ {/if} + + + +
+ + +
+ + + + + +
+ + +
+ + + +
+ Genres +
+ {#each DEFAULT_GENRES as g (g)} + + {/each} +
+
+ +
+ Typ-spezifische Details +
+ {#if kind === 'book'} +
+ + +
+ + {:else if kind === 'movie'} +
+ + +
+ {:else if kind === 'series'} +
+ + +
+ {:else if kind === 'comic'} +
+ + +
+ + + {/if} +
+
+ + + + + +
+ + +
+
+ + diff --git a/apps/mana/apps/web/src/lib/modules/library/components/KindTabs.svelte b/apps/mana/apps/web/src/lib/modules/library/components/KindTabs.svelte new file mode 100644 index 000000000..54383f1c0 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/library/components/KindTabs.svelte @@ -0,0 +1,91 @@ + + +
+ + {#each ORDER as kind (kind)} + + {/each} +
+ + diff --git a/apps/mana/apps/web/src/lib/modules/library/components/RatingStars.svelte b/apps/mana/apps/web/src/lib/modules/library/components/RatingStars.svelte new file mode 100644 index 000000000..93a9279e0 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/library/components/RatingStars.svelte @@ -0,0 +1,90 @@ + + +
+ {#each positions as pos (pos)} + + {/each} + {#if value != null && !readonly} + {value.toFixed(1)} + {/if} +
+ + diff --git a/apps/mana/apps/web/src/lib/modules/library/components/StatusBadge.svelte b/apps/mana/apps/web/src/lib/modules/library/components/StatusBadge.svelte new file mode 100644 index 000000000..ffc6a939a --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/library/components/StatusBadge.svelte @@ -0,0 +1,27 @@ + + + + {STATUS_LABELS[status].de} + + + diff --git a/apps/mana/apps/web/src/lib/modules/library/components/StatusFilter.svelte b/apps/mana/apps/web/src/lib/modules/library/components/StatusFilter.svelte new file mode 100644 index 000000000..85a893b31 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/library/components/StatusFilter.svelte @@ -0,0 +1,56 @@ + + +
+ + {#each ORDER as status (status)} + + {/each} +
+ + diff --git a/apps/mana/apps/web/src/lib/modules/library/views/DetailView.svelte b/apps/mana/apps/web/src/lib/modules/library/views/DetailView.svelte new file mode 100644 index 000000000..266083201 --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/library/views/DetailView.svelte @@ -0,0 +1,351 @@ + + +
+ ← Zurück zur Bibliothek + + {#if editing} + (editing = false)} /> + {:else} +
+
+ +
+ + + +
+
+ +
+
+ {KIND_LABELS[entry.kind].emoji} + {KIND_LABELS[entry.kind].de} +
+

{entry.title}

+ {#if entry.originalTitle && entry.originalTitle !== entry.title} +

{entry.originalTitle}

+ {/if} + {#if entry.creators.length > 0} +

{entry.creators.join(' · ')}

+ {/if} + {#if entry.year}

{entry.year}

{/if} + +
+ +
+ +
+ {#each STATUS_ORDER as s (s)} + + {/each} +
+ + {#if entry.times > 0} +

+ {entry.kind === 'book' || entry.kind === 'comic' ? 'Gelesen' : 'Gesehen'}: {entry.times}× +

+ {/if} + + {#if entry.genres.length > 0 || entry.tags.length > 0} +
+ {#each entry.genres as g (g)} + {g} + {/each} + {#each entry.tags as t (t)} + #{t} + {/each} +
+ {/if} + +
+ {#if entry.details.kind === 'book'} + {#if entry.details.pages} +
Seiten
+
+ {entry.details.currentPage + ? `${entry.details.currentPage} / ${entry.details.pages}` + : entry.details.pages} +
+ {/if} + {#if entry.details.format} +
Format
+
{BOOK_FORMAT_LABELS[entry.details.format].de}
+ {/if} + {:else if entry.details.kind === 'movie'} + {#if entry.details.runtimeMin} +
Laufzeit
+
{entry.details.runtimeMin} min
+ {/if} + {#if entry.details.director} +
Regie
+
{entry.details.director}
+ {/if} + {:else if entry.details.kind === 'series'} + {#if entry.details.totalSeasons} +
Staffeln
+
{entry.details.totalSeasons}
+ {/if} + {#if entry.details.totalEpisodes} +
Episoden
+
{entry.details.totalEpisodes}
+ {/if} + {:else if entry.details.kind === 'comic'} + {#if entry.details.publisher} +
Verlag
+
{entry.details.publisher}
+ {/if} + {#if entry.details.issueCount} +
Ausgaben
+
+ {entry.details.currentIssue + ? `${entry.details.currentIssue} / ${entry.details.issueCount}` + : entry.details.issueCount} +
+ {/if} + {#if entry.details.isOngoing} +
Status
+
laufend
+ {/if} + {/if} + {#if entry.startedAt} +
Gestartet
+
{entry.startedAt}
+ {/if} + {#if entry.completedAt} +
Fertig
+
{entry.completedAt}
+ {/if} +
+ + {#if entry.review} +
+

Review

+

{entry.review}

+
+ {/if} +
+
+ {/if} +
+ + diff --git a/apps/mana/apps/web/src/lib/modules/library/views/GridView.svelte b/apps/mana/apps/web/src/lib/modules/library/views/GridView.svelte new file mode 100644 index 000000000..8165221ae --- /dev/null +++ b/apps/mana/apps/web/src/lib/modules/library/views/GridView.svelte @@ -0,0 +1,46 @@ + + +{#if entries.length === 0} +
+

Noch keine Einträge.

+

Klick auf „+ Neu" um deinen ersten Eintrag anzulegen.

+
+{:else} +
+ {#each entries as entry (entry.id)} + + {/each} +
+{/if} + + diff --git a/apps/mana/apps/web/src/routes/(app)/library/entry/[id]/+page.svelte b/apps/mana/apps/web/src/routes/(app)/library/entry/[id]/+page.svelte new file mode 100644 index 000000000..e3ec3e552 --- /dev/null +++ b/apps/mana/apps/web/src/routes/(app)/library/entry/[id]/+page.svelte @@ -0,0 +1,35 @@ + + + + {entry?.title ?? 'Bibliothek'} - Mana + + +{#if entries$.loading} +

Lädt…

+{:else if !entry} +
+

Eintrag nicht gefunden.

+ ← Zurück zur Bibliothek +
+{:else} + +{/if} + +