Merge pull request #119 from chamikaJ/imp/task-list-loading-improvement
Imp/task list loading improvement
This commit is contained in:
@@ -1,9 +1,21 @@
|
||||
# WorkLenz Task Progress Guide for Users
|
||||
|
||||
## Introduction
|
||||
|
||||
WorkLenz offers three different ways to track and calculate task progress, each designed for different project management needs. This guide explains how each method works and when to use them.
|
||||
|
||||
## Default Progress Method
|
||||
|
||||
WorkLenz uses a simple completion-based approach as the default progress calculation method. This method is applied when no special progress methods are enabled.
|
||||
|
||||
### Example
|
||||
|
||||
If you have a parent task with four subtasks and two of the subtasks are marked complete:
|
||||
- Parent task: Not done
|
||||
- 2 subtasks: Done
|
||||
- 2 subtasks: Not done
|
||||
|
||||
The parent task will show as 40% complete (2 completed out of 5 total tasks).
|
||||
|
||||
## Available Progress Tracking Methods
|
||||
|
||||
WorkLenz provides these progress tracking methods:
|
||||
@@ -150,10 +162,6 @@ The parent task would be approximately 29% complete.
|
||||
|
||||
This demonstrates how tasks with longer time estimates (like Subtask C) have more influence on the overall progress calculation. Even though Subtask B is 80% complete, its shorter time estimate means it contributes less to the overall progress than the partially-completed but longer Subtask A.
|
||||
|
||||
## Default Progress Method
|
||||
|
||||
If none of the special progress methods are enabled, WorkLenz uses a simple completion-based approach:
|
||||
|
||||
### How It Works
|
||||
|
||||
- Tasks are either 0% (not done) or 100% (done)
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
-- Migration: Update socket event handlers to set progress-mode handlers
|
||||
-- Date: 2025-04-26
|
||||
-- Version: 1.0.0
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Create ENUM type for progress modes
|
||||
CREATE TYPE progress_mode_type AS ENUM ('manual', 'weighted', 'time', 'default');
|
||||
|
||||
-- Alter tasks table to use ENUM type
|
||||
ALTER TABLE tasks
|
||||
ALTER COLUMN progress_mode TYPE progress_mode_type
|
||||
USING progress_mode::text::progress_mode_type;
|
||||
|
||||
-- Update the on_update_task_progress function to set progress_mode
|
||||
CREATE OR REPLACE FUNCTION on_update_task_progress(_body json) RETURNS json
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
_task_id UUID;
|
||||
_progress_value INTEGER;
|
||||
_parent_task_id UUID;
|
||||
_project_id UUID;
|
||||
_current_mode progress_mode_type;
|
||||
BEGIN
|
||||
_task_id = (_body ->> 'task_id')::UUID;
|
||||
_progress_value = (_body ->> 'progress_value')::INTEGER;
|
||||
_parent_task_id = (_body ->> 'parent_task_id')::UUID;
|
||||
|
||||
-- Get the project ID and determine the current progress mode
|
||||
SELECT project_id INTO _project_id FROM tasks WHERE id = _task_id;
|
||||
|
||||
IF _project_id IS NOT NULL THEN
|
||||
SELECT
|
||||
CASE
|
||||
WHEN use_manual_progress IS TRUE THEN 'manual'
|
||||
WHEN use_weighted_progress IS TRUE THEN 'weighted'
|
||||
WHEN use_time_progress IS TRUE THEN 'time'
|
||||
ELSE 'default'
|
||||
END
|
||||
INTO _current_mode
|
||||
FROM projects
|
||||
WHERE id = _project_id;
|
||||
ELSE
|
||||
_current_mode := 'default';
|
||||
END IF;
|
||||
|
||||
-- Update the task with progress value and set the progress mode
|
||||
UPDATE tasks
|
||||
SET progress_value = _progress_value,
|
||||
manual_progress = TRUE,
|
||||
progress_mode = _current_mode,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = _task_id;
|
||||
|
||||
-- Return the updated task info
|
||||
RETURN JSON_BUILD_OBJECT(
|
||||
'task_id', _task_id,
|
||||
'progress_value', _progress_value,
|
||||
'progress_mode', _current_mode
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Update the on_update_task_weight function to set progress_mode when weight is updated
|
||||
CREATE OR REPLACE FUNCTION on_update_task_weight(_body json) RETURNS json
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
_task_id UUID;
|
||||
_weight INTEGER;
|
||||
_parent_task_id UUID;
|
||||
_project_id UUID;
|
||||
BEGIN
|
||||
_task_id = (_body ->> 'task_id')::UUID;
|
||||
_weight = (_body ->> 'weight')::INTEGER;
|
||||
_parent_task_id = (_body ->> 'parent_task_id')::UUID;
|
||||
|
||||
-- Get the project ID
|
||||
SELECT project_id INTO _project_id FROM tasks WHERE id = _task_id;
|
||||
|
||||
-- Update the task with weight value and set progress_mode to 'weighted'
|
||||
UPDATE tasks
|
||||
SET weight = _weight,
|
||||
progress_mode = 'weighted',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = _task_id;
|
||||
|
||||
-- Return the updated task info
|
||||
RETURN JSON_BUILD_OBJECT(
|
||||
'task_id', _task_id,
|
||||
'weight', _weight
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Create a function to reset progress values when switching project progress modes
|
||||
CREATE OR REPLACE FUNCTION reset_project_progress_values() RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
_old_mode progress_mode_type;
|
||||
_new_mode progress_mode_type;
|
||||
_project_id UUID;
|
||||
BEGIN
|
||||
_project_id := NEW.id;
|
||||
|
||||
-- Determine old and new modes
|
||||
_old_mode :=
|
||||
CASE
|
||||
WHEN OLD.use_manual_progress IS TRUE THEN 'manual'
|
||||
WHEN OLD.use_weighted_progress IS TRUE THEN 'weighted'
|
||||
WHEN OLD.use_time_progress IS TRUE THEN 'time'
|
||||
ELSE 'default'
|
||||
END;
|
||||
|
||||
_new_mode :=
|
||||
CASE
|
||||
WHEN NEW.use_manual_progress IS TRUE THEN 'manual'
|
||||
WHEN NEW.use_weighted_progress IS TRUE THEN 'weighted'
|
||||
WHEN NEW.use_time_progress IS TRUE THEN 'time'
|
||||
ELSE 'default'
|
||||
END;
|
||||
|
||||
-- If mode has changed, reset progress values for tasks with the old mode
|
||||
IF _old_mode <> _new_mode THEN
|
||||
-- Reset progress values for tasks that were set in the old mode
|
||||
UPDATE tasks
|
||||
SET progress_value = NULL,
|
||||
progress_mode = NULL
|
||||
WHERE project_id = _project_id
|
||||
AND progress_mode = _old_mode;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Create trigger to reset progress values when project progress mode changes
|
||||
DROP TRIGGER IF EXISTS reset_progress_on_mode_change ON projects;
|
||||
CREATE TRIGGER reset_progress_on_mode_change
|
||||
AFTER UPDATE OF use_manual_progress, use_weighted_progress, use_time_progress
|
||||
ON projects
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION reset_project_progress_values();
|
||||
|
||||
COMMIT;
|
||||
@@ -1,5 +1,15 @@
|
||||
BEGIN;
|
||||
|
||||
-- Create ENUM type for progress modes
|
||||
CREATE TYPE PROGRESS_MODE_TYPE AS ENUM ('manual', 'weighted', 'time', 'default');
|
||||
|
||||
-- Alter tasks table to use ENUM type
|
||||
ALTER TABLE tasks
|
||||
ADD COLUMN IF NOT EXISTS manual_progress BOOLEAN DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS progress_value INTEGER DEFAULT NULL,
|
||||
ADD COLUMN IF NOT EXISTS progress_mode PROGRESS_MODE_TYPE DEFAULT 'default',
|
||||
ADD COLUMN IF NOT EXISTS weight INTEGER DEFAULT NULL;
|
||||
|
||||
-- Add manual progress fields to tasks table
|
||||
ALTER TABLE tasks
|
||||
ADD COLUMN IF NOT EXISTS manual_progress BOOLEAN DEFAULT FALSE,
|
||||
@@ -13,7 +23,7 @@ ADD COLUMN IF NOT EXISTS use_weighted_progress BOOLEAN DEFAULT FALSE,
|
||||
ADD COLUMN IF NOT EXISTS use_time_progress BOOLEAN DEFAULT FALSE;
|
||||
|
||||
-- Update function to consider manual progress
|
||||
CREATE OR REPLACE FUNCTION get_task_complete_ratio(_task_id uuid) RETURNS json
|
||||
CREATE OR REPLACE FUNCTION get_task_complete_ratio(_task_id UUID) RETURNS JSON
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
@@ -38,7 +48,8 @@ BEGIN
|
||||
INTO _is_manual, _manual_value, _project_id;
|
||||
|
||||
-- Check if the project uses manual progress
|
||||
IF _project_id IS NOT NULL THEN
|
||||
IF _project_id IS NOT NULL
|
||||
THEN
|
||||
SELECT COALESCE(use_manual_progress, FALSE),
|
||||
COALESCE(use_weighted_progress, FALSE),
|
||||
COALESCE(use_time_progress, FALSE)
|
||||
@@ -48,7 +59,8 @@ BEGIN
|
||||
END IF;
|
||||
|
||||
-- If manual progress is enabled and has a value, use it directly
|
||||
IF _is_manual IS TRUE AND _manual_value IS NOT NULL THEN
|
||||
IF _is_manual IS TRUE AND _manual_value IS NOT NULL
|
||||
THEN
|
||||
RETURN JSON_BUILD_OBJECT(
|
||||
'ratio', _manual_value,
|
||||
'total_completed', 0,
|
||||
@@ -76,7 +88,8 @@ BEGIN
|
||||
_total_completed = _parent_task_done + _sub_tasks_done;
|
||||
_total_tasks = _sub_tasks_count; -- +1 for the parent task
|
||||
|
||||
IF _total_tasks > 0 THEN
|
||||
IF _total_tasks > 0
|
||||
THEN
|
||||
_ratio = (_total_completed / _total_tasks) * 100;
|
||||
ELSE
|
||||
_ratio = _parent_task_done * 100;
|
||||
@@ -92,7 +105,7 @@ END
|
||||
$$;
|
||||
|
||||
-- Update project functions to handle progress-related fields
|
||||
CREATE OR REPLACE FUNCTION update_project(_body json) RETURNS json
|
||||
CREATE OR REPLACE FUNCTION update_project(_body JSON) RETURNS JSON
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
@@ -124,10 +137,11 @@ BEGIN
|
||||
END IF;
|
||||
|
||||
-- check whether the project name is already in
|
||||
IF EXISTS(
|
||||
SELECT name FROM projects WHERE LOWER(name) = LOWER(_project_name)
|
||||
AND team_id = _team_id AND id != (_body ->> 'id')::UUID
|
||||
)
|
||||
IF EXISTS(SELECT name
|
||||
FROM projects
|
||||
WHERE LOWER(name) = LOWER(_project_name)
|
||||
AND team_id = _team_id
|
||||
AND id != (_body ->> 'id')::UUID)
|
||||
THEN
|
||||
RAISE 'PROJECT_EXISTS_ERROR:%', _project_name;
|
||||
END IF;
|
||||
@@ -156,7 +170,9 @@ BEGIN
|
||||
AND team_id = _team_id
|
||||
RETURNING id INTO _project_id;
|
||||
|
||||
UPDATE project_members SET project_access_level_id = (SELECT id FROM project_access_levels WHERE key = 'MEMBER') WHERE project_id = _project_id;
|
||||
UPDATE project_members
|
||||
SET project_access_level_id = (SELECT id FROM project_access_levels WHERE key = 'MEMBER')
|
||||
WHERE project_id = _project_id;
|
||||
|
||||
IF NOT (_project_manager_team_member_id IS NULL)
|
||||
THEN
|
||||
@@ -171,7 +187,7 @@ BEGIN
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE OR REPLACE FUNCTION create_project(_body json) RETURNS json
|
||||
CREATE OR REPLACE FUNCTION create_project(_body JSON) RETURNS JSON
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
@@ -217,7 +233,8 @@ BEGIN
|
||||
|
||||
-- create the project
|
||||
INSERT
|
||||
INTO projects (name, key, color_code, start_date, end_date, team_id, notes, owner_id, status_id, health_id, folder_id,
|
||||
INTO projects (name, key, color_code, start_date, end_date, team_id, notes, owner_id, status_id, health_id,
|
||||
folder_id,
|
||||
category_id, estimated_working_days, estimated_man_days, hours_per_day,
|
||||
use_manual_progress, use_weighted_progress, use_time_progress, client_id)
|
||||
VALUES (_project_name,
|
||||
@@ -264,7 +281,8 @@ BEGIN
|
||||
PERFORM insert_task_list_columns(_project_id);
|
||||
|
||||
-- add project manager role if exists
|
||||
IF NOT is_null_or_empty(_project_manager_team_member_id) THEN
|
||||
IF NOT is_null_or_empty(_project_manager_team_member_id)
|
||||
THEN
|
||||
PERFORM update_project_manager(_project_manager_team_member_id, _project_id);
|
||||
END IF;
|
||||
|
||||
@@ -280,8 +298,152 @@ COMMIT;
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Update function to use time-based progress for all tasks
|
||||
CREATE OR REPLACE FUNCTION get_task_complete_ratio(_task_id uuid) RETURNS json
|
||||
-- Update the on_update_task_progress function to set progress_mode
|
||||
CREATE OR REPLACE FUNCTION on_update_task_progress(_body JSON) RETURNS JSON
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
_task_id UUID;
|
||||
_progress_value INTEGER;
|
||||
_parent_task_id UUID;
|
||||
_project_id UUID;
|
||||
_current_mode VARCHAR(20);
|
||||
BEGIN
|
||||
_task_id = (_body ->> 'task_id')::UUID;
|
||||
_progress_value = (_body ->> 'progress_value')::INTEGER;
|
||||
_parent_task_id = (_body ->> 'parent_task_id')::UUID;
|
||||
|
||||
-- Get the project ID and determine the current progress mode
|
||||
SELECT project_id INTO _project_id FROM tasks WHERE id = _task_id;
|
||||
|
||||
IF _project_id IS NOT NULL
|
||||
THEN
|
||||
SELECT CASE
|
||||
WHEN use_manual_progress IS TRUE THEN 'manual'
|
||||
WHEN use_weighted_progress IS TRUE THEN 'weighted'
|
||||
WHEN use_time_progress IS TRUE THEN 'time'
|
||||
ELSE 'default'
|
||||
END
|
||||
INTO _current_mode
|
||||
FROM projects
|
||||
WHERE id = _project_id;
|
||||
ELSE
|
||||
_current_mode := 'default';
|
||||
END IF;
|
||||
|
||||
-- Update the task with progress value and set the progress mode
|
||||
UPDATE tasks
|
||||
SET progress_value = _progress_value,
|
||||
manual_progress = TRUE,
|
||||
progress_mode = _current_mode,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = _task_id;
|
||||
|
||||
-- Return the updated task info
|
||||
RETURN JSON_BUILD_OBJECT(
|
||||
'task_id', _task_id,
|
||||
'progress_value', _progress_value,
|
||||
'progress_mode', _current_mode
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Update the on_update_task_weight function to set progress_mode when weight is updated
|
||||
CREATE OR REPLACE FUNCTION on_update_task_weight(_body JSON) RETURNS JSON
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
_task_id UUID;
|
||||
_weight INTEGER;
|
||||
_parent_task_id UUID;
|
||||
_project_id UUID;
|
||||
BEGIN
|
||||
_task_id = (_body ->> 'task_id')::UUID;
|
||||
_weight = (_body ->> 'weight')::INTEGER;
|
||||
_parent_task_id = (_body ->> 'parent_task_id')::UUID;
|
||||
|
||||
-- Get the project ID
|
||||
SELECT project_id INTO _project_id FROM tasks WHERE id = _task_id;
|
||||
|
||||
-- Update the task with weight value and set progress_mode to 'weighted'
|
||||
UPDATE tasks
|
||||
SET weight = _weight,
|
||||
progress_mode = 'weighted',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = _task_id;
|
||||
|
||||
-- Return the updated task info
|
||||
RETURN JSON_BUILD_OBJECT(
|
||||
'task_id', _task_id,
|
||||
'weight', _weight
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Create a function to reset progress values when switching project progress modes
|
||||
CREATE OR REPLACE FUNCTION reset_project_progress_values() RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
_old_mode VARCHAR(20);
|
||||
_new_mode VARCHAR(20);
|
||||
_project_id UUID;
|
||||
BEGIN
|
||||
_project_id := NEW.id;
|
||||
|
||||
-- Determine old and new modes
|
||||
_old_mode :=
|
||||
CASE
|
||||
WHEN OLD.use_manual_progress IS TRUE THEN 'manual'
|
||||
WHEN OLD.use_weighted_progress IS TRUE THEN 'weighted'
|
||||
WHEN OLD.use_time_progress IS TRUE THEN 'time'
|
||||
ELSE 'default'
|
||||
END;
|
||||
|
||||
_new_mode :=
|
||||
CASE
|
||||
WHEN NEW.use_manual_progress IS TRUE THEN 'manual'
|
||||
WHEN NEW.use_weighted_progress IS TRUE THEN 'weighted'
|
||||
WHEN NEW.use_time_progress IS TRUE THEN 'time'
|
||||
ELSE 'default'
|
||||
END;
|
||||
|
||||
-- If mode has changed, reset progress values for tasks with the old mode
|
||||
IF _old_mode <> _new_mode
|
||||
THEN
|
||||
-- Reset progress values for tasks that were set in the old mode
|
||||
UPDATE tasks
|
||||
SET progress_value = NULL,
|
||||
progress_mode = NULL
|
||||
WHERE project_id = _project_id
|
||||
AND progress_mode = _old_mode;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Create trigger to reset progress values when project progress mode changes
|
||||
DROP TRIGGER IF EXISTS reset_progress_on_mode_change ON projects;
|
||||
CREATE TRIGGER reset_progress_on_mode_change
|
||||
AFTER UPDATE OF use_manual_progress, use_weighted_progress, use_time_progress
|
||||
ON projects
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION reset_project_progress_values();
|
||||
|
||||
COMMIT;
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Add progress_mode column to tasks table to track which mode the progress was set in
|
||||
ALTER TABLE tasks
|
||||
ADD COLUMN IF NOT EXISTS progress_mode VARCHAR(20) DEFAULT NULL;
|
||||
|
||||
-- Update function to use time-based progress for all tasks and respect mode changes
|
||||
CREATE OR REPLACE FUNCTION get_task_complete_ratio(_task_id UUID) RETURNS JSON
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
@@ -299,21 +461,24 @@ DECLARE
|
||||
_use_weighted_progress BOOLEAN = FALSE;
|
||||
_use_time_progress BOOLEAN = FALSE;
|
||||
_task_complete BOOLEAN = FALSE;
|
||||
_progress_mode VARCHAR(20) = NULL;
|
||||
BEGIN
|
||||
-- Check if manual progress is set for this task
|
||||
SELECT manual_progress, progress_value, project_id,
|
||||
EXISTS(
|
||||
SELECT 1
|
||||
SELECT manual_progress,
|
||||
progress_value,
|
||||
project_id,
|
||||
progress_mode,
|
||||
EXISTS(SELECT 1
|
||||
FROM tasks_with_status_view
|
||||
WHERE tasks_with_status_view.task_id = tasks.id
|
||||
AND is_done IS TRUE
|
||||
) AS is_complete
|
||||
AND is_done IS TRUE) AS is_complete
|
||||
FROM tasks
|
||||
WHERE id = _task_id
|
||||
INTO _is_manual, _manual_value, _project_id, _task_complete;
|
||||
INTO _is_manual, _manual_value, _project_id, _progress_mode, _task_complete;
|
||||
|
||||
-- Check if the project uses manual progress
|
||||
IF _project_id IS NOT NULL THEN
|
||||
IF _project_id IS NOT NULL
|
||||
THEN
|
||||
SELECT COALESCE(use_manual_progress, FALSE),
|
||||
COALESCE(use_weighted_progress, FALSE),
|
||||
COALESCE(use_time_progress, FALSE)
|
||||
@@ -325,11 +490,13 @@ BEGIN
|
||||
-- Get all subtasks
|
||||
SELECT COUNT(*)
|
||||
FROM tasks
|
||||
WHERE parent_task_id = _task_id AND archived IS FALSE
|
||||
WHERE parent_task_id = _task_id
|
||||
AND archived IS FALSE
|
||||
INTO _sub_tasks_count;
|
||||
|
||||
-- If task is complete, always return 100%
|
||||
IF _task_complete IS TRUE THEN
|
||||
IF _task_complete IS TRUE
|
||||
THEN
|
||||
RETURN JSON_BUILD_OBJECT(
|
||||
'ratio', 100,
|
||||
'total_completed', 1,
|
||||
@@ -338,11 +505,22 @@ BEGIN
|
||||
);
|
||||
END IF;
|
||||
|
||||
-- Use manual progress value in two cases:
|
||||
-- 1. When task has manual_progress = TRUE and progress_value is set
|
||||
-- 2. When project has use_manual_progress = TRUE and progress_value is set
|
||||
IF (_is_manual IS TRUE AND _manual_value IS NOT NULL) OR
|
||||
(_use_manual_progress IS TRUE AND _manual_value IS NOT NULL) THEN
|
||||
-- Determine current active mode
|
||||
DECLARE
|
||||
_current_mode VARCHAR(20) = CASE
|
||||
WHEN _use_manual_progress IS TRUE THEN 'manual'
|
||||
WHEN _use_weighted_progress IS TRUE THEN 'weighted'
|
||||
WHEN _use_time_progress IS TRUE THEN 'time'
|
||||
ELSE 'default'
|
||||
END;
|
||||
BEGIN
|
||||
-- Only use manual progress value if it was set in the current active mode
|
||||
-- or if the task is explicitly marked for manual progress
|
||||
IF (_is_manual IS TRUE AND _manual_value IS NOT NULL AND
|
||||
(_progress_mode IS NULL OR _progress_mode = _current_mode)) OR
|
||||
(_use_manual_progress IS TRUE AND _manual_value IS NOT NULL AND
|
||||
(_progress_mode IS NULL OR _progress_mode = 'manual'))
|
||||
THEN
|
||||
RETURN JSON_BUILD_OBJECT(
|
||||
'ratio', _manual_value,
|
||||
'total_completed', 0,
|
||||
@@ -350,18 +528,22 @@ BEGIN
|
||||
'is_manual', TRUE
|
||||
);
|
||||
END IF;
|
||||
END;
|
||||
|
||||
-- If there are no subtasks, just use the parent task's status (unless in time-based mode)
|
||||
IF _sub_tasks_count = 0 THEN
|
||||
IF _sub_tasks_count = 0
|
||||
THEN
|
||||
-- Use time-based estimation for tasks without subtasks if enabled
|
||||
IF _use_time_progress IS TRUE THEN
|
||||
IF _use_time_progress IS TRUE
|
||||
THEN
|
||||
-- For time-based tasks without subtasks, we still need some progress calculation
|
||||
-- If the task is completed, return 100%
|
||||
-- Otherwise, use the progress value if set manually, or 0
|
||||
SELECT
|
||||
CASE
|
||||
-- Otherwise, use the progress value if set manually in the correct mode, or 0
|
||||
SELECT CASE
|
||||
WHEN _task_complete IS TRUE THEN 100
|
||||
ELSE COALESCE(_manual_value, 0)
|
||||
WHEN _manual_value IS NOT NULL AND (_progress_mode = 'time' OR _progress_mode IS NULL)
|
||||
THEN _manual_value
|
||||
ELSE 0
|
||||
END
|
||||
INTO _ratio;
|
||||
ELSE
|
||||
@@ -373,68 +555,60 @@ BEGIN
|
||||
END IF;
|
||||
ELSE
|
||||
-- If project uses manual progress, calculate based on subtask manual progress values
|
||||
IF _use_manual_progress IS TRUE THEN
|
||||
WITH subtask_progress AS (
|
||||
SELECT
|
||||
t.id,
|
||||
IF _use_manual_progress IS TRUE
|
||||
THEN
|
||||
WITH subtask_progress AS (SELECT t.id,
|
||||
t.manual_progress,
|
||||
t.progress_value,
|
||||
EXISTS(
|
||||
SELECT 1
|
||||
t.progress_mode,
|
||||
EXISTS(SELECT 1
|
||||
FROM tasks_with_status_view
|
||||
WHERE tasks_with_status_view.task_id = t.id
|
||||
AND is_done IS TRUE
|
||||
) AS is_complete
|
||||
AND is_done IS TRUE) AS is_complete
|
||||
FROM tasks t
|
||||
WHERE t.parent_task_id = _task_id
|
||||
AND t.archived IS FALSE
|
||||
),
|
||||
subtask_with_values AS (
|
||||
SELECT
|
||||
CASE
|
||||
AND t.archived IS FALSE),
|
||||
subtask_with_values AS (SELECT CASE
|
||||
-- For completed tasks, always use 100%
|
||||
WHEN is_complete IS TRUE THEN 100
|
||||
-- For tasks with progress value set, use it regardless of manual_progress flag
|
||||
WHEN progress_value IS NOT NULL THEN progress_value
|
||||
-- Default to 0 for incomplete tasks with no progress value
|
||||
-- For tasks with progress value set in the correct mode, use it
|
||||
WHEN progress_value IS NOT NULL AND
|
||||
(progress_mode = 'manual' OR progress_mode IS NULL)
|
||||
THEN progress_value
|
||||
-- Default to 0 for incomplete tasks with no progress value or wrong mode
|
||||
ELSE 0
|
||||
END AS progress_value
|
||||
FROM subtask_progress
|
||||
)
|
||||
FROM subtask_progress)
|
||||
SELECT COALESCE(AVG(progress_value), 0)
|
||||
FROM subtask_with_values
|
||||
INTO _ratio;
|
||||
-- If project uses weighted progress, calculate based on subtask weights
|
||||
ELSIF _use_weighted_progress IS TRUE THEN
|
||||
WITH subtask_progress AS (
|
||||
SELECT
|
||||
t.id,
|
||||
ELSIF _use_weighted_progress IS TRUE
|
||||
THEN
|
||||
WITH subtask_progress AS (SELECT t.id,
|
||||
t.manual_progress,
|
||||
t.progress_value,
|
||||
EXISTS(
|
||||
SELECT 1
|
||||
t.progress_mode,
|
||||
EXISTS(SELECT 1
|
||||
FROM tasks_with_status_view
|
||||
WHERE tasks_with_status_view.task_id = t.id
|
||||
AND is_done IS TRUE
|
||||
) AS is_complete,
|
||||
AND is_done IS TRUE) AS is_complete,
|
||||
COALESCE(t.weight, 100) AS weight
|
||||
FROM tasks t
|
||||
WHERE t.parent_task_id = _task_id
|
||||
AND t.archived IS FALSE
|
||||
),
|
||||
subtask_with_values AS (
|
||||
SELECT
|
||||
CASE
|
||||
AND t.archived IS FALSE),
|
||||
subtask_with_values AS (SELECT CASE
|
||||
-- For completed tasks, always use 100%
|
||||
WHEN is_complete IS TRUE THEN 100
|
||||
-- For tasks with progress value set, use it regardless of manual_progress flag
|
||||
WHEN progress_value IS NOT NULL THEN progress_value
|
||||
-- Default to 0 for incomplete tasks with no progress value
|
||||
-- For tasks with progress value set in the correct mode, use it
|
||||
WHEN progress_value IS NOT NULL AND
|
||||
(progress_mode = 'weighted' OR progress_mode IS NULL)
|
||||
THEN progress_value
|
||||
-- Default to 0 for incomplete tasks with no progress value or wrong mode
|
||||
ELSE 0
|
||||
END AS progress_value,
|
||||
weight
|
||||
FROM subtask_progress
|
||||
)
|
||||
FROM subtask_progress)
|
||||
SELECT COALESCE(
|
||||
SUM(progress_value * weight) / NULLIF(SUM(weight), 0),
|
||||
0
|
||||
@@ -442,36 +616,32 @@ BEGIN
|
||||
FROM subtask_with_values
|
||||
INTO _ratio;
|
||||
-- If project uses time-based progress, calculate based on estimated time
|
||||
ELSIF _use_time_progress IS TRUE THEN
|
||||
WITH subtask_progress AS (
|
||||
SELECT
|
||||
t.id,
|
||||
ELSIF _use_time_progress IS TRUE
|
||||
THEN
|
||||
WITH subtask_progress AS (SELECT t.id,
|
||||
t.manual_progress,
|
||||
t.progress_value,
|
||||
EXISTS(
|
||||
SELECT 1
|
||||
t.progress_mode,
|
||||
EXISTS(SELECT 1
|
||||
FROM tasks_with_status_view
|
||||
WHERE tasks_with_status_view.task_id = t.id
|
||||
AND is_done IS TRUE
|
||||
) AS is_complete,
|
||||
AND is_done IS TRUE) AS is_complete,
|
||||
COALESCE(t.total_minutes, 0) AS estimated_minutes
|
||||
FROM tasks t
|
||||
WHERE t.parent_task_id = _task_id
|
||||
AND t.archived IS FALSE
|
||||
),
|
||||
subtask_with_values AS (
|
||||
SELECT
|
||||
CASE
|
||||
AND t.archived IS FALSE),
|
||||
subtask_with_values AS (SELECT CASE
|
||||
-- For completed tasks, always use 100%
|
||||
WHEN is_complete IS TRUE THEN 100
|
||||
-- For tasks with progress value set, use it regardless of manual_progress flag
|
||||
WHEN progress_value IS NOT NULL THEN progress_value
|
||||
-- Default to 0 for incomplete tasks with no progress value
|
||||
-- For tasks with progress value set in the correct mode, use it
|
||||
WHEN progress_value IS NOT NULL AND
|
||||
(progress_mode = 'time' OR progress_mode IS NULL)
|
||||
THEN progress_value
|
||||
-- Default to 0 for incomplete tasks with no progress value or wrong mode
|
||||
ELSE 0
|
||||
END AS progress_value,
|
||||
estimated_minutes
|
||||
FROM subtask_progress
|
||||
)
|
||||
FROM subtask_progress)
|
||||
SELECT COALESCE(
|
||||
SUM(progress_value * estimated_minutes) / NULLIF(SUM(estimated_minutes), 0),
|
||||
0
|
||||
@@ -492,7 +662,8 @@ BEGIN
|
||||
_total_completed = _parent_task_done + _sub_tasks_done;
|
||||
_total_tasks = _sub_tasks_count + 1; -- +1 for the parent task
|
||||
|
||||
IF _total_tasks = 0 THEN
|
||||
IF _total_tasks = 0
|
||||
THEN
|
||||
_ratio = 0;
|
||||
ELSE
|
||||
_ratio = (_total_completed / _total_tasks) * 100;
|
||||
@@ -501,9 +672,11 @@ BEGIN
|
||||
END IF;
|
||||
|
||||
-- Ensure ratio is between 0 and 100
|
||||
IF _ratio < 0 THEN
|
||||
IF _ratio < 0
|
||||
THEN
|
||||
_ratio = 0;
|
||||
ELSIF _ratio > 100 THEN
|
||||
ELSIF _ratio > 100
|
||||
THEN
|
||||
_ratio = 100;
|
||||
END IF;
|
||||
|
||||
@@ -654,4 +827,144 @@ $$;
|
||||
|
||||
COMMIT;
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- Update the on_update_task_progress function to set progress_mode
|
||||
CREATE OR REPLACE FUNCTION on_update_task_progress(_body JSON) RETURNS JSON
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
_task_id UUID;
|
||||
_progress_value INTEGER;
|
||||
_parent_task_id UUID;
|
||||
_project_id UUID;
|
||||
_current_mode VARCHAR(20);
|
||||
BEGIN
|
||||
_task_id = (_body ->> 'task_id')::UUID;
|
||||
_progress_value = (_body ->> 'progress_value')::INTEGER;
|
||||
_parent_task_id = (_body ->> 'parent_task_id')::UUID;
|
||||
|
||||
-- Get the project ID and determine the current progress mode
|
||||
SELECT project_id INTO _project_id FROM tasks WHERE id = _task_id;
|
||||
|
||||
IF _project_id IS NOT NULL
|
||||
THEN
|
||||
SELECT CASE
|
||||
WHEN use_manual_progress IS TRUE THEN 'manual'
|
||||
WHEN use_weighted_progress IS TRUE THEN 'weighted'
|
||||
WHEN use_time_progress IS TRUE THEN 'time'
|
||||
ELSE 'default'
|
||||
END
|
||||
INTO _current_mode
|
||||
FROM projects
|
||||
WHERE id = _project_id;
|
||||
ELSE
|
||||
_current_mode := 'default';
|
||||
END IF;
|
||||
|
||||
-- Update the task with progress value and set the progress mode
|
||||
UPDATE tasks
|
||||
SET progress_value = _progress_value,
|
||||
manual_progress = TRUE,
|
||||
progress_mode = _current_mode,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = _task_id;
|
||||
|
||||
-- Return the updated task info
|
||||
RETURN JSON_BUILD_OBJECT(
|
||||
'task_id', _task_id,
|
||||
'progress_value', _progress_value,
|
||||
'progress_mode', _current_mode
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Update the on_update_task_weight function to set progress_mode when weight is updated
|
||||
CREATE OR REPLACE FUNCTION on_update_task_weight(_body JSON) RETURNS JSON
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
_task_id UUID;
|
||||
_weight INTEGER;
|
||||
_parent_task_id UUID;
|
||||
_project_id UUID;
|
||||
BEGIN
|
||||
_task_id = (_body ->> 'task_id')::UUID;
|
||||
_weight = (_body ->> 'weight')::INTEGER;
|
||||
_parent_task_id = (_body ->> 'parent_task_id')::UUID;
|
||||
|
||||
-- Get the project ID
|
||||
SELECT project_id INTO _project_id FROM tasks WHERE id = _task_id;
|
||||
|
||||
-- Update the task with weight value and set progress_mode to 'weighted'
|
||||
UPDATE tasks
|
||||
SET weight = _weight,
|
||||
progress_mode = 'weighted',
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = _task_id;
|
||||
|
||||
-- Return the updated task info
|
||||
RETURN JSON_BUILD_OBJECT(
|
||||
'task_id', _task_id,
|
||||
'weight', _weight
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Create a function to reset progress values when switching project progress modes
|
||||
CREATE OR REPLACE FUNCTION reset_project_progress_values() RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
_old_mode VARCHAR(20);
|
||||
_new_mode VARCHAR(20);
|
||||
_project_id UUID;
|
||||
BEGIN
|
||||
_project_id := NEW.id;
|
||||
|
||||
-- Determine old and new modes
|
||||
_old_mode :=
|
||||
CASE
|
||||
WHEN OLD.use_manual_progress IS TRUE THEN 'manual'
|
||||
WHEN OLD.use_weighted_progress IS TRUE THEN 'weighted'
|
||||
WHEN OLD.use_time_progress IS TRUE THEN 'time'
|
||||
ELSE 'default'
|
||||
END;
|
||||
|
||||
_new_mode :=
|
||||
CASE
|
||||
WHEN NEW.use_manual_progress IS TRUE THEN 'manual'
|
||||
WHEN NEW.use_weighted_progress IS TRUE THEN 'weighted'
|
||||
WHEN NEW.use_time_progress IS TRUE THEN 'time'
|
||||
ELSE 'default'
|
||||
END;
|
||||
|
||||
-- If mode has changed, reset progress values for tasks with the old mode
|
||||
IF _old_mode <> _new_mode
|
||||
THEN
|
||||
-- Reset progress values for tasks that were set in the old mode
|
||||
UPDATE tasks
|
||||
SET progress_value = NULL,
|
||||
progress_mode = NULL
|
||||
WHERE project_id = _project_id
|
||||
AND progress_mode = _old_mode;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- Create trigger to reset progress values when project progress mode changes
|
||||
DROP TRIGGER IF EXISTS reset_progress_on_mode_change ON projects;
|
||||
CREATE TRIGGER reset_progress_on_mode_change
|
||||
AFTER UPDATE OF use_manual_progress, use_weighted_progress, use_time_progress
|
||||
ON projects
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION reset_project_progress_values();
|
||||
|
||||
COMMIT;
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
<title>Worklenz</title>
|
||||
<!-- Environment configuration -->
|
||||
<script src="/env-config.js"></script>
|
||||
<!-- Unregister service worker -->
|
||||
<script src="/unregister-sw.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -109,5 +109,13 @@
|
||||
"expiredDaysAgo": "{{days}} days ago",
|
||||
|
||||
"continueWith": "Continue with {{plan}}",
|
||||
"changeToPlan": "Change to {{plan}}"
|
||||
"changeToPlan": "Change to {{plan}}",
|
||||
"creditPlan": "Credit Plan",
|
||||
"customPlan": "Custom Plan",
|
||||
"planValidTill": "Your plan is valid till {{date}}",
|
||||
"purchaseSeatsText": "To continue, you'll need to purchase additional seats.",
|
||||
"currentSeatsText": "You currently have {{seats}} seats available.",
|
||||
"selectSeatsText": "Please select the number of additional seats to purchase.",
|
||||
"purchase": "Purchase",
|
||||
"contactSales": "Contact sales"
|
||||
}
|
||||
|
||||
@@ -101,5 +101,13 @@
|
||||
|
||||
"expirestoday": "hoy",
|
||||
"expirestomorrow": "mañana",
|
||||
"expiredDaysAgo": "hace {{days}} días"
|
||||
"expiredDaysAgo": "hace {{days}} días",
|
||||
"creditPlan": "Plan de Crédito",
|
||||
"customPlan": "Plan Personalizado",
|
||||
"planValidTill": "Su plan es válido hasta {{date}}",
|
||||
"purchaseSeatsText": "Para continuar, deberá comprar asientos adicionales.",
|
||||
"currentSeatsText": "Actualmente tiene {{seats}} asientos disponibles.",
|
||||
"selectSeatsText": "Seleccione el número de asientos adicionales a comprar.",
|
||||
"purchase": "Comprar",
|
||||
"contactSales": "Contactar ventas"
|
||||
}
|
||||
|
||||
@@ -101,5 +101,13 @@
|
||||
|
||||
"expirestoday": "hoje",
|
||||
"expirestomorrow": "amanhã",
|
||||
"expiredDaysAgo": "há {{days}} dias"
|
||||
"expiredDaysAgo": "há {{days}} dias",
|
||||
"creditPlan": "Plano de Crédito",
|
||||
"customPlan": "Plano Personalizado",
|
||||
"planValidTill": "Seu plano é válido até {{date}}",
|
||||
"purchaseSeatsText": "Para continuar, você precisará comprar assentos adicionais.",
|
||||
"currentSeatsText": "Atualmente você tem {{seats}} assentos disponíveis.",
|
||||
"selectSeatsText": "Selecione o número de assentos adicionais para comprar.",
|
||||
"purchase": "Comprar",
|
||||
"contactSales": "Fale com vendas"
|
||||
}
|
||||
|
||||
7
worklenz-frontend/public/unregister-sw.js
Normal file
7
worklenz-frontend/public/unregister-sw.js
Normal file
@@ -0,0 +1,7 @@
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.getRegistrations().then(function(registrations) {
|
||||
for(let registration of registrations) {
|
||||
registration.unregister();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -246,7 +246,7 @@ const CurrentPlanDetails = () => {
|
||||
|
||||
const renderFreePlan = () => (
|
||||
<Flex vertical>
|
||||
<Typography.Text strong>Free Plan</Typography.Text>
|
||||
<Typography.Text strong>{t('freePlan')}</Typography.Text>
|
||||
<Typography.Text>
|
||||
<br />-{' '}
|
||||
{freePlanSettings?.team_member_limit === 0
|
||||
@@ -309,14 +309,14 @@ const CurrentPlanDetails = () => {
|
||||
|
||||
const renderCreditSubscriptionInfo = () => {
|
||||
return <Flex vertical>
|
||||
<Typography.Text strong>Credit Plan</Typography.Text>
|
||||
<Typography.Text strong>{t('creditPlan','Credit Plan')}</Typography.Text>
|
||||
</Flex>
|
||||
};
|
||||
|
||||
const renderCustomSubscriptionInfo = () => {
|
||||
return <Flex vertical>
|
||||
<Typography.Text strong>Custom Plan</Typography.Text>
|
||||
<Typography.Text>Your plan is valid till {billingInfo?.valid_till_date}</Typography.Text>
|
||||
<Typography.Text strong>{t('customPlan','Custom Plan')}</Typography.Text>
|
||||
<Typography.Text>{t('planValidTill','Your plan is valid till {{date}}',{date: billingInfo?.valid_till_date})}</Typography.Text>
|
||||
</Flex>
|
||||
};
|
||||
|
||||
@@ -381,15 +381,15 @@ const CurrentPlanDetails = () => {
|
||||
>
|
||||
<Flex vertical gap="middle" style={{ marginTop: '8px' }}>
|
||||
<Typography.Paragraph style={{ fontSize: '16px', margin: '0 0 16px 0', fontWeight: 500 }}>
|
||||
To continue, you'll need to purchase additional seats.
|
||||
{t('purchaseSeatsText','To continue, you\'ll need to purchase additional seats.')}
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Typography.Paragraph style={{ margin: '0 0 16px 0' }}>
|
||||
You currently have {billingInfo?.total_seats} seats available.
|
||||
{t('currentSeatsText','You currently have {{seats}} seats available.',{seats: billingInfo?.total_seats})}
|
||||
</Typography.Paragraph>
|
||||
|
||||
<Typography.Paragraph style={{ margin: '0 0 24px 0' }}>
|
||||
Please select the number of additional seats to purchase.
|
||||
{t('selectSeatsText','Please select the number of additional seats to purchase.')}
|
||||
</Typography.Paragraph>
|
||||
|
||||
<div style={{ marginBottom: '24px' }}>
|
||||
@@ -416,14 +416,14 @@ const CurrentPlanDetails = () => {
|
||||
borderRadius: '2px'
|
||||
}}
|
||||
>
|
||||
Purchase
|
||||
{t('purchase','Purchase')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
size="middle"
|
||||
>
|
||||
Contact sales
|
||||
{t('contactSales','Contact sales')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LabelType } from './label.type';
|
||||
import { MemberType } from './member.types';
|
||||
import { ProjectType } from './project.types';
|
||||
import { ITaskLabel } from './tasks/taskLabel.types';
|
||||
|
||||
export type TaskStatusType = 'doing' | 'todo' | 'done';
|
||||
export type TaskPriorityType = 'low' | 'medium' | 'high';
|
||||
@@ -13,13 +13,16 @@ export type SubTaskType = {
|
||||
subTaskDueDate?: Date;
|
||||
};
|
||||
|
||||
export type ProgressModeType = 'manual' | 'weighted' | 'time' | 'default';
|
||||
|
||||
export type TaskType = {
|
||||
taskId: string;
|
||||
progress_mode?: ProgressModeType;
|
||||
task: string;
|
||||
description?: string | null;
|
||||
progress?: number;
|
||||
members?: MemberType[];
|
||||
labels?: LabelType[];
|
||||
labels?: ITaskLabel[];
|
||||
status: TaskStatusType | string;
|
||||
priority: TaskPriorityType | string;
|
||||
timeTracking?: number;
|
||||
|
||||
Reference in New Issue
Block a user