From e4bc19f646b0fa0cf9395b3d679242a8cdf8875f Mon Sep 17 00:00:00 2001 From: Cemonix Date: Fri, 12 Sep 2025 17:16:16 +0200 Subject: [PATCH 1/2] refactor: Remove unused domain model imports in PermissionsController --- server/Server/Controllers/PermissionsController.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/Server/Controllers/PermissionsController.cs b/server/Server/Controllers/PermissionsController.cs index f9548ce..119f0af 100644 --- a/server/Server/Controllers/PermissionsController.cs +++ b/server/Server/Controllers/PermissionsController.cs @@ -1,8 +1,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Identity; -using server.Models.Domain; -using server.Models.Domain.Enums; using server.Models.DTOs.Authz; using server.Services.Interfaces; From fd8ea77672dacfdd03a356135ef2c25ed17727a9 Mon Sep 17 00:00:00 2001 From: Cemonix Date: Fri, 12 Sep 2025 17:16:21 +0200 Subject: [PATCH 2/2] refactor: Enhance workflow card to display task progress and update related services --- .../project/workflow/WorkflowCard.vue | 117 ++++++++++++++++++ .../project/dashboard/dashboardService.ts | 1 + .../project/workflow/workflowStageService.ts | 15 +++ frontend/src/views/project/WorkflowsView.vue | 28 ++++- 4 files changed, 157 insertions(+), 4 deletions(-) diff --git a/frontend/src/components/project/workflow/WorkflowCard.vue b/frontend/src/components/project/workflow/WorkflowCard.vue index 1447cb2..a54944d 100644 --- a/frontend/src/components/project/workflow/WorkflowCard.vue +++ b/frontend/src/components/project/workflow/WorkflowCard.vue @@ -19,6 +19,28 @@ +
+

Task Progress

+
+
+
+ {{ stage.stageName }} + {{ stage.completedTasks }}/{{ stage.totalTasks }} +
+
+
+
+
+
+
+
(); @@ -102,6 +126,11 @@ const formatDate = (dateString: string): string => { day: 'numeric' }); }; + +const getProgressPercentage = (stage: WorkflowStageProgressDto): number => { + if (stage.totalTasks === 0) return 0; + return Math.round((stage.completedTasks / stage.totalTasks) * 100); +}; diff --git a/frontend/src/services/project/dashboard/dashboardService.ts b/frontend/src/services/project/dashboard/dashboardService.ts index d1d00c7..a9c94ea 100644 --- a/frontend/src/services/project/dashboard/dashboardService.ts +++ b/frontend/src/services/project/dashboard/dashboardService.ts @@ -102,6 +102,7 @@ class DashboardService extends BaseProjectService { }); return response; } + } export const dashboardService = new DashboardService(); \ No newline at end of file diff --git a/frontend/src/services/project/workflow/workflowStageService.ts b/frontend/src/services/project/workflow/workflowStageService.ts index e5e0d5c..ccb359d 100644 --- a/frontend/src/services/project/workflow/workflowStageService.ts +++ b/frontend/src/services/project/workflow/workflowStageService.ts @@ -5,6 +5,7 @@ import type { CreateWorkflowStageRequest, UpdateWorkflowStageRequest, } from "./workflowStage.types"; +import type { WorkflowStageProgressDto } from '../dashboard/dashboard.types'; import type { QueryParams } from '@/services/base/requests'; import { workflowService } from './workflowService'; import type { WorkflowWithStages } from './workflow.types'; @@ -205,6 +206,20 @@ class WorkflowStageService extends BaseProjectService { this.logger.info(`Successfully reordered ${response.length} stages in workflow ${workflowId}`); return response; } + + /** + * Get task progress for all stages in a workflow + */ + async getWorkflowStageProgress(projectId: number, workflowId: number): Promise { + this.logger.info(`Fetching task progress for stages in workflow ${workflowId}, project ${projectId}`); + + const response = await this.get( + this.buildProjectUrl(projectId, `dashboard/analytics/workflow-stage-progress?workflowId=${workflowId}`) + ); + + this.logger.info(`Fetched task progress for ${response.length} stages in workflow ${workflowId}`); + return response; + } } export const workflowStageService = new WorkflowStageService(); \ No newline at end of file diff --git a/frontend/src/views/project/WorkflowsView.vue b/frontend/src/views/project/WorkflowsView.vue index 93cde46..382e0be 100644 --- a/frontend/src/views/project/WorkflowsView.vue +++ b/frontend/src/views/project/WorkflowsView.vue @@ -17,6 +17,7 @@ :stages="workflowStages.get(workflow.id) || []" :user-role="userRole || undefined" :user-email="userEmail || undefined" + :stage-progress="workflowStageProgress.get(workflow.id) || []" />

No workflows have been created for this project yet. @@ -51,6 +52,7 @@ import FloatingActionButton from '@/components/common/FloatingActionButton.vue'; import {type CreateWorkflowWithStagesRequest, type Workflow} from '@/services/project/workflow/workflow.types'; import {type WorkflowStage} from '@/services/project/workflow/workflowStage.types'; import {type ProjectRole} from '@/services/project/project.types'; +import {type WorkflowStageProgressDto} from '@/services/project/dashboard/dashboard.types'; import {workflowService} from '@/services/project/workflow/workflowService'; import {workflowStageService} from '@/services/project/workflow/workflowStageService'; import {projectMemberService} from '@/services/project'; @@ -68,6 +70,7 @@ const { handleError } = useErrorHandler(); const workflows = ref([]); const workflowStages = ref>(new Map()); +const workflowStageProgress = ref>(new Map()); const userRole = ref(null); const userEmail = ref(null); const isModalOpen = ref(false); @@ -103,6 +106,17 @@ const loadWorkflowStages = async (projectId: number, workflowId: number) => { } }; +const loadWorkflowStageProgress = async (projectId: number, workflowId: number) => { + try { + const progress = await workflowStageService.getWorkflowStageProgress(projectId, workflowId); + workflowStageProgress.value.set(workflowId, progress); + logger.info(`Loaded stage progress for workflow ${workflowId}: ${progress.length} stages`); + } catch (error) { + logger.error(`Failed to load stage progress for workflow ${workflowId}`, error); + workflowStageProgress.value.set(workflowId, []); // Set empty array as fallback + } +}; + const loadWorkflows = async () => { const projectId = Number(route.params.projectId); if (!projectId || isNaN(projectId)) { @@ -122,11 +136,14 @@ const loadWorkflows = async () => { workflows.value = workflowResponse.data; logger.info(`Loaded ${workflowResponse.data.length} workflows for project ${projectId}`); - // Load stages for each workflow + // Load stages and progress for each workflow const stagePromises = workflowResponse.data.map(workflow => loadWorkflowStages(projectId, workflow.id) ); - await Promise.all(stagePromises); + const progressPromises = workflowResponse.data.map(workflow => + loadWorkflowStageProgress(projectId, workflow.id) + ); + await Promise.all([...stagePromises, ...progressPromises]); } catch (error) { handleError(error, 'Failed to load workflows'); @@ -159,8 +176,11 @@ const handleCreateWorkflow = async (formData: CreateWorkflowWithStagesRequest) = workflows.value.push(newWorkflow); logger.info(`Created workflow with stages: ${newWorkflow.name} (ID: ${newWorkflow.id})`); - // Load stages for the newly created workflow - await loadWorkflowStages(projectId, newWorkflow.id); + // Load stages and progress for the newly created workflow + await Promise.all([ + loadWorkflowStages(projectId, newWorkflow.id), + loadWorkflowStageProgress(projectId, newWorkflow.id) + ]); closeModal(); showCreateSuccess("Workflow", newWorkflow.name);