feat: unify project milestone and task editing with modals
This commit is contained in:
@@ -1,18 +1,37 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useState, useEffect, useMemo } from 'react'
|
||||
import { useParams, useNavigate } from 'react-router-dom'
|
||||
import api from '@/services/api'
|
||||
import type { Task, Comment } from '@/types'
|
||||
import type { Task, Comment, Project, ProjectMember, Milestone } from '@/types'
|
||||
import dayjs from 'dayjs'
|
||||
import { useAuth } from '@/hooks/useAuth'
|
||||
import CreateTaskModal from '@/components/CreateTaskModal'
|
||||
|
||||
export default function TaskDetailPage() {
|
||||
const { id } = useParams()
|
||||
const navigate = useNavigate()
|
||||
const { user } = useAuth()
|
||||
const [task, setTask] = useState<Task | null>(null)
|
||||
const [milestone, setMilestone] = useState<Milestone | null>(null)
|
||||
const [project, setProject] = useState<Project | null>(null)
|
||||
const [members, setMembers] = useState<ProjectMember[]>([])
|
||||
const [comments, setComments] = useState<Comment[]>([])
|
||||
const [newComment, setNewComment] = useState('')
|
||||
const [showEditTask, setShowEditTask] = useState(false)
|
||||
|
||||
const refreshTask = async () => {
|
||||
const { data } = await api.get<Task>(`/tasks/${id}`)
|
||||
setTask(data)
|
||||
if (data.project_id) {
|
||||
api.get<Project>(`/projects/${data.project_id}`).then(({ data }) => setProject(data)).catch(() => {})
|
||||
api.get<ProjectMember[]>(`/projects/${data.project_id}/members`).then(({ data }) => setMembers(data)).catch(() => {})
|
||||
}
|
||||
if (data.milestone_id) {
|
||||
api.get<Milestone>(`/milestones/${data.milestone_id}`).then(({ data }) => setMilestone(data)).catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
api.get<Task>(`/tasks/${id}`).then(({ data }) => setTask(data))
|
||||
refreshTask().catch(console.error)
|
||||
api.get<Comment[]>(`/tasks/${id}/comments`).then(({ data }) => setComments(data))
|
||||
}, [id])
|
||||
|
||||
@@ -26,17 +45,27 @@ export default function TaskDetailPage() {
|
||||
|
||||
const transition = async (newStatus: string) => {
|
||||
await api.post(`/tasks/${id}/transition?new_status=${newStatus}`)
|
||||
const { data } = await api.get<Task>(`/tasks/${id}`)
|
||||
setTask(data)
|
||||
await refreshTask()
|
||||
}
|
||||
|
||||
const currentMemberRole = useMemo(
|
||||
() => members.find((m) => m.user_id === user?.id)?.role,
|
||||
[members, user?.id]
|
||||
)
|
||||
const canEditTask = Boolean(task && project && user && (
|
||||
user.is_admin ||
|
||||
user.id === project.owner_id ||
|
||||
user.id === task.created_by_id ||
|
||||
user.id === milestone?.created_by_id ||
|
||||
currentMemberRole === 'admin'
|
||||
))
|
||||
|
||||
if (!task) return <div className="loading">Loading...</div>
|
||||
|
||||
const statusActions: Record<string, string[]> = {
|
||||
open: ['in_progress', 'blocked'],
|
||||
in_progress: ['resolved', 'blocked'],
|
||||
blocked: ['open', 'in_progress'],
|
||||
resolved: ['closed', 'open'],
|
||||
open: ['progressing', 'closed'],
|
||||
pending: ['progressing', 'closed'],
|
||||
progressing: ['pending', 'closed'],
|
||||
closed: ['open'],
|
||||
}
|
||||
|
||||
@@ -52,8 +81,16 @@ 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 && <button className="btn-transition" style={{ marginTop: 8 }} onClick={() => setShowEditTask(true)}>Edit Task</button>}
|
||||
</div>
|
||||
|
||||
<CreateTaskModal
|
||||
isOpen={showEditTask}
|
||||
onClose={() => setShowEditTask(false)}
|
||||
task={task}
|
||||
onSaved={(data) => setTask(data)}
|
||||
/>
|
||||
|
||||
<div className="task-body">
|
||||
<div className="section">
|
||||
<h3>Description</h3>
|
||||
@@ -64,8 +101,9 @@ export default function TaskDetailPage() {
|
||||
<h3>Details</h3>
|
||||
<dl>
|
||||
<dt>Created</dt><dd>{dayjs(task.created_at).format('YYYY-MM-DD HH:mm')}</dd>
|
||||
{task.due_date && <><dt>Due date</dt><dd>{dayjs(task.due_date).format('YYYY-MM-DD')}</dd></>}
|
||||
{task.updated_at && <><dt>Updated</dt><dd>{dayjs(task.updated_at).format('YYYY-MM-DD HH:mm')}</dd></>}
|
||||
{project && <><dt>Project</dt><dd>{project.name}</dd></>}
|
||||
{milestone && <><dt>Milestone</dt><dd>{milestone.title}</dd></>}
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user