feat: Implement project management with new views, services, store, and sidebar project selection.

This commit is contained in:
xds
2026-02-09 16:06:50 +03:00
parent 70e96eb503
commit 1a8c66ca35
11 changed files with 915 additions and 97 deletions

View File

@@ -1,12 +1,60 @@
<script setup>
import { computed } from 'vue'
import { computed, onMounted, ref, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import Button from 'primevue/button'
import { useProjectsStore } from '@/stores/projectsStore'
import { storeToRefs } from 'pinia'
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
const projectsStore = useProjectsStore()
const { projects, currentProject } = storeToRefs(projectsStore)
const selectedProject = ref(null)
onMounted(async () => {
// Ensure we have projects
if (projects.value.length === 0) {
await projectsStore.fetchProjects()
}
// Sync local ref with store
if (currentProject.value) {
selectedProject.value = currentProject.value.id
}
})
// Watch for external changes (like selecting from the list view)
watch(currentProject, (newVal) => {
selectedProject.value = newVal ? newVal.id : null
})
const projectOptions = computed(() => {
return projects.value.map(p => ({ name: p.name, id: p.id }))
})
const getProjectName = (id) => {
const p = projects.value.find(p => p.id === id)
return p ? p.name : 'Unknown Project'
}
const isProjectMenuOpen = ref(false)
const selectProject = (projectId) => {
selectedProject.value = projectId
isProjectMenuOpen.value = false
if (projectId) {
projectsStore.selectProject(projectId)
} else {
// Clear selection
projectsStore.currentProject = null
localStorage.removeItem('active_project_id')
}
// Reload page to ensure all data is refreshed with new context
window.location.reload()
}
const handleLogout = () => {
authStore.logout()
@@ -23,9 +71,9 @@ const isActive = (path) => {
const navItems = computed(() => {
const items = [
{ path: '/', icon: '🏠', tooltip: 'Home' },
{ path: '/projects', icon: '📂', tooltip: 'Projects' },
{ path: '/flexible', icon: '🖌️', tooltip: 'Flexible Generation' },
{ path: '/albums', icon: '🖼️', tooltip: 'Albums' },
{ path: '/assets', icon: '📂', tooltip: 'Assets' },
{ path: '/albums', icon: '🖼️', tooltip: 'Library' },
{ path: '/characters', icon: '👥', tooltip: 'Characters' },
{ path: '/image-to-prompt', icon: '✨', tooltip: 'Image to Prompt' }
]
@@ -50,6 +98,54 @@ const navItems = computed(() => {
AI
</div>
<!-- Project Switcher -->
<div class="hidden lg:block ml-4 relative">
<button @click="isProjectMenuOpen = !isProjectMenuOpen"
class="flex items-center gap-2 px-3 py-1.5 rounded-lg hover:bg-white/5 transition-colors text-slate-400 hover:text-slate-200">
<i v-if="selectedProject" class="pi pi-folder text-violet-400"></i>
<i v-else class="pi pi-user"></i>
<span class="max-w-[150px] truncate font-medium">
{{ selectedProject ? getProjectName(selectedProject) : 'Personal Workspace' }}
</span>
<i class="pi pi-chevron-down text-xs ml-1 opacity-50"></i>
</button>
<!-- Custom Dropdown Menu -->
<div v-if="isProjectMenuOpen"
class="absolute top-full left-0 mt-2 w-56 bg-slate-900 border border-white/10 shadow-xl rounded-xl overflow-hidden z-50 py-1">
<!-- Personal Workspace Option -->
<div @click="selectProject(null)"
class="flex items-center gap-3 px-4 py-3 hover:bg-white/5 cursor-pointer transition-colors"
:class="{ 'text-violet-400 bg-white/5': !selectedProject, 'text-slate-300': selectedProject }">
<i class="pi pi-user"></i>
<span class="font-medium">Personal Workspace</span>
<i v-if="!selectedProject" class="pi pi-check ml-auto text-sm"></i>
</div>
<div class="h-px bg-white/5 my-1"></div>
<!-- Project Options -->
<div v-for="project in projects" :key="project.id" @click="selectProject(project.id)"
class="flex items-center gap-3 px-4 py-3 hover:bg-white/5 cursor-pointer transition-colors"
:class="{ 'text-violet-400 bg-white/5': selectedProject === project.id, 'text-slate-300': selectedProject !== project.id }">
<i class="pi pi-folder"></i>
<span class="truncate">{{ project.name }}</span>
<i v-if="selectedProject === project.id" class="pi pi-check ml-auto text-sm"></i>
</div>
<div v-if="projects.length === 0" class="px-4 py-3 text-slate-500 text-sm font-italic">
No projects found
</div>
</div>
<!-- Backdrop to close on click outside -->
<div v-if="isProjectMenuOpen" @click="isProjectMenuOpen = false"
class="fixed inset-0 z-40 bg-transparent"></div>
</div>
<!-- Nav Items -->
<div class="flex flex-row gap-2 items-center justify-center flex-1 mx-8">
<div v-for="item in navItems" :key="item.path" :class="[