diff --git a/backend/controllers/add_task.go b/backend/controllers/add_task.go index e44aa727..1d0b2c4e 100644 --- a/backend/controllers/add_task.go +++ b/backend/controllers/add_task.go @@ -66,18 +66,44 @@ func AddTaskHandler(w http.ResponseWriter, r *http.Request) { http.Error(w, fmt.Sprintf("Invalid dependencies: %v", err), http.StatusBadRequest) return } + + // Convert all date fields to Taskwarrior format dueDateStr, err := utils.ConvertOptionalISOToTaskwarriorFormat(dueDate) if err != nil { http.Error(w, fmt.Sprintf("Invalid due date format: %v", err), http.StatusBadRequest) return } + startStr, err := utils.ConvertISOToTaskwarriorFormat(start) + if err != nil { + http.Error(w, fmt.Sprintf("Invalid start date format: %v", err), http.StatusBadRequest) + return + } + + entryDateStr, err := utils.ConvertISOToTaskwarriorFormat(entryDate) + if err != nil { + http.Error(w, fmt.Sprintf("Invalid entry date format: %v", err), http.StatusBadRequest) + return + } + + waitDateStr, err := utils.ConvertISOToTaskwarriorFormat(waitDate) + if err != nil { + http.Error(w, fmt.Sprintf("Invalid wait date format: %v", err), http.StatusBadRequest) + return + } + + endStr, err := utils.ConvertISOToTaskwarriorFormat(end) + if err != nil { + http.Error(w, fmt.Sprintf("Invalid end date format: %v", err), http.StatusBadRequest) + return + } + logStore := models.GetLogStore() job := Job{ Name: "Add Task", Execute: func() error { logStore.AddLog("INFO", fmt.Sprintf("Adding task: %s", description), uuid, "Add Task") - err := tw.AddTaskToTaskwarrior(email, encryptionSecret, uuid, description, project, priority, dueDateStr, start, entryDate, waitDate, end, recur, tags, annotations, depends) + err := tw.AddTaskToTaskwarrior(email, encryptionSecret, uuid, description, project, priority, dueDateStr, startStr, entryDateStr, waitDateStr, endStr, recur, tags, annotations, depends) if err != nil { logStore.AddLog("ERROR", fmt.Sprintf("Failed to add task: %v", err), uuid, "Add Task") return err diff --git a/backend/controllers/edit_task.go b/backend/controllers/edit_task.go index f22cff37..1f769082 100644 --- a/backend/controllers/edit_task.go +++ b/backend/controllers/edit_task.go @@ -67,12 +67,43 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) { return } + // Convert all date fields to Taskwarrior format + startStr, err := utils.ConvertISOToTaskwarriorFormat(start) + if err != nil { + http.Error(w, fmt.Sprintf("Invalid start date format: %v", err), http.StatusBadRequest) + return + } + + entryStr, err := utils.ConvertISOToTaskwarriorFormat(entry) + if err != nil { + http.Error(w, fmt.Sprintf("Invalid entry date format: %v", err), http.StatusBadRequest) + return + } + + waitStr, err := utils.ConvertISOToTaskwarriorFormat(wait) + if err != nil { + http.Error(w, fmt.Sprintf("Invalid wait date format: %v", err), http.StatusBadRequest) + return + } + + endStr, err := utils.ConvertISOToTaskwarriorFormat(end) + if err != nil { + http.Error(w, fmt.Sprintf("Invalid end date format: %v", err), http.StatusBadRequest) + return + } + + dueStr, err := utils.ConvertISOToTaskwarriorFormat(due) + if err != nil { + http.Error(w, fmt.Sprintf("Invalid due date format: %v", err), http.StatusBadRequest) + return + } + logStore := models.GetLogStore() job := Job{ Name: "Edit Task", Execute: func() error { logStore.AddLog("INFO", fmt.Sprintf("Editing task ID: %s", taskUUID), uuid, "Edit Task") - err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskUUID, tags, project, start, entry, wait, end, depends, due, recur, annotations) + err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskUUID, tags, project, startStr, entryStr, waitStr, endStr, depends, dueStr, recur, annotations) if err != nil { logStore.AddLog("ERROR", fmt.Sprintf("Failed to edit task ID %s: %v", taskUUID, err), uuid, "Edit Task") return err diff --git a/backend/utils/tw/edit_task.go b/backend/utils/tw/edit_task.go index cf185ff8..605e989a 100644 --- a/backend/utils/tw/edit_task.go +++ b/backend/utils/tw/edit_task.go @@ -42,11 +42,8 @@ func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID st // Handle wait date if wait != "" { - // Convert `2025-11-29` -> `2025-11-29T00:00:00` - formattedWait := wait + "T00:00:00" - - if err := utils.ExecCommand("task", taskID, "modify", "wait:"+formattedWait); err != nil { - return fmt.Errorf("failed to set wait date %s: %v", formattedWait, err) + if err := utils.ExecCommand("task", taskID, "modify", "wait:"+wait); err != nil { + return fmt.Errorf("failed to set wait date %s: %v", wait, err) } } @@ -103,11 +100,8 @@ 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) + if err := utils.ExecCommand("task", taskID, "modify", "due:"+due); err != nil { + return fmt.Errorf("failed to set due date %s: %v", due, err) } } diff --git a/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx b/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx index 485f6c5b..04859157 100644 --- a/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx +++ b/frontend/src/components/HomeComponents/Tasks/AddTaskDialog.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; -import { DatePicker } from '@/components/ui/date-picker'; + import { DateTimePicker } from '@/components/ui/date-time-picker'; import { Dialog, @@ -285,15 +285,27 @@ export const AddTaskdialog = ({ Start
- { + { setNewTask({ ...newTask, - start: date ? format(date, 'yyyy-MM-dd') : '', + start: date + ? hasTime + ? date.toISOString() + : format(date, 'yyyy-MM-dd') + : '', }); }} - placeholder="Select a start date" + placeholder="Select start date and time" />
@@ -302,15 +314,27 @@ export const AddTaskdialog = ({ End
- { + { setNewTask({ ...newTask, - end: date ? format(date, 'yyyy-MM-dd') : '', + end: date + ? hasTime + ? date.toISOString() + : format(date, 'yyyy-MM-dd') + : '', }); }} - placeholder="Select an end date" + placeholder="Select end date and time" />
@@ -319,15 +343,27 @@ export const AddTaskdialog = ({ Entry
- { + { setNewTask({ ...newTask, - entry: date ? format(date, 'yyyy-MM-dd') : '', + entry: date + ? hasTime + ? date.toISOString() + : format(date, 'yyyy-MM-dd') + : '', }); }} - placeholder="Select an entry date" + placeholder="Select entry date and time" />
@@ -336,15 +372,27 @@ export const AddTaskdialog = ({ Wait
- { + { setNewTask({ ...newTask, - wait: date ? format(date, 'yyyy-MM-dd') : '', + wait: date + ? hasTime + ? date.toISOString() + : format(date, 'yyyy-MM-dd') + : '', }); }} - placeholder="Select a wait date" + placeholder="Select wait date and time" />
diff --git a/frontend/src/components/HomeComponents/Tasks/TaskDialog.tsx b/frontend/src/components/HomeComponents/Tasks/TaskDialog.tsx index 87aeff3a..9a5e77e7 100644 --- a/frontend/src/components/HomeComponents/Tasks/TaskDialog.tsx +++ b/frontend/src/components/HomeComponents/Tasks/TaskDialog.tsx @@ -1,7 +1,8 @@ import { EditTaskDialogProps } from '../../utils/types'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; -import { DatePicker } from '@/components/ui/date-picker'; + +import { DateTimePicker } from '@/components/ui/date-time-picker'; import { Dialog, DialogClose, @@ -265,19 +266,17 @@ export const TaskDialog = ({ {editState.isEditingDueDate ? (
- { try { - const dateStr = - editState.editedDueDate.includes('T') - ? editState.editedDueDate.split('T')[0] - : editState.editedDueDate; - const parsed = new Date( - dateStr + 'T00:00:00' - ); + const dateStr = editState.editedDueDate; + // Handle both ISO datetime and date-only formats + const parsed = dateStr.includes('T') + ? new Date(dateStr) + : new Date(dateStr + 'T00:00:00'); return isNaN(parsed.getTime()) ? undefined : parsed; @@ -287,14 +286,16 @@ export const TaskDialog = ({ })() : undefined } - onDateChange={(date) => + onDateTimeChange={(date, hasTime) => { onUpdateState({ editedDueDate: date - ? format(date, 'yyyy-MM-dd') + ? hasTime + ? date.toISOString() + : format(date, 'yyyy-MM-dd') : '', - }) - } - placeholder="Select due date" + }); + }} + placeholder="Select due date and time" />