Refactor: Extract sidebar navigation from individual views into a new AppSidebar component.
This commit is contained in:
@@ -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' }"
|
||||
|
||||
Reference in New Issue
Block a user