diff --git a/src/app/api/project-tasks/[id]/route.js b/src/app/api/project-tasks/[id]/route.js index ce96dd8..3ee8cc5 100644 --- a/src/app/api/project-tasks/[id]/route.js +++ b/src/app/api/project-tasks/[id]/route.js @@ -1,13 +1,45 @@ import { updateProjectTaskStatus, deleteProjectTask, + updateProjectTask, } from "@/lib/queries/tasks"; import { NextResponse } from "next/server"; import { withUserAuth } from "@/lib/middleware/auth"; -// PATCH: Update project task status +// PUT: Update project task (general update) async function updateProjectTaskHandler(req, { params }) { try { + const { id } = await params; + const updates = await req.json(); + + // Validate that we have at least one field to update + const allowedFields = ["priority", "status", "assigned_to", "date_started"]; + const hasValidFields = Object.keys(updates).some((key) => + allowedFields.includes(key) + ); + + if (!hasValidFields) { + return NextResponse.json( + { error: "No valid fields provided for update" }, + { status: 400 } + ); + } + + updateProjectTask(id, updates, req.user?.id || null); + return NextResponse.json({ success: true }); + } catch (error) { + console.error("Error updating task:", error); + return NextResponse.json( + { error: "Failed to update project task", details: error.message }, + { status: 500 } + ); + } +} + +// PATCH: Update project task status +async function updateProjectTaskStatusHandler(req, { params }) { + try { + const { id } = await params; const { status } = await req.json(); if (!status) { @@ -17,7 +49,7 @@ async function updateProjectTaskHandler(req, { params }) { ); } - updateProjectTaskStatus(params.id, status, req.user?.id || null); + updateProjectTaskStatus(id, status, req.user?.id || null); return NextResponse.json({ success: true }); } catch (error) { console.error("Error updating task status:", error); @@ -31,16 +63,19 @@ async function updateProjectTaskHandler(req, { params }) { // DELETE: Delete a project task async function deleteProjectTaskHandler(req, { params }) { try { - deleteProjectTask(params.id); + const { id } = await params; + const result = deleteProjectTask(id); return NextResponse.json({ success: true }); } catch (error) { + console.error("Error in deleteProjectTaskHandler:", error); return NextResponse.json( - { error: "Failed to delete project task" }, + { error: "Failed to delete project task", details: error.message }, { status: 500 } ); } } // Protected routes - require authentication -export const PATCH = withUserAuth(updateProjectTaskHandler); +export const PUT = withUserAuth(updateProjectTaskHandler); +export const PATCH = withUserAuth(updateProjectTaskStatusHandler); export const DELETE = withUserAuth(deleteProjectTaskHandler); diff --git a/src/components/ProjectTasksSection.js b/src/components/ProjectTasksSection.js index d435d7f..981350f 100644 --- a/src/components/ProjectTasksSection.js +++ b/src/components/ProjectTasksSection.js @@ -17,6 +17,15 @@ export default function ProjectTasksSection({ projectId }) { const [showAddTaskModal, setShowAddTaskModal] = useState(false); const [expandedDescriptions, setExpandedDescriptions] = useState({}); const [expandedNotes, setExpandedNotes] = useState({}); + const [editingTask, setEditingTask] = useState(null); + const [showEditTaskModal, setShowEditTaskModal] = useState(false); + const [editTaskForm, setEditTaskForm] = useState({ + priority: "", + date_started: "", + status: "", + assigned_to: "", + }); + const [users, setUsers] = useState([]); useEffect(() => { const fetchProjectTasks = async () => { try { @@ -49,22 +58,38 @@ export default function ProjectTasksSection({ projectId }) { } }; + // Fetch users for assignment dropdown + const fetchUsers = async () => { + try { + const res = await fetch("/api/project-tasks/users"); + const usersData = await res.json(); + setUsers(usersData); + } catch (error) { + console.error("Failed to fetch users:", error); + } + }; + fetchProjectTasks(); + fetchUsers(); }, [projectId]); - // Handle escape key to close modal + // Handle escape key to close modals useEffect(() => { const handleEscape = (e) => { - if (e.key === "Escape" && showAddTaskModal) { - setShowAddTaskModal(false); + if (e.key === "Escape") { + if (showEditTaskModal) { + handleCloseEditModal(); + } else if (showAddTaskModal) { + setShowAddTaskModal(false); + } } }; document.addEventListener("keydown", handleEscape); return () => document.removeEventListener("keydown", handleEscape); - }, [showAddTaskModal]); + }, [showAddTaskModal, showEditTaskModal]); // Prevent body scroll when modal is open and handle map z-index useEffect(() => { - if (showAddTaskModal) { + if (showAddTaskModal || showEditTaskModal) { // Prevent body scroll document.body.style.overflow = "hidden"; @@ -111,7 +136,7 @@ export default function ProjectTasksSection({ projectId }) { nav.style.zIndex = ""; }); }; - }, [showAddTaskModal]); + }, [showAddTaskModal, showEditTaskModal]); const refetchTasks = async () => { try { const res = await fetch(`/api/project-tasks?project_id=${projectId}`); @@ -171,10 +196,11 @@ export default function ProjectTasksSection({ projectId }) { if (res.ok) { refetchTasks(); // Refresh the list } else { - alert("Failed to delete task"); + const errorData = await res.json(); + alert("Failed to delete task: " + (errorData.error || "Unknown error")); } } catch (error) { - alert("Error deleting task"); + alert("Error deleting task: " + error.message); } }; @@ -210,26 +236,66 @@ export default function ProjectTasksSection({ projectId }) { } }; - const handleDeleteNote = async (noteId, taskId) => { - if (!confirm("Are you sure you want to delete this note?")) return; + const handleEditTask = (task) => { + setEditingTask(task); + + // Format date for HTML input (YYYY-MM-DD) + let dateStarted = ""; + if (task.date_started) { + const date = new Date(task.date_started); + if (!isNaN(date.getTime())) { + dateStarted = date.toISOString().split("T")[0]; + } + } + + const formData = { + priority: task.priority || "", + date_started: dateStarted, + status: task.status || "", + assigned_to: task.assigned_to || "", + }; + + setEditTaskForm(formData); + setShowEditTaskModal(true); + }; + + const handleUpdateTask = async () => { + if (!editingTask) return; try { - const res = await fetch(`/api/task-notes?note_id=${noteId}`, { - method: "DELETE", + const res = await fetch(`/api/project-tasks/${editingTask.id}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + priority: editTaskForm.priority, + date_started: editTaskForm.date_started || null, + status: editTaskForm.status, + assigned_to: editTaskForm.assigned_to || null, + }), }); if (res.ok) { - // Refresh notes for this task - const notesRes = await fetch(`/api/task-notes?task_id=${taskId}`); - const notes = await notesRes.json(); - setTaskNotes((prev) => ({ ...prev, [taskId]: notes })); + refetchTasks(); + handleCloseEditModal(); } else { - alert("Failed to delete note"); + alert("Failed to update task"); } } catch (error) { - alert("Error deleting note"); + alert("Error updating task"); } }; + + const handleCloseEditModal = () => { + setShowEditTaskModal(false); + setEditingTask(null); + setEditTaskForm({ + priority: "", + date_started: "", + status: "", + assigned_to: "", + }); + }; + const getPriorityVariant = (priority) => { switch (priority) { case "urgent": @@ -447,7 +513,7 @@ export default function ProjectTasksSection({ projectId }) { {task.date_started ? formatDate(task.date_started) : "Not started"} - {" "} +