diff --git a/e2e/tests/lock-due-date.spec.ts b/e2e/tests/lock-due-date.spec.ts index b7f6959..f927f67 100644 --- a/e2e/tests/lock-due-date.spec.ts +++ b/e2e/tests/lock-due-date.spec.ts @@ -167,7 +167,7 @@ test.describe("Lock Due Date Config", () => { const sheet = await openTaskSheet(page, pastDueTask.title); // Due date should be rendered as plain text (not a button/DatePicker) - const dueDateLabel = sheet.locator(".grid.gap-2").filter({ hasText: "Due Date" }); + const dueDateLabel = sheet.locator(".grid.gap-2").filter({ has: page.getByText("Due Date", { exact: true }) }); const lockedText = dueDateLabel.locator("p.text-sm.text-muted-foreground"); await expect(lockedText).toBeVisible(); await expect(lockedText).toHaveAttribute( @@ -187,7 +187,7 @@ test.describe("Lock Due Date Config", () => { const sheet = await openTaskSheet(page, futureDueTask.title); // Due date should be rendered as a DatePicker (button) - const dueDateLabel = sheet.locator(".grid.gap-2").filter({ hasText: "Due Date" }); + const dueDateLabel = sheet.locator(".grid.gap-2").filter({ has: page.getByText("Due Date", { exact: true }) }); // Should NOT have the locked text with title attribute const lockedText = dueDateLabel.locator('p[title="Due date is locked on or after the due date"]'); @@ -211,7 +211,7 @@ test.describe("Lock Due Date Config", () => { const sheet = await openTaskSheet(page, pastDueTask.title); // Even though due date is in the past, it should be editable because the setting is OFF - const dueDateLabel = sheet.locator(".grid.gap-2").filter({ hasText: "Due Date" }); + const dueDateLabel = sheet.locator(".grid.gap-2").filter({ has: page.getByText("Due Date", { exact: true }) }); // Should NOT have the locked title const lockedText = dueDateLabel.locator('p[title="Due date is locked on or after the due date"]'); diff --git a/frontend/src/components/TaskDetailSheet.tsx b/frontend/src/components/TaskDetailSheet.tsx index 604bfa9..dce6c8e 100644 --- a/frontend/src/components/TaskDetailSheet.tsx +++ b/frontend/src/components/TaskDetailSheet.tsx @@ -92,6 +92,7 @@ export function TaskDetailSheet({ task, open, onOpenChange, onUpdated, hasClient const [status, setStatus] = useState("Backlog") const [priority, setPriority] = useState("Medium") const [size, setSize] = useState("") + const [project, setProject] = useState("") const [milestone, setMilestone] = useState("") const [dependsOn, setDependsOn] = useState("") const [prLink, setPrLink] = useState("") @@ -181,8 +182,8 @@ export function TaskDetailSheet({ task, open, onOpenChange, onUpdated, hasClient }) }, [allMembers, user?.email]) - const milestoneFilters = useMemo(() => ({ project: task?.project }), [task?.project]) - const dependsOnFilters = useMemo(() => ({ project: task?.project, is_archived: 0 }), [task?.project]) + const milestoneFilters = useMemo(() => ({ project }), [project]) + const dependsOnFilters = useMemo(() => ({ project, is_archived: 0 }), [project]) const [assigneePopoverOpen, setAssigneePopoverOpen] = useState(false) @@ -197,6 +198,7 @@ export function TaskDetailSheet({ task, open, onOpenChange, onUpdated, hasClient setStatus(task.status) setPriority(task.priority) setSize(task.size || "") + setProject(task.project || "") setMilestone(task.milestone || "") setDependsOn(task.depends_on || "") setPrLink(task.pr_link || "") @@ -258,7 +260,7 @@ export function TaskDetailSheet({ task, open, onOpenChange, onUpdated, hasClient autosaveTimerRef.current = undefined } } - }, [title, description, status, priority, size, milestone, dependsOn, prLink, dueDate, startDate, completedOn, recurrenceFrequency, recurrenceEndDate, open, isClient]) + }, [title, description, status, priority, size, project, milestone, dependsOn, prLink, dueDate, startDate, completedOn, recurrenceFrequency, recurrenceEndDate, open, isClient]) const assignedMemberNames = useMemo(() => new Set(assignees.map((a) => a.member)), [assignees]) @@ -282,6 +284,16 @@ export function TaskDetailSheet({ task, open, onOpenChange, onUpdated, hasClient } } + const handleProjectChange = (newProject: string) => { + if (!newProject || newProject === project) return + setProject(newProject) + // Milestone and Depends On belong to the previous project — clear them so + // the task isn't left pointing at records from a different project. + setMilestone("") + setDependsOn("") + markEdited() + } + const handleSave = async (silent = false) => { if (saving || !title.trim()) return if (autosaveTimerRef.current) { @@ -297,6 +309,7 @@ export function TaskDetailSheet({ task, open, onOpenChange, onUpdated, hasClient status, priority, size: size || null, + project, milestone: milestone || null, depends_on: dependsOn || null, pr_link: prLink || null, @@ -512,6 +525,22 @@ export function TaskDetailSheet({ task, open, onOpenChange, onUpdated, hasClient + {/* Project */} +
+ + {isClient ? ( +

{project || "None"}

+ ) : ( + + )} +
+ {/* Milestone */}