Skip to content

Commit a641f0b

Browse files
authored
feat: make entry field editable (#200)
Added entry date editing functionality with DatePicker UI
1 parent 0da3ac7 commit a641f0b

6 files changed

Lines changed: 145 additions & 11 deletions

File tree

backend/controllers/edit_task.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) {
4747
tags := requestBody.Tags
4848
project := requestBody.Project
4949
start := requestBody.Start
50+
entry := requestBody.Entry
5051

5152
if taskID == "" {
5253
http.Error(w, "taskID is required", http.StatusBadRequest)
@@ -58,7 +59,7 @@ func EditTaskHandler(w http.ResponseWriter, r *http.Request) {
5859
Name: "Edit Task",
5960
Execute: func() error {
6061
logStore.AddLog("INFO", fmt.Sprintf("Editing task ID: %s", taskID), uuid, "Edit Task")
61-
err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID, tags, project, start)
62+
err := tw.EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID, tags, project, start, entry)
6263
if err != nil {
6364
logStore.AddLog("ERROR", fmt.Sprintf("Failed to edit task ID %s: %v", taskID, err), uuid, "Edit Task")
6465
return err

backend/models/request_body.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type EditTaskRequestBody struct {
3232
Tags []string `json:"tags"`
3333
Project string `json:"project"`
3434
Start string `json:"start"`
35+
Entry string `json:"entry"`
3536
}
3637
type CompleteTaskRequestBody struct {
3738
Email string `json:"email"`

backend/utils/tw/edit_task.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"strings"
88
)
99

10-
func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID string, tags []string, project string, start string) error {
10+
func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID string, tags []string, project string, start string, entry string) error {
1111
if err := utils.ExecCommand("rm", "-rf", "/root/.task"); err != nil {
1212
return fmt.Errorf("error deleting Taskwarrior data: %v", err)
1313
}
@@ -70,6 +70,13 @@ func EditTaskInTaskwarrior(uuid, description, email, encryptionSecret, taskID st
7070
}
7171
}
7272

73+
// Handle entry date
74+
if entry != "" {
75+
if err := utils.ExecCommand("task", taskID, "modify", "entry:"+entry); err != nil {
76+
return fmt.Errorf("failed to set entry date %s: %v", entry, err)
77+
}
78+
}
79+
7380
// Sync Taskwarrior again
7481
if err := SyncTaskwarrior(tempDir); err != nil {
7582
return err

backend/utils/tw/taskwarrior_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func TestSyncTaskwarrior(t *testing.T) {
2323
}
2424

2525
func TestEditTaskInATaskwarrior(t *testing.T) {
26-
err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", nil, "project", "2025-11-29T18:30:00.000Z")
26+
err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", nil, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z")
2727
if err != nil {
2828
t.Errorf("EditTaskInTaskwarrior() failed: %v", err)
2929
} else {
@@ -68,7 +68,7 @@ func TestAddTaskWithTags(t *testing.T) {
6868
}
6969

7070
func TestEditTaskWithTagAddition(t *testing.T) {
71-
err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "+important"}, "project", "2025-11-29T18:30:00.000Z")
71+
err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "+important"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z")
7272
if err != nil {
7373
t.Errorf("EditTaskInTaskwarrior with tag addition failed: %v", err)
7474
} else {
@@ -77,7 +77,7 @@ func TestEditTaskWithTagAddition(t *testing.T) {
7777
}
7878

7979
func TestEditTaskWithTagRemoval(t *testing.T) {
80-
err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"-work", "-lowpriority"}, "project", "2025-11-29T18:30:00.000Z")
80+
err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"-work", "-lowpriority"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z")
8181
if err != nil {
8282
t.Errorf("EditTaskInTaskwarrior with tag removal failed: %v", err)
8383
} else {
@@ -86,7 +86,7 @@ func TestEditTaskWithTagRemoval(t *testing.T) {
8686
}
8787

8888
func TestEditTaskWithMixedTagOperations(t *testing.T) {
89-
err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "-work", "normal"}, "project", "2025-11-29T18:30:00.000Z")
89+
err := EditTaskInTaskwarrior("uuid", "description", "email", "encryptionSecret", "taskuuid", []string{"+urgent", "-work", "normal"}, "project", "2025-11-29T18:30:00.000Z", "2025-11-29T18:30:00.000Z")
9090
if err != nil {
9191
t.Errorf("EditTaskInTaskwarrior with mixed tag operations failed: %v", err)
9292
} else {

frontend/src/components/HomeComponents/Tasks/Tasks.tsx

Lines changed: 127 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ export const Tasks = (
120120
);
121121
const [isEditingStartDate, setIsEditingStartDate] = useState(false);
122122
const [editedStartDate, setEditedStartDate] = useState('');
123+
const [isEditingEntryDate, setIsEditingEntryDate] = useState(false);
124+
const [editedEntryDate, setEditedEntryDate] = useState('');
123125
const [searchTerm, setSearchTerm] = useState('');
124126
const [lastSyncTime, setLastSyncTime] = useState<number | null>(null);
125127

@@ -321,7 +323,8 @@ export const Tasks = (
321323
tags: string[],
322324
taskID: string,
323325
project: string,
324-
start: string
326+
start: string,
327+
entry: string
325328
) {
326329
try {
327330
await editTaskOnBackend({
@@ -334,6 +337,7 @@ export const Tasks = (
334337
backendURL: url.backendURL,
335338
project,
336339
start,
340+
entry,
337341
});
338342

339343
console.log('Task edited successfully!');
@@ -377,7 +381,8 @@ export const Tasks = (
377381
task.tags,
378382
task.id.toString(),
379383
task.project,
380-
task.start
384+
task.start,
385+
task.entry || ''
381386
);
382387
setIsEditing(false);
383388
};
@@ -392,7 +397,8 @@ export const Tasks = (
392397
task.tags,
393398
task.id.toString(),
394399
task.project,
395-
task.start
400+
task.start,
401+
task.entry || ''
396402
);
397403
setIsEditingProject(false);
398404
};
@@ -408,12 +414,31 @@ export const Tasks = (
408414
task.tags,
409415
task.id.toString(),
410416
task.project,
411-
task.start
417+
task.start,
418+
task.entry || ''
412419
);
413420

414421
setIsEditingStartDate(false);
415422
};
416423

424+
const handleEntryDateSaveClick = (task: Task) => {
425+
task.entry = editedEntryDate;
426+
427+
handleEditTaskOnBackend(
428+
props.email,
429+
props.encryptionSecret,
430+
props.UUID,
431+
task.description,
432+
task.tags,
433+
task.id.toString(),
434+
task.project,
435+
task.start,
436+
task.entry
437+
);
438+
439+
setIsEditingEntryDate(false);
440+
};
441+
417442
const handleCancelClick = () => {
418443
setIsEditing(false);
419444
};
@@ -427,6 +452,10 @@ export const Tasks = (
427452
setEditedTags([]);
428453
setIsEditingPriority(false);
429454
setEditedPriority('NONE');
455+
setIsEditingStartDate(false);
456+
setEditedStartDate('');
457+
setIsEditingEntryDate(false);
458+
setEditedEntryDate('');
430459
} else {
431460
setSelectedTask(task);
432461
setEditedDescription(task?.description || '');
@@ -499,7 +528,8 @@ export const Tasks = (
499528
finalTags,
500529
task.id.toString(),
501530
task.project,
502-
task.start
531+
task.start,
532+
task.entry || ''
503533
);
504534

505535
setIsEditingTags(false); // Exit editing mode
@@ -1336,6 +1366,98 @@ export const Tasks = (
13361366
)}
13371367
</TableCell>
13381368
</TableRow>
1369+
<TableRow>
1370+
<TableCell>Entry:</TableCell>
1371+
<TableCell>
1372+
{isEditingEntryDate ? (
1373+
<div className="flex items-center gap-2">
1374+
<DatePicker
1375+
date={
1376+
editedEntryDate &&
1377+
editedEntryDate !== ''
1378+
? (() => {
1379+
try {
1380+
// Handle YYYY-MM-DD format
1381+
const dateStr =
1382+
editedEntryDate.includes(
1383+
'T'
1384+
)
1385+
? editedEntryDate.split(
1386+
'T'
1387+
)[0]
1388+
: editedEntryDate;
1389+
const parsed =
1390+
new Date(
1391+
dateStr +
1392+
'T00:00:00'
1393+
);
1394+
return isNaN(
1395+
parsed.getTime()
1396+
)
1397+
? undefined
1398+
: parsed;
1399+
} catch {
1400+
return undefined;
1401+
}
1402+
})()
1403+
: undefined
1404+
}
1405+
onDateChange={(date) =>
1406+
setEditedEntryDate(
1407+
date
1408+
? format(
1409+
date,
1410+
'yyyy-MM-dd'
1411+
)
1412+
: ''
1413+
)
1414+
}
1415+
/>
1416+
1417+
<Button
1418+
variant="ghost"
1419+
size="icon"
1420+
onClick={() =>
1421+
handleEntryDateSaveClick(task)
1422+
}
1423+
>
1424+
<CheckIcon className="h-4 w-4 text-green-500" />
1425+
</Button>
1426+
1427+
<Button
1428+
variant="ghost"
1429+
size="icon"
1430+
onClick={() =>
1431+
setIsEditingEntryDate(false)
1432+
}
1433+
>
1434+
<XIcon className="h-4 w-4 text-red-500" />
1435+
</Button>
1436+
</div>
1437+
) : (
1438+
<>
1439+
<span>
1440+
{formattedDate(task.entry)}
1441+
</span>
1442+
<Button
1443+
variant="ghost"
1444+
size="icon"
1445+
onClick={() => {
1446+
setIsEditingEntryDate(true);
1447+
const entryDate = task.entry
1448+
? task.entry.includes('T')
1449+
? task.entry.split('T')[0]
1450+
: task.entry
1451+
: '';
1452+
setEditedEntryDate(entryDate);
1453+
}}
1454+
>
1455+
<PencilIcon className="h-4 w-4 text-gray-500" />
1456+
</Button>
1457+
</>
1458+
)}
1459+
</TableCell>
1460+
</TableRow>
13391461
<TableRow>
13401462
<TableCell>Urgency:</TableCell>
13411463
<TableCell>{task.urgency}</TableCell>

frontend/src/components/HomeComponents/Tasks/hooks.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export const editTaskOnBackend = async ({
9090
backendURL,
9191
project,
9292
start,
93+
entry,
9394
}: {
9495
email: string;
9596
encryptionSecret: string;
@@ -100,6 +101,7 @@ export const editTaskOnBackend = async ({
100101
backendURL: string;
101102
project: string;
102103
start: string;
104+
entry: string;
103105
}) => {
104106
const response = await fetch(`${backendURL}edit-task`, {
105107
method: 'POST',
@@ -112,6 +114,7 @@ export const editTaskOnBackend = async ({
112114
tags,
113115
project,
114116
start,
117+
entry,
115118
}),
116119
headers: {
117120
'Content-Type': 'application/json',

0 commit comments

Comments
 (0)