Files
Archivum/frontend/src/components/layout/AppLayout.vue
brasse b d93f3bb888 fix+feat: SPA routing, theme system, modern UI, setup wizard improvements
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>
2026-04-10 13:40:42 +02:00

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>