Init Repo
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<div class="p-4 sm:p-6">
|
||||
<div class="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
||||
<!-- Project Stats -->
|
||||
<Card>
|
||||
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle class="text-sm font-medium">Total Shots</CardTitle>
|
||||
<Camera class="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-2xl font-bold">{{ project?.shot_count || 0 }}</div>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Across all episodes
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle class="text-sm font-medium">Assets</CardTitle>
|
||||
<Package class="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-2xl font-bold">{{ project?.asset_count || 0 }}</div>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Characters, props, sets
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||
<CardTitle class="text-sm font-medium">Team Members</CardTitle>
|
||||
<Users class="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-2xl font-bold">{{ project?.member_count || 0 }}</div>
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Active contributors
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<!-- Project Information -->
|
||||
<div class="mt-6 grid gap-6 md:grid-cols-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Project Details</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label class="text-sm font-medium text-muted-foreground">Code Name</Label>
|
||||
<p class="font-mono">{{ project?.code_name }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label class="text-sm font-medium text-muted-foreground">Client</Label>
|
||||
<p>{{ project?.client_name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label class="text-sm font-medium text-muted-foreground">Type</Label>
|
||||
<p>{{ formatProjectType(project?.project_type) }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<Label class="text-sm font-medium text-muted-foreground">Status</Label>
|
||||
<Badge :variant="getStatusVariant(project?.status)">
|
||||
{{ formatStatus(project?.status) }}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="project?.description">
|
||||
<Label class="text-sm font-medium text-muted-foreground">Description</Label>
|
||||
<p class="text-sm">{{ project.description }}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Timeline</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div v-if="project?.start_date">
|
||||
<Label class="text-sm font-medium text-muted-foreground">Start Date</Label>
|
||||
<p>{{ formatDate(project.start_date) }}</p>
|
||||
</div>
|
||||
<div v-if="project?.end_date">
|
||||
<Label class="text-sm font-medium text-muted-foreground">End Date</Label>
|
||||
<p>{{ formatDate(project.end_date) }}</p>
|
||||
</div>
|
||||
<div v-if="project?.start_date && project?.end_date">
|
||||
<Label class="text-sm font-medium text-muted-foreground">Duration</Label>
|
||||
<p>{{ calculateDuration(project.start_date, project.end_date) }}</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<!-- Technical Specifications Summary -->
|
||||
<div class="mt-6" v-if="project">
|
||||
<TechnicalSpecsSummary :project-id="project.id" />
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<Card class="mt-6">
|
||||
<CardHeader>
|
||||
<CardTitle>Recent Activity</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="text-center py-8 text-muted-foreground">
|
||||
<Activity class="h-8 w-8 mx-auto mb-2" />
|
||||
<p>Activity feed coming soon</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { Camera, Package, Users, Activity } from 'lucide-vue-next'
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Label } from '@/components/ui/label'
|
||||
import { useProjectsStore } from '@/stores/projects'
|
||||
import TechnicalSpecsSummary from '@/components/project/TechnicalSpecsSummary.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const projectsStore = useProjectsStore()
|
||||
|
||||
// Computed properties
|
||||
const projectId = computed(() => {
|
||||
const id = route.params.projectId
|
||||
return typeof id === 'string' ? parseInt(id) : null
|
||||
})
|
||||
|
||||
const project = computed(() => {
|
||||
if (!projectId.value) return null
|
||||
return projectsStore.getProjectById(projectId.value)
|
||||
})
|
||||
|
||||
// Methods
|
||||
const getStatusVariant = (status?: string) => {
|
||||
if (!status) return 'secondary'
|
||||
switch (status) {
|
||||
case 'planning': return 'secondary'
|
||||
case 'in_progress': return 'default'
|
||||
case 'on_hold': return 'outline'
|
||||
case 'completed': return 'success'
|
||||
case 'cancelled': return 'destructive'
|
||||
default: return 'secondary'
|
||||
}
|
||||
}
|
||||
|
||||
const formatStatus = (status?: string) => {
|
||||
if (!status) return ''
|
||||
return status.split('_').map(word =>
|
||||
word.charAt(0).toUpperCase() + word.slice(1)
|
||||
).join(' ')
|
||||
}
|
||||
|
||||
const formatProjectType = (type?: string) => {
|
||||
if (!type) return ''
|
||||
switch (type) {
|
||||
case 'tv': return 'TV Series'
|
||||
case 'cinema': return 'Cinema/Film'
|
||||
case 'game': return 'Game'
|
||||
default: return type
|
||||
}
|
||||
}
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})
|
||||
}
|
||||
|
||||
const calculateDuration = (startDate: string, endDate: string) => {
|
||||
const start = new Date(startDate)
|
||||
const end = new Date(endDate)
|
||||
const diffTime = Math.abs(end.getTime() - start.getTime())
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
||||
|
||||
if (diffDays < 30) {
|
||||
return `${diffDays} days`
|
||||
} else if (diffDays < 365) {
|
||||
const months = Math.floor(diffDays / 30)
|
||||
return `${months} month${months > 1 ? 's' : ''}`
|
||||
} else {
|
||||
const years = Math.floor(diffDays / 365)
|
||||
const remainingMonths = Math.floor((diffDays % 365) / 30)
|
||||
return `${years} year${years > 1 ? 's' : ''}${remainingMonths > 0 ? ` ${remainingMonths} month${remainingMonths > 1 ? 's' : ''}` : ''}`
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user