Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions e2e/tests/lock-due-date.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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"]');
Expand All @@ -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"]');
Expand Down
35 changes: 32 additions & 3 deletions frontend/src/components/TaskDetailSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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("")
Expand Down Expand Up @@ -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)

Expand All @@ -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 || "")
Expand Down Expand Up @@ -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])

Expand All @@ -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) {
Expand All @@ -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,
Expand Down Expand Up @@ -512,6 +525,22 @@ export function TaskDetailSheet({ task, open, onOpenChange, onUpdated, hasClient
</div>
</div>

{/* Project */}
<div className="grid gap-2">
<Label>Project</Label>
{isClient ? (
<p className="text-sm text-muted-foreground py-1">{project || "None"}</p>
) : (
<LinkField
doctype="Hive Project"
value={project}
onChange={handleProjectChange}
placeholder="Select project"
className="w-full"
/>
)}
</div>

{/* Milestone */}
<div className="grid gap-2">
<Label>Milestone</Label>
Expand Down
Loading