feat(10-04): collapsible panel with flyout, user menu with logout, breadcrumbs and dark mode

- useSidebar: collapsed below 1100px (matchMedia) or by the admin's choice,
  persisted as a boolean under summer-admin.sidebar; the viewport never
  overwrites the stored choice
- SectionPanel collapse and rail expand buttons; SectionFlyout (role menu)
  opens on hover, focus, Enter or ArrowDown on a rail item, closes on Esc or
  about 200 ms after leaving and returns focus to the rail item
- Breadcrumbs with plugin, controller and record crumbs; UserMenu (Reka
  DropdownMenu) with initials, name, role and Wyloguj
- useAuth.logout POSTs /auth/logout, clears user, navigation and settings
  and routes to login even when the call fails
- applyColorScheme toggles .dark from prefers-color-scheme (no toggle, A6)
- newest toast first, 200 ms fade and scale for dialogs and toasts
- one relation-manager type constant in the registry
- shell smoke tests; tests default to a desktop, light matchMedia
This commit is contained in:
Jakub Zych
2026-09-27 17:26:44 +02:00
parent f4e97cccad
commit 445404e394
24 changed files with 1056 additions and 77 deletions

View File

@@ -1,8 +1,11 @@
// Admin session state. The JWT travels in an HttpOnly cookie (D-19): the SPA
// keeps only the profile and the access lifetime, never a token.
import { readonly, ref } from 'vue'
import type { Router } from 'vue-router'
import { api, onRefreshed, refreshSession } from '../api/client'
import type { AdminProfile } from '../api/types'
import { setNavigation } from './useNavigation'
import { setSettings } from './useSettings'
const user = ref<AdminProfile | null>(null)
const expiresIn = ref<number | null>(null)
@@ -57,11 +60,33 @@ export function clearUser(): void {
expiresIn.value = null
}
/**
* Ends the session: POST /auth/logout (the server blacklists the token and
* expires the HttpOnly cookie), then clears the in-memory user, navigation
* and settings and routes to the login screen. The local state is cleared
* and the login screen shown even when the call fails (T-10-23).
*/
export async function logout(router?: Pick<Router, 'replace'>): Promise<void> {
try {
await api.POST('/auth/logout')
} catch {
// Network failure: the session still ends on this device.
} finally {
clearUser()
setNavigation([])
setSettings([])
}
if (router) {
await router.replace({ name: 'login' }).catch(() => undefined)
}
}
export function useAuth() {
return {
user: currentUser,
expiresIn: readonly(expiresIn),
login,
logout,
me,
clearUser,
}