Fixes: - Go server now serves index.html for all non-asset paths (SPA fallback) - Null guard on data.documents prevents undefined.length TypeError - App.vue waits for checkStatus() before rendering AppLayout (race condition) - PWA manifest no longer references missing icon files - index.html applies theme class before paint to prevent flash Features: - Theme store: dark/light toggle + 6 accent color presets + custom hex picker - ThemeToggle component in header dropdown - CSS custom properties for accent color (--accent-400/500/600/700) - Tailwind accent-* color utilities driven by CSS vars - SetupWizard: 3-step flow, password confirmation, LDAP test button, animated toggle, review step, proper error/success states - Sidebar: loading skeleton, active page highlight, null-safe document list - Global .input, .btn-primary, .btn-secondary, .card component classes - Stub GraphQL responses for setup/login/testLdapConnection Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
84 lines
2.6 KiB
Vue
84 lines
2.6 KiB
Vue
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
import Sidebar from './Sidebar.vue'
|
|
import ThemeToggle from './ThemeToggle.vue'
|
|
import { useAppStore } from '@/stores/app'
|
|
|
|
const app = useAppStore()
|
|
const sidebarOpen = ref(false)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="flex h-screen overflow-hidden bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100">
|
|
|
|
<!-- Mobile overlay -->
|
|
<transition name="fade">
|
|
<div
|
|
v-if="sidebarOpen"
|
|
class="fixed inset-0 z-20 bg-black/50 backdrop-blur-sm lg:hidden"
|
|
@click="sidebarOpen = false"
|
|
/>
|
|
</transition>
|
|
|
|
<!-- Sidebar -->
|
|
<aside
|
|
:class="[
|
|
'fixed inset-y-0 left-0 z-30 w-64 flex-shrink-0',
|
|
'bg-slate-50 dark:bg-slate-800/50',
|
|
'border-r border-slate-200 dark:border-slate-700/60',
|
|
'backdrop-blur-md',
|
|
'transform transition-transform duration-250 ease-in-out',
|
|
'lg:static lg:translate-x-0',
|
|
sidebarOpen ? 'translate-x-0' : '-translate-x-full',
|
|
]"
|
|
>
|
|
<Sidebar @close="sidebarOpen = false" />
|
|
</aside>
|
|
|
|
<!-- Main area -->
|
|
<div class="flex flex-col flex-1 min-w-0 overflow-hidden">
|
|
|
|
<!-- Top bar (always visible on mobile, only shows breadcrumb on desktop) -->
|
|
<header class="flex items-center gap-2 px-4 py-3 border-b border-slate-200 dark:border-slate-700/60 bg-white/80 dark:bg-slate-900/80 backdrop-blur-md">
|
|
<!-- Hamburger (mobile only) -->
|
|
<button
|
|
class="btn-ghost p-2 -ml-2 lg:hidden"
|
|
aria-label="Open navigation"
|
|
@click="sidebarOpen = true"
|
|
>
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
|
|
</svg>
|
|
</button>
|
|
|
|
<span class="font-semibold text-slate-900 dark:text-white lg:hidden">Archivum</span>
|
|
|
|
<div class="flex-1" />
|
|
|
|
<!-- Right side controls -->
|
|
<ThemeToggle />
|
|
|
|
<div class="flex items-center gap-1 pl-2 border-l border-slate-200 dark:border-slate-700 ml-1">
|
|
<span class="text-sm text-slate-500 dark:text-slate-400 hidden sm:block">
|
|
{{ app.username ?? 'Guest' }}
|
|
</span>
|
|
<button
|
|
v-if="app.token"
|
|
class="btn-ghost text-xs"
|
|
@click="app.logout()"
|
|
>Sign out</button>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="flex-1 overflow-auto">
|
|
<router-view />
|
|
</main>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.fade-enter-active, .fade-leave-active { transition: opacity 0.2s; }
|
|
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
|
</style>
|