feat(propose): P10.7 edit modal for open proposes — title+description editable, hidden for accepted/rejected

This commit is contained in:
zhi
2026-03-17 14:05:31 +00:00
parent 2897172213
commit 208538f930

View File

@@ -17,6 +17,12 @@ export default function ProposeDetailPage() {
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<Propose>(`/projects/${projectId}/proposes/${id}`).then(({ data }) => setPropose(data))
@@ -62,6 +68,32 @@ export default function ProposeDetailPage() {
}
}
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)
@@ -122,6 +154,12 @@ export default function ProposeDetailPage() {
<div className="section" style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
{propose.status === 'open' && (
<>
<button
className="btn-transition"
onClick={openEditModal}
>
Edit
</button>
<button
className="btn-primary"
disabled={actionLoading}
@@ -154,6 +192,43 @@ export default function ProposeDetailPage() {
)}
</div>
{/* Edit modal (P10.7 — only reachable when open) */}
{showEdit && (
<div className="modal-overlay" onClick={() => setShowEdit(false)}>
<div className="modal" onClick={(e) => e.stopPropagation()}>
<h3>Edit Propose</h3>
<label style={{ display: 'block', marginBottom: 8 }}>
<strong>Title</strong>
<input
type="text"
value={editTitle}
onChange={(e) => setEditTitle(e.target.value)}
style={{ width: '100%', marginTop: 4 }}
/>
</label>
<label style={{ display: 'block', marginBottom: 8 }}>
<strong>Description</strong>
<textarea
value={editDescription}
onChange={(e) => setEditDescription(e.target.value)}
rows={6}
style={{ width: '100%', marginTop: 4 }}
/>
</label>
<div style={{ marginTop: 12, display: 'flex', gap: 8 }}>
<button
className="btn-primary"
onClick={handleEdit}
disabled={!editTitle.trim() || editLoading}
>
{editLoading ? 'Saving...' : 'Save'}
</button>
<button className="btn-back" onClick={() => setShowEdit(false)}>Cancel</button>
</div>
</div>
</div>
)}
{/* Accept modal with milestone selector */}
{showAccept && (
<div className="modal-overlay" onClick={() => setShowAccept(false)}>