|
|
|
|
@@ -234,7 +234,10 @@
|
|
|
|
|
<span class="text-xs px-2 py-0.5 rounded bg-(--color-surface-3) text-(--color-text) border border-(--color-border)">{book.status}</span>
|
|
|
|
|
{/if}
|
|
|
|
|
{#each genres as genre}
|
|
|
|
|
<span class="text-xs px-2 py-0.5 rounded bg-(--color-surface-2) text-(--color-muted) border border-(--color-border)">{genre}</span>
|
|
|
|
|
<a
|
|
|
|
|
href="/catalogue?genre={encodeURIComponent(genre)}"
|
|
|
|
|
class="text-xs px-2 py-0.5 rounded bg-(--color-surface-2) text-(--color-muted) border border-(--color-border) hover:border-(--color-brand)/50 hover:text-(--color-text) transition-colors"
|
|
|
|
|
>{genre}</a>
|
|
|
|
|
{/each}
|
|
|
|
|
{#if data.readersThisWeek && data.readersThisWeek > 0}
|
|
|
|
|
<span class="text-xs px-2 py-0.5 rounded bg-(--color-surface-2) text-(--color-muted) border border-(--color-border) flex items-center gap-1">
|
|
|
|
|
@@ -348,65 +351,82 @@
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- CTA buttons — mobile only -->
|
|
|
|
|
<div class="flex sm:hidden gap-2 items-center">
|
|
|
|
|
{#if data.lastChapter}
|
|
|
|
|
<a
|
|
|
|
|
href="/books/{book.slug}/chapters/{data.lastChapter}"
|
|
|
|
|
class="flex-1 text-center px-4 py-2.5 bg-(--color-brand) text-(--color-surface) font-semibold rounded-lg text-sm hover:bg-(--color-brand-dim) transition-colors shadow"
|
|
|
|
|
>
|
|
|
|
|
{m.book_detail_continue_ch({ n: String(data.lastChapter) })}
|
|
|
|
|
</a>
|
|
|
|
|
{/if}
|
|
|
|
|
{#if chapterList.length > 0}
|
|
|
|
|
<a
|
|
|
|
|
href="/books/{book.slug}/chapters/1"
|
|
|
|
|
class="flex-1 text-center px-4 py-2.5 rounded-lg text-sm font-semibold transition-colors
|
|
|
|
|
{data.lastChapter
|
|
|
|
|
? 'bg-(--color-surface-3) text-(--color-text) hover:bg-(--color-surface-3)'
|
|
|
|
|
: 'bg-(--color-brand) text-(--color-surface) hover:bg-(--color-brand-dim) shadow'}"
|
|
|
|
|
>
|
|
|
|
|
{data.inLib ? m.book_detail_start_ch1() : m.book_detail_preview_ch1()}
|
|
|
|
|
</a>
|
|
|
|
|
{/if}
|
|
|
|
|
{#if !data.isLoggedIn}
|
|
|
|
|
<a
|
|
|
|
|
href="/login"
|
|
|
|
|
title={m.book_detail_signin_to_save()}
|
|
|
|
|
class="flex items-center justify-center w-10 h-10 flex-shrink-0 rounded-lg border border-(--color-border) bg-(--color-surface-3) text-(--color-muted) hover:text-(--color-text) transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 4a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 20V4z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
</a>
|
|
|
|
|
{:else if data.inLib}
|
|
|
|
|
<button
|
|
|
|
|
onclick={toggleSave}
|
|
|
|
|
disabled={saving}
|
|
|
|
|
title={saved ? m.book_detail_remove_from_library() : m.book_detail_add_to_library()}
|
|
|
|
|
class="flex items-center justify-center w-10 h-10 flex-shrink-0 rounded-lg border transition-colors disabled:opacity-50
|
|
|
|
|
{saved
|
|
|
|
|
? 'bg-(--color-brand)/20 text-(--color-brand-dim) border-(--color-brand)/30 hover:bg-red-500/20 hover:text-red-300 hover:border-red-400/30'
|
|
|
|
|
: 'bg-(--color-surface-3) text-(--color-muted) border-(--color-border) hover:bg-(--color-surface-3) hover:text-(--color-text)'}"
|
|
|
|
|
>
|
|
|
|
|
{#if saving}
|
|
|
|
|
<svg class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
|
|
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
|
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
|
|
|
|
</svg>
|
|
|
|
|
{:else if saved}
|
|
|
|
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path d="M5 4a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 20V4z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
{:else}
|
|
|
|
|
<div class="flex sm:hidden flex-col gap-2 mt-3">
|
|
|
|
|
<!-- Row 1: primary read button(s) -->
|
|
|
|
|
<div class="flex gap-2">
|
|
|
|
|
{#if data.lastChapter}
|
|
|
|
|
<a
|
|
|
|
|
href="/books/{book.slug}/chapters/{data.lastChapter}"
|
|
|
|
|
class="flex-1 text-center px-4 py-2.5 bg-(--color-brand) text-(--color-surface) font-semibold rounded-lg text-sm hover:bg-(--color-brand-dim) transition-colors shadow"
|
|
|
|
|
>
|
|
|
|
|
{m.book_detail_continue_ch({ n: String(data.lastChapter) })}
|
|
|
|
|
</a>
|
|
|
|
|
{/if}
|
|
|
|
|
{#if chapterList.length > 0}
|
|
|
|
|
<a
|
|
|
|
|
href="/books/{book.slug}/chapters/1"
|
|
|
|
|
class="text-center px-4 py-2.5 rounded-lg text-sm font-semibold transition-colors
|
|
|
|
|
{data.lastChapter
|
|
|
|
|
? 'bg-(--color-surface-3) text-(--color-text) hover:bg-(--color-surface-3)'
|
|
|
|
|
: 'flex-1 bg-(--color-brand) text-(--color-surface) hover:bg-(--color-brand-dim) shadow'}"
|
|
|
|
|
>
|
|
|
|
|
{data.inLib ? m.book_detail_start_ch1() : m.book_detail_preview_ch1()}
|
|
|
|
|
</a>
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Row 2: bookmark + shelf + stars -->
|
|
|
|
|
<div class="flex items-center gap-2 flex-wrap">
|
|
|
|
|
{#if !data.isLoggedIn}
|
|
|
|
|
<a
|
|
|
|
|
href="/login"
|
|
|
|
|
title={m.book_detail_signin_to_save()}
|
|
|
|
|
class="flex items-center justify-center w-9 h-9 rounded-lg border border-(--color-border) bg-(--color-surface-3) text-(--color-muted) hover:text-(--color-text) transition-colors flex-shrink-0"
|
|
|
|
|
>
|
|
|
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 4a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 20V4z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
{/if}
|
|
|
|
|
</button>
|
|
|
|
|
{/if}
|
|
|
|
|
</a>
|
|
|
|
|
{:else if data.inLib}
|
|
|
|
|
<button
|
|
|
|
|
onclick={toggleSave}
|
|
|
|
|
disabled={saving}
|
|
|
|
|
title={saved ? m.book_detail_remove_from_library() : m.book_detail_add_to_library()}
|
|
|
|
|
class="flex items-center justify-center w-9 h-9 flex-shrink-0 rounded-lg border transition-colors disabled:opacity-50
|
|
|
|
|
{saved
|
|
|
|
|
? 'bg-(--color-brand)/20 text-(--color-brand-dim) border-(--color-brand)/30 hover:bg-red-500/20 hover:text-red-300 hover:border-red-400/30'
|
|
|
|
|
: 'bg-(--color-surface-3) text-(--color-muted) border-(--color-border) hover:bg-(--color-surface-3) hover:text-(--color-text)'}"
|
|
|
|
|
>
|
|
|
|
|
{#if saving}
|
|
|
|
|
<svg class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
|
|
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
|
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
|
|
|
|
</svg>
|
|
|
|
|
{:else if saved}
|
|
|
|
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path d="M5 4a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 20V4z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
{:else}
|
|
|
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
|
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 4a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 20V4z"/>
|
|
|
|
|
</svg>
|
|
|
|
|
{/if}
|
|
|
|
|
</button>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
{#if saved}
|
|
|
|
|
<select
|
|
|
|
|
value={currentShelf}
|
|
|
|
|
onchange={(e) => setShelf((e.currentTarget as HTMLSelectElement).value as ShelfName)}
|
|
|
|
|
class="bg-(--color-surface-2) border border-(--color-border) rounded-lg px-3 py-1.5 text-sm text-(--color-muted) focus:outline-none focus:ring-2 focus:ring-(--color-brand) cursor-pointer flex-shrink-0"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Reading</option>
|
|
|
|
|
<option value="plan_to_read">Plan to Read</option>
|
|
|
|
|
<option value="completed">Completed</option>
|
|
|
|
|
<option value="dropped">Dropped</option>
|
|
|
|
|
</select>
|
|
|
|
|
{/if}
|
|
|
|
|
|
|
|
|
|
<!-- Ratings + shelf — mobile -->
|
|
|
|
|
<div class="flex sm:hidden items-center gap-3 flex-wrap mt-1">
|
|
|
|
|
<StarRating
|
|
|
|
|
rating={userRating}
|
|
|
|
|
avg={ratingAvg.avg}
|
|
|
|
|
@@ -414,20 +434,6 @@
|
|
|
|
|
onrate={rate}
|
|
|
|
|
size="sm"
|
|
|
|
|
/>
|
|
|
|
|
{#if saved}
|
|
|
|
|
<div class="relative">
|
|
|
|
|
<select
|
|
|
|
|
value={currentShelf}
|
|
|
|
|
onchange={(e) => setShelf((e.currentTarget as HTMLSelectElement).value as ShelfName)}
|
|
|
|
|
class="bg-(--color-surface-2) border border-(--color-border) rounded-lg px-3 py-1.5 text-sm text-(--color-muted) focus:outline-none focus:ring-2 focus:ring-(--color-brand) cursor-pointer"
|
|
|
|
|
>
|
|
|
|
|
<option value="">Reading</option>
|
|
|
|
|
<option value="plan_to_read">Plan to Read</option>
|
|
|
|
|
<option value="completed">Completed</option>
|
|
|
|
|
<option value="dropped">Dropped</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|