import { useState, useEffect } from 'react' import { useParams, useNavigate, useSearchParams } from 'react-router-dom' import api from '@/services/api' import type { Propose, Milestone } from '@/types' import dayjs from 'dayjs' export default function ProposeDetailPage() { const { id } = useParams() const [searchParams] = useSearchParams() const projectId = searchParams.get('project_id') const navigate = useNavigate() const [propose, setPropose] = useState(null) const [milestones, setMilestones] = useState([]) const [showAccept, setShowAccept] = useState(false) const [selectedMilestone, setSelectedMilestone] = useState('') const [actionLoading, setActionLoading] = useState(false) const [error, setError] = useState('') // Edit state (P10.7) const [showEdit, setShowEdit] = useState(false) const [editTitle, setEditTitle] = useState('') const [editDescription, setEditDescription] = useState('') const [editLoading, setEditLoading] = useState(false) const fetchPropose = () => { if (!projectId) return api.get(`/projects/${projectId}/proposes/${id}`).then(({ data }) => setPropose(data)) } useEffect(() => { fetchPropose() }, [id, projectId]) const loadMilestones = () => { if (!projectId) return api.get(`/milestones?project_id=${projectId}`) .then(({ data }) => setMilestones(data.filter((m) => m.status === 'open'))) } const handleAccept = async () => { if (!selectedMilestone || !projectId) return setActionLoading(true) setError('') try { await api.post(`/projects/${projectId}/proposes/${id}/accept`, { milestone_id: selectedMilestone }) setShowAccept(false) fetchPropose() } catch (err: any) { setError(err.response?.data?.detail || 'Accept failed') } finally { setActionLoading(false) } } const handleReject = async () => { if (!projectId) return const reason = prompt('Reject reason (optional):') setActionLoading(true) setError('') try { await api.post(`/projects/${projectId}/proposes/${id}/reject`, { reason: reason || undefined }) fetchPropose() } catch (err: any) { setError(err.response?.data?.detail || 'Reject failed') } finally { setActionLoading(false) } } const openEditModal = () => { if (!propose) return setEditTitle(propose.title) setEditDescription(propose.description || '') setError('') setShowEdit(true) } const handleEdit = async () => { if (!projectId) return setEditLoading(true) setError('') try { await api.patch(`/projects/${projectId}/proposes/${id}`, { title: editTitle, description: editDescription, }) setShowEdit(false) fetchPropose() } catch (err: any) { setError(err.response?.data?.detail || 'Update failed') } finally { setEditLoading(false) } } const handleReopen = async () => { if (!projectId) return setActionLoading(true) setError('') try { await api.post(`/projects/${projectId}/proposes/${id}/reopen`) fetchPropose() } catch (err: any) { setError(err.response?.data?.detail || 'Reopen failed') } finally { setActionLoading(false) } } if (!propose) return
Loading...
const statusBadgeClass = (s: string) => { if (s === 'open') return 'status-open' if (s === 'accepted') return 'status-completed' if (s === 'rejected') return 'status-closed' return '' } return (

💡 {propose.title} {propose.propose_code && {propose.propose_code}}

{propose.status}
{error &&
{error}
}

Details

Propose Code: {propose.propose_code || '—'}
Status: {propose.status}
Created By: User #{propose.created_by_id || '—'}
Created: {dayjs(propose.created_at).format('YYYY-MM-DD HH:mm')}
Updated: {propose.updated_at ? dayjs(propose.updated_at).format('YYYY-MM-DD HH:mm') : '—'}
Feature Task: {propose.feat_task_id || '—'}

Description

{propose.description || 'No description'}

{/* Action buttons */}
{propose.status === 'open' && ( <> )} {propose.status === 'accepted' && propose.feat_task_id && ( )} {propose.status === 'rejected' && ( )}
{/* Edit modal (P10.7 — only reachable when open) */} {showEdit && (
setShowEdit(false)}>
e.stopPropagation()}>

Edit Propose