From 2bdf08007f207f78cc9e3ca24d9ff67b664978db Mon Sep 17 00:00:00 2001 From: Rajat yadav Date: Fri, 28 Nov 2025 23:32:50 +0530 Subject: [PATCH] feat: make due field editable Added due field editing functionality to task details dialog. Users can now set and edit task due dates using a DatePicker. --- backend/controllers/edit_task.go | 3 +- backend/models/request_body.go | 1 + backend/utils/tw/edit_task.go | 12 +- backend/utils/tw/taskwarrior_test.go | 8 +- .../components/HomeComponents/Tasks/Tasks.tsx | 144 ++++++++++++++++-- .../components/HomeComponents/Tasks/hooks.ts | 3 + 6 files changed, 154 insertions(+), 17 deletions(-) diff --git a/backend/controllers/edit_task.go b/backend/controllers/edit_task.go index 8647ee04..c7ca747d 100644 --- a/backend/controllers/edit_task.go +++ b/backend/controllers/edit_task.go @@ -51,6 +51,7 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) { wait := requestBody.Wait end := requestBody.End depends := requestBody.Depends + due := requestBody.Due if taskID == "" { http.Error(w, "taskID is required", http.StatusBadRequest) @@ -62,7 +63,7 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) { Name: "Edit Task", Execute: func() error { logStore.AddLog("INFO", fmt.Sprintf("Editing task ID: %s", taskID), uuid, "Edit Task") - err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID, tags, project, start, entry, wait, end, depends) + err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID, tags, project, start, entry, wait, end, depends, due) if err != nil { logStore.AddLog("ERROR", fmt.Sprintf("Failed to edit task ID %s: %v", taskID, err), uuid, "Edit Task") return err diff --git a/backend/models/request_body.go b/backend/models/request_body.go index c6f1ceba..8e1d97ea 100644 --- a/backend/models/request_body.go +++ b/backend/models/request_body.go @@ -36,6 +36,7 @@ type EditTaskRequestBody struct { Wait string `json:"wait"` End string `json:"end"` Depends []string `json:"depends"` + Due string `json:"due"` } type CompleteTaskRequestBody struct { Email string `json:"email"` diff --git a/backend/utils/tw/edit_task.go b/backend/utils/tw/edit_task.go index 63c74aa8..fb7667f9 100644 --- a/backend/utils/tw/edit_task.go +++ b/backend/utils/tw/edit_task.go @@ -7,7 +7,7 @@ import ( "strings" ) -func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID string, tags []string, project string, start string, entry string, wait string, end string, depends []string) error { +func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID string, tags []string, project string, start string, entry string, wait string, end string, depends []string, due string) error { if err := utils.ExecCommand("rm", "-rf", "/root/.task"); err != nil { return fmt.Errorf("error deleting Taskwarrior data: %v", err) } @@ -102,6 +102,16 @@ func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID st } } + // Handle due date + if due != "" { + // Convert `2025-11-29` -> `2025-11-29T00:00:00` + formattedDue := due + "T00:00:00" + + if err := utils.ExecCommand("task", taskID, "modify", "due:"+formattedDue); err != nil { + return fmt.Errorf("failed to set due date %s: %v", formattedDue, err) + } + } + // Sync Taskwarrior again if err := SyncTaskwarrior(tempDir); err != nil { return err diff --git a/backend/utils/tw/taskwarrior_test.go b/backend/utils/tw/taskwarrior_test.go index fe8b0b63..c8be55fb 100644 --- a/backend/utils/tw/taskwarrior_test.go +++ b/backend/utils/tw/taskwarrior_test.go @@ -23,7 +23,7 @@ func TestSyncTaskwarrior(t *testing.T) { } func TestEditTaskInATaskwarrior(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", nil, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil) + err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", nil, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z") if err != nil { t.Errorf("EditTaskInTaskwarrior() failed: %v", err) } else { @@ -68,7 +68,7 @@ func TestAddTaskWithTags(t *testing.T) { } func TestEditTaskWithTagAddition(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "+important"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil) + err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "+important"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z") if err != nil { t.Errorf("EditTaskInTaskwarrior with tag addition failed: %v", err) } else { @@ -77,7 +77,7 @@ func TestEditTaskWithTagAddition(t *testing.T) { } func TestEditTaskWithTagRemoval(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"-work", "-lowpriority"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil) + err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"-work", "-lowpriority"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z") if err != nil { t.Errorf("EditTaskInTaskwarrior with tag removal failed: %v", err) } else { @@ -86,7 +86,7 @@ func TestEditTaskWithTagRemoval(t *testing.T) { } func TestEditTaskWithMixedTagOperations(t *testing.T) { - err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "-work", "normal"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil) + err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "-work", "normal"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z", "2025-11-30T18:30:00.000Z", nil, "2025-12-01T18:30:00.000Z") if err != nil { t.Errorf("EditTaskInTaskwarrior with mixed tag operations failed: %v", err) } else { diff --git a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx index d0b0f31f..5140c5c5 100644 --- a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx +++ b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx @@ -130,6 +130,8 @@ export const Tasks = ( const [editedEntryDate, setEditedEntryDate] = useState(''); const [isEditingEndDate, setIsEditingEndDate] = useState(false); const [editedEndDate, setEditedEndDate] = useState(''); + const [isEditingDueDate, setIsEditingDueDate] = useState(false); + const [editedDueDate, setEditedDueDate] = useState(''); const [isEditingDepends, setIsEditingDepends] = useState(false); const [editedDepends, setEditedDepends] = useState([]); const [dependsDropdownOpen, setDependsDropdownOpen] = useState(false); @@ -376,7 +378,8 @@ export const Tasks = ( entry: string, wait: string, end: string, - depends: string[] + depends: string[], + due: string ) { try { await editTaskOnBackend({ @@ -393,6 +396,7 @@ export const Tasks = ( wait, end, depends, + due, }); console.log('Task edited successfully!'); @@ -439,7 +443,8 @@ export const Tasks = ( task.entry || '', task.wait || '', task.end || '', - task.depends || [] + task.depends || [], + task.due || '' ); setIsEditing(false); }; @@ -458,7 +463,8 @@ export const Tasks = ( task.entry || '', task.wait || '', task.end || '', - task.depends || [] + task.depends || [], + task.due || '' ); setIsEditingProject(false); }; @@ -478,7 +484,8 @@ export const Tasks = ( task.entry || '', task.wait, task.end || '', - task.depends || [] + task.depends || [], + task.due || '' ); setIsEditingWaitDate(false); @@ -499,7 +506,8 @@ export const Tasks = ( task.entry || '', task.wait || '', task.end || '', - task.depends || [] + task.depends || [], + task.due || '' ); setIsEditingStartDate(false); @@ -520,7 +528,8 @@ export const Tasks = ( task.entry, task.wait, task.end, - task.depends || [] + task.depends || [], + task.due || '' ); setIsEditingEntryDate(false); @@ -541,12 +550,35 @@ export const Tasks = ( task.entry, task.wait, task.end, - task.depends || [] + task.depends || [], + task.due || '' ); setIsEditingEndDate(false); }; + const handleDueDateSaveClick = (task: Task) => { + task.due = editedDueDate; + + handleEditTaskOnBackend( + props.email, + props.encryptionSecret, + props.UUID, + task.description, + task.tags, + task.id.toString(), + task.project, + task.start, + task.entry, + task.wait, + task.end, + task.depends || [], + task.due + ); + + setIsEditingDueDate(false); + }; + const handleDependsSaveClick = (task: Task) => { task.depends = editedDepends; @@ -562,7 +594,8 @@ export const Tasks = ( task.entry || '', task.wait || '', task.end || '', - task.depends + task.depends, + task.due || '' ); setIsEditingDepends(false); @@ -598,6 +631,8 @@ export const Tasks = ( setEditedEntryDate(''); setIsEditingEndDate(false); setEditedEndDate(''); + setIsEditingDueDate(false); + setEditedDueDate(''); setIsEditingDepends(false); setEditedDepends([]); setDependsDropdownOpen(false); @@ -720,7 +755,8 @@ export const Tasks = ( task.entry || '', task.wait || '', task.end || '', - task.depends || [] + task.depends || [], + task.due || '' ); setIsEditingTags(false); @@ -1200,7 +1236,9 @@ export const Tasks = ( {/* Display task details */} @@ -1341,7 +1379,91 @@ export const Tasks = ( Due: - {formattedDate(task.due)} + {isEditingDueDate ? ( +
+ { + try { + const dateStr = + editedDueDate.includes( + 'T' + ) + ? editedDueDate.split( + 'T' + )[0] + : editedDueDate; + const parsed = + new Date( + dateStr + + 'T00:00:00' + ); + return isNaN( + parsed.getTime() + ) + ? undefined + : parsed; + } catch { + return undefined; + } + })() + : undefined + } + onDateChange={(date) => + setEditedDueDate( + date + ? format( + date, + 'yyyy-MM-dd' + ) + : '' + ) + } + placeholder="Select due date" + /> + + +
+ ) : ( + <> + + {formattedDate(task.due)} + + + + )}
diff --git a/frontend/src/components/HomeComponents/Tasks/hooks.ts b/frontend/src/components/HomeComponents/Tasks/hooks.ts index 11034d51..f0931f96 100644 --- a/frontend/src/components/HomeComponents/Tasks/hooks.ts +++ b/frontend/src/components/HomeComponents/Tasks/hooks.ts @@ -94,6 +94,7 @@ export const editTaskOnBackend = async ({ wait, end, depends, + due, }: { email: string; encryptionSecret: string; @@ -108,6 +109,7 @@ export const editTaskOnBackend = async ({ wait: string; end: string; depends: string[]; + due: string; }) => { const response = await fetch(`${backendURL}edit-task`, { method: 'POST', @@ -124,6 +126,7 @@ export const editTaskOnBackend = async ({ wait, end, depends, + due, }), headers: { 'Content-Type': 'application/json',