diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 332ac16..77e9e38 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -1517,7 +1517,7 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "mangalord" -version = "0.88.0" +version = "0.89.0" dependencies = [ "anyhow", "argon2", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 88ade25..765d388 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mangalord" -version = "0.88.0" +version = "0.89.0" edition = "2021" default-run = "mangalord" diff --git a/frontend/e2e/profile.spec.ts b/frontend/e2e/profile.spec.ts index 35c48e9..163a9e1 100644 --- a/frontend/e2e/profile.spec.ts +++ b/frontend/e2e/profile.spec.ts @@ -50,6 +50,33 @@ test('Profile link in nav for authed users; landing shows counts', async ({ page await expect(page.getByTestId('overview-collections')).toBeVisible(); }); +test('Page tags tab reaches the page-tags list on desktop', async ({ page }) => { + // Page tags are a first-class Library section on mobile; desktop reaches + // them through this profile tab. + await stubAuthenticated(page); + await page.route('**/api/v1/me/page-tags?*', (route) => + route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ items: [], page: { limit: 100, offset: 0, total: 0 } }) + }) + ); + // Registered after the general route so distinct URLs resolve here. + await page.route('**/api/v1/me/page-tags/distinct*', (route) => + route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ items: [] }) + }) + ); + + await page.goto('/profile'); + await expect(page.getByTestId('tab-page-tags')).toBeVisible(); + await page.getByTestId('tab-page-tags').click(); + await expect(page).toHaveURL(/\/profile\/page-tags$/); + await expect(page.getByTestId('library-page-tags-empty')).toBeVisible(); +}); + test('Account tab reaches the password form', async ({ page }) => { await stubAuthenticated(page); await page.goto('/profile'); diff --git a/frontend/package.json b/frontend/package.json index 18e80b3..50639c3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "mangalord-frontend", - "version": "0.88.0", + "version": "0.89.0", "private": true, "type": "module", "scripts": { diff --git a/frontend/src/routes/profile/+layout.svelte b/frontend/src/routes/profile/+layout.svelte index 1d1db32..48dc122 100644 --- a/frontend/src/routes/profile/+layout.svelte +++ b/frontend/src/routes/profile/+layout.svelte @@ -6,6 +6,7 @@ import KeyRound from '@lucide/svelte/icons/key-round'; import Bookmark from '@lucide/svelte/icons/bookmark'; import FolderOpen from '@lucide/svelte/icons/folder-open'; + import Tag from '@lucide/svelte/icons/tag'; import History from '@lucide/svelte/icons/history'; let { children } = $props(); @@ -27,6 +28,7 @@ { href: '/profile/account', label: 'Account', icon: KeyRound, testid: 'tab-account', guestVisible: false }, { href: '/profile/bookmarks', label: 'Bookmarks', icon: Bookmark, testid: 'tab-bookmarks', guestVisible: false }, { href: '/profile/collections', label: 'Collections', icon: FolderOpen, testid: 'tab-collections', guestVisible: false }, + { href: '/profile/page-tags', label: 'Page tags', icon: Tag, testid: 'tab-page-tags', guestVisible: false }, { href: '/profile/history', label: 'History', icon: History, testid: 'tab-history', guestVisible: false } ]; diff --git a/frontend/src/routes/profile/page-tags/+page.svelte b/frontend/src/routes/profile/page-tags/+page.svelte new file mode 100644 index 0000000..c9dcbd8 --- /dev/null +++ b/frontend/src/routes/profile/page-tags/+page.svelte @@ -0,0 +1,31 @@ + + + + Mangalord | Page tags + + +{#if data.error} + +{:else if !data.authenticated} +

+ Sign in to see your page tags. +

+{:else} + +{/if} + + diff --git a/frontend/src/routes/profile/page-tags/+page.ts b/frontend/src/routes/profile/page-tags/+page.ts new file mode 100644 index 0000000..4afd43c --- /dev/null +++ b/frontend/src/routes/profile/page-tags/+page.ts @@ -0,0 +1,32 @@ +import { ApiError } from '$lib/api/client'; +import { listMyPageTags, listMyDistinctPageTags } from '$lib/api/page_tags'; +import type { PageLoad } from './$types'; + +export const ssr = false; + +// Desktop home for the user's page tags — the parity counterpart to the +// mobile Library "Page tags" tab. Loads the same data the library wrapper +// feeds PageTagsList. 401 → unauthenticated path; the page shows a sign-in +// prompt. +export const load: PageLoad = async () => { + try { + const [pageTags, distinctPageTags] = await Promise.all([ + listMyPageTags({ limit: 100 }), + listMyDistinctPageTags(undefined, 100) + ]); + return { + authenticated: true, + pageTags: pageTags.items, + distinctPageTags, + error: null as string | null + }; + } catch (e) { + if (e instanceof ApiError && e.status === 401) { + return { authenticated: false, pageTags: [], distinctPageTags: [], error: null }; + } + if (e instanceof ApiError) { + return { authenticated: true, pageTags: [], distinctPageTags: [], error: e.message }; + } + throw e; + } +};