@hed-hog/operations 0.0.330 → 0.0.331

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.
Files changed (281) hide show
  1. package/README.md +5 -5
  2. package/dist/controllers/operations-collaborators.controller.d.ts +7 -216
  3. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  4. package/dist/controllers/operations-contracts.controller.d.ts +6 -6
  5. package/dist/controllers/operations-projects.controller.d.ts +25 -0
  6. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  7. package/dist/controllers/operations-projects.controller.js +48 -0
  8. package/dist/controllers/operations-projects.controller.js.map +1 -1
  9. package/dist/controllers/operations-reports.controller.d.ts +1 -1
  10. package/dist/controllers/operations-tasks.controller.d.ts +34 -9
  11. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
  12. package/dist/controllers/operations-tasks.controller.js +43 -32
  13. package/dist/controllers/operations-tasks.controller.js.map +1 -1
  14. package/dist/controllers/operations-timesheets.controller.d.ts +9 -9
  15. package/dist/dashboard/components/DashboardLayout.d.ts +30 -0
  16. package/dist/dashboard/components/DashboardLayout.d.ts.map +1 -0
  17. package/dist/dashboard/components/DashboardLayout.js +87 -0
  18. package/dist/dashboard/components/DashboardLayout.js.map +1 -0
  19. package/dist/dashboard/components/widget-registry.d.ts +23 -0
  20. package/dist/dashboard/components/widget-registry.d.ts.map +1 -0
  21. package/dist/dashboard/components/widget-registry.js +245 -0
  22. package/dist/dashboard/components/widget-registry.js.map +1 -0
  23. package/dist/dashboard/hooks/useDashboardData.d.ts +20 -0
  24. package/dist/dashboard/hooks/useDashboardData.d.ts.map +1 -0
  25. package/dist/dashboard/hooks/useDashboardData.js +24 -0
  26. package/dist/dashboard/hooks/useDashboardData.js.map +1 -0
  27. package/dist/dashboard/types/widgets.types.d.ts +233 -0
  28. package/dist/dashboard/types/widgets.types.d.ts.map +1 -0
  29. package/dist/dashboard/types/widgets.types.js +6 -0
  30. package/dist/dashboard/types/widgets.types.js.map +1 -0
  31. package/dist/dashboard/widgets/CapacityDistribution.d.ts +23 -0
  32. package/dist/dashboard/widgets/CapacityDistribution.d.ts.map +1 -0
  33. package/dist/dashboard/widgets/CapacityDistribution.js +11 -0
  34. package/dist/dashboard/widgets/CapacityDistribution.js.map +1 -0
  35. package/dist/dashboard/widgets/EffortByProject.d.ts +22 -0
  36. package/dist/dashboard/widgets/EffortByProject.d.ts.map +1 -0
  37. package/dist/dashboard/widgets/EffortByProject.js +11 -0
  38. package/dist/dashboard/widgets/EffortByProject.js.map +1 -0
  39. package/dist/dashboard/widgets/HeadcountByArea.d.ts +24 -0
  40. package/dist/dashboard/widgets/HeadcountByArea.d.ts.map +1 -0
  41. package/dist/dashboard/widgets/HeadcountByArea.js +11 -0
  42. package/dist/dashboard/widgets/HeadcountByArea.js.map +1 -0
  43. package/dist/dashboard/widgets/ManagedProjectsStatus.d.ts +18 -0
  44. package/dist/dashboard/widgets/ManagedProjectsStatus.d.ts.map +1 -0
  45. package/dist/dashboard/widgets/ManagedProjectsStatus.js +12 -0
  46. package/dist/dashboard/widgets/ManagedProjectsStatus.js.map +1 -0
  47. package/dist/dashboard/widgets/MyHoursPeriodKpi.d.ts +22 -0
  48. package/dist/dashboard/widgets/MyHoursPeriodKpi.d.ts.map +1 -0
  49. package/dist/dashboard/widgets/MyHoursPeriodKpi.js +12 -0
  50. package/dist/dashboard/widgets/MyHoursPeriodKpi.js.map +1 -0
  51. package/dist/dashboard/widgets/MyOpenRequestsKpi.d.ts +19 -0
  52. package/dist/dashboard/widgets/MyOpenRequestsKpi.d.ts.map +1 -0
  53. package/dist/dashboard/widgets/MyOpenRequestsKpi.js +17 -0
  54. package/dist/dashboard/widgets/MyOpenRequestsKpi.js.map +1 -0
  55. package/dist/dashboard/widgets/MyPendingRequestsList.d.ts +23 -0
  56. package/dist/dashboard/widgets/MyPendingRequestsList.d.ts.map +1 -0
  57. package/dist/dashboard/widgets/MyPendingRequestsList.js +14 -0
  58. package/dist/dashboard/widgets/MyPendingRequestsList.js.map +1 -0
  59. package/dist/dashboard/widgets/MyProjectAllocationsKpi.d.ts +22 -0
  60. package/dist/dashboard/widgets/MyProjectAllocationsKpi.d.ts.map +1 -0
  61. package/dist/dashboard/widgets/MyProjectAllocationsKpi.js +11 -0
  62. package/dist/dashboard/widgets/MyProjectAllocationsKpi.js.map +1 -0
  63. package/dist/dashboard/widgets/MyQuickActions.d.ts +23 -0
  64. package/dist/dashboard/widgets/MyQuickActions.d.ts.map +1 -0
  65. package/dist/dashboard/widgets/MyQuickActions.js +18 -0
  66. package/dist/dashboard/widgets/MyQuickActions.js.map +1 -0
  67. package/dist/dashboard/widgets/MyRelevantDeadlines.d.ts +23 -0
  68. package/dist/dashboard/widgets/MyRelevantDeadlines.d.ts.map +1 -0
  69. package/dist/dashboard/widgets/MyRelevantDeadlines.js +22 -0
  70. package/dist/dashboard/widgets/MyRelevantDeadlines.js.map +1 -0
  71. package/dist/dashboard/widgets/MyTimesheetStatusKpi.d.ts +17 -0
  72. package/dist/dashboard/widgets/MyTimesheetStatusKpi.d.ts.map +1 -0
  73. package/dist/dashboard/widgets/MyTimesheetStatusKpi.js +11 -0
  74. package/dist/dashboard/widgets/MyTimesheetStatusKpi.js.map +1 -0
  75. package/dist/dashboard/widgets/MyWeeklyJourney.d.ts +21 -0
  76. package/dist/dashboard/widgets/MyWeeklyJourney.d.ts.map +1 -0
  77. package/dist/dashboard/widgets/MyWeeklyJourney.js +19 -0
  78. package/dist/dashboard/widgets/MyWeeklyJourney.js.map +1 -0
  79. package/dist/dashboard/widgets/PortfolioCostsKpi.d.ts +19 -0
  80. package/dist/dashboard/widgets/PortfolioCostsKpi.d.ts.map +1 -0
  81. package/dist/dashboard/widgets/PortfolioCostsKpi.js +12 -0
  82. package/dist/dashboard/widgets/PortfolioCostsKpi.js.map +1 -0
  83. package/dist/dashboard/widgets/PortfolioEffortKpi.d.ts +18 -0
  84. package/dist/dashboard/widgets/PortfolioEffortKpi.d.ts.map +1 -0
  85. package/dist/dashboard/widgets/PortfolioEffortKpi.js +8 -0
  86. package/dist/dashboard/widgets/PortfolioEffortKpi.js.map +1 -0
  87. package/dist/dashboard/widgets/PortfolioProjectsKpi.d.ts +22 -0
  88. package/dist/dashboard/widgets/PortfolioProjectsKpi.d.ts.map +1 -0
  89. package/dist/dashboard/widgets/PortfolioProjectsKpi.js +56 -0
  90. package/dist/dashboard/widgets/PortfolioProjectsKpi.js.map +1 -0
  91. package/dist/dashboard/widgets/PortfolioRiskKpi.d.ts +19 -0
  92. package/dist/dashboard/widgets/PortfolioRiskKpi.d.ts.map +1 -0
  93. package/dist/dashboard/widgets/PortfolioRiskKpi.js +11 -0
  94. package/dist/dashboard/widgets/PortfolioRiskKpi.js.map +1 -0
  95. package/dist/dashboard/widgets/ProjectStatusOverview.d.ts +19 -0
  96. package/dist/dashboard/widgets/ProjectStatusOverview.d.ts.map +1 -0
  97. package/dist/dashboard/widgets/ProjectStatusOverview.js +18 -0
  98. package/dist/dashboard/widgets/ProjectStatusOverview.js.map +1 -0
  99. package/dist/dashboard/widgets/StrategicDeadlines.d.ts +24 -0
  100. package/dist/dashboard/widgets/StrategicDeadlines.d.ts.map +1 -0
  101. package/dist/dashboard/widgets/StrategicDeadlines.js +22 -0
  102. package/dist/dashboard/widgets/StrategicDeadlines.js.map +1 -0
  103. package/dist/dashboard/widgets/TeamApprovalQueue.d.ts +24 -0
  104. package/dist/dashboard/widgets/TeamApprovalQueue.d.ts.map +1 -0
  105. package/dist/dashboard/widgets/TeamApprovalQueue.js +12 -0
  106. package/dist/dashboard/widgets/TeamApprovalQueue.js.map +1 -0
  107. package/dist/dashboard/widgets/TeamCapacityKpi.d.ts +18 -0
  108. package/dist/dashboard/widgets/TeamCapacityKpi.d.ts.map +1 -0
  109. package/dist/dashboard/widgets/TeamCapacityKpi.js +19 -0
  110. package/dist/dashboard/widgets/TeamCapacityKpi.js.map +1 -0
  111. package/dist/dashboard/widgets/TeamHeadcountKpi.d.ts +22 -0
  112. package/dist/dashboard/widgets/TeamHeadcountKpi.d.ts.map +1 -0
  113. package/dist/dashboard/widgets/TeamHeadcountKpi.js +56 -0
  114. package/dist/dashboard/widgets/TeamHeadcountKpi.js.map +1 -0
  115. package/dist/dashboard/widgets/TeamHoursKpi.d.ts +19 -0
  116. package/dist/dashboard/widgets/TeamHoursKpi.d.ts.map +1 -0
  117. package/dist/dashboard/widgets/TeamHoursKpi.js +13 -0
  118. package/dist/dashboard/widgets/TeamHoursKpi.js.map +1 -0
  119. package/dist/dashboard/widgets/TeamPendingApprovalsKpi.d.ts +20 -0
  120. package/dist/dashboard/widgets/TeamPendingApprovalsKpi.d.ts.map +1 -0
  121. package/dist/dashboard/widgets/TeamPendingApprovalsKpi.js +11 -0
  122. package/dist/dashboard/widgets/TeamPendingApprovalsKpi.js.map +1 -0
  123. package/dist/dashboard/widgets/TeamUtilizationOverview.d.ts +18 -0
  124. package/dist/dashboard/widgets/TeamUtilizationOverview.d.ts.map +1 -0
  125. package/dist/dashboard/widgets/TeamUtilizationOverview.js +17 -0
  126. package/dist/dashboard/widgets/TeamUtilizationOverview.js.map +1 -0
  127. package/dist/dashboard/widgets/TeamWorkloadAlerts.d.ts +24 -0
  128. package/dist/dashboard/widgets/TeamWorkloadAlerts.d.ts.map +1 -0
  129. package/dist/dashboard/widgets/TeamWorkloadAlerts.js +19 -0
  130. package/dist/dashboard/widgets/TeamWorkloadAlerts.js.map +1 -0
  131. package/dist/dashboard/widgets/index.d.ts +24 -0
  132. package/dist/dashboard/widgets/index.d.ts.map +1 -0
  133. package/dist/dashboard/widgets/index.js +54 -0
  134. package/dist/dashboard/widgets/index.js.map +1 -0
  135. package/dist/dto/create-collaborator.dto.d.ts +0 -1
  136. package/dist/dto/create-collaborator.dto.d.ts.map +1 -1
  137. package/dist/dto/create-collaborator.dto.js +0 -6
  138. package/dist/dto/create-collaborator.dto.js.map +1 -1
  139. package/dist/index.d.ts +2 -0
  140. package/dist/index.d.ts.map +1 -1
  141. package/dist/index.js +2 -0
  142. package/dist/index.js.map +1 -1
  143. package/dist/operations.controller.d.ts +42 -0
  144. package/dist/operations.controller.d.ts.map +1 -1
  145. package/dist/operations.service.d.ts +182 -268
  146. package/dist/operations.service.d.ts.map +1 -1
  147. package/dist/operations.service.js +2147 -1337
  148. package/dist/operations.service.js.map +1 -1
  149. package/dist/operations.service.spec.js +345 -174
  150. package/dist/operations.service.spec.js.map +1 -1
  151. package/hedhog/data/dashboard_component.yaml +66 -0
  152. package/hedhog/data/dashboard_item.yaml +25 -25
  153. package/hedhog/data/route.yaml +61 -0
  154. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +39 -99
  155. package/hedhog/frontend/app/_components/collaborator-picker.tsx.ejs +158 -0
  156. package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +314 -116
  157. package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +434 -449
  158. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +289 -412
  159. package/hedhog/frontend/app/_components/project-file-attachments.tsx.ejs +371 -0
  160. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +426 -374
  161. package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +803 -581
  162. package/hedhog/frontend/app/_components/task-file-attachments.tsx.ejs +4 -1
  163. package/hedhog/frontend/app/_components/task-form-fields.tsx.ejs +406 -0
  164. package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +629 -784
  165. package/hedhog/frontend/app/_components/task-info-display.tsx.ejs +137 -0
  166. package/hedhog/frontend/app/_components/timesheet-entry-create-sheet.tsx.ejs +306 -0
  167. package/hedhog/frontend/app/_lib/api.ts.ejs +480 -476
  168. package/hedhog/frontend/app/_lib/types.ts.ejs +66 -5
  169. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +0 -2
  170. package/hedhog/frontend/app/_lib/utils/task-ui.ts.ejs +43 -0
  171. package/hedhog/frontend/app/approvals/page.tsx.ejs +6 -1
  172. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +6 -1
  173. package/hedhog/frontend/app/collaborators/page.tsx.ejs +59 -8
  174. package/hedhog/frontend/app/contracts/page.tsx.ejs +29 -8
  175. package/hedhog/frontend/app/dashboard/widgets/CapacityDistribution.tsx.ejs +84 -0
  176. package/hedhog/frontend/app/dashboard/widgets/EffortByProject.tsx.ejs +85 -0
  177. package/hedhog/frontend/app/dashboard/widgets/HeadcountByArea.tsx.ejs +101 -0
  178. package/hedhog/frontend/app/dashboard/widgets/ManagedProjectsStatus.tsx.ejs +113 -0
  179. package/hedhog/frontend/app/dashboard/widgets/MyHoursPeriodKpi.tsx.ejs +87 -0
  180. package/hedhog/frontend/app/dashboard/widgets/MyOpenRequestsKpi.tsx.ejs +97 -0
  181. package/hedhog/frontend/app/dashboard/widgets/MyPendingRequestsList.tsx.ejs +99 -0
  182. package/hedhog/frontend/app/dashboard/widgets/MyProjectAllocationsKpi.tsx.ejs +78 -0
  183. package/hedhog/frontend/app/dashboard/widgets/MyQuickActions.tsx.ejs +130 -0
  184. package/hedhog/frontend/app/dashboard/widgets/MyRelevantDeadlines.tsx.ejs +144 -0
  185. package/hedhog/frontend/app/dashboard/widgets/MyTimesheetStatusKpi.tsx.ejs +78 -0
  186. package/hedhog/frontend/app/dashboard/widgets/MyWeeklyJourney.tsx.ejs +99 -0
  187. package/hedhog/frontend/app/dashboard/widgets/PortfolioCostsKpi.tsx.ejs +112 -0
  188. package/hedhog/frontend/app/dashboard/widgets/PortfolioEffortKpi.tsx.ejs +93 -0
  189. package/hedhog/frontend/app/dashboard/widgets/PortfolioProjectsKpi.tsx.ejs +96 -0
  190. package/hedhog/frontend/app/dashboard/widgets/PortfolioRiskKpi.tsx.ejs +115 -0
  191. package/hedhog/frontend/app/dashboard/widgets/ProjectStatusOverview.tsx.ejs +120 -0
  192. package/hedhog/frontend/app/dashboard/widgets/StrategicDeadlines.tsx.ejs +146 -0
  193. package/hedhog/frontend/app/dashboard/widgets/TeamApprovalQueue.tsx.ejs +108 -0
  194. package/hedhog/frontend/app/dashboard/widgets/TeamCapacityKpi.tsx.ejs +97 -0
  195. package/hedhog/frontend/app/dashboard/widgets/TeamHeadcountKpi.tsx.ejs +100 -0
  196. package/hedhog/frontend/app/dashboard/widgets/TeamHoursKpi.tsx.ejs +104 -0
  197. package/hedhog/frontend/app/dashboard/widgets/TeamPendingApprovalsKpi.tsx.ejs +110 -0
  198. package/hedhog/frontend/app/dashboard/widgets/TeamUtilizationOverview.tsx.ejs +115 -0
  199. package/hedhog/frontend/app/dashboard/widgets/TeamWorkloadAlerts.tsx.ejs +117 -0
  200. package/hedhog/frontend/app/dashboard/widgets/index.ts.ejs +26 -0
  201. package/hedhog/frontend/app/departments/page.tsx.ejs +6 -1
  202. package/hedhog/frontend/app/my-projects/page.tsx.ejs +14 -10
  203. package/hedhog/frontend/app/my-tasks/page.tsx.ejs +328 -105
  204. package/hedhog/frontend/app/project-cost-categories/page.tsx.ejs +58 -52
  205. package/hedhog/frontend/app/project-cost-types/page.tsx.ejs +58 -51
  206. package/hedhog/frontend/app/projects/page.tsx.ejs +376 -30
  207. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +6 -1
  208. package/hedhog/frontend/app/time-off/page.tsx.ejs +6 -1
  209. package/hedhog/frontend/app/timesheets/page.tsx.ejs +10 -4
  210. package/hedhog/frontend/messages/en.json +238 -46
  211. package/hedhog/frontend/messages/operations/en.json +61 -52
  212. package/hedhog/frontend/messages/operations/pt.json +59 -43
  213. package/hedhog/frontend/messages/pt.json +238 -46
  214. package/hedhog/frontend/widgets/capacity-distribution.tsx.ejs +17 -0
  215. package/hedhog/frontend/widgets/effort-by-project.tsx.ejs +17 -0
  216. package/hedhog/frontend/widgets/headcount-by-area.tsx.ejs +17 -0
  217. package/hedhog/frontend/widgets/index.ts.ejs +25 -0
  218. package/hedhog/frontend/widgets/managed-projects-status.tsx.ejs +17 -0
  219. package/hedhog/frontend/widgets/my-hours-period-kpi.tsx.ejs +17 -0
  220. package/hedhog/frontend/widgets/my-open-requests-kpi.tsx.ejs +17 -0
  221. package/hedhog/frontend/widgets/my-pending-requests-list.tsx.ejs +17 -0
  222. package/hedhog/frontend/widgets/my-project-allocations-kpi.tsx.ejs +17 -0
  223. package/hedhog/frontend/widgets/my-quick-actions.tsx.ejs +17 -0
  224. package/hedhog/frontend/widgets/my-relevant-deadlines.tsx.ejs +17 -0
  225. package/hedhog/frontend/widgets/my-timesheet-status-kpi.tsx.ejs +17 -0
  226. package/hedhog/frontend/widgets/my-weekly-journey.tsx.ejs +17 -0
  227. package/hedhog/frontend/widgets/portfolio-costs-kpi.tsx.ejs +17 -0
  228. package/hedhog/frontend/widgets/portfolio-effort-kpi.tsx.ejs +17 -0
  229. package/hedhog/frontend/widgets/portfolio-projects-kpi.tsx.ejs +17 -0
  230. package/hedhog/frontend/widgets/portfolio-risk-kpi.tsx.ejs +17 -0
  231. package/hedhog/frontend/widgets/project-status-overview.tsx.ejs +17 -0
  232. package/hedhog/frontend/widgets/shared-operations-widget.tsx.ejs +170 -0
  233. package/hedhog/frontend/widgets/strategic-deadlines.tsx.ejs +17 -0
  234. package/hedhog/frontend/widgets/team-approval-queue.tsx.ejs +17 -0
  235. package/hedhog/frontend/widgets/team-capacity-kpi.tsx.ejs +17 -0
  236. package/hedhog/frontend/widgets/team-headcount-kpi.tsx.ejs +17 -0
  237. package/hedhog/frontend/widgets/team-hours-kpi.tsx.ejs +17 -0
  238. package/hedhog/frontend/widgets/team-pending-approvals-kpi.tsx.ejs +17 -0
  239. package/hedhog/frontend/widgets/team-utilization-overview.tsx.ejs +17 -0
  240. package/hedhog/frontend/widgets/team-workload-alerts.tsx.ejs +17 -0
  241. package/hedhog/table/operations_collaborator.yaml +8 -13
  242. package/hedhog/table/operations_project.yaml +1 -1
  243. package/hedhog/table/operations_project_file.yaml +23 -0
  244. package/hedhog/table/operations_task.yaml +76 -69
  245. package/hedhog/table/operations_task_activity.yaml +51 -0
  246. package/package.json +6 -5
  247. package/src/controllers/operations-projects.controller.ts +41 -8
  248. package/src/controllers/operations-tasks.controller.ts +156 -166
  249. package/src/dashboard/README.md +214 -0
  250. package/src/dashboard/components/DashboardLayout.tsx +131 -0
  251. package/src/dashboard/components/widget-registry.ts +255 -0
  252. package/src/dashboard/hooks/useDashboardData.ts +29 -0
  253. package/src/dashboard/types/widgets.types.ts +237 -0
  254. package/src/dashboard/widgets/CapacityDistribution.tsx +56 -0
  255. package/src/dashboard/widgets/EffortByProject.tsx +51 -0
  256. package/src/dashboard/widgets/HeadcountByArea.tsx +57 -0
  257. package/src/dashboard/widgets/ManagedProjectsStatus.tsx +53 -0
  258. package/src/dashboard/widgets/MyHoursPeriodKpi.tsx +87 -0
  259. package/src/dashboard/widgets/MyOpenRequestsKpi.tsx +51 -0
  260. package/src/dashboard/widgets/MyPendingRequestsList.tsx +63 -0
  261. package/src/dashboard/widgets/MyProjectAllocationsKpi.tsx +57 -0
  262. package/src/dashboard/widgets/MyQuickActions.tsx +62 -0
  263. package/src/dashboard/widgets/MyRelevantDeadlines.tsx +84 -0
  264. package/src/dashboard/widgets/MyTimesheetStatusKpi.tsx +65 -0
  265. package/src/dashboard/widgets/MyWeeklyJourney.tsx +57 -0
  266. package/src/dashboard/widgets/PortfolioCostsKpi.tsx +48 -0
  267. package/src/dashboard/widgets/PortfolioEffortKpi.tsx +41 -0
  268. package/src/dashboard/widgets/PortfolioRiskKpi.tsx +50 -0
  269. package/src/dashboard/widgets/ProjectStatusOverview.tsx +52 -0
  270. package/src/dashboard/widgets/StrategicDeadlines.tsx +93 -0
  271. package/src/dashboard/widgets/TeamApprovalQueue.tsx +70 -0
  272. package/src/dashboard/widgets/TeamCapacityKpi.tsx +50 -0
  273. package/src/dashboard/widgets/TeamHoursKpi.tsx +51 -0
  274. package/src/dashboard/widgets/TeamPendingApprovalsKpi.tsx +53 -0
  275. package/src/dashboard/widgets/TeamUtilizationOverview.tsx +62 -0
  276. package/src/dashboard/widgets/TeamWorkloadAlerts.tsx +81 -0
  277. package/src/dashboard/widgets/index.ts +26 -0
  278. package/src/dto/create-collaborator.dto.ts +4 -11
  279. package/src/index.ts +3 -0
  280. package/src/operations.service.spec.ts +988 -764
  281. package/src/operations.service.ts +4277 -2535
@@ -0,0 +1,100 @@
1
+ 'use client';
2
+
3
+ import { Users } from 'lucide-react';
4
+ import React from 'react';
5
+
6
+ interface TeamHeadcountKpiProps {
7
+ slug: string;
8
+ title: string;
9
+ roleSlug: string;
10
+ width?: number;
11
+ height?: number;
12
+ data?: {
13
+ totalHeadcount: number;
14
+ activeCollaborators: number;
15
+ onLeave: number;
16
+ newThisMonth: number;
17
+ growthPercent: number;
18
+ };
19
+ style?: React.CSSProperties;
20
+ }
21
+
22
+ const TeamHeadcountKpi: React.FC<TeamHeadcountKpiProps> = ({
23
+ title,
24
+ data = {
25
+ totalHeadcount: 0,
26
+ activeCollaborators: 0,
27
+ onLeave: 0,
28
+ newThisMonth: 0,
29
+ growthPercent: 0,
30
+ },
31
+ style,
32
+ }) => {
33
+ const activePercent =
34
+ data.totalHeadcount > 0
35
+ ? Math.round((data.activeCollaborators / data.totalHeadcount) * 100)
36
+ : 0;
37
+ const isGrowth = data.growthPercent >= 0;
38
+
39
+ return (
40
+ <div
41
+ className="bg-linear-to-br from-white to-teal-50 rounded-lg shadow-sm p-4 border border-teal-100"
42
+ style={style}
43
+ >
44
+ <div className="flex items-center justify-between mb-4">
45
+ <h3 className="text-sm font-medium text-gray-700">{title}</h3>
46
+ <Users className="w-4 h-4 text-teal-600" />
47
+ </div>
48
+ <div className="bg-linear-to-r from-teal-500 to-cyan-600 rounded-lg p-4 text-white mb-3">
49
+ <div className="flex items-center justify-between">
50
+ <div>
51
+ <div className="text-xs opacity-90">Headcount Total</div>
52
+ <div className="text-3xl font-bold">{data.totalHeadcount}</div>
53
+ </div>
54
+ <Users className="w-12 h-12 opacity-30" />
55
+ </div>
56
+ </div>
57
+ <div className="space-y-2">
58
+ <div className="flex justify-between items-center">
59
+ <span className="text-xs font-medium text-gray-700">
60
+ Colaboradores Ativos
61
+ </span>
62
+ <span className="text-sm font-bold px-2 py-1 rounded-full bg-teal-100 text-teal-700">
63
+ {activePercent}%
64
+ </span>
65
+ </div>
66
+ <div className="relative w-full h-4 bg-gray-200 rounded-full overflow-hidden">
67
+ <div
68
+ className="h-full transition-all duration-500 ease-out rounded-full bg-linear-to-r from-teal-400 to-teal-600"
69
+ style={{ width: `${activePercent}%` }}
70
+ />
71
+ </div>
72
+ </div>
73
+ <div className="grid grid-cols-3 gap-2 bg-teal-50 rounded-lg p-3 border border-teal-200 mt-3">
74
+ <div>
75
+ <div className="text-xs text-gray-600 mb-1">Ativos</div>
76
+ <div className="text-xl font-bold text-teal-600">
77
+ {data.activeCollaborators}
78
+ </div>
79
+ </div>
80
+ <div>
81
+ <div className="text-xs text-gray-600 mb-1">Afastados</div>
82
+ <div className="text-xl font-bold text-yellow-600">
83
+ {data.onLeave}
84
+ </div>
85
+ </div>
86
+ <div>
87
+ <div className="text-xs text-gray-600 mb-1">Crescimento</div>
88
+ <div
89
+ className={`text-xl font-bold ${isGrowth ? 'text-green-600' : 'text-red-600'}`}
90
+ >
91
+ {isGrowth ? '+' : ''}
92
+ {data.growthPercent}%
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ );
98
+ };
99
+
100
+ export default TeamHeadcountKpi;
@@ -0,0 +1,104 @@
1
+ 'use client';
2
+
3
+ import { TrendingUp } from 'lucide-react';
4
+ import { useTranslations } from 'next-intl';
5
+ import React from 'react';
6
+
7
+ interface TeamHoursKpiProps {
8
+ slug: string;
9
+ title: string;
10
+ roleSlug: string;
11
+ width?: number;
12
+ height?: number;
13
+ data?: {
14
+ totalHours: number;
15
+ targetHours: number;
16
+ teamMembers: number;
17
+ avgHoursPerMember: number;
18
+ variance: number;
19
+ };
20
+ style?: React.CSSProperties;
21
+ }
22
+
23
+ const TeamHoursKpi: React.FC<TeamHoursKpiProps> = ({
24
+ title,
25
+ data = {
26
+ totalHours: 0,
27
+ targetHours: 0,
28
+ teamMembers: 0,
29
+ avgHoursPerMember: 0,
30
+ variance: 0,
31
+ },
32
+ style,
33
+ }) => {
34
+ const hoursPercent = Math.round((data.totalHours / data.targetHours) * 100);
35
+ const isAboveTarget = data.totalHours > data.targetHours;
36
+ const varianceSign = data.variance >= 0 ? '+' : '';
37
+ const t = useTranslations('operations.TeamHoursKpi');
38
+ return (
39
+ <div
40
+ className="bg-linear-to-br from-white to-blue-50 rounded-lg shadow-sm p-4 border border-blue-100"
41
+ style={style}
42
+ >
43
+ <h3 className="text-sm font-medium text-gray-700 mb-4">{title}</h3>
44
+ <div className="space-y-4">
45
+ <div className="space-y-2">
46
+ <div className="flex justify-between items-center">
47
+ <span className="text-xs font-medium text-gray-700">
48
+ {t('registeredHours')}
49
+ </span>
50
+ <span
51
+ className={`text-sm font-bold px-2 py-1 rounded-full ${isAboveTarget ? 'bg-green-100 text-green-700' : hoursPercent >= 90 ? 'bg-blue-100 text-blue-700' : 'bg-yellow-100 text-yellow-700'}`}
52
+ >
53
+ {hoursPercent}%
54
+ </span>
55
+ </div>
56
+ <div className="relative w-full h-4 bg-gray-200 rounded-full overflow-hidden">
57
+ <div
58
+ className={`h-full transition-all duration-500 ease-out rounded-full ${isAboveTarget ? 'bg-linear-to-r from-green-400 to-green-600' : 'bg-linear-to-r from-blue-400 to-blue-600'}`}
59
+ style={{ width: `${Math.min(hoursPercent, 100)}%` }}
60
+ />
61
+ </div>
62
+ <div className="flex justify-between text-xs text-gray-600">
63
+ <span>{t('registered', { hours: data.totalHours })}</span>
64
+ <span>{t('goal', { hours: data.targetHours })}</span>
65
+ </div>
66
+ </div>
67
+ <div className="grid grid-cols-2 gap-2 bg-blue-50 rounded-lg p-3 border border-blue-100">
68
+ <div>
69
+ <div className="text-xs text-gray-600 mb-1">
70
+ {t('collaborators')}
71
+ </div>
72
+ <div className="text-2xl font-bold text-blue-600">
73
+ {data.teamMembers}
74
+ </div>
75
+ </div>
76
+ <div>
77
+ <div className="text-xs text-gray-600 mb-1">
78
+ {t('averagePerPerson')}
79
+ </div>
80
+ <div className="text-2xl font-bold text-blue-600">
81
+ {data.avgHoursPerMember}h
82
+ </div>
83
+ </div>
84
+ </div>
85
+ <div className="flex items-center gap-2 p-3 bg-linear-to-r from-blue-100 to-blue-50 rounded-lg border border-blue-200">
86
+ <TrendingUp
87
+ className={`w-5 h-5 ${isAboveTarget ? 'text-green-600' : 'text-blue-600'}`}
88
+ />
89
+ <div>
90
+ <div className="text-xs text-gray-600">{t('goalVariation')}</div>
91
+ <div
92
+ className={`text-sm font-bold ${isAboveTarget ? 'text-green-600' : 'text-blue-600'}`}
93
+ >
94
+ {varianceSign}
95
+ {data.variance}%
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ );
102
+ };
103
+
104
+ export default TeamHoursKpi;
@@ -0,0 +1,110 @@
1
+ 'use client';
2
+
3
+ import { AlertTriangle, CheckCircle2, Clock } from 'lucide-react';
4
+ import { useTranslations } from 'next-intl';
5
+ import React from 'react';
6
+
7
+ interface TeamPendingApprovalsKpiProps {
8
+ slug: string;
9
+ title: string;
10
+ roleSlug: string;
11
+ width?: number;
12
+ height?: number;
13
+ data?: {
14
+ totalPending: number;
15
+ urgent: number;
16
+ timesheets: number;
17
+ expenses: number;
18
+ requests: number;
19
+ avgWaitingDays: number;
20
+ };
21
+ style?: React.CSSProperties;
22
+ }
23
+
24
+ const TeamPendingApprovalsKpi: React.FC<TeamPendingApprovalsKpiProps> = ({
25
+ title,
26
+ data = {
27
+ totalPending: 0,
28
+ urgent: 0,
29
+ timesheets: 0,
30
+ expenses: 0,
31
+ requests: 0,
32
+ avgWaitingDays: 0,
33
+ },
34
+ style,
35
+ }) => {
36
+ const hasUrgent = data.urgent > 0;
37
+ const t = useTranslations('operations.TeamPendingApprovalsKpi');
38
+ return (
39
+ <div
40
+ className={`bg-linear-to-br from-white to-red-50 rounded-lg shadow-sm p-4 border ${hasUrgent ? 'border-red-400 border-2' : 'border-red-100'} transition-all`}
41
+ style={style}
42
+ >
43
+ <div className="flex items-center justify-between mb-4">
44
+ <h3 className="text-sm font-medium text-gray-700">{title}</h3>
45
+ {hasUrgent && (
46
+ <div className="relative inline-block">
47
+ <div
48
+ className="absolute inset-0 bg-red-600 rounded-full animate-pulse"
49
+ style={{ opacity: 0.25 }}
50
+ />
51
+ <AlertTriangle className="w-5 h-5 text-red-600 relative" />
52
+ </div>
53
+ )}
54
+ </div>
55
+ <div className="bg-linear-to-r from-red-500 to-red-600 rounded-lg p-4 text-white mb-3">
56
+ <div className="flex items-center justify-between">
57
+ <div>
58
+ <div className="text-sm opacity-90">{t('label')}</div>
59
+ <div className="text-4xl font-bold">{data.totalPending}</div>
60
+ </div>
61
+ <CheckCircle2 className="w-12 h-12 opacity-30" />
62
+ </div>
63
+ </div>
64
+ <div className="grid grid-cols-3 gap-2 mb-3">
65
+ <div className="bg-blue-50 rounded-lg p-2 border border-blue-200">
66
+ <div className="text-2xl font-bold text-blue-600">
67
+ {data.timesheets}
68
+ </div>
69
+ <div className="text-xs text-gray-600">Timesheets</div>
70
+ </div>
71
+ <div className="bg-green-50 rounded-lg p-2 border border-green-200">
72
+ <div className="text-2xl font-bold text-green-600">
73
+ {data.expenses}
74
+ </div>
75
+ <div className="text-xs text-gray-600">Despesas</div>
76
+ </div>
77
+ <div className="bg-purple-50 rounded-lg p-2 border border-purple-200">
78
+ <div className="text-2xl font-bold text-purple-600">
79
+ {data.requests}
80
+ </div>
81
+ <div className="text-xs text-gray-600">{t('requests')}</div>
82
+ </div>
83
+ </div>
84
+ <div className="space-y-2 text-xs border-t border-red-100 pt-3">
85
+ <div className="flex items-center justify-between">
86
+ <div className="flex items-center gap-2">
87
+ <AlertTriangle className="w-4 h-4 text-red-600" />
88
+ <span className="text-gray-600">{t('urgent')}</span>
89
+ </div>
90
+ <span
91
+ className={`font-bold px-2 py-1 rounded ${data.urgent > 5 ? 'bg-red-100 text-red-700' : 'bg-yellow-100 text-yellow-700'}`}
92
+ >
93
+ {data.urgent}
94
+ </span>
95
+ </div>
96
+ <div className="flex items-center justify-between">
97
+ <div className="flex items-center gap-2">
98
+ <Clock className="w-4 h-4 text-gray-400" />
99
+ <span className="text-gray-600">{t('avgTime')}</span>
100
+ </div>
101
+ <span className="font-semibold text-gray-900">
102
+ {t('days', { count: data.avgWaitingDays })}
103
+ </span>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ );
108
+ };
109
+
110
+ export default TeamPendingApprovalsKpi;
@@ -0,0 +1,115 @@
1
+ 'use client';
2
+
3
+ import { AlertCircle, BarChart3 } from 'lucide-react';
4
+ import { useTranslations } from 'next-intl';
5
+ import React from 'react';
6
+
7
+ interface TeamUtilizationOverviewProps {
8
+ slug: string;
9
+ title: string;
10
+ roleSlug: string;
11
+ width?: number;
12
+ height?: number;
13
+ data?: {
14
+ overUtilized: number;
15
+ wellUtilized: number;
16
+ underUtilized: number;
17
+ idle: number;
18
+ };
19
+ style?: React.CSSProperties;
20
+ }
21
+
22
+ const TeamUtilizationOverview: React.FC<TeamUtilizationOverviewProps> = ({
23
+ title,
24
+ data = { overUtilized: 0, wellUtilized: 0, underUtilized: 0, idle: 0 },
25
+ style,
26
+ }) => {
27
+ const t = useTranslations('operations.TeamUtilizationOverview');
28
+ const total =
29
+ data.overUtilized + data.wellUtilized + data.underUtilized + data.idle;
30
+ const categories = [
31
+ {
32
+ label: t('categories.overUtilized'),
33
+ count: data.overUtilized,
34
+ emoji: '🔴',
35
+ percent: total > 0 ? Math.round((data.overUtilized / total) * 100) : 0,
36
+ color: 'bg-red-500',
37
+ },
38
+ {
39
+ label: t('categories.wellUtilized'),
40
+ count: data.wellUtilized,
41
+ emoji: '🟢',
42
+ percent: total > 0 ? Math.round((data.wellUtilized / total) * 100) : 0,
43
+ color: 'bg-green-500',
44
+ },
45
+ {
46
+ label: t('categories.underUtilized'),
47
+ count: data.underUtilized,
48
+ emoji: '🟡',
49
+ percent: total > 0 ? Math.round((data.underUtilized / total) * 100) : 0,
50
+ color: 'bg-yellow-500',
51
+ },
52
+ {
53
+ label: t('categories.idle'),
54
+ count: data.idle,
55
+ emoji: '⚪',
56
+ percent: total > 0 ? Math.round((data.idle / total) * 100) : 0,
57
+ color: 'bg-gray-400',
58
+ },
59
+ ];
60
+
61
+ return (
62
+ <div
63
+ className="bg-linear-to-br from-white to-lime-50 rounded-lg shadow-sm p-4 border border-lime-100"
64
+ style={style}
65
+ >
66
+ <div className="flex items-center justify-between mb-4">
67
+ <h3 className="text-sm font-medium text-gray-700">{title}</h3>
68
+ <BarChart3 className="w-4 h-4 text-lime-600" />
69
+ </div>
70
+ <div className="space-y-4">
71
+ <div className="space-y-3">
72
+ {categories.map((cat) => (
73
+ <div key={cat.label} className="space-y-1">
74
+ <div className="flex items-center justify-between">
75
+ <span className="text-xs font-medium text-gray-700">
76
+ {cat.emoji} {cat.label}
77
+ </span>
78
+ <span className="text-xs font-bold text-gray-900">
79
+ {cat.count} ({cat.percent}%)
80
+ </span>
81
+ </div>
82
+ <div className="relative w-full h-3 bg-gray-100 rounded-full overflow-hidden">
83
+ <div
84
+ className={`h-full ${cat.color} transition-all duration-500 ease-out`}
85
+ style={{ width: `${cat.percent}%` }}
86
+ />
87
+ </div>
88
+ </div>
89
+ ))}
90
+ </div>
91
+ {data.overUtilized > 0 && (
92
+ <div className="bg-red-50 border border-red-300 rounded-lg p-3 flex gap-2">
93
+ <AlertCircle className="w-4 h-4 text-red-600 flex-shrink-0 mt-0.5" />
94
+ <div className="text-xs text-red-700">
95
+ <p className="font-semibold">
96
+ {t('overUtilizedAlert', { count: data.overUtilized })}
97
+ </p>
98
+ <p className="text-red-600 mt-1">
99
+ {t('rebalanceRecommendation')}
100
+ </p>
101
+ </div>
102
+ </div>
103
+ )}
104
+ <div className="bg-linear-to-r from-lime-50 to-lime-100 rounded-lg p-3 border border-lime-200 text-center">
105
+ <p className="text-xs text-gray-600 mb-1">
106
+ {t('totalCollaborators')}
107
+ </p>
108
+ <p className="text-2xl font-bold text-lime-700">{total}</p>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ );
113
+ };
114
+
115
+ export default TeamUtilizationOverview;
@@ -0,0 +1,117 @@
1
+ 'use client';
2
+
3
+ import { AlertTriangle, Clock, TrendingDown, Users } from 'lucide-react';
4
+ import { useTranslations } from 'next-intl';
5
+ import React from 'react';
6
+
7
+ interface Alert {
8
+ id: string;
9
+ type: 'overload' | 'underload' | 'deadline' | 'absence';
10
+ severity: 'critical' | 'warning' | 'info';
11
+ title: string;
12
+ description: string;
13
+ affectedCount: number;
14
+ }
15
+
16
+ interface TeamWorkloadAlertsProps {
17
+ slug: string;
18
+ title: string;
19
+ roleSlug: string;
20
+ width?: number;
21
+ height?: number;
22
+ data?: { alerts: Alert[]; criticalCount: number };
23
+ style?: React.CSSProperties;
24
+ }
25
+
26
+ const TeamWorkloadAlerts: React.FC<TeamWorkloadAlertsProps> = ({
27
+ title,
28
+ data = { alerts: [], criticalCount: 0 },
29
+ style,
30
+ }) => {
31
+ const t = useTranslations('operations.TeamWorkloadAlerts');
32
+ const getAlertIcon = (type: string) =>
33
+ ({ overload: '⚠️', underload: '📉', deadline: '⏰', absence: '👤' })[
34
+ type
35
+ ] || '📋';
36
+ const getAlertColor = (severity: string) => {
37
+ switch (severity) {
38
+ case 'critical':
39
+ return 'bg-red-50 border-red-300 border-l-4';
40
+ case 'warning':
41
+ return 'bg-yellow-50 border-yellow-300 border-l-4';
42
+ default:
43
+ return 'bg-blue-50 border-blue-300 border-l-4';
44
+ }
45
+ };
46
+ const getSeverityDot = (severity: string) =>
47
+ severity === 'critical'
48
+ ? 'bg-red-500 animate-pulse'
49
+ : severity === 'warning'
50
+ ? 'bg-yellow-500'
51
+ : 'bg-blue-500';
52
+
53
+ return (
54
+ <div
55
+ className="bg-linear-to-br from-white to-yellow-50 rounded-lg shadow-sm p-4 border border-yellow-100"
56
+ style={style}
57
+ >
58
+ <div className="flex items-center justify-between mb-4">
59
+ <h3 className="text-sm font-medium text-gray-700">{title}</h3>
60
+ {data.criticalCount > 0 && (
61
+ <AlertTriangle className="w-5 h-5 text-red-600 animate-pulse" />
62
+ )}
63
+ </div>
64
+ <div className="space-y-3">
65
+ {data.alerts.length === 0 ? (
66
+ <div className="text-center py-6">
67
+ <TrendingDown className="w-8 h-8 text-green-400 mx-auto mb-2" />
68
+ <p className="text-sm text-gray-600">{t('empty')}</p>
69
+ </div>
70
+ ) : (
71
+ <>
72
+ {data.criticalCount > 0 && (
73
+ <div className="bg-red-100 rounded-lg p-3 border border-red-300 flex items-center gap-2">
74
+ <AlertTriangle className="w-4 h-4 text-red-600 flex-shrink-0" />
75
+ <span className="text-xs font-bold text-red-700">
76
+ {data.criticalCount} alerta(s) crítico(s)
77
+ </span>
78
+ </div>
79
+ )}
80
+ <div className="space-y-2 max-h-64 overflow-y-auto">
81
+ {data.alerts.map((alert) => (
82
+ <div
83
+ key={alert.id}
84
+ className={`rounded-lg p-3 border ${getAlertColor(alert.severity)}`}
85
+ >
86
+ <div className="flex items-start gap-2">
87
+ <div
88
+ className={`w-2 h-2 rounded-full ${getSeverityDot(alert.severity)} mt-1.5 flex-shrink-0`}
89
+ />
90
+ <div className="flex-1">
91
+ <p className="text-sm font-medium text-gray-900">
92
+ {alert.title}
93
+ </p>
94
+ <p className="text-xs text-gray-700 my-1">
95
+ {alert.description}
96
+ </p>
97
+ <div className="flex items-center gap-1 text-xs text-gray-600">
98
+ <Users className="w-3 h-3" />
99
+ <span>{alert.affectedCount} envolvido(s)</span>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ ))}
105
+ </div>
106
+ </>
107
+ )}
108
+ <div className="pt-2 border-t border-yellow-100 text-xs text-gray-600 flex items-center gap-1">
109
+ <Clock className="w-3 h-3" />
110
+ <span>{t('lastUpdated')}</span>
111
+ </div>
112
+ </div>
113
+ </div>
114
+ );
115
+ };
116
+
117
+ export default TeamWorkloadAlerts;
@@ -0,0 +1,26 @@
1
+ // Dashboard Widgets - Exports
2
+ export { default as MyHoursPeriodKpi } from './MyHoursPeriodKpi';
3
+ export { default as MyOpenRequestsKpi } from './MyOpenRequestsKpi';
4
+ export { default as MyPendingRequestsList } from './MyPendingRequestsList';
5
+ export { default as MyProjectAllocationsKpi } from './MyProjectAllocationsKpi';
6
+ export { default as MyQuickActions } from './MyQuickActions';
7
+ export { default as MyRelevantDeadlines } from './MyRelevantDeadlines';
8
+ export { default as MyTimesheetStatusKpi } from './MyTimesheetStatusKpi';
9
+ export { default as MyWeeklyJourney } from './MyWeeklyJourney';
10
+
11
+ export { default as ManagedProjectsStatus } from './ManagedProjectsStatus';
12
+ export { default as TeamApprovalQueue } from './TeamApprovalQueue';
13
+ export { default as TeamCapacityKpi } from './TeamCapacityKpi';
14
+ export { default as TeamHoursKpi } from './TeamHoursKpi';
15
+ export { default as TeamPendingApprovalsKpi } from './TeamPendingApprovalsKpi';
16
+ export { default as TeamUtilizationOverview } from './TeamUtilizationOverview';
17
+ export { default as TeamWorkloadAlerts } from './TeamWorkloadAlerts';
18
+
19
+ export { default as CapacityDistribution } from './CapacityDistribution';
20
+ export { default as EffortByProject } from './EffortByProject';
21
+ export { default as HeadcountByArea } from './HeadcountByArea';
22
+ export { default as PortfolioCostsKpi } from './PortfolioCostsKpi';
23
+ export { default as PortfolioEffortKpi } from './PortfolioEffortKpi';
24
+ export { default as PortfolioRiskKpi } from './PortfolioRiskKpi';
25
+ export { default as ProjectStatusOverview } from './ProjectStatusOverview';
26
+ export { default as StrategicDeadlines } from './StrategicDeadlines';
@@ -33,6 +33,7 @@ import { useMemo, useState } from 'react';
33
33
  import { OperationsHeader } from '../_components/operations-header';
34
34
  import { StatusBadge } from '../_components/status-badge';
35
35
  import { fetchOperations, mutateOperations } from '../_lib/api';
36
+ import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
36
37
  import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
37
38
  import type { OperationsDepartment, PaginatedResponse } from '../_lib/types';
38
39
  import { formatEnumLabel, getStatusBadgeClass } from '../_lib/utils/format';
@@ -68,7 +69,11 @@ export default function OperationsDepartmentsPage() {
68
69
  'all' | 'active' | 'inactive'
69
70
  >('all');
70
71
  const [page, setPage] = useState(1);
71
- const [pageSize, setPageSize] = useState(12);
72
+ const [pageSize, setPageSize] = usePersistedPageSize({
73
+ storageKey: 'pagination:operations-departments:pageSize',
74
+ defaultValue: 12,
75
+ allowedValues: [12, 24, 48],
76
+ });
72
77
  const [viewMode, setViewMode] = useState<'table' | 'cards'>(() => {
73
78
  if (typeof window === 'undefined') return 'table';
74
79
  const saved = window.localStorage.getItem(
@@ -19,15 +19,16 @@ import {
19
19
  TableRow,
20
20
  } from '@/components/ui/table';
21
21
  import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
22
+ import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
22
23
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
23
24
  import {
24
25
  CalendarDays,
26
+ CheckCircle2,
25
27
  Eye,
26
28
  FolderKanban,
27
29
  LayoutGrid,
28
30
  List,
29
31
  PlayCircle,
30
- ShieldAlert,
31
32
  } from 'lucide-react';
32
33
  import { useTranslations } from 'next-intl';
33
34
  import Link from 'next/link';
@@ -73,7 +74,11 @@ export default function OperationsMyProjectsPage() {
73
74
  const [search, setSearch] = useState('');
74
75
  const [statusFilter, setStatusFilter] = useState('all');
75
76
  const [page, setPage] = useState(1);
76
- const [pageSize, setPageSize] = useState(12);
77
+ const [pageSize, setPageSize] = usePersistedPageSize({
78
+ storageKey: 'pagination:operations-my-projects:pageSize',
79
+ defaultValue: 12,
80
+ allowedValues: [12, 24, 48],
81
+ });
77
82
  const [viewMode, setViewMode] = useState<ProjectViewMode>(() => {
78
83
  if (typeof window === 'undefined') {
79
84
  return 'table';
@@ -141,13 +146,13 @@ export default function OperationsMyProjectsPage() {
141
146
  iconContainerClassName: 'bg-green-50 text-green-600',
142
147
  },
143
148
  {
144
- key: 'atRisk',
145
- title: t('cards.atRisk'),
146
- description: t('cards.atRiskDescription'),
147
- value: projects.filter((item) => item.status === 'at_risk').length,
148
- icon: ShieldAlert,
149
- accentClassName: 'from-amber-500/20 via-orange-500/10 to-transparent',
150
- iconContainerClassName: 'bg-amber-50 text-amber-600',
149
+ key: 'completed',
150
+ title: t('cards.completed'),
151
+ description: t('cards.completedDescription'),
152
+ value: projects.filter((item) => item.status === 'completed').length,
153
+ icon: CheckCircle2,
154
+ accentClassName: 'from-sky-500/20 via-cyan-500/10 to-transparent',
155
+ iconContainerClassName: 'bg-sky-50 text-sky-600',
151
156
  },
152
157
  {
153
158
  key: 'upcomingDeliveries',
@@ -210,7 +215,6 @@ export default function OperationsMyProjectsPage() {
210
215
  { value: 'all', label: commonT('filters.allStatuses') },
211
216
  { value: 'planning', label: tProjectStatus('planning') },
212
217
  { value: 'active', label: tProjectStatus('active') },
213
- { value: 'at_risk', label: tProjectStatus('at_risk') },
214
218
  { value: 'paused', label: tProjectStatus('paused') },
215
219
  { value: 'completed', label: tProjectStatus('completed') },
216
220
  { value: 'archived', label: tProjectStatus('archived') },