feat: production-harden app and finalize merge-ready UX/theme baseline
- complete tokenized theme preset system and admin style guide visibility - refine dashboard layout/scroll behavior and shared async/request UI states - add user nav-visibility settings across API, migration, and frontend stores - harden security headers/CSP handling and related test coverage - enforce CI repository hygiene + frontend contract/theme/build quality gates - update docs/env references and make migration smoke test head-aware
This commit is contained in:
parent
ab1d29b203
commit
ae2ca8f149
66 changed files with 5655 additions and 853 deletions
|
|
@ -2,7 +2,9 @@
|
|||
import { computed } from "vue";
|
||||
|
||||
import AppButton from "@/components/ui/AppButton.vue";
|
||||
import AppSelect from "@/components/ui/AppSelect.vue";
|
||||
import { useThemeStore } from "@/stores/theme";
|
||||
import type { ThemePresetId } from "@/theme/presets";
|
||||
|
||||
const theme = useThemeStore();
|
||||
|
||||
|
|
@ -10,6 +12,12 @@ const isDarkTheme = computed(() => theme.active === "dark");
|
|||
const toggleThemeLabel = computed(() =>
|
||||
isDarkTheme.value ? "Switch to light theme" : "Switch to dark theme",
|
||||
);
|
||||
const selectedPreset = computed<ThemePresetId>({
|
||||
get: () => theme.preset,
|
||||
set: (value) => theme.setPreset(value),
|
||||
});
|
||||
const presetOptions = computed(() => theme.availablePresets);
|
||||
const themePresetLabel = computed(() => theme.presetLabel);
|
||||
|
||||
function onToggleTheme(): void {
|
||||
theme.setPreference(isDarkTheme.value ? "light" : "dark");
|
||||
|
|
@ -17,18 +25,32 @@ function onToggleTheme(): void {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<header class="sticky top-0 z-30 border-b border-zinc-200/70 bg-white/90 backdrop-blur dark:border-zinc-800 dark:bg-zinc-900/90">
|
||||
<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">
|
||||
<RouterLink
|
||||
to="/dashboard"
|
||||
class="rounded-sm font-display text-lg tracking-tight text-zinc-900 transition hover:text-zinc-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 focus-visible:ring-offset-zinc-100 dark:text-zinc-100 dark:hover:text-zinc-300 dark:focus-visible:ring-brand-400 dark:focus-visible:ring-offset-zinc-950"
|
||||
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"
|
||||
>
|
||||
scholarr
|
||||
</RouterLink>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-end">
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
<div class="w-36 sm:w-44">
|
||||
<label for="theme-preset-select" class="sr-only">Theme preset</label>
|
||||
<AppSelect
|
||||
id="theme-preset-select"
|
||||
v-model="selectedPreset"
|
||||
:disabled="presetOptions.length <= 1"
|
||||
:title="`Theme preset: ${themePresetLabel}`"
|
||||
class="py-1.5"
|
||||
>
|
||||
<option v-for="preset in presetOptions" :key="preset.id" :value="preset.id">
|
||||
{{ preset.label }}
|
||||
</option>
|
||||
</AppSelect>
|
||||
</div>
|
||||
<AppButton
|
||||
variant="ghost"
|
||||
class="h-10 w-10 rounded-full p-0"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue