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:
2025-10-07 21:58:08 +02:00
parent e19172d2bb
commit 952caf10d1
14 changed files with 1838 additions and 77 deletions

View File

@@ -413,3 +413,192 @@ export function updateProjectTask(taskId, updates, userId = null) {
return result;
}
// ===== TASK SETS =====
// Get all task sets
export function getAllTaskSets() {
const taskSets = db
.prepare("SELECT * FROM task_sets ORDER BY name ASC")
.all();
// Add templates to each task set
return taskSets.map(taskSet => {
const templates = db
.prepare(`
SELECT
tst.sort_order,
t.task_id,
t.name,
t.max_wait_days,
t.description
FROM task_set_templates tst
JOIN tasks t ON tst.task_template_id = t.task_id
WHERE tst.set_id = ?
ORDER BY tst.sort_order ASC
`)
.all(taskSet.set_id);
return { ...taskSet, templates };
});
}
// Get task sets by task category
export function getTaskSetsByTaskCategory(taskCategory) {
const taskSets = db
.prepare("SELECT * FROM task_sets WHERE task_category = ? ORDER BY name ASC")
.all(taskCategory);
// Add templates to each task set
return taskSets.map(taskSet => {
const templates = db
.prepare(`
SELECT
tst.sort_order,
t.task_id,
t.name,
t.max_wait_days,
t.description
FROM task_set_templates tst
JOIN tasks t ON tst.task_template_id = t.task_id
WHERE tst.set_id = ?
ORDER BY tst.sort_order ASC
`)
.all(taskSet.set_id);
return { ...taskSet, templates };
});
}
// Get task set by ID with templates
export function getTaskSetById(setId) {
const taskSet = db
.prepare("SELECT * FROM task_sets WHERE set_id = ?")
.get(setId);
if (taskSet) {
const templates = db
.prepare(`
SELECT
tst.sort_order,
t.task_id,
t.name,
t.max_wait_days,
t.description
FROM task_set_templates tst
JOIN tasks t ON tst.task_template_id = t.task_id
WHERE tst.set_id = ?
ORDER BY tst.sort_order ASC
`)
.all(setId);
return { ...taskSet, templates };
}
return null;
}
// Create a new task set
export function createTaskSet(data) {
const result = db
.prepare(`
INSERT INTO task_sets (name, description, task_category, created_at, updated_at)
VALUES (?, ?, ?, datetime('now', 'localtime'), datetime('now', 'localtime'))
`)
.run(data.name, data.description || null, data.task_category);
return result.lastInsertRowid;
}
// Update a task set
export function updateTaskSet(setId, data) {
const fields = [];
const values = [];
if (data.name !== undefined) {
fields.push("name = ?");
values.push(data.name);
}
if (data.description !== undefined) {
fields.push("description = ?");
values.push(data.description || null);
}
if (data.task_category !== undefined) {
fields.push("task_category = ?");
values.push(data.task_category);
}
fields.push("updated_at = datetime('now', 'localtime')");
values.push(setId);
const stmt = db.prepare(`
UPDATE task_sets
SET ${fields.join(", ")}
WHERE set_id = ?
`);
return stmt.run(...values);
}
// Delete a task set
export function deleteTaskSet(setId) {
// Delete task set templates first (cascade should handle this, but being explicit)
db.prepare("DELETE FROM task_set_templates WHERE set_id = ?").run(setId);
// Delete the task set
return db.prepare("DELETE FROM task_sets WHERE set_id = ?").run(setId);
}
// Add task template to set
export function addTaskTemplateToSet(setId, taskTemplateId, sortOrder = 0) {
return db
.prepare(`
INSERT OR REPLACE INTO task_set_templates (set_id, task_template_id, sort_order)
VALUES (?, ?, ?)
`)
.run(setId, taskTemplateId, sortOrder);
}
// Remove task template from set
export function removeTaskTemplateFromSet(setId, taskTemplateId) {
return db
.prepare(`
DELETE FROM task_set_templates
WHERE set_id = ? AND task_template_id = ?
`)
.run(setId, taskTemplateId);
}
// Apply task set to project (bulk create project tasks)
export function applyTaskSetToProject(setId, projectId, userId = null) {
// Get the task set with templates
const taskSet = getTaskSetById(setId);
if (!taskSet) {
throw new Error(`Task set with ID ${setId} not found`);
}
const createdTasks = [];
const language = getUserLanguage();
// Create project tasks for each template in the set
for (const template of taskSet.templates) {
const result = createProjectTask({
project_id: projectId,
task_template_id: template.task_id,
status: "pending",
priority: "normal",
created_by: userId,
assigned_to: null, // Will be assigned based on user role logic in createProjectTask
});
createdTasks.push(result.lastInsertRowid);
// Add system note for task set application
const logMessage = `${serverT("Task added from set", language)} "${taskSet.name}"`;
addNoteToTask(result.lastInsertRowid, logMessage, true, userId);
}
return createdTasks;
}