diff --git a/ui/src/routes/books/+page.server.ts b/ui/src/routes/books/+page.server.ts new file mode 100644 index 0000000..615b208 --- /dev/null +++ b/ui/src/routes/books/+page.server.ts @@ -0,0 +1,17 @@ +import type { PageServerLoad } from './$types'; +import { listBooks, allProgress } from '$lib/server/pocketbase'; + +export const load: PageServerLoad = async ({ locals }) => { + const [books, progressList] = await Promise.all([ + listBooks(), + allProgress(locals.sessionId) + ]); + + // Build a quick lookup: slug → last chapter read + const progressMap: Record = {}; + for (const p of progressList) { + progressMap[p.slug] = p.chapter; + } + + return { books, progressMap }; +}; diff --git a/ui/src/routes/books/+page.svelte b/ui/src/routes/books/+page.svelte new file mode 100644 index 0000000..2138d13 --- /dev/null +++ b/ui/src/routes/books/+page.svelte @@ -0,0 +1,91 @@ + + + + Library — libnovel + + +
+

Library

+

{data.books.length} books

+
+ +{#if data.books.length === 0} +
+

No books scraped yet.

+

Use the scraper API to add books.

+
+{:else} +
+ {#each data.books as book} + {@const lastChapter = data.progressMap[book.slug]} + {@const genres = parseGenres(book.genres)} + + +
+ {#if book.cover} + {book.title} + {:else} +
+ + + +
+ {/if} +
+ + +
+

+ {book.title} +

+ {#if book.author} +

{book.author}

+ {/if} + +
+ {#if book.status} + + {book.status} + + {/if} + {#if lastChapter} + + ch.{lastChapter} + + {/if} +
+ + {#if genres.length > 0} +
+ {#each genres.slice(0, 2) as genre} + {genre} + {/each} +
+ {/if} +
+
+ {/each} +
+{/if} diff --git a/ui/src/routes/books/[slug]/+page.server.ts b/ui/src/routes/books/[slug]/+page.server.ts new file mode 100644 index 0000000..e6b0e4e --- /dev/null +++ b/ui/src/routes/books/[slug]/+page.server.ts @@ -0,0 +1,23 @@ +import { error } from '@sveltejs/kit'; +import type { PageServerLoad } from './$types'; +import { getBook, listChapterIdx, getProgress } from '$lib/server/pocketbase'; + +export const load: PageServerLoad = async ({ params, locals }) => { + const { slug } = params; + + const [book, chapters, progress] = await Promise.all([ + getBook(slug), + listChapterIdx(slug), + getProgress(locals.sessionId, slug) + ]); + + if (!book) { + error(404, `Book "${slug}" not found`); + } + + return { + book, + chapters, + lastChapter: progress?.chapter ?? null + }; +}; diff --git a/ui/src/routes/books/[slug]/+page.svelte b/ui/src/routes/books/[slug]/+page.svelte new file mode 100644 index 0000000..26c36a1 --- /dev/null +++ b/ui/src/routes/books/[slug]/+page.svelte @@ -0,0 +1,142 @@ + + + + {data.book.title} — libnovel + + + +
+ {#if data.book.cover} + {data.book.title} + {/if} + +
+

{data.book.title}

+ + {#if data.book.author} +

{data.book.author}

+ {/if} + +
+ {#if data.book.status} + {data.book.status} + {/if} + {#each genres as genre} + {genre} + {/each} +
+ + {#if data.book.summary} +

{data.book.summary}

+ {/if} + +
+ {#if data.lastChapter} + + Continue ch.{data.lastChapter} + + {/if} + {#if data.chapters.length > 0} + + Start from ch.1 + + {/if} +
+
+
+ + +
+
+

+ Chapters + ({data.chapters.length}) +

+ + {#if totalPages > 1} +
+ + {page + 1} / {totalPages} + +
+ {/if} +
+ + {#if data.chapters.length === 0} +

No chapters available yet.

+ {:else} + + {/if} +