@hed-hog/operations 0.0.330 → 0.0.332
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/controllers/operations-collaborators.controller.d.ts +58 -213
- package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
- package/dist/controllers/operations-collaborators.controller.js +100 -0
- package/dist/controllers/operations-collaborators.controller.js.map +1 -1
- package/dist/controllers/operations-contracts.controller.d.ts +6 -6
- package/dist/controllers/operations-projects.controller.d.ts +25 -0
- package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
- package/dist/controllers/operations-projects.controller.js +48 -0
- package/dist/controllers/operations-projects.controller.js.map +1 -1
- package/dist/controllers/operations-reports.controller.d.ts +1 -1
- package/dist/controllers/operations-tasks.controller.d.ts +34 -9
- package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
- package/dist/controllers/operations-tasks.controller.js +43 -32
- package/dist/controllers/operations-tasks.controller.js.map +1 -1
- package/dist/controllers/operations-timesheets.controller.d.ts +9 -9
- package/dist/dashboard/components/DashboardLayout.d.ts +30 -0
- package/dist/dashboard/components/DashboardLayout.d.ts.map +1 -0
- package/dist/dashboard/components/DashboardLayout.js +87 -0
- package/dist/dashboard/components/DashboardLayout.js.map +1 -0
- package/dist/dashboard/components/widget-registry.d.ts +23 -0
- package/dist/dashboard/components/widget-registry.d.ts.map +1 -0
- package/dist/dashboard/components/widget-registry.js +245 -0
- package/dist/dashboard/components/widget-registry.js.map +1 -0
- package/dist/dashboard/hooks/useDashboardData.d.ts +20 -0
- package/dist/dashboard/hooks/useDashboardData.d.ts.map +1 -0
- package/dist/dashboard/hooks/useDashboardData.js +24 -0
- package/dist/dashboard/hooks/useDashboardData.js.map +1 -0
- package/dist/dashboard/types/widgets.types.d.ts +233 -0
- package/dist/dashboard/types/widgets.types.d.ts.map +1 -0
- package/dist/dashboard/types/widgets.types.js +6 -0
- package/dist/dashboard/types/widgets.types.js.map +1 -0
- package/dist/dashboard/widgets/CapacityDistribution.d.ts +23 -0
- package/dist/dashboard/widgets/CapacityDistribution.d.ts.map +1 -0
- package/dist/dashboard/widgets/CapacityDistribution.js +11 -0
- package/dist/dashboard/widgets/CapacityDistribution.js.map +1 -0
- package/dist/dashboard/widgets/EffortByProject.d.ts +22 -0
- package/dist/dashboard/widgets/EffortByProject.d.ts.map +1 -0
- package/dist/dashboard/widgets/EffortByProject.js +11 -0
- package/dist/dashboard/widgets/EffortByProject.js.map +1 -0
- package/dist/dashboard/widgets/HeadcountByArea.d.ts +24 -0
- package/dist/dashboard/widgets/HeadcountByArea.d.ts.map +1 -0
- package/dist/dashboard/widgets/HeadcountByArea.js +11 -0
- package/dist/dashboard/widgets/HeadcountByArea.js.map +1 -0
- package/dist/dashboard/widgets/ManagedProjectsStatus.d.ts +18 -0
- package/dist/dashboard/widgets/ManagedProjectsStatus.d.ts.map +1 -0
- package/dist/dashboard/widgets/ManagedProjectsStatus.js +12 -0
- package/dist/dashboard/widgets/ManagedProjectsStatus.js.map +1 -0
- package/dist/dashboard/widgets/MyHoursPeriodKpi.d.ts +22 -0
- package/dist/dashboard/widgets/MyHoursPeriodKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyHoursPeriodKpi.js +12 -0
- package/dist/dashboard/widgets/MyHoursPeriodKpi.js.map +1 -0
- package/dist/dashboard/widgets/MyOpenRequestsKpi.d.ts +19 -0
- package/dist/dashboard/widgets/MyOpenRequestsKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyOpenRequestsKpi.js +17 -0
- package/dist/dashboard/widgets/MyOpenRequestsKpi.js.map +1 -0
- package/dist/dashboard/widgets/MyPendingRequestsList.d.ts +23 -0
- package/dist/dashboard/widgets/MyPendingRequestsList.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyPendingRequestsList.js +14 -0
- package/dist/dashboard/widgets/MyPendingRequestsList.js.map +1 -0
- package/dist/dashboard/widgets/MyProjectAllocationsKpi.d.ts +22 -0
- package/dist/dashboard/widgets/MyProjectAllocationsKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyProjectAllocationsKpi.js +11 -0
- package/dist/dashboard/widgets/MyProjectAllocationsKpi.js.map +1 -0
- package/dist/dashboard/widgets/MyQuickActions.d.ts +23 -0
- package/dist/dashboard/widgets/MyQuickActions.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyQuickActions.js +18 -0
- package/dist/dashboard/widgets/MyQuickActions.js.map +1 -0
- package/dist/dashboard/widgets/MyRelevantDeadlines.d.ts +23 -0
- package/dist/dashboard/widgets/MyRelevantDeadlines.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyRelevantDeadlines.js +22 -0
- package/dist/dashboard/widgets/MyRelevantDeadlines.js.map +1 -0
- package/dist/dashboard/widgets/MyTimesheetStatusKpi.d.ts +17 -0
- package/dist/dashboard/widgets/MyTimesheetStatusKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyTimesheetStatusKpi.js +11 -0
- package/dist/dashboard/widgets/MyTimesheetStatusKpi.js.map +1 -0
- package/dist/dashboard/widgets/MyWeeklyJourney.d.ts +21 -0
- package/dist/dashboard/widgets/MyWeeklyJourney.d.ts.map +1 -0
- package/dist/dashboard/widgets/MyWeeklyJourney.js +19 -0
- package/dist/dashboard/widgets/MyWeeklyJourney.js.map +1 -0
- package/dist/dashboard/widgets/PortfolioCostsKpi.d.ts +19 -0
- package/dist/dashboard/widgets/PortfolioCostsKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/PortfolioCostsKpi.js +12 -0
- package/dist/dashboard/widgets/PortfolioCostsKpi.js.map +1 -0
- package/dist/dashboard/widgets/PortfolioEffortKpi.d.ts +18 -0
- package/dist/dashboard/widgets/PortfolioEffortKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/PortfolioEffortKpi.js +8 -0
- package/dist/dashboard/widgets/PortfolioEffortKpi.js.map +1 -0
- package/dist/dashboard/widgets/PortfolioProjectsKpi.d.ts +22 -0
- package/dist/dashboard/widgets/PortfolioProjectsKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/PortfolioProjectsKpi.js +56 -0
- package/dist/dashboard/widgets/PortfolioProjectsKpi.js.map +1 -0
- package/dist/dashboard/widgets/PortfolioRiskKpi.d.ts +19 -0
- package/dist/dashboard/widgets/PortfolioRiskKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/PortfolioRiskKpi.js +11 -0
- package/dist/dashboard/widgets/PortfolioRiskKpi.js.map +1 -0
- package/dist/dashboard/widgets/ProjectStatusOverview.d.ts +19 -0
- package/dist/dashboard/widgets/ProjectStatusOverview.d.ts.map +1 -0
- package/dist/dashboard/widgets/ProjectStatusOverview.js +18 -0
- package/dist/dashboard/widgets/ProjectStatusOverview.js.map +1 -0
- package/dist/dashboard/widgets/StrategicDeadlines.d.ts +24 -0
- package/dist/dashboard/widgets/StrategicDeadlines.d.ts.map +1 -0
- package/dist/dashboard/widgets/StrategicDeadlines.js +22 -0
- package/dist/dashboard/widgets/StrategicDeadlines.js.map +1 -0
- package/dist/dashboard/widgets/TeamApprovalQueue.d.ts +24 -0
- package/dist/dashboard/widgets/TeamApprovalQueue.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamApprovalQueue.js +12 -0
- package/dist/dashboard/widgets/TeamApprovalQueue.js.map +1 -0
- package/dist/dashboard/widgets/TeamCapacityKpi.d.ts +18 -0
- package/dist/dashboard/widgets/TeamCapacityKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamCapacityKpi.js +19 -0
- package/dist/dashboard/widgets/TeamCapacityKpi.js.map +1 -0
- package/dist/dashboard/widgets/TeamHeadcountKpi.d.ts +22 -0
- package/dist/dashboard/widgets/TeamHeadcountKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamHeadcountKpi.js +56 -0
- package/dist/dashboard/widgets/TeamHeadcountKpi.js.map +1 -0
- package/dist/dashboard/widgets/TeamHoursKpi.d.ts +19 -0
- package/dist/dashboard/widgets/TeamHoursKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamHoursKpi.js +13 -0
- package/dist/dashboard/widgets/TeamHoursKpi.js.map +1 -0
- package/dist/dashboard/widgets/TeamPendingApprovalsKpi.d.ts +20 -0
- package/dist/dashboard/widgets/TeamPendingApprovalsKpi.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamPendingApprovalsKpi.js +11 -0
- package/dist/dashboard/widgets/TeamPendingApprovalsKpi.js.map +1 -0
- package/dist/dashboard/widgets/TeamUtilizationOverview.d.ts +18 -0
- package/dist/dashboard/widgets/TeamUtilizationOverview.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamUtilizationOverview.js +17 -0
- package/dist/dashboard/widgets/TeamUtilizationOverview.js.map +1 -0
- package/dist/dashboard/widgets/TeamWorkloadAlerts.d.ts +24 -0
- package/dist/dashboard/widgets/TeamWorkloadAlerts.d.ts.map +1 -0
- package/dist/dashboard/widgets/TeamWorkloadAlerts.js +19 -0
- package/dist/dashboard/widgets/TeamWorkloadAlerts.js.map +1 -0
- package/dist/dashboard/widgets/index.d.ts +24 -0
- package/dist/dashboard/widgets/index.d.ts.map +1 -0
- package/dist/dashboard/widgets/index.js +54 -0
- package/dist/dashboard/widgets/index.js.map +1 -0
- package/dist/dto/create-collaborator-invoice.dto.d.ts +11 -0
- package/dist/dto/create-collaborator-invoice.dto.d.ts.map +1 -0
- package/dist/dto/create-collaborator-invoice.dto.js +55 -0
- package/dist/dto/create-collaborator-invoice.dto.js.map +1 -0
- package/dist/dto/create-collaborator-payment.dto.d.ts +10 -0
- package/dist/dto/create-collaborator-payment.dto.d.ts.map +1 -0
- package/dist/dto/create-collaborator-payment.dto.js +50 -0
- package/dist/dto/create-collaborator-payment.dto.js.map +1 -0
- package/dist/dto/create-collaborator.dto.d.ts +0 -1
- package/dist/dto/create-collaborator.dto.d.ts.map +1 -1
- package/dist/dto/create-collaborator.dto.js +0 -6
- package/dist/dto/create-collaborator.dto.js.map +1 -1
- package/dist/dto/list-collaborator-invoice.dto.d.ts +4 -0
- package/dist/dto/list-collaborator-invoice.dto.d.ts.map +1 -0
- package/dist/dto/list-collaborator-invoice.dto.js +8 -0
- package/dist/dto/list-collaborator-invoice.dto.js.map +1 -0
- package/dist/dto/list-collaborator-payment.dto.d.ts +4 -0
- package/dist/dto/list-collaborator-payment.dto.d.ts.map +1 -0
- package/dist/dto/list-collaborator-payment.dto.js +8 -0
- package/dist/dto/list-collaborator-payment.dto.js.map +1 -0
- package/dist/dto/update-collaborator-invoice.dto.d.ts +6 -0
- package/dist/dto/update-collaborator-invoice.dto.d.ts.map +1 -0
- package/dist/dto/update-collaborator-invoice.dto.js +9 -0
- package/dist/dto/update-collaborator-invoice.dto.js.map +1 -0
- package/dist/dto/update-collaborator-payment.dto.d.ts +6 -0
- package/dist/dto/update-collaborator-payment.dto.d.ts.map +1 -0
- package/dist/dto/update-collaborator-payment.dto.js +9 -0
- package/dist/dto/update-collaborator-payment.dto.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/operations.controller.d.ts +42 -0
- package/dist/operations.controller.d.ts.map +1 -1
- package/dist/operations.service.d.ts +258 -268
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +2381 -1341
- package/dist/operations.service.js.map +1 -1
- package/dist/operations.service.spec.js +345 -174
- package/dist/operations.service.spec.js.map +1 -1
- package/hedhog/data/dashboard_component.yaml +66 -0
- package/hedhog/data/dashboard_item.yaml +25 -25
- package/hedhog/data/menu.yaml +27 -8
- package/hedhog/data/route.yaml +133 -0
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +78 -102
- package/hedhog/frontend/app/_components/collaborator-invoices-tab.tsx.ejs +443 -0
- package/hedhog/frontend/app/_components/collaborator-payment-history-tab.tsx.ejs +429 -0
- package/hedhog/frontend/app/_components/collaborator-picker.tsx.ejs +158 -0
- package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +247 -50
- package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +643 -450
- package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +992 -431
- package/hedhog/frontend/app/_components/project-file-attachments.tsx.ejs +371 -0
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +558 -386
- package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +383 -157
- package/hedhog/frontend/app/_components/task-file-attachments.tsx.ejs +4 -1
- package/hedhog/frontend/app/_components/task-form-fields.tsx.ejs +406 -0
- package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +629 -784
- package/hedhog/frontend/app/_components/task-info-display.tsx.ejs +137 -0
- package/hedhog/frontend/app/_components/timesheet-entry-create-sheet.tsx.ejs +306 -0
- package/hedhog/frontend/app/_lib/api.ts.ejs +155 -0
- package/hedhog/frontend/app/_lib/types.ts.ejs +62 -0
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +0 -2
- package/hedhog/frontend/app/_lib/utils/task-ui.ts.ejs +61 -0
- package/hedhog/frontend/app/approvals/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +59 -8
- package/hedhog/frontend/app/contracts/page.tsx.ejs +29 -8
- package/hedhog/frontend/app/dashboard/widgets/CapacityDistribution.tsx.ejs +84 -0
- package/hedhog/frontend/app/dashboard/widgets/EffortByProject.tsx.ejs +85 -0
- package/hedhog/frontend/app/dashboard/widgets/HeadcountByArea.tsx.ejs +101 -0
- package/hedhog/frontend/app/dashboard/widgets/ManagedProjectsStatus.tsx.ejs +113 -0
- package/hedhog/frontend/app/dashboard/widgets/MyHoursPeriodKpi.tsx.ejs +87 -0
- package/hedhog/frontend/app/dashboard/widgets/MyOpenRequestsKpi.tsx.ejs +97 -0
- package/hedhog/frontend/app/dashboard/widgets/MyPendingRequestsList.tsx.ejs +99 -0
- package/hedhog/frontend/app/dashboard/widgets/MyProjectAllocationsKpi.tsx.ejs +78 -0
- package/hedhog/frontend/app/dashboard/widgets/MyQuickActions.tsx.ejs +130 -0
- package/hedhog/frontend/app/dashboard/widgets/MyRelevantDeadlines.tsx.ejs +144 -0
- package/hedhog/frontend/app/dashboard/widgets/MyTimesheetStatusKpi.tsx.ejs +78 -0
- package/hedhog/frontend/app/dashboard/widgets/MyWeeklyJourney.tsx.ejs +99 -0
- package/hedhog/frontend/app/dashboard/widgets/PortfolioCostsKpi.tsx.ejs +112 -0
- package/hedhog/frontend/app/dashboard/widgets/PortfolioEffortKpi.tsx.ejs +93 -0
- package/hedhog/frontend/app/dashboard/widgets/PortfolioProjectsKpi.tsx.ejs +96 -0
- package/hedhog/frontend/app/dashboard/widgets/PortfolioRiskKpi.tsx.ejs +115 -0
- package/hedhog/frontend/app/dashboard/widgets/ProjectStatusOverview.tsx.ejs +120 -0
- package/hedhog/frontend/app/dashboard/widgets/StrategicDeadlines.tsx.ejs +146 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamApprovalQueue.tsx.ejs +108 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamCapacityKpi.tsx.ejs +97 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamHeadcountKpi.tsx.ejs +100 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamHoursKpi.tsx.ejs +104 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamPendingApprovalsKpi.tsx.ejs +110 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamUtilizationOverview.tsx.ejs +115 -0
- package/hedhog/frontend/app/dashboard/widgets/TeamWorkloadAlerts.tsx.ejs +117 -0
- package/hedhog/frontend/app/dashboard/widgets/index.ts.ejs +26 -0
- package/hedhog/frontend/app/departments/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/my-projects/page.tsx.ejs +30 -12
- package/hedhog/frontend/app/my-tasks/page.tsx.ejs +286 -125
- package/hedhog/frontend/app/project-cost-categories/page.tsx.ejs +58 -52
- package/hedhog/frontend/app/project-cost-types/page.tsx.ejs +58 -51
- package/hedhog/frontend/app/projects/page.tsx.ejs +415 -33
- package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/tasks-gantt/page.tsx.ejs +953 -0
- package/hedhog/frontend/app/time-off/page.tsx.ejs +6 -1
- package/hedhog/frontend/app/timesheets/page.tsx.ejs +10 -4
- package/hedhog/frontend/messages/en.json +332 -46
- package/hedhog/frontend/messages/operations/en.json +61 -52
- package/hedhog/frontend/messages/operations/pt.json +59 -43
- package/hedhog/frontend/messages/pt.json +332 -46
- package/hedhog/frontend/widgets/capacity-distribution.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/effort-by-project.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/headcount-by-area.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/index.ts.ejs +25 -0
- package/hedhog/frontend/widgets/managed-projects-status.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-hours-period-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-open-requests-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-pending-requests-list.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-project-allocations-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-quick-actions.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-relevant-deadlines.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-timesheet-status-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/my-weekly-journey.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/portfolio-costs-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/portfolio-effort-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/portfolio-projects-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/portfolio-risk-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/project-status-overview.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/shared-operations-widget.tsx.ejs +170 -0
- package/hedhog/frontend/widgets/strategic-deadlines.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-approval-queue.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-capacity-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-headcount-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-hours-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-pending-approvals-kpi.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-utilization-overview.tsx.ejs +17 -0
- package/hedhog/frontend/widgets/team-workload-alerts.tsx.ejs +17 -0
- package/hedhog/table/operations_collaborator.yaml +8 -13
- package/hedhog/table/operations_collaborator_invoice.yaml +35 -0
- package/hedhog/table/operations_collaborator_payment.yaml +32 -0
- package/hedhog/table/operations_project.yaml +1 -1
- package/hedhog/table/operations_project_file.yaml +23 -0
- package/hedhog/table/operations_task.yaml +76 -69
- package/hedhog/table/operations_task_activity.yaml +51 -0
- package/package.json +6 -5
- package/src/controllers/operations-collaborators.controller.ts +117 -8
- package/src/controllers/operations-projects.controller.ts +41 -8
- package/src/controllers/operations-tasks.controller.ts +156 -166
- package/src/dashboard/README.md +214 -0
- package/src/dashboard/components/DashboardLayout.tsx +131 -0
- package/src/dashboard/components/widget-registry.ts +255 -0
- package/src/dashboard/hooks/useDashboardData.ts +29 -0
- package/src/dashboard/types/widgets.types.ts +237 -0
- package/src/dashboard/widgets/CapacityDistribution.tsx +56 -0
- package/src/dashboard/widgets/EffortByProject.tsx +51 -0
- package/src/dashboard/widgets/HeadcountByArea.tsx +57 -0
- package/src/dashboard/widgets/ManagedProjectsStatus.tsx +53 -0
- package/src/dashboard/widgets/MyHoursPeriodKpi.tsx +87 -0
- package/src/dashboard/widgets/MyOpenRequestsKpi.tsx +51 -0
- package/src/dashboard/widgets/MyPendingRequestsList.tsx +63 -0
- package/src/dashboard/widgets/MyProjectAllocationsKpi.tsx +57 -0
- package/src/dashboard/widgets/MyQuickActions.tsx +62 -0
- package/src/dashboard/widgets/MyRelevantDeadlines.tsx +84 -0
- package/src/dashboard/widgets/MyTimesheetStatusKpi.tsx +65 -0
- package/src/dashboard/widgets/MyWeeklyJourney.tsx +57 -0
- package/src/dashboard/widgets/PortfolioCostsKpi.tsx +48 -0
- package/src/dashboard/widgets/PortfolioEffortKpi.tsx +41 -0
- package/src/dashboard/widgets/PortfolioRiskKpi.tsx +50 -0
- package/src/dashboard/widgets/ProjectStatusOverview.tsx +52 -0
- package/src/dashboard/widgets/StrategicDeadlines.tsx +93 -0
- package/src/dashboard/widgets/TeamApprovalQueue.tsx +70 -0
- package/src/dashboard/widgets/TeamCapacityKpi.tsx +50 -0
- package/src/dashboard/widgets/TeamHoursKpi.tsx +51 -0
- package/src/dashboard/widgets/TeamPendingApprovalsKpi.tsx +53 -0
- package/src/dashboard/widgets/TeamUtilizationOverview.tsx +62 -0
- package/src/dashboard/widgets/TeamWorkloadAlerts.tsx +81 -0
- package/src/dashboard/widgets/index.ts +26 -0
- package/src/dto/create-collaborator-invoice.dto.ts +39 -0
- package/src/dto/create-collaborator-payment.dto.ts +35 -0
- package/src/dto/create-collaborator.dto.ts +4 -11
- package/src/dto/list-collaborator-invoice.dto.ts +3 -0
- package/src/dto/list-collaborator-payment.dto.ts +3 -0
- package/src/dto/update-collaborator-invoice.dto.ts +6 -0
- package/src/dto/update-collaborator-payment.dto.ts +6 -0
- package/src/index.ts +3 -0
- package/src/operations.service.spec.ts +988 -764
- package/src/operations.service.ts +4689 -2624
|
@@ -20,6 +20,19 @@ export type OperationsTaskComment = {
|
|
|
20
20
|
updatedAt: string | null;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
export type OperationsTaskActivity = {
|
|
24
|
+
id: number;
|
|
25
|
+
taskId: number;
|
|
26
|
+
actorCollaboratorId: number | null;
|
|
27
|
+
actorName: string | null;
|
|
28
|
+
actorUserPhotoId: number | null;
|
|
29
|
+
actorPersonAvatarId: number | null;
|
|
30
|
+
action: string | null;
|
|
31
|
+
fromStatus: string;
|
|
32
|
+
toStatus: string;
|
|
33
|
+
createdAt: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
23
36
|
export type OperationsDashboard = {
|
|
24
37
|
actor: {
|
|
25
38
|
roleScope: 'self' | 'team' | 'full';
|
|
@@ -36,6 +49,48 @@ export type OperationsDashboard = {
|
|
|
36
49
|
scheduleAdjustmentRequests: number;
|
|
37
50
|
pendingApprovals: number;
|
|
38
51
|
};
|
|
52
|
+
windowDays: number;
|
|
53
|
+
taskSummary: {
|
|
54
|
+
total: number;
|
|
55
|
+
backlog: number;
|
|
56
|
+
inProgress: number;
|
|
57
|
+
completed: number;
|
|
58
|
+
};
|
|
59
|
+
avgTaskCompletionHours: number;
|
|
60
|
+
completedTasksInWindow: number;
|
|
61
|
+
hoursInExecution: number;
|
|
62
|
+
timesheetHeatmap: Array<{
|
|
63
|
+
workDate: string;
|
|
64
|
+
weekday: number;
|
|
65
|
+
hours: number;
|
|
66
|
+
}>;
|
|
67
|
+
activeProjects: Array<{
|
|
68
|
+
id: number;
|
|
69
|
+
name: string;
|
|
70
|
+
code?: string | null;
|
|
71
|
+
taskBacklog: number;
|
|
72
|
+
taskInProgress: number;
|
|
73
|
+
taskCompleted: number;
|
|
74
|
+
}>;
|
|
75
|
+
recentTaskActivities: Array<{
|
|
76
|
+
taskId: number;
|
|
77
|
+
taskName: string;
|
|
78
|
+
taskStatus: string;
|
|
79
|
+
taskPriority?: string | null;
|
|
80
|
+
taskDueDate?: string | null;
|
|
81
|
+
taskEstimateHours?: number | null;
|
|
82
|
+
taskTags?: string | null;
|
|
83
|
+
taskAssigneeCollaboratorId?: number | null;
|
|
84
|
+
taskAssigneeName?: string | null;
|
|
85
|
+
projectId: number;
|
|
86
|
+
projectName: string;
|
|
87
|
+
projectCode?: string | null;
|
|
88
|
+
projectAssignmentId?: number | null;
|
|
89
|
+
action?: string | null;
|
|
90
|
+
fromStatus: string;
|
|
91
|
+
toStatus: string;
|
|
92
|
+
createdAt: string;
|
|
93
|
+
}>;
|
|
39
94
|
recentTimesheets: Array<{
|
|
40
95
|
id: number;
|
|
41
96
|
collaboratorName: string;
|
|
@@ -260,6 +315,7 @@ export type OperationsCollaborator = {
|
|
|
260
315
|
supervisorName?: string | null;
|
|
261
316
|
contractId?: number | null;
|
|
262
317
|
contractStatus?: string | null;
|
|
318
|
+
hasUser?: boolean | null;
|
|
263
319
|
activeAssignments?: number;
|
|
264
320
|
pendingApprovals?: number;
|
|
265
321
|
pendingTimeOffRequests?: number;
|
|
@@ -354,6 +410,7 @@ export type OperationsTaskOption = {
|
|
|
354
410
|
description?: string | null;
|
|
355
411
|
status: string;
|
|
356
412
|
priority?: string | null;
|
|
413
|
+
assigneeCollaboratorId?: number | null;
|
|
357
414
|
projectId: number;
|
|
358
415
|
projectAssignmentId: number | null;
|
|
359
416
|
projectName: string;
|
|
@@ -366,6 +423,8 @@ export type OperationsTaskOption = {
|
|
|
366
423
|
assigneePersonAvatarId?: number | null;
|
|
367
424
|
commentCount?: number | null;
|
|
368
425
|
fileCount?: number | null;
|
|
426
|
+
doingStartedAt?: string | null;
|
|
427
|
+
totalDoingMinutes?: number | null;
|
|
369
428
|
createdAt?: string | null;
|
|
370
429
|
deletedAt?: string | null;
|
|
371
430
|
};
|
|
@@ -376,6 +435,7 @@ export type OperationsProject = {
|
|
|
376
435
|
managerCollaboratorId?: number | null;
|
|
377
436
|
clientPersonId?: number | null;
|
|
378
437
|
clientAvatarId?: number | null;
|
|
438
|
+
clientUserPhotoId?: number | null;
|
|
379
439
|
code: string;
|
|
380
440
|
name: string;
|
|
381
441
|
clientName?: string | null;
|
|
@@ -659,6 +719,8 @@ export type OperationsMyProjectSummary = {
|
|
|
659
719
|
assigneeUserPhotoId: number | null;
|
|
660
720
|
assigneePersonAvatarId: number | null;
|
|
661
721
|
projectAssignmentId: number | null;
|
|
722
|
+
doingStartedAt?: string | null;
|
|
723
|
+
totalDoingMinutes?: number | null;
|
|
662
724
|
createdAt: string;
|
|
663
725
|
}>;
|
|
664
726
|
};
|
|
@@ -222,8 +222,6 @@ export function getStatusBadgeClass(value?: string | null) {
|
|
|
222
222
|
case 'inactive':
|
|
223
223
|
case 'on_leave':
|
|
224
224
|
return 'bg-slate-100 text-slate-800';
|
|
225
|
-
case 'at_risk':
|
|
226
|
-
return 'bg-orange-100 text-orange-900';
|
|
227
225
|
default:
|
|
228
226
|
return 'bg-sky-100 text-sky-800';
|
|
229
227
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export function getTaskPriorityLabel(value?: string | null) {
|
|
2
|
+
const labels: Record<string, string> = {
|
|
3
|
+
low: 'Baixa',
|
|
4
|
+
medium: 'Média',
|
|
5
|
+
high: 'Alta',
|
|
6
|
+
};
|
|
7
|
+
return labels[value ?? ''] ?? String(value ?? '');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function getInitials(value?: string | null) {
|
|
11
|
+
const parts = String(value ?? '')
|
|
12
|
+
.trim()
|
|
13
|
+
.split(/\s+/)
|
|
14
|
+
.filter(Boolean)
|
|
15
|
+
.slice(0, 2);
|
|
16
|
+
if (!parts.length) return '??';
|
|
17
|
+
return parts.map((part) => part[0]?.toUpperCase() ?? '').join('');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getElapsedDoingMinutes(
|
|
21
|
+
task?: {
|
|
22
|
+
status?: string | null;
|
|
23
|
+
doingStartedAt?: string | null;
|
|
24
|
+
totalDoingMinutes?: number | null;
|
|
25
|
+
} | null,
|
|
26
|
+
tick = 0
|
|
27
|
+
) {
|
|
28
|
+
void tick;
|
|
29
|
+
const base = Number(task?.totalDoingMinutes ?? 0);
|
|
30
|
+
if (!task || task.status !== 'doing' || !task.doingStartedAt) return base;
|
|
31
|
+
const startedAt = new Date(task.doingStartedAt).getTime();
|
|
32
|
+
if (Number.isNaN(startedAt)) return base;
|
|
33
|
+
return base + Math.max(Math.floor((Date.now() - startedAt) / 60000), 0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function formatDurationMinutes(minutes?: number | null) {
|
|
37
|
+
const total = Math.max(Number(minutes ?? 0), 0);
|
|
38
|
+
const hours = Math.floor(total / 60);
|
|
39
|
+
const remainingMinutes = total % 60;
|
|
40
|
+
if (hours <= 0) return `${remainingMinutes}min`;
|
|
41
|
+
if (remainingMinutes <= 0) return `${hours}h`;
|
|
42
|
+
return `${hours}h ${remainingMinutes}min`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function getTaskDescriptionPreview(value?: string | null) {
|
|
46
|
+
if (!value) return '';
|
|
47
|
+
|
|
48
|
+
return String(value)
|
|
49
|
+
.replace(/<br\s*\/?>/gi, ' ')
|
|
50
|
+
.replace(/<\/(p|div|li|ul|ol|h1|h2|h3|h4|h5|h6)>/gi, ' ')
|
|
51
|
+
.replace(/<li[^>]*>/gi, ' ')
|
|
52
|
+
.replace(/<[^>]*>/g, '')
|
|
53
|
+
.replace(/ /gi, ' ')
|
|
54
|
+
.replace(/&/gi, '&')
|
|
55
|
+
.replace(/</gi, '<')
|
|
56
|
+
.replace(/>/gi, '>')
|
|
57
|
+
.replace(/'/gi, "'")
|
|
58
|
+
.replace(/"/gi, '"')
|
|
59
|
+
.replace(/\s+/g, ' ')
|
|
60
|
+
.trim();
|
|
61
|
+
}
|
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
import { OperationsHeader } from '../_components/operations-header';
|
|
58
58
|
import { StatusBadge } from '../_components/status-badge';
|
|
59
59
|
import { fetchOperations } from '../_lib/api';
|
|
60
|
+
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
60
61
|
import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
|
|
61
62
|
import type { OperationsApproval, PaginatedResponse } from '../_lib/types';
|
|
62
63
|
import {
|
|
@@ -232,7 +233,11 @@ export default function OperationsApprovalsPage() {
|
|
|
232
233
|
() => new Date().getMonth() + 1
|
|
233
234
|
);
|
|
234
235
|
const [page, setPage] = useState(1);
|
|
235
|
-
const [pageSize, setPageSize] =
|
|
236
|
+
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
237
|
+
storageKey: 'pagination:operations-approvals:pageSize',
|
|
238
|
+
defaultValue: 12,
|
|
239
|
+
allowedValues: [12, 24, 48],
|
|
240
|
+
});
|
|
236
241
|
const [viewMode, setViewMode] = useState<'table' | 'cards' | 'calendar'>(
|
|
237
242
|
() => {
|
|
238
243
|
if (typeof window === 'undefined') return 'table';
|
|
@@ -55,6 +55,7 @@ import { useEffect, useMemo, useState } from 'react';
|
|
|
55
55
|
import { OperationsHeader } from '../_components/operations-header';
|
|
56
56
|
import { StatusBadge } from '../_components/status-badge';
|
|
57
57
|
import { fetchOperations, mutateOperations } from '../_lib/api';
|
|
58
|
+
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
58
59
|
import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
|
|
59
60
|
import type {
|
|
60
61
|
OperationsCollaboratorType,
|
|
@@ -195,7 +196,11 @@ export default function OperationsCollaboratorTypesPage() {
|
|
|
195
196
|
'all' | 'active' | 'inactive'
|
|
196
197
|
>('all');
|
|
197
198
|
const [page, setPage] = useState(1);
|
|
198
|
-
const [pageSize, setPageSize] =
|
|
199
|
+
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
200
|
+
storageKey: 'pagination:operations-collaborator-types:pageSize',
|
|
201
|
+
defaultValue: 100,
|
|
202
|
+
allowedValues: [25, 50, 100, 200],
|
|
203
|
+
});
|
|
199
204
|
const [viewMode, setViewMode] = useState<'table' | 'cards'>(() => {
|
|
200
205
|
if (typeof window === 'undefined') return 'table';
|
|
201
206
|
const saved = window.localStorage.getItem(
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
UserCheck,
|
|
48
48
|
UserRound,
|
|
49
49
|
Users,
|
|
50
|
+
UserX,
|
|
50
51
|
Wallet,
|
|
51
52
|
} from 'lucide-react';
|
|
52
53
|
import { useTranslations } from 'next-intl';
|
|
@@ -57,6 +58,7 @@ import { CollaboratorFormScreen } from '../_components/collaborator-form-screen'
|
|
|
57
58
|
import { OperationsHeader } from '../_components/operations-header';
|
|
58
59
|
import { StatusBadge } from '../_components/status-badge';
|
|
59
60
|
import { fetchOperations, mutateOperations } from '../_lib/api';
|
|
61
|
+
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
60
62
|
import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
|
|
61
63
|
import {
|
|
62
64
|
MASKED_VALUE,
|
|
@@ -121,7 +123,11 @@ export default function OperationsCollaboratorsPage() {
|
|
|
121
123
|
const [statusFilter, setStatusFilter] = useState('all');
|
|
122
124
|
const [typeFilter, setTypeFilter] = useState('all');
|
|
123
125
|
const [page, setPage] = useState(1);
|
|
124
|
-
const [pageSize, setPageSize] =
|
|
126
|
+
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
127
|
+
storageKey: 'pagination:operations-collaborators:pageSize',
|
|
128
|
+
defaultValue: 12,
|
|
129
|
+
allowedValues: [12, 24, 48],
|
|
130
|
+
});
|
|
125
131
|
const [viewMode, setViewMode] = useState<CollaboratorViewMode>(() => {
|
|
126
132
|
if (typeof window === 'undefined') {
|
|
127
133
|
return 'table';
|
|
@@ -685,6 +691,20 @@ export default function OperationsCollaboratorsPage() {
|
|
|
685
691
|
</div>
|
|
686
692
|
) : null}
|
|
687
693
|
|
|
694
|
+
<div className="flex items-center gap-2 text-xs">
|
|
695
|
+
{collaborator.hasUser ? (
|
|
696
|
+
<span className="inline-flex items-center gap-1 font-medium text-emerald-600 dark:text-emerald-400">
|
|
697
|
+
<UserCheck className="size-3.5" />
|
|
698
|
+
{t('columns.linkedUserYes')}
|
|
699
|
+
</span>
|
|
700
|
+
) : (
|
|
701
|
+
<span className="inline-flex items-center gap-1 font-medium text-muted-foreground">
|
|
702
|
+
<UserX className="size-3.5" />
|
|
703
|
+
{t('columns.linkedUserNo')}
|
|
704
|
+
</span>
|
|
705
|
+
)}
|
|
706
|
+
</div>
|
|
707
|
+
|
|
688
708
|
<div className="flex items-center justify-end gap-1.5 border-t border-border/60 pt-3">
|
|
689
709
|
{access.isDirector ? (
|
|
690
710
|
<Button
|
|
@@ -750,14 +770,32 @@ export default function OperationsCollaboratorsPage() {
|
|
|
750
770
|
<TableHead className="hidden lg:table-cell">
|
|
751
771
|
{commonT('labels.supervisor')}
|
|
752
772
|
</TableHead>
|
|
753
|
-
<TableHead className="hidden
|
|
754
|
-
|
|
773
|
+
<TableHead className="hidden 2xl:table-cell">
|
|
774
|
+
<span
|
|
775
|
+
className="block truncate"
|
|
776
|
+
title={commonT('labels.weeklyCapacity')}
|
|
777
|
+
>
|
|
778
|
+
{commonT('labels.weeklyCapacity')}
|
|
779
|
+
</span>
|
|
755
780
|
</TableHead>
|
|
756
781
|
<TableHead className="hidden 2xl:table-cell">
|
|
757
|
-
|
|
782
|
+
<span
|
|
783
|
+
className="block truncate"
|
|
784
|
+
title={formT('fields.compensationAmount')}
|
|
785
|
+
>
|
|
786
|
+
{formT('fields.compensationAmount')}
|
|
787
|
+
</span>
|
|
758
788
|
</TableHead>
|
|
759
|
-
<TableHead className="hidden
|
|
760
|
-
|
|
789
|
+
<TableHead className="hidden 2xl:table-cell">
|
|
790
|
+
<span
|
|
791
|
+
className="block truncate"
|
|
792
|
+
title={commonT('labels.startDate')}
|
|
793
|
+
>
|
|
794
|
+
{commonT('labels.startDate')}
|
|
795
|
+
</span>
|
|
796
|
+
</TableHead>
|
|
797
|
+
<TableHead className="hidden md:table-cell">
|
|
798
|
+
{t('columns.linkedUser')}
|
|
761
799
|
</TableHead>
|
|
762
800
|
<TableHead className="w-30 text-right sm:w-42.5">
|
|
763
801
|
{commonT('labels.actions')}
|
|
@@ -850,7 +888,7 @@ export default function OperationsCollaboratorsPage() {
|
|
|
850
888
|
commonT('labels.notAssigned')}
|
|
851
889
|
</div>
|
|
852
890
|
</TableCell>
|
|
853
|
-
<TableCell className="hidden
|
|
891
|
+
<TableCell className="hidden 2xl:table-cell">
|
|
854
892
|
{formatHours(collaborator.weeklyCapacityHours)}
|
|
855
893
|
</TableCell>
|
|
856
894
|
<TableCell className="hidden 2xl:table-cell">
|
|
@@ -864,13 +902,26 @@ export default function OperationsCollaboratorsPage() {
|
|
|
864
902
|
: MASKED_VALUE
|
|
865
903
|
: commonT('labels.notAvailable')}
|
|
866
904
|
</TableCell>
|
|
867
|
-
<TableCell className="hidden
|
|
905
|
+
<TableCell className="hidden 2xl:table-cell">
|
|
868
906
|
{formatDate(
|
|
869
907
|
collaborator.joinedAt,
|
|
870
908
|
getSettingValue,
|
|
871
909
|
currentLocaleCode
|
|
872
910
|
)}
|
|
873
911
|
</TableCell>
|
|
912
|
+
<TableCell className="hidden md:table-cell">
|
|
913
|
+
{collaborator.hasUser ? (
|
|
914
|
+
<span className="inline-flex items-center gap-1 text-xs font-medium text-emerald-600 dark:text-emerald-400">
|
|
915
|
+
<UserCheck className="size-3.5" />
|
|
916
|
+
{t('columns.linkedUserYes')}
|
|
917
|
+
</span>
|
|
918
|
+
) : (
|
|
919
|
+
<span className="inline-flex items-center gap-1 text-xs font-medium text-muted-foreground">
|
|
920
|
+
<UserX className="size-3.5" />
|
|
921
|
+
{t('columns.linkedUserNo')}
|
|
922
|
+
</span>
|
|
923
|
+
)}
|
|
924
|
+
</TableCell>
|
|
874
925
|
<TableCell>
|
|
875
926
|
<div className="flex items-center justify-end gap-1.5">
|
|
876
927
|
{access.isDirector ? (
|
|
@@ -58,6 +58,7 @@ import { ContractFormScreen } from '../_components/contract-form-screen';
|
|
|
58
58
|
import { OperationsHeader } from '../_components/operations-header';
|
|
59
59
|
import { StatusBadge } from '../_components/status-badge';
|
|
60
60
|
import { fetchOperations, mutateOperations } from '../_lib/api';
|
|
61
|
+
import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
|
|
61
62
|
import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
|
|
62
63
|
import type {
|
|
63
64
|
OperationsContract,
|
|
@@ -97,7 +98,11 @@ export default function OperationsContractsPage() {
|
|
|
97
98
|
const [search, setSearch] = useState('');
|
|
98
99
|
const [isActiveFilter, setIsActiveFilter] = useState('all');
|
|
99
100
|
const [page, setPage] = useState(1);
|
|
100
|
-
const [pageSize, setPageSize] =
|
|
101
|
+
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
102
|
+
storageKey: 'pagination:operations-contracts:pageSize',
|
|
103
|
+
defaultValue: 12,
|
|
104
|
+
allowedValues: [12, 24, 48],
|
|
105
|
+
});
|
|
101
106
|
const [isCreateSheetOpen, setIsCreateSheetOpen] = useState(false);
|
|
102
107
|
const [contractToDelete, setContractToDelete] =
|
|
103
108
|
useState<OperationsContract | null>(null);
|
|
@@ -159,7 +164,10 @@ export default function OperationsContractsPage() {
|
|
|
159
164
|
const { data: stats } = useQuery<OperationsContractStats>({
|
|
160
165
|
queryKey: ['operations-contracts-stats', currentLocaleCode],
|
|
161
166
|
queryFn: () =>
|
|
162
|
-
fetchOperations<OperationsContractStats>(
|
|
167
|
+
fetchOperations<OperationsContractStats>(
|
|
168
|
+
request,
|
|
169
|
+
'/operations/contracts/stats'
|
|
170
|
+
),
|
|
163
171
|
});
|
|
164
172
|
|
|
165
173
|
const rows = contractsResponse?.data ?? [];
|
|
@@ -206,7 +214,11 @@ export default function OperationsContractsPage() {
|
|
|
206
214
|
) ?? null;
|
|
207
215
|
|
|
208
216
|
if (document?.fileId) {
|
|
209
|
-
window.open(
|
|
217
|
+
window.open(
|
|
218
|
+
buildStoredFileUrl(document.fileId) || '',
|
|
219
|
+
'_blank',
|
|
220
|
+
'noopener,noreferrer'
|
|
221
|
+
);
|
|
210
222
|
return;
|
|
211
223
|
}
|
|
212
224
|
|
|
@@ -288,7 +300,9 @@ export default function OperationsContractsPage() {
|
|
|
288
300
|
</DropdownMenuItem>
|
|
289
301
|
{access.isDirector ? <DropdownMenuSeparator /> : null}
|
|
290
302
|
{access.isDirector ? (
|
|
291
|
-
<DropdownMenuItem
|
|
303
|
+
<DropdownMenuItem
|
|
304
|
+
onSelect={() => updateSheetQuery({ editId: contract.id })}
|
|
305
|
+
>
|
|
292
306
|
<Pencil className="size-4" />
|
|
293
307
|
{commonT('actions.edit')}
|
|
294
308
|
</DropdownMenuItem>
|
|
@@ -300,7 +314,9 @@ export default function OperationsContractsPage() {
|
|
|
300
314
|
) : (
|
|
301
315
|
<ShieldCheck className="size-4" />
|
|
302
316
|
)}
|
|
303
|
-
{contract.isActive
|
|
317
|
+
{contract.isActive
|
|
318
|
+
? detailT('labels.inactive')
|
|
319
|
+
: detailT('labels.active')}
|
|
304
320
|
</DropdownMenuItem>
|
|
305
321
|
) : null}
|
|
306
322
|
{access.isDirector ? <DropdownMenuSeparator /> : null}
|
|
@@ -384,7 +400,9 @@ export default function OperationsContractsPage() {
|
|
|
384
400
|
<TableRow
|
|
385
401
|
key={contract.id}
|
|
386
402
|
className="cursor-pointer hover:bg-muted/30"
|
|
387
|
-
onClick={() =>
|
|
403
|
+
onClick={() =>
|
|
404
|
+
router.push(`/operations/contracts/${contract.id}`)
|
|
405
|
+
}
|
|
388
406
|
>
|
|
389
407
|
<TableCell>
|
|
390
408
|
<div className="min-w-0">
|
|
@@ -401,7 +419,8 @@ export default function OperationsContractsPage() {
|
|
|
401
419
|
</TableCell>
|
|
402
420
|
<TableCell>
|
|
403
421
|
<span className="truncate text-sm text-muted-foreground">
|
|
404
|
-
{contract.currentPdfFileName ||
|
|
422
|
+
{contract.currentPdfFileName ||
|
|
423
|
+
commonT('labels.notAvailable')}
|
|
405
424
|
</span>
|
|
406
425
|
</TableCell>
|
|
407
426
|
<TableCell>
|
|
@@ -510,7 +529,9 @@ export default function OperationsContractsPage() {
|
|
|
510
529
|
<SheetContent className="flex h-full w-full flex-col overflow-hidden p-0 sm:max-w-xl lg:max-w-4xl">
|
|
511
530
|
<SheetHeader className="sr-only">
|
|
512
531
|
<SheetTitle>
|
|
513
|
-
{editingContractId
|
|
532
|
+
{editingContractId
|
|
533
|
+
? commonT('actions.edit')
|
|
534
|
+
: commonT('actions.create')}
|
|
514
535
|
</SheetTitle>
|
|
515
536
|
<SheetDescription>{t('description')}</SheetDescription>
|
|
516
537
|
</SheetHeader>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { PieChart } from 'lucide-react';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
interface CapacityArea {
|
|
8
|
+
name: string;
|
|
9
|
+
capacity: number;
|
|
10
|
+
percentage: number;
|
|
11
|
+
color: string;
|
|
12
|
+
icon: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface CapacityDistributionProps {
|
|
16
|
+
slug: string;
|
|
17
|
+
title: string;
|
|
18
|
+
roleSlug: string;
|
|
19
|
+
width?: number;
|
|
20
|
+
height?: number;
|
|
21
|
+
data?: { areas: CapacityArea[]; totalCapacity: number };
|
|
22
|
+
style?: React.CSSProperties;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const CapacityDistribution: React.FC<CapacityDistributionProps> = ({
|
|
26
|
+
title,
|
|
27
|
+
data = { areas: [], totalCapacity: 0 },
|
|
28
|
+
style,
|
|
29
|
+
}) => {
|
|
30
|
+
const t = useTranslations('operations.CapacityDistribution');
|
|
31
|
+
const colorMap = ['blue', 'purple', 'pink', 'green', 'yellow', 'red'];
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
className="bg-linear-to-br from-white to-teal-50 rounded-lg shadow-sm p-4 border border-teal-100"
|
|
35
|
+
style={style}
|
|
36
|
+
>
|
|
37
|
+
<div className="flex items-center justify-between mb-4">
|
|
38
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
39
|
+
<PieChart className="w-4 h-4 text-teal-600" />
|
|
40
|
+
</div>
|
|
41
|
+
<div className="space-y-3">
|
|
42
|
+
{data.areas.length === 0 ? (
|
|
43
|
+
<div className="text-center py-6 text-gray-500">
|
|
44
|
+
<PieChart className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
|
45
|
+
<p className="text-sm">{t('empty')}</p>
|
|
46
|
+
</div>
|
|
47
|
+
) : (
|
|
48
|
+
<>
|
|
49
|
+
{data.areas.map((area, idx) => (
|
|
50
|
+
<div key={idx} className="space-y-1">
|
|
51
|
+
<div className="flex items-center justify-between">
|
|
52
|
+
<div className="flex items-center gap-2">
|
|
53
|
+
<span className="text-lg">{area.icon}</span>
|
|
54
|
+
<span className="text-xs font-medium text-gray-900 truncate">
|
|
55
|
+
{area.name}
|
|
56
|
+
</span>
|
|
57
|
+
</div>
|
|
58
|
+
<span className="text-xs font-bold text-gray-700">
|
|
59
|
+
{area.percentage}%
|
|
60
|
+
</span>
|
|
61
|
+
</div>
|
|
62
|
+
<div className="relative w-full h-3 bg-gray-100 rounded-full overflow-hidden">
|
|
63
|
+
<div
|
|
64
|
+
className={`h-full bg-${colorMap[idx % colorMap.length]}-500 transition-all`}
|
|
65
|
+
style={{ width: `${area.percentage}%` }}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
<div className="text-xs text-gray-600">{area.capacity}h</div>
|
|
69
|
+
</div>
|
|
70
|
+
))}
|
|
71
|
+
<div className="pt-3 border-t border-teal-100 bg-teal-50 rounded-lg p-3 text-center">
|
|
72
|
+
<p className="text-xs text-teal-700 mb-1">{t('total')}</p>
|
|
73
|
+
<p className="text-2xl font-bold text-teal-600">
|
|
74
|
+
{data.totalCapacity}h
|
|
75
|
+
</p>
|
|
76
|
+
</div>
|
|
77
|
+
</>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default CapacityDistribution;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { BarChart3 } from 'lucide-react';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
interface ProjectEffort {
|
|
8
|
+
name: string;
|
|
9
|
+
hours: number;
|
|
10
|
+
percent: number;
|
|
11
|
+
color: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface EffortByProjectProps {
|
|
15
|
+
slug: string;
|
|
16
|
+
title: string;
|
|
17
|
+
roleSlug: string;
|
|
18
|
+
width?: number;
|
|
19
|
+
height?: number;
|
|
20
|
+
data?: { projects: ProjectEffort[]; totalHours: number };
|
|
21
|
+
style?: React.CSSProperties;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const EffortByProject: React.FC<EffortByProjectProps> = ({
|
|
25
|
+
title,
|
|
26
|
+
data = { projects: [], totalHours: 0 },
|
|
27
|
+
style,
|
|
28
|
+
}) => {
|
|
29
|
+
const t = useTranslations('operations.EffortByProject');
|
|
30
|
+
const colorMap = [
|
|
31
|
+
'bg-blue-500',
|
|
32
|
+
'bg-purple-500',
|
|
33
|
+
'bg-pink-500',
|
|
34
|
+
'bg-green-500',
|
|
35
|
+
'bg-orange-500',
|
|
36
|
+
];
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
className="bg-linear-to-br from-white to-pink-50 rounded-lg shadow-sm p-4 border border-pink-100"
|
|
40
|
+
style={style}
|
|
41
|
+
>
|
|
42
|
+
<div className="flex items-center justify-between mb-4">
|
|
43
|
+
<h3 className="text-sm font-medium text-gray-700">{title}</h3>
|
|
44
|
+
<BarChart3 className="w-4 h-4 text-pink-600" />
|
|
45
|
+
</div>
|
|
46
|
+
<div className="space-y-3">
|
|
47
|
+
{data.projects.length === 0 ? (
|
|
48
|
+
<div className="text-center py-6 text-gray-500">
|
|
49
|
+
<BarChart3 className="w-8 h-8 mx-auto mb-2 opacity-50" />
|
|
50
|
+
<p className="text-sm">{t('empty')}</p>
|
|
51
|
+
</div>
|
|
52
|
+
) : (
|
|
53
|
+
<>
|
|
54
|
+
{data.projects.slice(0, 5).map((project, idx) => (
|
|
55
|
+
<div key={idx} className="space-y-1">
|
|
56
|
+
<div className="flex justify-between items-center">
|
|
57
|
+
<span className="text-xs font-medium text-gray-900 truncate">
|
|
58
|
+
{project.name}
|
|
59
|
+
</span>
|
|
60
|
+
<span className="text-xs font-bold text-gray-700">
|
|
61
|
+
{project.hours}h ({project.percent}%)
|
|
62
|
+
</span>
|
|
63
|
+
</div>
|
|
64
|
+
<div className="relative w-full h-4 bg-gray-100 rounded-full overflow-hidden">
|
|
65
|
+
<div
|
|
66
|
+
className={`h-full ${colorMap[idx % colorMap.length]} transition-all`}
|
|
67
|
+
style={{ width: `${project.percent}%` }}
|
|
68
|
+
/>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
))}
|
|
72
|
+
<div className="pt-2 border-t border-pink-100 text-center">
|
|
73
|
+
<p className="text-xs text-gray-600 mb-1">{t('total')}</p>
|
|
74
|
+
<p className="text-2xl font-bold text-pink-600">
|
|
75
|
+
{data.totalHours}h
|
|
76
|
+
</p>
|
|
77
|
+
</div>
|
|
78
|
+
</>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default EffortByProject;
|