feat: add task sets functionality with CRUD operations and UI integration
- Implemented NewTaskSetPage for creating task sets with templates. - Created TaskSetsPage for listing and filtering task sets. - Enhanced TaskTemplatesPage with navigation to task sets. - Updated ProjectTaskForm to support task set selection. - Modified PageHeader to support multiple action buttons. - Initialized database with task_sets and task_set_templates tables. - Added queries for task sets including creation, retrieval, and deletion. - Implemented applyTaskSetToProject function for bulk task creation. - Added test script for verifying task sets functionality.
This commit is contained in:
35
src/app/api/task-sets/[id]/apply/route.js
Normal file
35
src/app/api/task-sets/[id]/apply/route.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { applyTaskSetToProject } from "@/lib/queries/tasks";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withUserAuth } from "@/lib/middleware/auth";
|
||||
|
||||
// POST: Apply a task set to a project (bulk create project tasks)
|
||||
async function applyTaskSetHandler(req, { params }) {
|
||||
try {
|
||||
const { id } = await params;
|
||||
const { project_id } = await req.json();
|
||||
|
||||
if (!project_id) {
|
||||
return NextResponse.json(
|
||||
{ error: "project_id is required" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const createdTaskIds = applyTaskSetToProject(id, project_id, req.user?.id || null);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: `Task set applied successfully. Created ${createdTaskIds.length} tasks.`,
|
||||
createdTaskIds
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error applying task set:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to apply task set", details: error.message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Protected route - require authentication
|
||||
export const POST = withUserAuth(applyTaskSetHandler);
|
||||
130
src/app/api/task-sets/[id]/route.js
Normal file
130
src/app/api/task-sets/[id]/route.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import {
|
||||
getTaskSetById,
|
||||
updateTaskSet,
|
||||
deleteTaskSet,
|
||||
addTaskTemplateToSet,
|
||||
removeTaskTemplateFromSet,
|
||||
} from "@/lib/queries/tasks";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withReadAuth, withUserAuth } from "@/lib/middleware/auth";
|
||||
import initializeDatabase from "@/lib/init-db";
|
||||
|
||||
// GET: Get a specific task set with its templates
|
||||
async function getTaskSetHandler(req, { params }) {
|
||||
initializeDatabase();
|
||||
try {
|
||||
const { id } = await params;
|
||||
const taskSet = getTaskSetById(id);
|
||||
|
||||
if (!taskSet) {
|
||||
return NextResponse.json(
|
||||
{ error: "Task set not found" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json(taskSet);
|
||||
} catch (error) {
|
||||
console.error("Error fetching task set:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to fetch task set" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// PUT: Update a task set
|
||||
async function updateTaskSetHandler(req, { params }) {
|
||||
initializeDatabase();
|
||||
try {
|
||||
const { id } = await params;
|
||||
const updates = await req.json();
|
||||
|
||||
// Validate required fields
|
||||
if (updates.name !== undefined && !updates.name.trim()) {
|
||||
return NextResponse.json(
|
||||
{ error: "Name cannot be empty" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
if (updates.task_category !== undefined) {
|
||||
const validTypes = ["design", "construction"];
|
||||
if (!validTypes.includes(updates.task_category)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Invalid task_category. Must be one of: design, construction" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle template updates
|
||||
if (updates.templates !== undefined) {
|
||||
// Clear existing templates
|
||||
// Note: This is a simple implementation. In a real app, you might want to handle this more efficiently
|
||||
const currentSet = getTaskSetById(id);
|
||||
if (currentSet) {
|
||||
for (const template of currentSet.templates) {
|
||||
removeTaskTemplateFromSet(id, template.task_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new templates
|
||||
if (Array.isArray(updates.templates)) {
|
||||
for (let i = 0; i < updates.templates.length; i++) {
|
||||
const template = updates.templates[i];
|
||||
addTaskTemplateToSet(id, template.task_id, i);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove templates from updates object so it doesn't interfere with task set update
|
||||
delete updates.templates;
|
||||
}
|
||||
|
||||
const result = updateTaskSet(id, updates);
|
||||
|
||||
if (result.changes === 0) {
|
||||
return NextResponse.json(
|
||||
{ error: "Task set not found" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("Error updating task set:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to update task set", details: error.message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE: Delete a task set
|
||||
async function deleteTaskSetHandler(req, { params }) {
|
||||
initializeDatabase();
|
||||
try {
|
||||
const { id } = await params;
|
||||
const result = deleteTaskSet(id);
|
||||
|
||||
if (result.changes === 0) {
|
||||
return NextResponse.json(
|
||||
{ error: "Task set not found" },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error("Error deleting task set:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to delete task set", details: error.message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const GET = withReadAuth(getTaskSetHandler);
|
||||
export const PUT = withUserAuth(updateTaskSetHandler);
|
||||
export const DELETE = withUserAuth(deleteTaskSetHandler);
|
||||
60
src/app/api/task-sets/route.js
Normal file
60
src/app/api/task-sets/route.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
getAllTaskSets,
|
||||
getTaskSetsByProjectType,
|
||||
createTaskSet,
|
||||
} from "@/lib/queries/tasks";
|
||||
import { NextResponse } from "next/server";
|
||||
import { withReadAuth, withUserAuth } from "@/lib/middleware/auth";
|
||||
import initializeDatabase from "@/lib/init-db";
|
||||
|
||||
// GET: Get all task sets or filter by task category
|
||||
async function getTaskSetsHandler(req) {
|
||||
initializeDatabase();
|
||||
const { searchParams } = new URL(req.url);
|
||||
const taskCategory = searchParams.get("task_category");
|
||||
|
||||
if (taskCategory) {
|
||||
const taskSets = getTaskSetsByTaskCategory(taskCategory);
|
||||
return NextResponse.json(taskSets);
|
||||
} else {
|
||||
const taskSets = getAllTaskSets();
|
||||
return NextResponse.json(taskSets);
|
||||
}
|
||||
}
|
||||
|
||||
// POST: Create a new task set
|
||||
async function createTaskSetHandler(req) {
|
||||
initializeDatabase();
|
||||
try {
|
||||
const data = await req.json();
|
||||
|
||||
if (!data.name || !data.task_category) {
|
||||
return NextResponse.json(
|
||||
{ error: "Name and task_category are required" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Validate task_category
|
||||
const validTypes = ["design", "construction"];
|
||||
if (!validTypes.includes(data.task_category)) {
|
||||
return NextResponse.json(
|
||||
{ error: "Invalid task_category. Must be one of: design, construction" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const setId = createTaskSet(data);
|
||||
return NextResponse.json({ success: true, id: setId });
|
||||
} catch (error) {
|
||||
console.error("Error creating task set:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Failed to create task set", details: error.message },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Protected routes - require authentication
|
||||
export const GET = withReadAuth(getTaskSetsHandler);
|
||||
export const POST = withUserAuth(createTaskSetHandler);
|
||||
Reference in New Issue
Block a user