- `GET /v1/authors/:id` returns `AuthorWithCount` (id, name, manga_count). - `GET /v1/authors/:id/mangas` paged works by that author. - `GET /v1/authors?search=` autocomplete (already used by Phase 1 forms; now formally exposed). - New `/authors/:id` page on the frontend; author chips on the manga detail page (added in Phase 1) now link to a real page. - Extracts `lib/components/MangaCard.svelte` — already used by the home page, ready for the collection page in Phase 3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
97 lines
2.2 KiB
Svelte
97 lines
2.2 KiB
Svelte
<script lang="ts">
|
|
import MangaCard from '$lib/components/MangaCard.svelte';
|
|
import ArrowLeft from '@lucide/svelte/icons/arrow-left';
|
|
|
|
let { data } = $props();
|
|
const author = $derived(data.author);
|
|
const mangas = $derived(data.mangas);
|
|
const total = $derived(data.total);
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>{author.name} — Mangalord</title>
|
|
</svelte:head>
|
|
|
|
<nav class="back">
|
|
<a href="/" class="back-link">
|
|
<ArrowLeft size={16} aria-hidden="true" />
|
|
<span>Back to search</span>
|
|
</a>
|
|
</nav>
|
|
|
|
<header class="overview">
|
|
<h1 data-testid="author-name">{author.name}</h1>
|
|
<p class="count" data-testid="author-manga-count">
|
|
{author.manga_count}
|
|
{author.manga_count === 1 ? 'work' : 'works'}
|
|
</p>
|
|
</header>
|
|
|
|
{#if mangas.length === 0}
|
|
<p class="status" data-testid="author-no-mangas">
|
|
No mangas attributed to this author.
|
|
</p>
|
|
{:else}
|
|
{#if total != null}
|
|
<p class="meta" data-testid="author-shown-of-total">
|
|
Showing {mangas.length} of {total}
|
|
</p>
|
|
{/if}
|
|
<ul class="manga-grid" data-testid="author-manga-list">
|
|
{#each mangas as m (m.id)}
|
|
<MangaCard manga={m} testid={`author-manga-${m.id}`} />
|
|
{/each}
|
|
</ul>
|
|
{/if}
|
|
|
|
<style>
|
|
.back {
|
|
margin-bottom: var(--space-3);
|
|
}
|
|
|
|
.back-link {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: var(--space-1);
|
|
color: var(--text-muted);
|
|
font-size: var(--font-sm);
|
|
}
|
|
|
|
.back-link:hover {
|
|
color: var(--primary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.overview {
|
|
margin-bottom: var(--space-5);
|
|
}
|
|
|
|
.overview h1 {
|
|
margin: 0 0 var(--space-1);
|
|
}
|
|
|
|
.count {
|
|
color: var(--text-muted);
|
|
margin: 0 0 var(--space-2);
|
|
}
|
|
|
|
.meta {
|
|
color: var(--text-muted);
|
|
font-size: var(--font-sm);
|
|
margin: 0 0 var(--space-3);
|
|
}
|
|
|
|
.status {
|
|
color: var(--text-muted);
|
|
}
|
|
|
|
.manga-grid {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
|
gap: var(--space-4);
|
|
}
|
|
</style>
|