Refactor: Extract sidebar navigation from individual views into a new AppSidebar component.

This commit is contained in:
xds
2026-02-07 15:04:12 +03:00
parent 7f8ce19cb1
commit 1b9fddd209
8 changed files with 774 additions and 948 deletions

View File

@@ -249,10 +249,7 @@ onMounted(() => {
})
// --- Sidebar Logic (Duplicated for now) ---
const handleLogout = () => {
localStorage.removeItem('auth_code')
router.push('/login')
}
// handleLogout removed - handled in AppSidebar
// Image Preview
const isImagePreviewVisible = ref(false)
@@ -330,56 +327,32 @@ const clearPrompt = () => {
previousPrompt.value = ''
}
const deleteGeneration = async (gen) => {
if (!gen) return
try {
// Optimistic update
historyGenerations.value = historyGenerations.value.filter(g => g.id !== gen.id)
historyTotal.value--
// Use deleteAsset since generations are essentially assets in this view's context,
// or we need a way to delete the generation record.
// The user said "delete asset", and these are likely linked.
// If gen has a result list, we delete the first asset.
if (gen.result_list && gen.result_list.length > 0) {
await dataService.deleteAsset(gen.result_list[0])
}
} catch (e) {
console.error('Failed to delete generation', e)
// Reload to restore state if failed
loadData()
}
}
</script>
<template>
<div class="flex h-screen bg-slate-900 overflow-hidden text-slate-100 font-sans">
<!-- Sidebar -->
<nav
class="glass-panel w-20 m-4 flex flex-col items-center py-6 rounded-3xl z-10 border border-white/5 bg-slate-900/50 backdrop-blur-md">
<div class="mb-12">
<div
class="w-10 h-10 bg-gradient-to-br from-violet-600 to-cyan-500 rounded-xl flex items-center justify-center font-bold text-white text-xl shadow-lg shadow-violet-500/20">
AI
</div>
</div>
<div class="flex-1 flex flex-col gap-6 w-full items-center">
<div class="w-12 h-12 flex items-center justify-center rounded-xl cursor-pointer transition-all duration-300 text-slate-400 hover:bg-white/10 hover:text-slate-50"
@click="router.push('/')" v-tooltip.right="'Home'">
<span class="text-2xl">🏠</span>
</div>
<div class="w-12 h-12 flex items-center justify-center rounded-xl cursor-pointer transition-all duration-300 text-slate-400 hover:bg-white/10 hover:text-slate-50"
@click="router.push('/assets')" v-tooltip.right="'Assets'">
<span class="text-2xl">📂</span>
</div>
<!-- Image Generation -->
<div class="w-12 h-12 flex items-center justify-center rounded-xl cursor-pointer transition-all duration-300 text-slate-400 hover:bg-white/10 hover:text-slate-50"
@click="router.push('/generation')" v-tooltip.right="'Image Generation'">
<span class="text-2xl">🎨</span>
</div>
<!-- Active State for Flexible Generation -->
<div class="w-12 h-12 flex items-center justify-center rounded-xl cursor-pointer transition-all duration-300 bg-white/10 text-slate-50 shadow-inner"
v-tooltip.right="'Flexible Generation'">
<span class="text-2xl">🖌</span>
</div>
<div class="w-12 h-12 flex items-center justify-center rounded-xl cursor-pointer transition-all duration-300 text-slate-400 hover:bg-white/10 hover:text-slate-50"
@click="router.push('/characters')" v-tooltip.right="'Characters'">
<span class="text-2xl">👥</span>
</div>
<div class="w-12 h-12 flex items-center justify-center rounded-xl cursor-pointer transition-all duration-300 text-slate-400 hover:bg-white/10 hover:text-slate-50"
@click="router.push('/image-to-prompt')" v-tooltip.right="'Image to Prompt'">
<span class="text-2xl"></span>
</div>
</div>
<div class="mt-auto">
<div @click="handleLogout"
class="w-10 h-10 rounded-xl bg-red-500/10 text-red-400 flex items-center justify-center cursor-pointer hover:bg-red-500/20 transition-all font-bold">
<i class="pi pi-power-off"></i>
</div>
</div>
</nav>
<div class="flex flex-col h-full font-sans">
<!-- Sidebar and Mobile Nav removed (handled by App.vue) -->
<!-- Main Content -->
<main class="flex-1 relative flex flex-col h-full overflow-hidden">
@@ -397,10 +370,10 @@ const clearPrompt = () => {
</header>
<!-- Gallery Grid -->
<div class="flex-1 overflow-y-auto p-4 pb-32"> <!-- pb-32 to allow space for bottom panel -->
<div class="flex-1 overflow-y-auto p-4 pb-32 md:pb-32"> <!-- pb-32 to allow space for bottom panel -->
<div v-if="historyGenerations.length > 0"
class="grid grid-cols-3 md:grid-cols-5 lg:grid-cols-6 xl:grid-cols-8 gap-1">
class="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 xl:grid-cols-8 gap-2 md:gap-1">
<div v-for="gen in historyGenerations" :key="gen.id"
class="aspect-[9/16] relative group overflow-hidden bg-slate-800 transition-all duration-300">
@@ -419,7 +392,11 @@ const clearPrompt = () => {
<!-- Top Actions -->
<div
class="flex justify-end gap-1 translate-y-[-10px] group-hover:translate-y-0 transition-transform duration-200">
class="flex justify-between items-start translate-y-[-10px] group-hover:translate-y-0 transition-transform duration-200">
<Button icon="pi pi-trash" v-tooltip.right="'Delete'"
class="!w-6 !h-6 !rounded-full !bg-red-500/20 !border-none !text-red-400 text-[10px] hover:!bg-red-500 hover:!text-white"
@click.stop="deleteGeneration(gen)" />
<Button icon="pi pi-pencil" v-tooltip.left="'Edit (Use Result)'"
class="!w-6 !h-6 !rounded-full !bg-white/20 !border-none !text-white text-[10px] hover:!bg-violet-500"
@click.stop="useResultAsAsset(gen)" />
@@ -451,7 +428,7 @@ const clearPrompt = () => {
<!-- Bottom Settings Panel -->
<transition name="slide-up">
<div v-if="isSettingsVisible"
class="absolute bottom-0 left-0 right-0 glass-panel border-t border-white/10 bg-slate-900/95 backdrop-blur-xl p-6 z-20 rounded-t-3xl shadow-[0_-10px_40px_rgba(0,0,0,0.5)] flex flex-col gap-4 max-h-[85vh] overflow-y-auto">
class="absolute bottom-0 left-0 right-0 glass-panel border-t border-white/10 bg-slate-900/95 backdrop-blur-xl p-6 z-[60] rounded-t-3xl shadow-[0_-10px_40px_rgba(0,0,0,0.5)] flex flex-col gap-4 max-h-[85vh] overflow-y-auto">
<!-- Handle / Close Button -->
<div class="w-full flex justify-center -mt-2 mb-2 cursor-pointer"
@@ -483,10 +460,11 @@ const clearPrompt = () => {
<Textarea v-model="prompt" rows="3" autoResize
placeholder="Describe what you want to create..."
class="w-full bg-slate-800 border-white/10 text-white rounded-xl p-3 focus:border-violet-500 focus:ring-1 focus:ring-violet-500/50 transition-all resize-none shadow-inner" />
</div>
<!-- Assets & Character Row -->
<div class="flex gap-4">
<div class="flex flex-col md:flex-row gap-4">
<div class="flex-1 flex flex-col gap-2">
<label class="text-xs font-bold text-slate-400 uppercase tracking-wider">Character
(Optional)</label>
@@ -544,7 +522,8 @@ const clearPrompt = () => {
<div class="flex items-center gap-2">
<img :src="API_URL + slotProps.option.url + '?thumbnail=true'"
class="w-8 h-8 rounded object-cover border border-white/10" />
<span class="text-xs truncate max-w-[150px]">{{ slotProps.option.name ||
<span class="text-xs truncate max-w-[150px]">{{
slotProps.option.name ||
'Asset ' + slotProps.option.id.substring(0, 4) }}</span>
</div>
</template>
@@ -611,7 +590,7 @@ const clearPrompt = () => {
<!-- Toggle Button (when hidden) -->
<transition name="fade">
<div v-if="!isSettingsVisible" class="absolute bottom-8 left-1/2 -translate-x-1/2 z-10">
<div v-if="!isSettingsVisible" class="absolute bottom-24 md:bottom-8 left-1/2 -translate-x-1/2 z-10">
<Button label="Open Controls" icon="pi pi-chevron-up" @click="isSettingsVisible = true" rounded
class="!bg-violet-600 !border-none !shadow-xl !font-bold shadow-violet-500/40 !px-6 !py-3" />
</div>
@@ -619,6 +598,8 @@ const clearPrompt = () => {
</main>
<!-- Image Preview Modal -->
<Dialog v-model:visible="isImagePreviewVisible" modal dismissableMask
:style="{ width: '90vw', maxWidth: '1000px', background: 'transparent', boxShadow: 'none' }"