feat: Implement project management with new views, services, store, and sidebar project selection.
This commit is contained in:
@@ -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="[
|
||||
|
||||
Reference in New Issue
Block a user