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/mobile-account-library.spec.ts b/frontend/e2e/mobile-account-library.spec.ts index 04d562e..1890f4f 100644 --- a/frontend/e2e/mobile-account-library.spec.ts +++ b/frontend/e2e/mobile-account-library.spec.ts @@ -11,7 +11,7 @@ const DESKTOP = { width: 1280, height: 720 } as const; async function mockSession( page: Page, - opts: { authed?: boolean; logoutCalls?: { count: number } } = {} + opts: { authed?: boolean; admin?: boolean; logoutCalls?: { count: number } } = {} ) { const authed = opts.authed ?? false; await page.route('**/api/v1/auth/config', (route) => @@ -31,7 +31,7 @@ async function mockSession( id: 'u1', username: 'fabian', created_at: '2026-01-05T00:00:00Z', - is_admin: false + is_admin: opts.admin ?? false } }) : JSON.stringify({ @@ -175,11 +175,27 @@ test.describe('mobile account hub', () => { await expect(page.getByTestId('account-row-preferences')).toBeVisible(); await expect(page.getByTestId('account-row-change-password')).toBeVisible(); await expect(page.getByTestId('account-row-logout')).toBeVisible(); + // A non-admin never sees the Admin entry. + await expect(page.getByTestId('account-row-admin')).toHaveCount(0); // Desktop card MUST not render — only the hub view is active // on mobile so we don't double-up the password form testids. await expect(page.getByTestId('account-desktop-card')).toHaveCount(0); }); + test('phone viewport admin: the account hub offers an Admin row linking to /admin', async ({ + page + }) => { + // Admin is reachable from the desktop header but had no mobile entry + // point — admins had to type the URL. The Account hub now carries it. + await mockSession(page, { authed: true, admin: true }); + await page.setViewportSize(MOBILE); + await page.goto('/profile/account'); + + const adminRow = page.getByTestId('account-row-admin'); + await expect(adminRow).toBeVisible(); + await expect(adminRow).toHaveAttribute('href', '/admin'); + }); + test('phone viewport authed: Change password row opens the bottom sheet', async ({ page }) => { 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/+layout.svelte b/frontend/src/routes/+layout.svelte index c8d4095..4c19986 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -81,7 +81,9 @@ label: 'Account', href: '/profile/account', icon: User, - match: ['/profile', '/profile/preferences'] + // `/admin` keeps the Account tab highlighted while an admin is in + // the admin area — its only mobile entry point is the Account hub. + match: ['/profile', '/profile/preferences', '/admin'] } ]; diff --git a/frontend/src/routes/profile/account/+page.svelte b/frontend/src/routes/profile/account/+page.svelte index c5513ad..dae522c 100644 --- a/frontend/src/routes/profile/account/+page.svelte +++ b/frontend/src/routes/profile/account/+page.svelte @@ -10,6 +10,7 @@ import SlidersHorizontal from '@lucide/svelte/icons/sliders-horizontal'; import KeyRound from '@lucide/svelte/icons/key-round'; import LogOut from '@lucide/svelte/icons/log-out'; + import Shield from '@lucide/svelte/icons/shield'; import ChevronRight from '@lucide/svelte/icons/chevron-right'; let currentPassword = $state(''); @@ -193,6 +194,18 @@ + {#if session.user.is_admin} + +