'use client'; import { useEffect, useState } from 'react'; import { useParams } from 'next/navigation'; import { ArrowLeft, Calendar, CheckCircle2, Circle, Flag } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Progress } from '@/components/ui/progress'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import Link from 'next/link'; interface Project { id: string; name: string; description?: string; status: 'active' | 'paused' | 'archived'; domain: string; progress: number; task_count: number; completed_count: number; due_date?: string; } interface Task { id: string; title: string; status: 'todo' | 'in_progress' | 'done'; priority: 'low' | 'medium' | 'high' | 'urgent'; due_date?: string; } interface Milestone { id: string; name: string; description?: string; due_date?: string; status: 'planned' | 'in_progress' | 'completed'; completed_tasks: number; total_tasks: number; } export default function ProjectDetailPage() { const params = useParams(); const projectId = params.id as string; const [project, setProject] = useState(null); const [tasks, setTasks] = useState([]); const [milestones, setMilestones] = useState([]); const [loading, setLoading] = useState(true); const [domainMap, setDomainMap] = useState>(new Map()); useEffect(() => { if (projectId) { fetchDomains(); fetchProject(); fetchTasks(); fetchMilestones(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [projectId]); async function fetchDomains() { try { const res = await fetch('/api/domains?sort=sort_order'); if (res.ok) { const data = await res.json(); const map = new Map(); for (const d of data.items || []) map.set(d.id, d.name); setDomainMap(map); } } catch {} } async function fetchProject() { try { const response = await fetch(`/api/projects/${projectId}`); if (response.ok) { const data = await response.json(); setProject(data); } } catch (error) { console.error('Failed to fetch project:', error); } } async function fetchTasks() { try { const response = await fetch( `/api/tasks?filter=project_id%3D%22${projectId}%22&sort=-created` ); if (response.ok) { const data = await response.json(); setTasks(data.items || []); } } catch (error) { console.error('Failed to fetch tasks:', error); } finally { setLoading(false); } } async function fetchMilestones() { try { const response = await fetch( `/api/milestones?filter=project_id%3D%22${projectId}%22&sort=due_date` ); if (response.ok) { const data = await response.json(); setMilestones(data.items || []); } } catch (error) { console.error('Failed to fetch milestones:', error); } } async function toggleTaskComplete(taskId: string, currentStatus: string) { const newStatus = currentStatus === 'done' ? 'todo' : 'done'; try { await fetch(`/api/tasks/${taskId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status: newStatus }), }); fetchTasks(); fetchProject(); } catch (error) { console.error('Failed to toggle task:', error); } } if (loading || !project) { return

Loading project...

; } return (
{/* Back button */} {/* Project header */}

{project.name}

{project.description && (

{project.description}

)}
{project.status} {domainMap.get(project.domain) || project.domain}
{/* Project stats */}

Progress

{project.progress}%

Tasks

{project.completed_count} / {project.task_count}

Due Date

{project.due_date ? new Date(project.due_date).toLocaleDateString() : 'No date'}

{/* Tabs */} Tasks ({tasks.length}) Milestones ({milestones.length}) Habits Notes Project Tasks {tasks.length === 0 ? (

No tasks yet

) : (
{tasks.map((task) => (

{task.title}

{task.priority} {task.due_date && ( {new Date(task.due_date).toLocaleDateString()} )}
))}
)}
Milestones {milestones.length === 0 ? (

No milestones yet

) : (
{milestones.map((milestone, index) => (
{/* Timeline line */} {index < milestones.length - 1 && (
)} {/* Milestone marker */}
{/* Milestone content */}

{milestone.name}

{milestone.description && (

{milestone.description}

)}
{milestone.status}
{milestone.due_date && (

Due:{' '} {new Date( milestone.due_date ).toLocaleDateString()}

)}
Tasks {milestone.completed_tasks} /{' '} {milestone.total_tasks}
0 ? (milestone.completed_tasks / milestone.total_tasks) * 100 : 0 } className="h-1.5" aria-label={`${milestone.name} task progress: ${milestone.completed_tasks} of ${milestone.total_tasks}`} />
))}
)} Habits linked to this project will appear here Notes linked to this project will appear here
); }