Files
worklenz/worklenz-backend/src/controllers/tasks-controller-base.ts
chamiakJ 6128c64c31 Add task progress tracking methods and enhance UI components
- Introduced a comprehensive guide for users on task progress tracking methods, including manual, weighted, and time-based progress.
- Implemented backend support for progress calculations, including SQL functions and migrations to accommodate new progress features.
- Enhanced frontend components to support progress input and display, including updates to task and project drawers.
- Added localization for new progress-related terms and validation messages.
- Integrated real-time updates for task progress and weight changes through socket events.
2025-04-30 15:24:07 +05:30

136 lines
5.3 KiB
TypeScript

import WorklenzControllerBase from "./worklenz-controller-base";
import {getColor} from "../shared/utils";
import {PriorityColorCodes, TASK_PRIORITY_COLOR_ALPHA, TASK_STATUS_COLOR_ALPHA} from "../shared/constants";
import moment from "moment/moment";
export const GroupBy = {
STATUS: "status",
PRIORITY: "priority",
LABELS: "labels",
PHASE: "phase"
};
export interface ITaskGroup {
id?: string;
name: string;
start_date?: string;
end_date?: string;
color_code: string;
category_id: string | null;
old_category_id?: string;
todo_progress?: number;
doing_progress?: number;
done_progress?: number;
tasks: any[];
}
export default class TasksControllerBase extends WorklenzControllerBase {
protected static calculateTaskCompleteRatio(totalCompleted: number, totalTasks: number) {
if (totalCompleted === 0 && totalTasks === 0) return 0;
const ratio = ((totalCompleted / totalTasks) * 100);
return ratio == Infinity ? 100 : ratio.toFixed();
}
public static updateTaskViewModel(task: any) {
console.log(`Processing task ${task.id} (${task.name})`);
console.log(` manual_progress: ${task.manual_progress}, progress_value: ${task.progress_value}`);
console.log(` project_use_manual_progress: ${task.project_use_manual_progress}, project_use_weighted_progress: ${task.project_use_weighted_progress}`);
console.log(` has subtasks: ${task.sub_tasks_count > 0}`);
// For parent tasks (with subtasks), always use calculated progress from subtasks
if (task.sub_tasks_count > 0) {
// For parent tasks without manual progress, calculate from subtasks (already done via db function)
console.log(` Parent task with subtasks: complete_ratio=${task.complete_ratio}`);
// Ensure progress matches complete_ratio for consistency
task.progress = task.complete_ratio || 0;
// Important: Parent tasks should not have manual progress
// If they somehow do, reset it
if (task.manual_progress) {
console.log(` WARNING: Parent task ${task.id} had manual_progress set to true, resetting`);
task.manual_progress = false;
task.progress_value = null;
}
}
// For tasks without subtasks, respect manual progress if set
else if (task.manual_progress === true && task.progress_value !== null && task.progress_value !== undefined) {
// For manually set progress, use that value directly
task.progress = parseInt(task.progress_value);
task.complete_ratio = parseInt(task.progress_value);
console.log(` Using manual progress: progress=${task.progress}, complete_ratio=${task.complete_ratio}`);
}
// For tasks with no subtasks and no manual progress, calculate based on time
else {
task.progress = task.total_minutes_spent && task.total_minutes
? ~~(task.total_minutes_spent / task.total_minutes * 100)
: 0;
// Set complete_ratio to match progress
task.complete_ratio = task.progress;
console.log(` Calculated time-based progress: progress=${task.progress}, complete_ratio=${task.complete_ratio}`);
}
// Ensure numeric values
task.progress = parseInt(task.progress) || 0;
task.complete_ratio = parseInt(task.complete_ratio) || 0;
task.overdue = task.total_minutes < task.total_minutes_spent;
task.time_spent = {hours: ~~(task.total_minutes_spent / 60), minutes: task.total_minutes_spent % 60};
task.comments_count = Number(task.comments_count) ? +task.comments_count : 0;
task.attachments_count = Number(task.attachments_count) ? +task.attachments_count : 0;
if (typeof task.sub_tasks_count === "undefined") task.sub_tasks_count = "0";
task.is_sub_task = !!task.parent_task_id;
task.time_spent_string = `${task.time_spent.hours}h ${(task.time_spent.minutes)}m`;
task.total_time_string = `${~~(task.total_minutes / 60)}h ${(task.total_minutes % 60)}m`;
task.name_color = getColor(task.name);
task.priority_color = PriorityColorCodes[task.priority_value] || PriorityColorCodes["0"];
task.show_sub_tasks = false;
if (task.phase_id) {
task.phase_color = task.phase_color_code
? task.phase_color_code : getColor(task.phase_name) + TASK_PRIORITY_COLOR_ALPHA;
}
if (Array.isArray(task.assignees)) {
for (const assignee of task.assignees) {
assignee.color_code = getColor(assignee.name);
}
}
task.names = TasksControllerBase.createTagList(task.assignees);
task.all_labels = task.labels;
task.labels = TasksControllerBase.createTagList(task.labels, 2);
task.status_color = task.status_color + TASK_STATUS_COLOR_ALPHA;
task.priority_color = task.priority_color + TASK_PRIORITY_COLOR_ALPHA;
if (task.timer_start_time)
task.timer_start_time = moment(task.timer_start_time).valueOf();
// Set completed_count and total_tasks_count regardless of progress calculation method
const totalCompleted = (+task.completed_sub_tasks + +task.parent_task_completed) || 0;
const totalTasks = +task.sub_tasks_count || 0;
task.completed_count = totalCompleted;
task.total_tasks_count = totalTasks;
task.width = 35;
if (task.chart_start) {
const fToday = moment().format("YYYY-MM-DD");
task.offset_from = (moment(fToday).diff(task.chart_start, "days")) * 35;
}
return task;
}
}