From d40af1ff31eaa4d74c3cf7b7ff102c2a2c2410c9 Mon Sep 17 00:00:00 2001 From: Chop <28534054+RChopin@users.noreply.github.com> Date: Thu, 19 Jun 2025 22:01:39 +0200 Subject: [PATCH] feat: Enhance project task management with expandable notes and descriptions, and add date_started column for task tracking --- src/components/ProjectTasksSection.js | 386 ++++++++++++++++---------- src/lib/init-db.js | 10 +- src/lib/queries/tasks.js | 35 ++- 3 files changed, 273 insertions(+), 158 deletions(-) diff --git a/src/components/ProjectTasksSection.js b/src/components/ProjectTasksSection.js index 07ead22..2bb6874 100644 --- a/src/components/ProjectTasksSection.js +++ b/src/components/ProjectTasksSection.js @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect } from "react"; +import React, { useState, useEffect } from "react"; import ProjectTaskForm from "./ProjectTaskForm"; import { Card, CardHeader, CardContent } from "./ui/Card"; import Button from "./ui/Button"; @@ -13,6 +13,8 @@ export default function ProjectTasksSection({ projectId }) { const [newNote, setNewNote] = useState({}); const [loadingNotes, setLoadingNotes] = useState({}); const [showAddTaskModal, setShowAddTaskModal] = useState(false); + const [expandedDescriptions, setExpandedDescriptions] = useState({}); + const [expandedNotes, setExpandedNotes] = useState({}); useEffect(() => { const fetchProjectTasks = async () => { try { @@ -241,7 +243,6 @@ export default function ProjectTasksSection({ projectId }) { return "default"; } }; - const getStatusVariant = (status) => { switch (status) { case "completed": @@ -256,6 +257,20 @@ export default function ProjectTasksSection({ projectId }) { return "default"; } }; + + const toggleDescription = (taskId) => { + setExpandedDescriptions((prev) => ({ + ...prev, + [taskId]: !prev[taskId], + })); + }; + + const toggleNotes = (taskId) => { + setExpandedNotes((prev) => ({ + ...prev, + [taskId]: !prev[taskId], + })); + }; return (
@@ -369,159 +384,228 @@ export default function ProjectTasksSection({ projectId }) {

) : ( -
- {projectTasks.map((task) => ( -
-
-
-
-

- {task.task_name} -

- - {task.priority} - -
{" "} - {/* Task Description */} - {task.description && ( -
-

- {task.description} -

-
- )} -
-
- Max wait days:{" "} - {task.max_wait_days} -
-
- Type:{" "} - {task.task_type || "template"} -
-
- Added:{" "} - {new Date(task.date_added).toLocaleDateString()} -
-
-
- - Status: - - - - {task.status.replace("_", " ")} - -
- {/* Notes Section */} -
-
- Notes ({taskNotes[task.id]?.length || 0}) -
- - {/* Existing Notes */} - {taskNotes[task.id] && - taskNotes[task.id].length > 0 && ( -
- {taskNotes[task.id].map((note) => ( -
+ + + + + + {" "} + + + + + + + {projectTasks.map((task) => ( + + {/* Main task row */} + + + + {" "} + + + + -
- -
- - - ))} + {/* Description row (expandable) */} + {task.description && expandedDescriptions[task.id] && ( + + + + )} + + {/* Notes row (expandable) */} + {expandedNotes[task.id] && ( + + + + )} + + ))} + +
+ Task + + Priority + + Max Wait + + Date Started + + Status + + Actions +
+
+

+ {task.task_name} +

+ {task.description && ( + -
- ))} - - )} - - {/* Add New Note */} -
- - setNewNote((prev) => ({ - ...prev, - [task.id]: e.target.value, - })) - } - className="flex-1 px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500" - onKeyPress={(e) => { - if (e.key === "Enter") { - handleAddNote(task.id); - } - }} - /> - + )} +
+
+ handleAddNote(task.id)} - disabled={ - loadingNotes[task.id] || !newNote[task.id]?.trim() - } > - {loadingNotes[task.id] ? "Adding..." : "Add"} - - - - + {task.priority} + + + {task.max_wait_days} days + + {task.date_started + ? new Date(task.date_started).toLocaleDateString() + : "Not started"} + +
+ + + {task.status.replace("_", " ")} + +
+
+
+ + +
+
+
+ + Description: + +

{task.description}

+
+
+
+
+ Notes ({taskNotes[task.id]?.length || 0}) +
+ + {/* Existing Notes */} + {taskNotes[task.id] && + taskNotes[task.id].length > 0 && ( +
+ {taskNotes[task.id].map((note) => ( +
+
+

+ {note.note} +

+

+ {new Date( + note.note_date + ).toLocaleDateString()}{" "} + at{" "} + {new Date( + note.note_date + ).toLocaleTimeString()} +

+
+ +
+ ))} +
+ )} + + {/* Add New Note */} +
+ + setNewNote((prev) => ({ + ...prev, + [task.id]: e.target.value, + })) + } + className="flex-1 px-3 py-1.5 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500" + onKeyPress={(e) => { + if (e.key === "Enter") { + handleAddNote(task.id); + } + }} + /> + +
+
+
)} diff --git a/src/lib/init-db.js b/src/lib/init-db.js index fdc8f4a..925994a 100644 --- a/src/lib/init-db.js +++ b/src/lib/init-db.js @@ -126,7 +126,6 @@ export default function initializeDatabase() { } catch (e) { // Column already exists, ignore error } - // Migration: Copy data from geo_info to coordinates and drop geo_info try { db.exec(` @@ -138,4 +137,13 @@ export default function initializeDatabase() { } catch (e) { // Column migration already done or geo_info doesn't exist, ignore error } + + // Migration: Add date_started column to project_tasks table + try { + db.exec(` + ALTER TABLE project_tasks ADD COLUMN date_started TEXT; + `); + } catch (e) { + // Column already exists, ignore error + } } diff --git a/src/lib/queries/tasks.js b/src/lib/queries/tasks.js index 5914674..6e558aa 100644 --- a/src/lib/queries/tasks.js +++ b/src/lib/queries/tasks.js @@ -90,12 +90,35 @@ export function createProjectTask(data) { // Update project task status export function updateProjectTaskStatus(taskId, status) { - const stmt = db.prepare(` - UPDATE project_tasks - SET status = ? - WHERE id = ? - `); - return stmt.run(status, taskId); + // First get the current status to check if we're transitioning from pending to in_progress + const getCurrentStatus = db.prepare( + "SELECT status FROM project_tasks WHERE id = ?" + ); + const currentTask = getCurrentStatus.get(taskId); + + let stmt; + + if ( + currentTask && + currentTask.status === "pending" && + status === "in_progress" + ) { + // Starting a task - set date_started + stmt = db.prepare(` + UPDATE project_tasks + SET status = ?, date_started = CURRENT_TIMESTAMP + WHERE id = ? + `); + return stmt.run(status, taskId); + } else { + // Just updating status without changing date_started + stmt = db.prepare(` + UPDATE project_tasks + SET status = ? + WHERE id = ? + `); + return stmt.run(status, taskId); + } } // Delete a project task