diff --git a/index.html b/index.html index 8fe1303d..7a0fdb21 100644 --- a/index.html +++ b/index.html @@ -345,6 +345,31 @@

Account overview

+
+
+
Manual Task Entry
+
Use this form when AI extraction isn't needed.
+
+ + + + + + + + + + + + + +
+ +
+
+
diff --git a/js/app.js b/js/app.js index 6842db4e..92fff46f 100644 --- a/js/app.js +++ b/js/app.js @@ -357,8 +357,29 @@ const newTaskEstimatedDuration = document.getElementById('new-task-estimated-dur const newTaskDurationSwitch = document.getElementById('new-task-duration-switch'); const newTaskDurationMin = document.getElementById('new-task-duration-min'); const newTaskDurationHr = document.getElementById('new-task-duration-hr'); +const manualTaskSubject = document.getElementById('manual-task-subject'); +const manualTaskTitle = document.getElementById('manual-task-title'); +const manualTaskDate = document.getElementById('manual-task-date'); +const manualTaskNotes = document.getElementById('manual-task-notes'); +const manualTaskSubmit = document.getElementById('manual-task-submit'); let selectedTaskDurationUnit = 'minutes'; +function renderManualTaskSubjects() { + if (!manualTaskSubject) return; + + manualTaskSubject.innerHTML = ` + + ${store.subjects.map(s => ``).join('')} + `; +} + +function clearManualTaskForm() { + if (manualTaskSubject) manualTaskSubject.value = ''; + if (manualTaskTitle) manualTaskTitle.value = ''; + if (manualTaskDate) manualTaskDate.value = ''; + if (manualTaskNotes) manualTaskNotes.value = ''; +} + // Timer elements const timerText = document.getElementById('timer-text'); const timerPathRemaining = document.getElementById('timer-path-remaining'); @@ -1647,6 +1668,7 @@ store.subscribe(renderFocusTasks); store.subscribe(renderProfileSection); store.subscribe(renderSidebarSubjects); store.subscribe(renderStreak); +store.subscribe(renderManualTaskSubjects); document.addEventListener('DOMContentLoaded', () => { if (newSubjectColorsEl) { @@ -1846,6 +1868,49 @@ newTaskModal.addEventListener('click', (e) => { newTaskModal.style.display = 'none'; } }); + +manualTaskSubmit?.addEventListener('click', async () => { + if (!manualTaskSubject || !manualTaskTitle || !manualTaskDate) return; + + const subject_id = manualTaskSubject.value; + const rawTitle = manualTaskTitle.value.trim(); + const dueDateValue = manualTaskDate.value; + const notes = manualTaskNotes ? manualTaskNotes.value.trim() : ''; + + if (!subject_id) { + Toast.show('Please select a subject', 'warning'); + return; + } + + if (!rawTitle) { + Toast.show('Please enter a task title', 'warning'); + return; + } + + if (!dueDateValue) { + Toast.show('Please select a due date', 'warning'); + return; + } + + const due_at = new Date(dueDateValue).toISOString(); + const { cleanTitle, labels } = extractLabels(rawTitle); + + const manualTask = { + title: cleanTitle || rawTitle, + subject_id, + due_at, + notes, + priority: 'medium', + status: 'Not Started', + archived: 0, + labels + }; + + const result = await store.addTasks([manualTask]); + if (result && result.inserted > 0) { + clearManualTaskForm(); + } +}); function setNewTaskDurationUnit(unit) { selectedTaskDurationUnit = unit; diff --git a/js/store.js b/js/store.js index 075bc62b..b72beaa6 100644 --- a/js/store.js +++ b/js/store.js @@ -125,7 +125,7 @@ export const store = { // Backend error Toast.show(`❌ ${data.message || "Failed to add tasks"}`, 'error'); console.error('Add task error:', data); - return; + return data; } // ================= USER MESSAGES ================= @@ -151,9 +151,11 @@ export const store = { this.tasks = await tasksRes.json(); this.notify(); + return data; } catch (e) { console.error('Failed to add tasks', e); Toast.show("❌ Network error. Please try again.", 'error'); + return null; } }, diff --git a/package-lock.json b/package-lock.json index 56528823..e9685a4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "sqlite3": "^6.0.1" }, "engines": { - "node": "20.x" + "node": ">=20.x" } }, "node_modules/@google/genai": {