Skip to content

Commit 786a6f6

Browse files
authored
feat: make due date optional in task creation (#256)
* feat: make due date optional in task creation * fix: remove unnecessary due date validation in frontend
1 parent 606b247 commit 786a6f6

File tree

5 files changed

+119
-43
lines changed

5 files changed

+119
-43
lines changed

backend/controllers/add_task.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,17 @@ func AddTaskHandler(w http.ResponseWriter, r *http.Request) {
5252
http.Error(w, "Description is required, and cannot be empty!", http.StatusBadRequest)
5353
return
5454
}
55-
if dueDate == "" {
56-
http.Error(w, "Due Date is required, and cannot be empty!", http.StatusBadRequest)
57-
return
55+
var dueDateStr string
56+
if dueDate != nil && *dueDate != "" {
57+
dueDateStr = *dueDate
5858
}
5959

6060
logStore := models.GetLogStore()
6161
job := Job{
6262
Name: "Add Task",
6363
Execute: func() error {
6464
logStore.AddLog("INFO", fmt.Sprintf("Adding task: %s", description), uuid, "Add Task")
65-
err := tw.AddTaskToTaskwarrior(email, encryptionSecret, uuid, description, project, priority, dueDate, tags)
65+
err := tw.AddTaskToTaskwarrior(email, encryptionSecret, uuid, description, project, priority, dueDateStr, tags)
6666
if err != nil {
6767
logStore.AddLog("ERROR", fmt.Sprintf("Failed to add task: %v", err), uuid, "Add Task")
6868
return err

backend/controllers/controllers_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package controllers
22

33
import (
4+
"bytes"
45
"encoding/gob"
56
"encoding/json"
67
"net/http"
@@ -122,3 +123,76 @@ func Test_LogoutHandler(t *testing.T) {
122123
session, _ := app.SessionStore.Get(req, "session-name")
123124
assert.Equal(t, -1, session.Options.MaxAge)
124125
}
126+
127+
func Test_AddTaskHandler_WithDueDate(t *testing.T) {
128+
// Initialize job queue
129+
GlobalJobQueue = NewJobQueue()
130+
131+
requestBody := map[string]interface{}{
132+
"email": "test@example.com",
133+
"encryptionSecret": "secret",
134+
"UUID": "test-uuid",
135+
"description": "Test task",
136+
"project": "TestProject",
137+
"priority": "H",
138+
"due": "2025-12-31",
139+
"tags": []string{"test", "important"},
140+
}
141+
142+
body, _ := json.Marshal(requestBody)
143+
req, err := http.NewRequest("POST", "/add-task", bytes.NewBuffer(body))
144+
assert.NoError(t, err)
145+
req.Header.Set("Content-Type", "application/json")
146+
147+
rr := httptest.NewRecorder()
148+
AddTaskHandler(rr, req)
149+
150+
assert.Equal(t, http.StatusAccepted, rr.Code)
151+
}
152+
153+
func Test_AddTaskHandler_WithoutDueDate(t *testing.T) {
154+
// Initialize job queue
155+
GlobalJobQueue = NewJobQueue()
156+
157+
requestBody := map[string]interface{}{
158+
"email": "test@example.com",
159+
"encryptionSecret": "secret",
160+
"UUID": "test-uuid",
161+
"description": "Test task without due date",
162+
"project": "TestProject",
163+
"priority": "M",
164+
"tags": []string{"test"},
165+
}
166+
167+
body, _ := json.Marshal(requestBody)
168+
req, err := http.NewRequest("POST", "/add-task", bytes.NewBuffer(body))
169+
assert.NoError(t, err)
170+
req.Header.Set("Content-Type", "application/json")
171+
172+
rr := httptest.NewRecorder()
173+
AddTaskHandler(rr, req)
174+
175+
assert.Equal(t, http.StatusAccepted, rr.Code)
176+
}
177+
178+
func Test_AddTaskHandler_MissingDescription(t *testing.T) {
179+
requestBody := map[string]interface{}{
180+
"email": "test@example.com",
181+
"encryptionSecret": "secret",
182+
"UUID": "test-uuid",
183+
"description": "",
184+
"project": "TestProject",
185+
"priority": "H",
186+
}
187+
188+
body, _ := json.Marshal(requestBody)
189+
req, err := http.NewRequest("POST", "/add-task", bytes.NewBuffer(body))
190+
assert.NoError(t, err)
191+
req.Header.Set("Content-Type", "application/json")
192+
193+
rr := httptest.NewRecorder()
194+
AddTaskHandler(rr, req)
195+
196+
assert.Equal(t, http.StatusBadRequest, rr.Code)
197+
assert.Contains(t, rr.Body.String(), "Description is required")
198+
}

backend/models/request_body.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ type AddTaskRequestBody struct {
88
Description string `json:"description"`
99
Project string `json:"project"`
1010
Priority string `json:"priority"`
11-
DueDate string `json:"due"`
11+
DueDate *string `json:"due"`
1212
Tags []string `json:"tags"`
1313
}
1414
type ModifyTaskRequestBody struct {

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

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { Input } from '@/components/ui/input';
1919
import { Label } from '@/components/ui/label';
2020
import {
2121
getDisplayedPages,
22-
handleDate,
2322
markTaskAsCompleted,
2423
markTaskAsDeleted,
2524
Props,
@@ -297,32 +296,30 @@ export const Tasks = (
297296
}, [props.email, props.encryptionSecret, props.UUID]); // Add dependencies
298297

299298
async function handleAddTask(task: TaskFormData) {
300-
if (handleDate(newTask.due)) {
301-
try {
302-
await addTaskToBackend({
303-
email: props.email,
304-
encryptionSecret: props.encryptionSecret,
305-
UUID: props.UUID,
306-
description: task.description,
307-
project: task.project,
308-
priority: task.priority,
309-
due: task.due,
310-
tags: task.tags,
311-
backendURL: url.backendURL,
312-
});
313-
314-
console.log('Task added successfully!');
315-
setNewTask({
316-
description: '',
317-
priority: '',
318-
project: '',
319-
due: '',
320-
tags: [],
321-
});
322-
setIsAddTaskOpen(false);
323-
} catch (error) {
324-
console.error('Failed to add task:', error);
325-
}
299+
try {
300+
await addTaskToBackend({
301+
email: props.email,
302+
encryptionSecret: props.encryptionSecret,
303+
UUID: props.UUID,
304+
description: task.description,
305+
project: task.project,
306+
priority: task.priority,
307+
due: task.due || undefined,
308+
tags: task.tags,
309+
backendURL: url.backendURL,
310+
});
311+
312+
console.log('Task added successfully!');
313+
setNewTask({
314+
description: '',
315+
priority: '',
316+
project: '',
317+
due: '',
318+
tags: [],
319+
});
320+
setIsAddTaskOpen(false);
321+
} catch (error) {
322+
console.error('Failed to add task:', error);
326323
}
327324
}
328325

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,27 @@ export const addTaskToBackend = async ({
5151
description: string;
5252
project: string;
5353
priority: string;
54-
due: string;
54+
due?: string;
5555
tags: string[];
5656
backendURL: string;
5757
}) => {
58+
const requestBody: any = {
59+
email,
60+
encryptionSecret,
61+
UUID,
62+
description,
63+
project,
64+
priority,
65+
tags,
66+
};
67+
68+
// Only include due if it's provided
69+
if (due !== undefined && due !== '') {
70+
requestBody.due = due;
71+
}
5872
const response = await fetch(`${backendURL}add-task`, {
5973
method: 'POST',
60-
body: JSON.stringify({
61-
email,
62-
encryptionSecret,
63-
UUID,
64-
description,
65-
project,
66-
priority,
67-
due,
68-
tags,
69-
}),
74+
body: JSON.stringify(requestBody),
7075
headers: {
7176
'Content-Type': 'application/json',
7277
},

0 commit comments

Comments
 (0)