modularize service layer, add mobile nav drawer, and sync docs

This commit is contained in:
Justin Visser 2026-02-20 01:28:20 +01:00
parent 7f7a8ce2b0
commit 6b6ed91b92
72 changed files with 8122 additions and 7501 deletions

View file

@ -6,12 +6,29 @@ import AppSelect from "@/components/ui/AppSelect.vue";
import { useThemeStore } from "@/stores/theme";
import type { ThemePresetId } from "@/theme/presets";
const props = withDefaults(
defineProps<{
showMenuButton?: boolean;
menuOpen?: boolean;
}>(),
{
showMenuButton: false,
menuOpen: false,
},
);
const emit = defineEmits<{
(e: "toggle-menu"): void;
}>();
const theme = useThemeStore();
const isDarkTheme = computed(() => theme.active === "dark");
const toggleThemeLabel = computed(() =>
isDarkTheme.value ? "Switch to light theme" : "Switch to dark theme",
);
const menuButtonLabel = computed(() =>
props.menuOpen ? "Close navigation menu" : "Open navigation menu",
);
const selectedPreset = computed<ThemePresetId>({
get: () => theme.preset,
set: (value) => theme.setPreset(value),
@ -22,12 +39,40 @@ const themePresetLabel = computed(() => theme.presetLabel);
function onToggleTheme(): void {
theme.setPreference(isDarkTheme.value ? "light" : "dark");
}
function onToggleMenu(): void {
emit("toggle-menu");
}
</script>
<template>
<header class="sticky top-0 z-30 border-b border-stroke-subtle bg-surface-nav/90 backdrop-blur">
<div class="flex min-h-[4.5rem] w-full items-center justify-between gap-4 px-4 sm:px-6 lg:px-8">
<div class="flex min-w-0 items-center gap-2">
<AppButton
v-if="props.showMenuButton"
variant="ghost"
class="h-10 w-10 rounded-full p-0 lg:hidden"
:aria-label="menuButtonLabel"
:aria-expanded="props.menuOpen"
aria-controls="mobile-primary-nav"
:title="menuButtonLabel"
@click="onToggleMenu"
>
<span class="sr-only">{{ menuButtonLabel }}</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.8"
class="h-5 w-5"
aria-hidden="true"
>
<path v-if="props.menuOpen" d="M6 6l12 12M18 6 6 18" />
<path v-else d="M4 7h16M4 12h16M4 17h16" />
</svg>
</AppButton>
<RouterLink
to="/dashboard"
class="rounded-sm font-display text-lg tracking-tight text-ink-primary transition hover:text-ink-secondary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2 focus-visible:ring-offset-focus-offset"

View file

@ -14,6 +14,9 @@ const auth = useAuthStore();
const runStatus = useRunStatusStore();
const userSettings = useUserSettingsStore();
const router = useRouter();
const emit = defineEmits<{
(e: "navigate"): void;
}>();
const links = computed(() => {
const base = [
@ -63,9 +66,14 @@ const showSafetyRow = computed(
);
async function onLogout(): Promise<void> {
emit("navigate");
await auth.logout();
await router.replace({ name: "login" });
}
function onNavigate(): void {
emit("navigate");
}
</script>
<template>
@ -80,6 +88,7 @@ async function onLogout(): Promise<void> {
:to="link.to"
class="inline-flex min-h-10 min-w-0 items-center rounded-lg border border-transparent px-3 py-2 text-sm font-medium text-ink-secondary transition hover:border-stroke-interactive hover:bg-surface-card-muted hover:text-ink-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2 focus-visible:ring-offset-focus-offset"
active-class="border-stroke-interactive bg-surface-nav-active font-semibold text-ink-primary shadow-sm hover:border-stroke-interactive hover:bg-surface-nav-active hover:text-ink-primary"
@click="onNavigate"
>
{{ link.label }}
</RouterLink>