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>
This commit is contained in:
@@ -1,18 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { gql } from '@/lib/gql'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
|
||||
const emit = defineEmits<{ close: [] }>()
|
||||
|
||||
interface DocMeta { slug: string; title: string }
|
||||
|
||||
const docs = ref<DocMeta[]>([])
|
||||
const loading = ref(true)
|
||||
const app = useAppStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await gql<{ documents: DocMeta[] }>(`{ documents { slug title } }`)
|
||||
docs.value = data.documents
|
||||
try {
|
||||
const data = await gql<{ documents?: DocMeta[] }>(`{ documents { slug title } }`)
|
||||
docs.value = data.documents ?? []
|
||||
} catch {
|
||||
docs.value = []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
function navigate(slug: string) {
|
||||
@@ -22,38 +32,81 @@ function navigate(slug: string) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between px-4 py-3 border-b border-slate-700">
|
||||
<router-link to="/" class="font-bold text-lg" @click="emit('close')">Archivum</router-link>
|
||||
<button class="lg:hidden p-1 rounded hover:bg-surface-700" @click="emit('close')">
|
||||
<div class="flex flex-col h-full select-none">
|
||||
|
||||
<!-- Logo / header -->
|
||||
<div class="flex items-center justify-between px-4 py-4 border-b border-slate-200 dark:border-slate-700/60">
|
||||
<router-link
|
||||
to="/"
|
||||
class="flex items-center gap-2 font-bold text-slate-900 dark:text-white text-lg"
|
||||
@click="emit('close')"
|
||||
>
|
||||
<!-- Book icon -->
|
||||
<svg class="w-6 h-6 text-accent-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
|
||||
</svg>
|
||||
Archivum
|
||||
</router-link>
|
||||
<button
|
||||
class="btn-ghost p-1.5 lg:hidden"
|
||||
@click="emit('close')"
|
||||
>
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Search placeholder -->
|
||||
<div class="px-3 py-3 border-b border-slate-200 dark:border-slate-700/60">
|
||||
<div class="flex items-center gap-2 px-3 py-2 rounded-lg bg-slate-100 dark:bg-slate-800 text-slate-400 text-sm cursor-not-allowed">
|
||||
<svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
||||
</svg>
|
||||
<span>Search…</span>
|
||||
<kbd class="ml-auto text-xs bg-slate-200 dark:bg-slate-700 rounded px-1.5 py-0.5">⌘K</kbd>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Document tree -->
|
||||
<nav class="flex-1 overflow-y-auto py-2">
|
||||
<nav class="flex-1 overflow-y-auto py-2 px-2">
|
||||
<div v-if="loading" class="space-y-1 px-2 mt-1">
|
||||
<div v-for="i in 4" :key="i" class="h-7 rounded-md bg-slate-200 dark:bg-slate-700 animate-pulse" :style="{ width: `${60 + i * 8}%` }" />
|
||||
</div>
|
||||
|
||||
<p v-else-if="docs.length === 0" class="text-xs text-slate-400 px-3 py-2">
|
||||
No documents yet.
|
||||
</p>
|
||||
|
||||
<button
|
||||
v-for="doc in docs"
|
||||
:key="doc.slug"
|
||||
class="w-full text-left px-4 py-2 text-sm hover:bg-surface-700 truncate"
|
||||
:class="[
|
||||
'w-full text-left px-3 py-2 rounded-lg text-sm truncate transition',
|
||||
route.params.slug === doc.slug
|
||||
? 'bg-accent-500/10 text-accent-600 dark:text-accent-400 font-medium'
|
||||
: 'text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700/60'
|
||||
]"
|
||||
@click="navigate(doc.slug)"
|
||||
>
|
||||
{{ doc.title }}
|
||||
{{ doc.title || doc.slug }}
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<!-- New document button -->
|
||||
<div class="p-3 border-t border-slate-700">
|
||||
<!-- New document -->
|
||||
<div class="p-3 border-t border-slate-200 dark:border-slate-700/60">
|
||||
<router-link
|
||||
to="/doc/new"
|
||||
class="block w-full text-center py-2 rounded bg-blue-700 hover:bg-blue-600 text-sm font-medium"
|
||||
class="btn-primary w-full text-center text-sm py-2"
|
||||
@click="emit('close')"
|
||||
>
|
||||
+ New document
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
|
||||
</svg>
|
||||
New document
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user