import { useState, useEffect, useMemo } from 'react' import { useParams, useNavigate } from 'react-router-dom' import api from '@/services/api' import type { Project, ProjectMember, Milestone } from '@/types' import dayjs from 'dayjs' import { useAuth } from '@/hooks/useAuth' import ProjectFormModal from '@/components/ProjectFormModal' import MilestoneFormModal from '@/components/MilestoneFormModal' import CopyableCode from '@/components/CopyableCode' export default function ProjectDetailPage() { const { id } = useParams() const navigate = useNavigate() const { user } = useAuth() const [project, setProject] = useState(null) const [members, setMembers] = useState([]) const [milestones, setMilestones] = useState([]) const [showAddMember, setShowAddMember] = useState(false) const [showMilestoneModal, setShowMilestoneModal] = useState(false) const [showProjectEdit, setShowProjectEdit] = useState(false) const [newMemberUserId, setNewMemberUserId] = useState(1) const [newMemberRole, setNewMemberRole] = useState('developer') const [users, setUsers] = useState([]) const [roles, setRoles] = useState([]) const fetchProject = () => { api.get(`/projects/${id}`).then(({ data }) => setProject(data)) api.get(`/projects/${id}/members`).then(({ data }) => setMembers(data)) api.get(`/projects/${id}/milestones`).then(({ data }) => setMilestones(data)) } useEffect(() => { fetchProject() api.get('/users').then(r => setUsers(r.data)).catch(() => {}) api.get('/roles').then(r => setRoles(r.data)).catch(() => {}) }, [id]) const currentMemberRole = useMemo( () => members.find((m) => m.user_id === user?.id)?.role, [members, user?.id] ) const canEditProject = Boolean(project && user && (user.is_admin || user.id === project.owner_id || currentMemberRole === 'admin')) const addMember = async () => { if (!newMemberUserId) return await api.post(`/projects/${id}/members`, { user_id: newMemberUserId, role: newMemberRole }) setShowAddMember(false) fetchProject() } const removeMember = async (userId: number, role: string) => { if (role === 'admin') { alert('Cannot remove project owner (admin)') return } if (!confirm('Remove this member?')) return await api.delete(`/projects/${id}/members/${userId}`) fetchProject() } const deleteProject = async () => { const confirmName = prompt(`Type the project name "${project?.name}" to confirm deletion:`) if (confirmName !== project?.name) { alert('Project name does not match. Deletion cancelled.') return } await api.delete(`/projects/${id}`) navigate('/projects') } if (!project) return
Loading...
return (

๐Ÿ“ {project.name} {project.project_code && }

{project.description || 'No description'}

{project.repo &&

๐Ÿ“ฆ {project.repo}

}
Owner: {project.owner_name || 'Unknown'}
{canEditProject && (
)}

Members ({members.length}) {canEditProject && }

{members.length > 0 ? (
{members.map((m) => ( {`${m.username || m.full_name || `User #${m.user_id}`} (${m.role})`} {canEditProject && ( )} ))}
) : (

No members

)}

Milestones ({milestones.length}) {canEditProject && }

{milestones.map((ms) => (
ms.milestone_code && navigate(`/milestones/${ms.milestone_code}`)}> {ms.status} {ms.title} {ms.due_date && ยท Due {dayjs(ms.due_date).format('YYYY-MM-DD')}}
))} {milestones.length === 0 &&

No milestones

}
setShowProjectEdit(false)} project={project} onSaved={(data) => { setProject(data) fetchProject() }} /> setShowMilestoneModal(false)} initialProjectCode={project.project_code || ''} lockProject onSaved={() => fetchProject()} /> {showAddMember && (
setShowAddMember(false)}>
e.stopPropagation()}>

Add Member

)}
) }