import db from "../db.js"; import { addNoteToTask } from "./notes.js"; import { getUserLanguage, serverT, translateStatus, translatePriority } from "../serverTranslations.js"; // Get all task templates (for dropdown selection) export function getAllTaskTemplates() { return db .prepare("SELECT * FROM tasks WHERE is_standard = 1 ORDER BY name ASC") .all(); } // Get all project tasks across all projects export function getAllProjectTasks() { return db .prepare( ` SELECT pt.*, COALESCE(pt.custom_task_name, t.name) as task_name, COALESCE(pt.custom_max_wait_days, t.max_wait_days) as max_wait_days, COALESCE(pt.custom_description, t.description) as description, CASE WHEN pt.task_template_id IS NOT NULL THEN 'template' ELSE 'custom' END as task_type, p.project_name, p.wp, p.plot, p.city, p.address, p.finish_date, creator.name as created_by_name, creator.username as created_by_username, assignee.name as assigned_to_name, assignee.username as assigned_to_username FROM project_tasks pt LEFT JOIN tasks t ON pt.task_template_id = t.task_id LEFT JOIN projects p ON pt.project_id = p.project_id LEFT JOIN users creator ON pt.created_by = creator.id LEFT JOIN users assignee ON pt.assigned_to = assignee.id ORDER BY pt.date_added DESC ` ) .all(); } // Get project tasks for a specific project export function getProjectTasks(projectId) { return db .prepare( ` SELECT pt.*, COALESCE(pt.custom_task_name, t.name) as task_name, COALESCE(pt.custom_max_wait_days, t.max_wait_days) as max_wait_days, COALESCE(pt.custom_description, t.description) as description, CASE WHEN pt.task_template_id IS NOT NULL THEN 'template' ELSE 'custom' END as task_type, creator.name as created_by_name, creator.username as created_by_username, assignee.name as assigned_to_name, assignee.username as assigned_to_username FROM project_tasks pt LEFT JOIN tasks t ON pt.task_template_id = t.task_id LEFT JOIN users creator ON pt.created_by = creator.id LEFT JOIN users assignee ON pt.assigned_to = assignee.id WHERE pt.project_id = ? ORDER BY pt.date_added DESC ` ) .all(projectId); } // Create a new project task export function createProjectTask(data) { let result; let taskName; if (data.task_template_id) { // Creating from template - explicitly set custom_max_wait_days to NULL so COALESCE uses template value const stmt = db.prepare(` INSERT INTO project_tasks ( project_id, task_template_id, custom_max_wait_days, status, priority, created_by, assigned_to, created_at, updated_at ) VALUES (?, ?, NULL, ?, ?, ?, ?, datetime('now', 'localtime'), datetime('now', 'localtime')) `); result = stmt.run( data.project_id, data.task_template_id, data.status || "pending", data.priority || "normal", data.created_by || null, data.assigned_to || null ); // Get the template name for the log const templateStmt = db.prepare("SELECT name FROM tasks WHERE task_id = ?"); const template = templateStmt.get(data.task_template_id); taskName = template?.name || "Unknown template"; } else { // Creating custom task const stmt = db.prepare(` INSERT INTO project_tasks ( project_id, custom_task_name, custom_max_wait_days, custom_description, status, priority, created_by, assigned_to, created_at, updated_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, datetime('now', 'localtime'), datetime('now', 'localtime')) `); result = stmt.run( data.project_id, data.custom_task_name, data.custom_max_wait_days || 0, data.custom_description || "", data.status || "pending", data.priority || "normal", data.created_by || null, data.assigned_to || null ); taskName = data.custom_task_name; } // Add system note for task creation if (result.lastInsertRowid) { const language = getUserLanguage(); const priority = data.priority || "normal"; const status = data.status || "pending"; const translatedPriority = translatePriority(priority, language); const translatedStatus = translateStatus(status, language); const logMessage = `${serverT("Task created", language)} "${taskName}" ${serverT("with priority", language)}: ${translatedPriority}, ${serverT("status", language)}: ${translatedStatus}`; addNoteToTask(result.lastInsertRowid, logMessage, true, data.created_by); } return result; } // Update project task status export function updateProjectTaskStatus(taskId, status, userId = null) { // First get the current task details for logging const getCurrentTask = db.prepare(` SELECT pt.status, COALESCE(pt.custom_task_name, t.name) as task_name FROM project_tasks pt LEFT JOIN tasks t ON pt.task_template_id = t.task_id WHERE pt.id = ? `); const currentTask = getCurrentTask.get(taskId); if (!currentTask) { throw new Error(`Task with ID ${taskId} not found`); } const oldStatus = currentTask.status; let stmt; let result; if (oldStatus === "pending" && status === "in_progress") { // Starting a task - set date_started stmt = db.prepare(` UPDATE project_tasks SET status = ?, date_started = datetime('now', 'localtime'), updated_at = datetime('now', 'localtime') WHERE id = ? `); result = stmt.run(status, taskId); } else if (status === "completed") { // Completing a task - set date_completed stmt = db.prepare(` UPDATE project_tasks SET status = ?, date_completed = datetime('now', 'localtime'), updated_at = datetime('now', 'localtime') WHERE id = ? `); result = stmt.run(status, taskId); } else { // Just updating status without changing timestamps stmt = db.prepare(` UPDATE project_tasks SET status = ?, updated_at = datetime('now', 'localtime') WHERE id = ? `); result = stmt.run(status, taskId); } // Add system note for status change (only if status actually changed) if (result.changes > 0 && oldStatus !== status) { const language = getUserLanguage(); // Default to Polish for now const fromStatus = translateStatus(oldStatus, language); const toStatus = translateStatus(status, language); const logMessage = `${serverT("Status changed from", language)} "${fromStatus}" ${serverT("to", language)} "${toStatus}"`; addNoteToTask(taskId, logMessage, true, userId); } return result; } // Delete a project task export function deleteProjectTask(taskId) { // First delete all related task notes const deleteNotesStmt = db.prepare("DELETE FROM notes WHERE task_id = ?"); deleteNotesStmt.run(taskId); // Then delete the task itself const deleteTaskStmt = db.prepare("DELETE FROM project_tasks WHERE id = ?"); return deleteTaskStmt.run(taskId); } // Get project tasks assigned to a specific user export function getProjectTasksByAssignedUser(userId) { return db .prepare( ` SELECT pt.*, COALESCE(pt.custom_task_name, t.name) as task_name, COALESCE(pt.custom_max_wait_days, t.max_wait_days) as max_wait_days, COALESCE(pt.custom_description, t.description) as description, CASE WHEN pt.task_template_id IS NOT NULL THEN 'template' ELSE 'custom' END as task_type, p.project_name, p.wp, p.plot, p.city, p.address, p.finish_date, creator.name as created_by_name, creator.username as created_by_username, assignee.name as assigned_to_name, assignee.username as assigned_to_username FROM project_tasks pt LEFT JOIN tasks t ON pt.task_template_id = t.task_id LEFT JOIN projects p ON pt.project_id = p.project_id LEFT JOIN users creator ON pt.created_by = creator.id LEFT JOIN users assignee ON pt.assigned_to = assignee.id WHERE pt.assigned_to = ? ORDER BY pt.date_added DESC ` ) .all(userId); } // Get project tasks created by a specific user export function getProjectTasksByCreator(userId) { return db .prepare( ` SELECT pt.*, COALESCE(pt.custom_task_name, t.name) as task_name, COALESCE(pt.custom_max_wait_days, t.max_wait_days) as max_wait_days, COALESCE(pt.custom_description, t.description) as description, CASE WHEN pt.task_template_id IS NOT NULL THEN 'template' ELSE 'custom' END as task_type, p.project_name, p.wp, p.plot, p.city, p.address, p.finish_date, creator.name as created_by_name, creator.username as created_by_username, assignee.name as assigned_to_name, assignee.username as assigned_to_username FROM project_tasks pt LEFT JOIN tasks t ON pt.task_template_id = t.task_id LEFT JOIN projects p ON pt.project_id = p.project_id LEFT JOIN users creator ON pt.created_by = creator.id LEFT JOIN users assignee ON pt.assigned_to = assignee.id WHERE pt.created_by = ? ORDER BY pt.date_added DESC ` ) .all(userId); } // Update project task assignment export function updateProjectTaskAssignment(taskId, assignedToUserId) { const stmt = db.prepare(` UPDATE project_tasks SET assigned_to = ?, updated_at = datetime('now', 'localtime') WHERE id = ? `); return stmt.run(assignedToUserId, taskId); } // Get active users for task assignment (same as projects) export function getAllUsersForTaskAssignment() { return db .prepare( ` SELECT id, name, username, role FROM users WHERE is_active = 1 AND role != 'admin' ORDER BY name ASC ` ) .all(); } // Update project task (general update for edit modal) export function updateProjectTask(taskId, updates, userId = null) { // Get current task for logging const getCurrentTask = db.prepare(` SELECT pt.*, COALESCE(pt.custom_task_name, t.name) as task_name FROM project_tasks pt LEFT JOIN tasks t ON pt.task_template_id = t.task_id WHERE pt.id = ? `); const currentTask = getCurrentTask.get(taskId); if (!currentTask) { throw new Error(`Task with ID ${taskId} not found`); } // Build dynamic update query const fields = []; const values = []; if (updates.priority !== undefined) { fields.push("priority = ?"); values.push(updates.priority); } if (updates.status !== undefined) { fields.push("status = ?"); values.push(updates.status); // Handle status-specific timestamp updates if (currentTask.status === "pending" && updates.status === "in_progress") { fields.push("date_started = datetime('now', 'localtime')"); } else if (updates.status === "completed") { fields.push("date_completed = datetime('now', 'localtime')"); } } if (updates.assigned_to !== undefined) { fields.push("assigned_to = ?"); values.push(updates.assigned_to || null); } if (updates.date_started !== undefined) { fields.push("date_started = ?"); values.push(updates.date_started || null); } // Always update the updated_at timestamp fields.push("updated_at = datetime('now', 'localtime')"); values.push(taskId); const stmt = db.prepare(` UPDATE project_tasks SET ${fields.join(", ")} WHERE id = ? `); const result = stmt.run(...values); // Log the update if (userId) { const language = getUserLanguage(); // Default to Polish for now const changes = []; if ( updates.priority !== undefined && updates.priority !== currentTask.priority ) { const oldPriority = translatePriority(currentTask.priority, language) || serverT("None", language); const newPriority = translatePriority(updates.priority, language) || serverT("None", language); changes.push( `${serverT("Priority", language)}: ${oldPriority} → ${newPriority}` ); } if (updates.status !== undefined && updates.status !== currentTask.status) { const oldStatus = translateStatus(currentTask.status, language) || serverT("None", language); const newStatus = translateStatus(updates.status, language) || serverT("None", language); changes.push( `${serverT("Status", language)}: ${oldStatus} → ${newStatus}` ); } if ( updates.assigned_to !== undefined && updates.assigned_to !== currentTask.assigned_to ) { changes.push(serverT("Assignment updated", language)); } if ( updates.date_started !== undefined && updates.date_started !== currentTask.date_started ) { const oldDate = currentTask.date_started || serverT("None", language); const newDate = updates.date_started || serverT("None", language); changes.push( `${serverT("Date started", language)}: ${oldDate} → ${newDate}` ); } if (changes.length > 0) { const logMessage = `${serverT("Task updated", language)}: ${changes.join(", ")}`; addNoteToTask(taskId, logMessage, true, userId); } } return result; }