'use client'; import { useCallback, useEffect, useState } from 'react'; import { useParams, useRouter } from 'next/navigation'; import { api } from '@/lib/api'; import { cn } from '@/lib/cn'; import type { Mission, Project, Task, TaskStatus } from '@/lib/types'; import { MissionTimeline } from '@/components/projects/mission-timeline'; import { PrdViewer } from '@/components/projects/prd-viewer'; import { TaskDetailModal } from '@/components/tasks/task-detail-modal'; import { TaskListView } from '@/components/tasks/task-list-view'; import { TaskStatusSummary } from '@/components/tasks/task-status-summary'; type Tab = 'overview' | 'tasks' | 'missions' | 'prd'; const statusColors: Record = { active: 'bg-success/20 text-success', paused: 'bg-warning/20 text-warning', completed: 'bg-blue-600/20 text-blue-400', archived: 'bg-gray-600/20 text-gray-400', }; interface TabButtonProps { id: Tab; label: string; activeTab: Tab; onClick: (tab: Tab) => void; } function TabButton({ id, label, activeTab, onClick }: TabButtonProps): React.ReactElement { return ( ); } export default function ProjectDetailPage(): React.ReactElement { const params = useParams(); const router = useRouter(); const id = typeof params['id'] === 'string' ? params['id'] : ''; const [project, setProject] = useState(null); const [missions, setMissions] = useState([]); const [tasks, setTasks] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [activeTab, setActiveTab] = useState('overview'); const [taskFilter, setTaskFilter] = useState('all'); const [selectedTask, setSelectedTask] = useState(null); useEffect(() => { if (!id) return; setLoading(true); setError(null); Promise.all([ api(`/api/projects/${id}`), api('/api/missions').catch(() => [] as Mission[]), api(`/api/tasks?projectId=${id}`).catch(() => [] as Task[]), ]) .then(([proj, allMissions, tks]) => { setProject(proj); setMissions(allMissions.filter((m) => m.projectId === id)); setTasks(tks); }) .catch((err: Error) => { setError(err.message ?? 'Failed to load project'); }) .finally(() => setLoading(false)); }, [id]); const handleTaskClick = useCallback((task: Task) => { setSelectedTask(task); }, []); const handleCloseTaskModal = useCallback(() => { setSelectedTask(null); }, []); if (loading) { return (

Loading project...

); } if (error || !project) { return (

{error ?? 'Project not found'}

); } const filteredTasks = taskFilter === 'all' ? tasks : tasks.filter((t) => t.status === taskFilter); const prdContent = getPrdContent(project); const hasPrd = Boolean(prdContent); const tabs: { id: Tab; label: string }[] = [ { id: 'overview', label: 'Overview' }, { id: 'tasks', label: `Tasks (${tasks.length})` }, { id: 'missions', label: `Missions (${missions.length})` }, ...(hasPrd ? [{ id: 'prd' as Tab, label: 'PRD' }] : []), ]; return (
{/* Breadcrumb */} {/* Project header */}

{project.name}

{project.status}
{project.description && (

{project.description}

)}

Created {new Date(project.createdAt).toLocaleDateString()} ยท Updated{' '} {new Date(project.updatedAt).toLocaleDateString()}

{/* Stats bar */}
t.status === 'done').length)} valueClass="text-success" /> t.status === 'in-progress').length)} valueClass="text-blue-400" /> t.status === 'blocked').length)} valueClass={tasks.some((t) => t.status === 'blocked') ? 'text-error' : undefined} />
{/* Tabs */}
{tabs.map((tab) => ( ))}
{/* Tab content */} {activeTab === 'overview' && ( )} {activeTab === 'tasks' && (
)} {activeTab === 'missions' && } {activeTab === 'prd' && prdContent && (
)} {/* Task detail modal */} {selectedTask && }
); } interface OverviewTabProps { project: Project; missions: Mission[]; tasks: Task[]; } function OverviewTab({ project, missions, tasks }: OverviewTabProps): React.ReactElement { const recentTasks = [...tasks] .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()) .slice(0, 5); return (
{/* Recent tasks */}

Recent Tasks

{recentTasks.length === 0 ? (

No tasks yet

) : (
{recentTasks.map((task) => ( ))}
)}
{/* Mission summary */}

Missions

{missions.length === 0 ? (

No missions yet

) : ( )}
{/* Metadata */} {project.metadata && Object.keys(project.metadata).length > 0 && (

Project Metadata

              {JSON.stringify(project.metadata, null, 2)}
            
)}
); } const taskStatusColors: Record = { 'not-started': 'bg-gray-600/20 text-gray-300', 'in-progress': 'bg-blue-600/20 text-blue-400', blocked: 'bg-error/20 text-error', done: 'bg-success/20 text-success', cancelled: 'bg-gray-600/20 text-gray-500', }; function TaskSummaryRow({ task }: { task: Task }): React.ReactElement { return (
{task.title} {task.status}
); } function StatCard({ label, value, valueClass, }: { label: string; value: string; valueClass?: string; }): React.ReactElement { return (

{label}

{value}

); } function getPrdContent(project: Project): string | null { if (!project.metadata) return null; const prd = project.metadata['prd']; if (typeof prd === 'string' && prd.trim().length > 0) return prd; const prdContent = project.metadata['prdContent']; if (typeof prdContent === 'string' && prdContent.trim().length > 0) return prdContent; return null; }