Init Repo
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
|
||||
<!-- Project Header -->
|
||||
<div class="p-0 sm:p-0 pb-0" v-if="project">
|
||||
<!-- <div class="flex flex-col sm:flex-row sm:items-start justify-between gap-4 mb-4"> -->
|
||||
<!-- <div class="flex-1 min-w-0">
|
||||
<h1 class="text-2xl sm:text-3xl font-bold truncate">{{ project.name }}</h1>
|
||||
<p class="text-muted-foreground text-sm sm:text-base mt-1">{{ project.description }}</p>
|
||||
</div> -->
|
||||
<!-- <div class="flex items-center gap-2 flex-shrink-0">
|
||||
<Badge :variant="getStatusVariant(project.status)">
|
||||
{{ formatStatus(project.status) }}
|
||||
</Badge>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="sm">
|
||||
<MoreHorizontal class="h-4 w-4" />
|
||||
<span class="sr-only">Project actions</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem @click="editProject">
|
||||
<Edit class="h-4 w-4 mr-2" />
|
||||
Edit Project
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="manageTechnicalSpecs">
|
||||
<Settings class="h-4 w-4 mr-2" />
|
||||
Technical Specs
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem @click="manageMembers">
|
||||
<Users class="h-4 w-4 mr-2" />
|
||||
Manage Members
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div> -->
|
||||
<!-- </div> -->
|
||||
</div>
|
||||
|
||||
<!-- Project Tabs -->
|
||||
<div class="px-0 sm:px-0 pb-0 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60" v-if="project">
|
||||
<ProjectTabs
|
||||
:project-id="project.id"
|
||||
:shot-count="project.shot_count"
|
||||
:asset-count="project.asset_count"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Tab Content Area -->
|
||||
<div class="flex-1 overflow-auto bg-muted/30">
|
||||
<router-view :key="route.fullPath" />
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div v-if="isLoading" class="flex items-center justify-center py-12">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="animate-spin rounded-full h-6 w-6 border-b-2 border-primary"></div>
|
||||
<span class="text-muted-foreground">Loading project...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error State -->
|
||||
<div v-else-if="error" class="text-center py-12">
|
||||
<AlertCircle class="h-12 w-12 mx-auto text-destructive mb-4" />
|
||||
<h3 class="text-lg font-semibold mb-2">Failed to load project</h3>
|
||||
<p class="text-muted-foreground mb-4">{{ error }}</p>
|
||||
<Button @click="loadProject" variant="outline">
|
||||
<RefreshCw class="h-4 w-4 mr-2" />
|
||||
Try Again
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import {
|
||||
MoreHorizontal, Edit, Settings, Users,
|
||||
AlertCircle, RefreshCw
|
||||
} from 'lucide-vue-next'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import {
|
||||
DropdownMenu, DropdownMenuContent, DropdownMenuItem,
|
||||
DropdownMenuTrigger
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { useProjectsStore } from '@/stores/projects'
|
||||
import ProjectTabs from '@/components/project/ProjectTabs.vue'
|
||||
import type { Project } from '@/stores/projects'
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const projectsStore = useProjectsStore()
|
||||
|
||||
// Reactive state
|
||||
const project = ref<Project | null>(null)
|
||||
const isLoading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
// Computed properties
|
||||
const projectId = computed(() => {
|
||||
const id = route.params.projectId
|
||||
return typeof id === 'string' ? parseInt(id) : null
|
||||
})
|
||||
|
||||
|
||||
|
||||
// Methods
|
||||
const loadProject = async () => {
|
||||
if (!projectId.value) return
|
||||
|
||||
try {
|
||||
isLoading.value = true
|
||||
error.value = null
|
||||
|
||||
// Try to get project from store first
|
||||
let projectData = projectsStore.getProjectById(projectId.value)
|
||||
|
||||
if (!projectData) {
|
||||
// If not in store, fetch from API
|
||||
projectData = await projectsStore.getProject(projectId.value, true)
|
||||
}
|
||||
|
||||
project.value = projectData
|
||||
|
||||
// Set as active project in store
|
||||
if (projectData) {
|
||||
projectsStore.setActiveProject(projectData)
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : 'Failed to load project'
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const editProject = () => {
|
||||
router.push('/projects')
|
||||
}
|
||||
|
||||
const manageTechnicalSpecs = () => {
|
||||
if (projectId.value) {
|
||||
router.push(`/projects/${projectId.value}/technical-specs`)
|
||||
}
|
||||
}
|
||||
|
||||
const manageMembers = () => {
|
||||
// TODO: Implement member management
|
||||
console.log('Manage members')
|
||||
}
|
||||
|
||||
const getStatusVariant = (status: string) => {
|
||||
switch (status) {
|
||||
case 'planning': return 'secondary'
|
||||
case 'in_progress': return 'default'
|
||||
case 'on_hold': return 'outline'
|
||||
case 'completed': return 'default'
|
||||
case 'cancelled': return 'destructive'
|
||||
default: return 'secondary'
|
||||
}
|
||||
}
|
||||
|
||||
const formatStatus = (status: string) => {
|
||||
return status.split('_').map(word =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1)
|
||||
).join(' ')
|
||||
}
|
||||
|
||||
// Watchers
|
||||
watch(projectId, (newId) => {
|
||||
if (newId) {
|
||||
loadProject()
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// Lifecycle
|
||||
onMounted(() => {
|
||||
if (projectId.value) {
|
||||
loadProject()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user