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"
/>