diff --git a/backend/controllers/edit_task.go b/backend/controllers/edit_task.go index 3c55c509..c0122ab3 100644 --- a/backend/controllers/edit_task.go +++ b/backend/controllers/edit_task.go @@ -49,6 +49,7 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) { start := requestBody.Start entry := requestBody.Entry wait := requestBody.Wait + end := requestBody.End if taskID == "" { http.Error(w, "taskID is required", http.StatusBadRequest) @@ -60,7 +61,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) + err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID, tags, project, start, entry, wait, end) 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 87edaf02..2744249d 100644 --- a/backend/models/request_body.go +++ b/backend/models/request_body.go @@ -34,6 +34,7 @@ type EditTaskRequestBody struct { Start string `json:"start"` Entry string `json:"entry"` Wait string `json:"wait"` + End string `json:"end"` } type CompleteTaskRequestBody struct { Email string `json:"email"` diff --git a/backend/utils/tw/edit_task.go b/backend/utils/tw/edit_task.go index 5e361663..1e40cfad 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) error { +func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID string, tags []string, project string, start string, entry string, wait string, end string) error { if err := utils.ExecCommand("rm", "-rf", "/root/.task"); err != nil { return fmt.Errorf("error deleting Taskwarrior data: %v", err) } @@ -87,6 +87,13 @@ func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID st } } + // Handle end date + if end != "" { + if err := utils.ExecCommand("task", taskID, "modify", "end:"+end); err != nil { + return fmt.Errorf("failed to set end date %s: %v", end, 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 e4d378b1..cf55f6a6 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") + 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") 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") + 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") 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") + 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") 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") + 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") 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 125e7fa9..995674ae 100644 --- a/frontend/src/components/HomeComponents/Tasks/Tasks.tsx +++ b/frontend/src/components/HomeComponents/Tasks/Tasks.tsx @@ -124,6 +124,8 @@ export const Tasks = ( const [editedStartDate, setEditedStartDate] = useState(''); const [isEditingEntryDate, setIsEditingEntryDate] = useState(false); const [editedEntryDate, setEditedEntryDate] = useState(''); + const [isEditingEndDate, setIsEditingEndDate] = useState(false); + const [editedEndDate, setEditedEndDate] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [lastSyncTime, setLastSyncTime] = useState(null); @@ -327,7 +329,8 @@ export const Tasks = ( project: string, start: string, entry: string, - wait: string + wait: string, + end: string ) { try { await editTaskOnBackend({ @@ -342,6 +345,7 @@ export const Tasks = ( start, entry, wait, + end, }); console.log('Task edited successfully!'); @@ -387,7 +391,8 @@ export const Tasks = ( task.project, task.start, task.entry || '', - task.wait + task.wait || '', + task.end || '' ); setIsEditing(false); }; @@ -404,7 +409,8 @@ export const Tasks = ( task.project, task.start, task.entry || '', - task.wait + task.wait || '', + task.end || '' ); setIsEditingProject(false); }; @@ -422,7 +428,8 @@ export const Tasks = ( task.project, task.start, task.entry || '', - task.wait + task.wait, + task.end || '' ); setIsEditingWaitDate(false); @@ -441,7 +448,8 @@ export const Tasks = ( task.project, task.start, task.entry || '', - task.wait + task.wait || '', + task.end || '' ); setIsEditingStartDate(false); @@ -460,12 +468,33 @@ export const Tasks = ( task.project, task.start, task.entry, - task.wait + task.wait, + task.end ); setIsEditingEntryDate(false); }; + const handleEndDateSaveClick = (task: Task) => { + task.end = editedEndDate; + + handleEditTaskOnBackend( + props.email, + props.encryptionSecret, + props.UUID, + task.description, + task.tags, + task.id.toString(), + task.project, + task.start, + task.entry, + task.wait, + task.end + ); + + setIsEditingEndDate(false); + }; + const handleCancelClick = () => { setIsEditing(false); }; @@ -483,6 +512,8 @@ export const Tasks = ( setEditedStartDate(''); setIsEditingEntryDate(false); setEditedEntryDate(''); + setIsEditingEndDate(false); + setEditedEndDate(''); } else { setSelectedTask(task); setEditedDescription(task?.description || ''); @@ -557,7 +588,8 @@ export const Tasks = ( task.project, task.start, task.entry || '', - task.wait + task.wait || '', + task.end || '' ); setIsEditingTags(false); // Exit editing mode @@ -1176,7 +1208,91 @@ export const Tasks = ( End: - {formattedDate(task.end)} + {isEditingEndDate ? ( +
+ { + try { + const dateStr = + editedEndDate.includes( + 'T' + ) + ? editedEndDate.split( + 'T' + )[0] + : editedEndDate; + const parsed = + new Date( + dateStr + + 'T00:00:00' + ); + return isNaN( + parsed.getTime() + ) + ? undefined + : parsed; + } catch { + return undefined; + } + })() + : undefined + } + onDateChange={(date) => + setEditedEndDate( + date + ? format( + date, + 'yyyy-MM-dd' + ) + : '' + ) + } + placeholder="Select end date" + /> + + +
+ ) : ( +
+ + {formattedDate(task.end)} + + +
+ )}
diff --git a/frontend/src/components/HomeComponents/Tasks/hooks.ts b/frontend/src/components/HomeComponents/Tasks/hooks.ts index 26f9f346..06601fae 100644 --- a/frontend/src/components/HomeComponents/Tasks/hooks.ts +++ b/frontend/src/components/HomeComponents/Tasks/hooks.ts @@ -92,6 +92,7 @@ export const editTaskOnBackend = async ({ start, entry, wait, + end, }: { email: string; encryptionSecret: string; @@ -104,6 +105,7 @@ export const editTaskOnBackend = async ({ start: string; entry: string; wait: string; + end: string; }) => { const response = await fetch(`${backendURL}edit-task`, { method: 'POST', @@ -118,6 +120,7 @@ export const editTaskOnBackend = async ({ start, entry, wait, + end, }), headers: { 'Content-Type': 'application/json',