import { useState, useEffect, useMemo } from 'react' import { useParams, useNavigate } from 'react-router-dom' import api from '@/services/api' import type { Milestone, MilestoneProgress, Task, Project, ProjectMember } from '@/types' import dayjs from 'dayjs' import CreateTaskModal from '@/components/CreateTaskModal' import MilestoneFormModal from '@/components/MilestoneFormModal' import { useAuth } from '@/hooks/useAuth' interface MilestoneTask { id: number title: string description?: string status: string task_code?: string task_status?: string estimated_effort?: number estimated_working_time?: string started_on?: string finished_on?: string assignee_id?: number created_at: string } export default function MilestoneDetailPage() { const { id } = useParams() const navigate = useNavigate() const { user } = useAuth() const [milestone, setMilestone] = useState(null) const [project, setProject] = useState(null) const [members, setMembers] = useState([]) const [progress, setProgress] = useState(null) const [tasks, setTasks] = useState([]) const [supports, setSupports] = useState([]) const [meetings, setMeetings] = useState([]) const [activeTab, setActiveTab] = useState<'tasks' | 'supports' | 'meetings'>('tasks') const [showCreateTask, setShowCreateTask] = useState(false) const [showEditMilestone, setShowEditMilestone] = useState(false) const [showCreateSupport, setShowCreateSupport] = useState(false) const [showCreateMeeting, setShowCreateMeeting] = useState(false) const [newTitle, setNewTitle] = useState('') const [newDesc, setNewDesc] = useState('') const [projectCode, setProjectCode] = useState('') const fetchMilestone = () => { api.get(`/milestones/${id}`).then(({ data }) => { setMilestone(data) if (data.project_id) { api.get(`/projects/${data.project_id}`).then(({ data: proj }) => { setProject(proj) setProjectCode(proj.project_code || '') }) api.get(`/projects/${data.project_id}/members`).then(({ data }) => setMembers(data)).catch(() => {}) } }) api.get(`/milestones/${id}/progress`).then(({ data }) => setProgress(data)).catch(() => {}) } useEffect(() => { fetchMilestone() }, [id]) const refreshMilestoneItems = () => { if (!projectCode || !id) return api.get(`/tasks/${projectCode}/${id}`).then(({ data }) => setTasks(data)).catch(() => {}) api.get(`/supports/${projectCode}/${id}`).then(({ data }) => setSupports(data)).catch(() => {}) api.get(`/meetings/${projectCode}/${id}`).then(({ data }) => setMeetings(data)).catch(() => {}) } useEffect(() => { refreshMilestoneItems() }, [projectCode, id]) const createItem = async (type: 'supports' | 'meetings') => { if (!newTitle.trim() || !projectCode) return const payload = { title: newTitle, description: newDesc || null, } await api.post(`/${type}/${projectCode}/${id}`, payload) setNewTitle('') setNewDesc('') setShowCreateSupport(false) setShowCreateMeeting(false) refreshMilestoneItems() } const currentMemberRole = useMemo( () => members.find((m) => m.user_id === user?.id)?.role, [members, user?.id] ) const canEditMilestone = Boolean(milestone && project && user && ( user.is_admin || user.id === project.owner_id || user.id === milestone.created_by_id || currentMemberRole === 'admin' )) const isProgressing = milestone?.status === 'progressing' if (!milestone) return
Loading...
const renderTaskRow = (t: MilestoneTask) => ( navigate(`/tasks/${t.id}`)}> {t.task_code || t.id} {t.title} {t.task_status || t.status} {t.estimated_effort || '-'} {t.estimated_working_time || '-'} ) return (

🏁 {milestone.title}

{milestone.status} {milestone.due_date && Due {dayjs(milestone.due_date).format('YYYY-MM-DD')}} {milestone.planned_release_date && Planned Release: {dayjs(milestone.planned_release_date).format('YYYY-MM-DD')}}
{canEditMilestone && }
{milestone.description && (

Description

{milestone.description}

)} {progress && (

Progress (Tasks: {progress.completed}/{progress.total})

{progress.progress_pct.toFixed(0)}%
{progress.time_progress_pct !== null && ( <>

Time Progress

{progress.time_progress_pct.toFixed(0)}%
)}
)}
{!isProgressing && canEditMilestone && ( <> )} {isProgressing && Milestone is in progress - cannot add new items}
setShowEditMilestone(false)} milestone={milestone} lockProject onSaved={(data) => { setMilestone(data) fetchMilestone() }} /> setShowCreateTask(false)} initialProjectId={milestone.project_id} initialMilestoneId={milestone.id} lockProject lockMilestone onCreated={() => { setActiveTab('tasks') refreshMilestoneItems() }} /> {(showCreateSupport || showCreateMeeting) && (
setNewTitle(e.target.value)} style={{ marginBottom: 8 }} />