From ad1689b81852e9b417e3574e8f78cf87f30d97f8 Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Sat, 4 Jul 2026 21:04:30 +0200 Subject: [PATCH] feat(detail): link genre and tag chips to the filtered catalogue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Genre chips now deep-link to /?genres= and user-tag chips to /?tags=, reusing the homepage catalogue's existing filter params — turning the detail page's metadata into one-tap discovery. Tag chips keep their remove affordance (the Chip's href branch preventDefaults the remove button). e2e asserts both chip hrefs. Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/Cargo.lock | 2 +- backend/Cargo.toml | 2 +- frontend/e2e/chip-catalog-links.spec.ts | 60 +++++++++++++++++++++ frontend/package-lock.json | 4 +- frontend/package.json | 2 +- frontend/src/routes/manga/[id]/+page.svelte | 4 +- 6 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 frontend/e2e/chip-catalog-links.spec.ts diff --git a/backend/Cargo.lock b/backend/Cargo.lock index af21620..c34799a 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1558,7 +1558,7 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "mangalord" -version = "0.97.0" +version = "0.98.0" dependencies = [ "anyhow", "argon2", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index ea55d71..8539c0c 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mangalord" -version = "0.97.0" +version = "0.98.0" edition = "2021" default-run = "mangalord" diff --git a/frontend/e2e/chip-catalog-links.spec.ts b/frontend/e2e/chip-catalog-links.spec.ts new file mode 100644 index 0000000..f992fa4 --- /dev/null +++ b/frontend/e2e/chip-catalog-links.spec.ts @@ -0,0 +1,60 @@ +import { test, expect, type Page } from './fixtures'; + +// Genre and user-tag chips on the manga detail page deep-link into the +// homepage catalogue pre-filtered by that genre / tag. + +const mangaId = 'a1111111-1111-1111-1111-111111111111'; +const genreId = 'g0000000-0000-0000-0000-000000000005'; +const tagId = 't0000000-0000-0000-0000-000000000042'; + +async function mockDetail(page: Page) { + await page.route('**/api/v1/auth/config', (r) => + r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ self_register_enabled: true, private_mode: false }) }) + ); + await page.route('**/api/v1/auth/me', (r) => + r.fulfill({ status: 401, contentType: 'application/json', body: JSON.stringify({ error: { code: 'unauthenticated', message: 'no' } }) }) + ); + await page.route('**/api/v1/auth/me/preferences', (r) => + r.fulfill({ status: 401, contentType: 'application/json', body: '{}' }) + ); + await page.route('**/api/v1/me/bookmarks*', (r) => + r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ items: [], page: { limit: 50, offset: 0, total: 0 } }) }) + ); + await page.route(`**/api/v1/mangas/${mangaId}/chapters*`, (r) => + r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ items: [], page: { limit: 50, offset: 0, total: 0 } }) }) + ); + await page.route(`**/api/v1/mangas/${mangaId}/similar`, (r) => + r.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ items: [] }) }) + ); + await page.route(`**/api/v1/me/read-progress/${mangaId}`, (r) => + r.fulfill({ status: 404, contentType: 'application/json', body: JSON.stringify({ error: { code: 'not_found', message: 'no' } }) }) + ); + await page.route(`**/api/v1/mangas/${mangaId}`, (r) => + r.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + id: mangaId, title: 'Berserk', status: 'ongoing', alt_titles: [], description: null, + cover_image_path: null, created_at: '2026-01-01T00:00:00Z', updated_at: '2026-01-01T00:00:00Z', + authors: [], + genres: [{ id: genreId, name: 'Fantasy' }], + tags: [{ id: tagId, name: 'isekai', added_by: null }], + content_warnings: [], chapter_storage_bytes: 0 + }) + }) + ); +} + +test('genre and tag chips link to the filtered catalogue', async ({ page }) => { + await mockDetail(page); + await page.goto(`/manga/${mangaId}`); + + await expect(page.getByTestId(`genre-chip-${genreId}`)).toHaveAttribute( + 'href', + `/?genres=${genreId}` + ); + await expect(page.getByTestId(`tag-chip-${tagId}`)).toHaveAttribute( + 'href', + `/?tags=${tagId}` + ); +}); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 58d2ae0..ba9a4f1 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "mangalord-frontend", - "version": "0.97.0", + "version": "0.98.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mangalord-frontend", - "version": "0.97.0", + "version": "0.98.0", "devDependencies": { "@lucide/svelte": "^1.16.0", "@playwright/test": "^1.48.0", diff --git a/frontend/package.json b/frontend/package.json index 440bbc6..69cde4d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "mangalord-frontend", - "version": "0.97.0", + "version": "0.98.0", "private": true, "type": "module", "scripts": { diff --git a/frontend/src/routes/manga/[id]/+page.svelte b/frontend/src/routes/manga/[id]/+page.svelte index 76fec67..fa92a81 100644 --- a/frontend/src/routes/manga/[id]/+page.svelte +++ b/frontend/src/routes/manga/[id]/+page.svelte @@ -426,7 +426,7 @@
Genres {#each genres as g (g.id)} - + {/each}
{/if} @@ -436,6 +436,8 @@ {#each tags as t (t.id)} removeTag(t)