feat: milestone/task status UI + propose pages + action buttons #8

Merged
hzhang merged 11 commits from feat/milestone-propose-state-machine into main 2026-03-19 11:12:03 +00:00
Showing only changes of commit 638427db65 - Show all commits

View File

@@ -88,13 +88,27 @@ export default function TaskDetailPage() {
() => members.find((m) => m.user_id === user?.id)?.role,
[members, user?.id]
)
const canEditTask = Boolean(task && project && user && (
const isAdmin = Boolean(user && (
user.is_admin ||
user.id === project.owner_id ||
user.id === task.created_by_id ||
user.id === milestone?.created_by_id ||
(project && user.id === project.owner_id) ||
currentMemberRole === 'admin'
))
// P5.7/P9.3: assignee-aware edit permission
const canEditTask = Boolean(task && project && user && (() => {
const st = task.status
// undergoing/completed/closed: no body edits
if (st === 'undergoing' || st === 'completed' || st === 'closed') return false
// open + assignee set: only assignee or admin
if (st === 'open' && task.assignee_id != null) {
return user.id === task.assignee_id || isAdmin
}
// open + no assignee, or pending: general permission
return (
isAdmin ||
user.id === task.created_by_id ||
user.id === milestone?.created_by_id
)
})())
if (!task) return <div className="loading">Loading...</div>
@@ -113,7 +127,7 @@ export default function TaskDetailPage() {
<span className="badge">{task.task_type}</span>{task.task_subtype && <span className="badge">{task.task_subtype}</span>}
{task.tags && <span className="tags">{task.tags}</span>}
</div>
{canEditTask && !isTerminal && task.status !== 'undergoing' && (
{canEditTask && (
<button className="btn-transition" style={{ marginTop: 8 }} onClick={() => setShowEditTask(true)}>Edit Task</button>
)}
</div>