@hed-hog/operations 0.0.329 → 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 (290) 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 +30 -5
  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 +178 -264
  146. package/dist/operations.service.d.ts.map +1 -1
  147. package/dist/operations.service.js +2170 -1340
  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_component_role.yaml +8 -8
  153. package/hedhog/data/dashboard_item.yaml +25 -25
  154. package/hedhog/data/dashboard_role.yaml +1 -1
  155. package/hedhog/data/menu.yaml +6 -16
  156. package/hedhog/data/role.yaml +1 -1
  157. package/hedhog/data/route.yaml +116 -55
  158. package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +15 -9
  159. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +39 -99
  160. package/hedhog/frontend/app/_components/collaborator-picker.tsx.ejs +158 -0
  161. package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +314 -116
  162. package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +434 -449
  163. package/hedhog/frontend/app/_components/project-costs-section.tsx.ejs +51 -81
  164. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +328 -423
  165. package/hedhog/frontend/app/_components/project-file-attachments.tsx.ejs +371 -0
  166. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +446 -377
  167. package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +803 -581
  168. package/hedhog/frontend/app/_components/task-file-attachments.tsx.ejs +14 -9
  169. package/hedhog/frontend/app/_components/task-form-fields.tsx.ejs +406 -0
  170. package/hedhog/frontend/app/_components/task-form-sheet.tsx.ejs +629 -784
  171. package/hedhog/frontend/app/_components/task-info-display.tsx.ejs +137 -0
  172. package/hedhog/frontend/app/_components/timesheet-entry-create-sheet.tsx.ejs +306 -0
  173. package/hedhog/frontend/app/_lib/api.ts.ejs +480 -476
  174. package/hedhog/frontend/app/_lib/hooks/use-values-visibility.ts.ejs +61 -0
  175. package/hedhog/frontend/app/_lib/types.ts.ejs +66 -5
  176. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +0 -2
  177. package/hedhog/frontend/app/_lib/utils/task-ui.ts.ejs +43 -0
  178. package/hedhog/frontend/app/approvals/page.tsx.ejs +11 -2
  179. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +6 -1
  180. package/hedhog/frontend/app/collaborators/page.tsx.ejs +127 -42
  181. package/hedhog/frontend/app/contracts/page.tsx.ejs +29 -8
  182. package/hedhog/frontend/app/dashboard/widgets/CapacityDistribution.tsx.ejs +84 -0
  183. package/hedhog/frontend/app/dashboard/widgets/EffortByProject.tsx.ejs +85 -0
  184. package/hedhog/frontend/app/dashboard/widgets/HeadcountByArea.tsx.ejs +101 -0
  185. package/hedhog/frontend/app/dashboard/widgets/ManagedProjectsStatus.tsx.ejs +113 -0
  186. package/hedhog/frontend/app/dashboard/widgets/MyHoursPeriodKpi.tsx.ejs +87 -0
  187. package/hedhog/frontend/app/dashboard/widgets/MyOpenRequestsKpi.tsx.ejs +97 -0
  188. package/hedhog/frontend/app/dashboard/widgets/MyPendingRequestsList.tsx.ejs +99 -0
  189. package/hedhog/frontend/app/dashboard/widgets/MyProjectAllocationsKpi.tsx.ejs +78 -0
  190. package/hedhog/frontend/app/dashboard/widgets/MyQuickActions.tsx.ejs +130 -0
  191. package/hedhog/frontend/app/dashboard/widgets/MyRelevantDeadlines.tsx.ejs +144 -0
  192. package/hedhog/frontend/app/dashboard/widgets/MyTimesheetStatusKpi.tsx.ejs +78 -0
  193. package/hedhog/frontend/app/dashboard/widgets/MyWeeklyJourney.tsx.ejs +99 -0
  194. package/hedhog/frontend/app/dashboard/widgets/PortfolioCostsKpi.tsx.ejs +112 -0
  195. package/hedhog/frontend/app/dashboard/widgets/PortfolioEffortKpi.tsx.ejs +93 -0
  196. package/hedhog/frontend/app/dashboard/widgets/PortfolioProjectsKpi.tsx.ejs +96 -0
  197. package/hedhog/frontend/app/dashboard/widgets/PortfolioRiskKpi.tsx.ejs +115 -0
  198. package/hedhog/frontend/app/dashboard/widgets/ProjectStatusOverview.tsx.ejs +120 -0
  199. package/hedhog/frontend/app/dashboard/widgets/StrategicDeadlines.tsx.ejs +146 -0
  200. package/hedhog/frontend/app/dashboard/widgets/TeamApprovalQueue.tsx.ejs +108 -0
  201. package/hedhog/frontend/app/dashboard/widgets/TeamCapacityKpi.tsx.ejs +97 -0
  202. package/hedhog/frontend/app/dashboard/widgets/TeamHeadcountKpi.tsx.ejs +100 -0
  203. package/hedhog/frontend/app/dashboard/widgets/TeamHoursKpi.tsx.ejs +104 -0
  204. package/hedhog/frontend/app/dashboard/widgets/TeamPendingApprovalsKpi.tsx.ejs +110 -0
  205. package/hedhog/frontend/app/dashboard/widgets/TeamUtilizationOverview.tsx.ejs +115 -0
  206. package/hedhog/frontend/app/dashboard/widgets/TeamWorkloadAlerts.tsx.ejs +117 -0
  207. package/hedhog/frontend/app/dashboard/widgets/index.ts.ejs +26 -0
  208. package/hedhog/frontend/app/departments/page.tsx.ejs +6 -1
  209. package/hedhog/frontend/app/my-projects/page.tsx.ejs +59 -16
  210. package/hedhog/frontend/app/my-tasks/page.tsx.ejs +329 -106
  211. package/hedhog/frontend/app/project-cost-categories/page.tsx.ejs +58 -52
  212. package/hedhog/frontend/app/project-cost-types/page.tsx.ejs +58 -51
  213. package/hedhog/frontend/app/projects/page.tsx.ejs +436 -35
  214. package/hedhog/frontend/app/reports/collaborators/page.tsx.ejs +65 -52
  215. package/hedhog/frontend/app/reports/projects/page.tsx.ejs +80 -82
  216. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +13 -2
  217. package/hedhog/frontend/app/time-off/page.tsx.ejs +6 -1
  218. package/hedhog/frontend/app/timesheets/page.tsx.ejs +10 -4
  219. package/hedhog/frontend/messages/en.json +460 -61
  220. package/hedhog/frontend/messages/operations/en.json +61 -52
  221. package/hedhog/frontend/messages/operations/pt.json +59 -43
  222. package/hedhog/frontend/messages/pt.json +460 -61
  223. package/hedhog/frontend/widgets/capacity-distribution.tsx.ejs +17 -0
  224. package/hedhog/frontend/widgets/effort-by-project.tsx.ejs +17 -0
  225. package/hedhog/frontend/widgets/headcount-by-area.tsx.ejs +17 -0
  226. package/hedhog/frontend/widgets/index.ts.ejs +25 -0
  227. package/hedhog/frontend/widgets/managed-projects-status.tsx.ejs +17 -0
  228. package/hedhog/frontend/widgets/my-hours-period-kpi.tsx.ejs +17 -0
  229. package/hedhog/frontend/widgets/my-open-requests-kpi.tsx.ejs +17 -0
  230. package/hedhog/frontend/widgets/my-pending-requests-list.tsx.ejs +17 -0
  231. package/hedhog/frontend/widgets/my-project-allocations-kpi.tsx.ejs +17 -0
  232. package/hedhog/frontend/widgets/my-quick-actions.tsx.ejs +17 -0
  233. package/hedhog/frontend/widgets/my-relevant-deadlines.tsx.ejs +17 -0
  234. package/hedhog/frontend/widgets/my-timesheet-status-kpi.tsx.ejs +17 -0
  235. package/hedhog/frontend/widgets/my-weekly-journey.tsx.ejs +17 -0
  236. package/hedhog/frontend/widgets/portfolio-costs-kpi.tsx.ejs +17 -0
  237. package/hedhog/frontend/widgets/portfolio-effort-kpi.tsx.ejs +17 -0
  238. package/hedhog/frontend/widgets/portfolio-projects-kpi.tsx.ejs +17 -0
  239. package/hedhog/frontend/widgets/portfolio-risk-kpi.tsx.ejs +17 -0
  240. package/hedhog/frontend/widgets/project-status-overview.tsx.ejs +17 -0
  241. package/hedhog/frontend/widgets/shared-operations-widget.tsx.ejs +170 -0
  242. package/hedhog/frontend/widgets/strategic-deadlines.tsx.ejs +17 -0
  243. package/hedhog/frontend/widgets/team-approval-queue.tsx.ejs +17 -0
  244. package/hedhog/frontend/widgets/team-capacity-kpi.tsx.ejs +17 -0
  245. package/hedhog/frontend/widgets/team-headcount-kpi.tsx.ejs +17 -0
  246. package/hedhog/frontend/widgets/team-hours-kpi.tsx.ejs +17 -0
  247. package/hedhog/frontend/widgets/team-pending-approvals-kpi.tsx.ejs +17 -0
  248. package/hedhog/frontend/widgets/team-utilization-overview.tsx.ejs +17 -0
  249. package/hedhog/frontend/widgets/team-workload-alerts.tsx.ejs +17 -0
  250. package/hedhog/table/operations_collaborator.yaml +8 -13
  251. package/hedhog/table/operations_project.yaml +1 -1
  252. package/hedhog/table/operations_project_file.yaml +23 -0
  253. package/hedhog/table/operations_task.yaml +76 -69
  254. package/hedhog/table/operations_task_activity.yaml +51 -0
  255. package/package.json +7 -6
  256. package/src/controllers/operations-projects.controller.ts +41 -8
  257. package/src/controllers/operations-tasks.controller.ts +156 -166
  258. package/src/dashboard/README.md +214 -0
  259. package/src/dashboard/components/DashboardLayout.tsx +131 -0
  260. package/src/dashboard/components/widget-registry.ts +255 -0
  261. package/src/dashboard/hooks/useDashboardData.ts +29 -0
  262. package/src/dashboard/types/widgets.types.ts +237 -0
  263. package/src/dashboard/widgets/CapacityDistribution.tsx +56 -0
  264. package/src/dashboard/widgets/EffortByProject.tsx +51 -0
  265. package/src/dashboard/widgets/HeadcountByArea.tsx +57 -0
  266. package/src/dashboard/widgets/ManagedProjectsStatus.tsx +53 -0
  267. package/src/dashboard/widgets/MyHoursPeriodKpi.tsx +87 -0
  268. package/src/dashboard/widgets/MyOpenRequestsKpi.tsx +51 -0
  269. package/src/dashboard/widgets/MyPendingRequestsList.tsx +63 -0
  270. package/src/dashboard/widgets/MyProjectAllocationsKpi.tsx +57 -0
  271. package/src/dashboard/widgets/MyQuickActions.tsx +62 -0
  272. package/src/dashboard/widgets/MyRelevantDeadlines.tsx +84 -0
  273. package/src/dashboard/widgets/MyTimesheetStatusKpi.tsx +65 -0
  274. package/src/dashboard/widgets/MyWeeklyJourney.tsx +57 -0
  275. package/src/dashboard/widgets/PortfolioCostsKpi.tsx +48 -0
  276. package/src/dashboard/widgets/PortfolioEffortKpi.tsx +41 -0
  277. package/src/dashboard/widgets/PortfolioRiskKpi.tsx +50 -0
  278. package/src/dashboard/widgets/ProjectStatusOverview.tsx +52 -0
  279. package/src/dashboard/widgets/StrategicDeadlines.tsx +93 -0
  280. package/src/dashboard/widgets/TeamApprovalQueue.tsx +70 -0
  281. package/src/dashboard/widgets/TeamCapacityKpi.tsx +50 -0
  282. package/src/dashboard/widgets/TeamHoursKpi.tsx +51 -0
  283. package/src/dashboard/widgets/TeamPendingApprovalsKpi.tsx +53 -0
  284. package/src/dashboard/widgets/TeamUtilizationOverview.tsx +62 -0
  285. package/src/dashboard/widgets/TeamWorkloadAlerts.tsx +81 -0
  286. package/src/dashboard/widgets/index.ts +26 -0
  287. package/src/dto/create-collaborator.dto.ts +4 -11
  288. package/src/index.ts +3 -0
  289. package/src/operations.service.spec.ts +988 -764
  290. package/src/operations.service.ts +4300 -2538
@@ -26,35 +26,173 @@ import {
26
26
  TableRow,
27
27
  } from '@/components/ui/table';
28
28
  import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
29
+ import { usePersistedPageSize } from '@/hooks/use-persisted-page-size';
30
+ import {
31
+ closestCenter,
32
+ DndContext,
33
+ PointerSensor,
34
+ useDraggable,
35
+ useDroppable,
36
+ useSensor,
37
+ useSensors,
38
+ type DragEndEvent,
39
+ type UniqueIdentifier,
40
+ } from '@dnd-kit/core';
41
+ import { CSS } from '@dnd-kit/utilities';
29
42
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
30
43
  import {
31
44
  Archive,
32
45
  ArchiveRestore,
33
46
  CalendarDays,
47
+ CheckCircle2,
34
48
  Eye,
49
+ EyeOff,
35
50
  FileText,
36
51
  FolderKanban,
52
+ KanbanSquare,
37
53
  LayoutGrid,
38
54
  List,
39
55
  Pencil,
40
56
  PlayCircle,
41
- ShieldAlert,
42
57
  } from 'lucide-react';
43
58
  import { useTranslations } from 'next-intl';
44
59
  import Link from 'next/link';
45
60
  import { usePathname, useRouter, useSearchParams } from 'next/navigation';
46
- import { useMemo, useState } from 'react';
61
+ import { useMemo, useState, type ReactNode } from 'react';
47
62
  import { OperationsHeader } from '../_components/operations-header';
48
63
  import { ProjectFormScreen } from '../_components/project-form-screen';
49
64
  import { StatusBadge } from '../_components/status-badge';
50
65
  import { fetchOperations, mutateOperations } from '../_lib/api';
51
66
  import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
67
+ import {
68
+ MASKED_VALUE,
69
+ useValuesVisibility,
70
+ } from '../_lib/hooks/use-values-visibility';
52
71
  import type { OperationsProject, PaginatedResponse } from '../_lib/types';
53
- import { formatDate, getStatusBadgeClass } from '../_lib/utils/format';
72
+ import {
73
+ formatCurrency,
74
+ formatDate,
75
+ getStatusBadgeClass,
76
+ } from '../_lib/utils/format';
54
77
 
55
78
  const PROJECT_VIEW_STORAGE_KEY = 'operations-projects-view-mode';
56
79
 
57
- type ProjectViewMode = 'table' | 'cards';
80
+ type ProjectViewMode = 'table' | 'cards' | 'board';
81
+
82
+ type ProjectBoardStatus = 'planning' | 'active' | 'paused' | 'completed';
83
+
84
+ type ProjectBoardColumns = Record<ProjectBoardStatus, OperationsProject[]>;
85
+
86
+ const PROJECT_BOARD_COLUMNS: Array<{ id: ProjectBoardStatus; color: string }> =
87
+ [
88
+ { id: 'planning', color: 'bg-amber-500' },
89
+ { id: 'active', color: 'bg-emerald-500' },
90
+ { id: 'paused', color: 'bg-slate-500' },
91
+ { id: 'completed', color: 'bg-sky-500' },
92
+ ];
93
+
94
+ function projectDragId(projectId: number) {
95
+ return `project-${projectId}`;
96
+ }
97
+
98
+ function columnDropId(columnId: ProjectBoardStatus) {
99
+ return `project-col-${columnId}`;
100
+ }
101
+
102
+ function parseProjectId(value: UniqueIdentifier | null | undefined) {
103
+ if (!value) return null;
104
+ const match = String(value).match(/^project-(\d+)$/);
105
+ return match ? Number(match[1]) : null;
106
+ }
107
+
108
+ function parseBoardColumnId(
109
+ value: UniqueIdentifier | null | undefined
110
+ ): ProjectBoardStatus | null {
111
+ if (!value) return null;
112
+ const match = String(value).match(/^project-col-(.+)$/);
113
+ const id = match?.[1];
114
+ return PROJECT_BOARD_COLUMNS.some((column) => column.id === id)
115
+ ? (id as ProjectBoardStatus)
116
+ : null;
117
+ }
118
+
119
+ function splitProjectsByStatus(
120
+ projects: OperationsProject[]
121
+ ): ProjectBoardColumns {
122
+ return {
123
+ planning: projects.filter((project) => project.status === 'planning'),
124
+ active: projects.filter((project) => project.status === 'active'),
125
+ paused: projects.filter((project) => project.status === 'paused'),
126
+ completed: projects.filter((project) => project.status === 'completed'),
127
+ };
128
+ }
129
+
130
+ function moveProjectToColumn(
131
+ columns: ProjectBoardColumns,
132
+ projectId: number,
133
+ target: ProjectBoardStatus
134
+ ): ProjectBoardColumns {
135
+ const sourceProject =
136
+ columns.planning.find((project) => project.id === projectId) ??
137
+ columns.active.find((project) => project.id === projectId) ??
138
+ columns.paused.find((project) => project.id === projectId) ??
139
+ columns.completed.find((project) => project.id === projectId);
140
+
141
+ if (!sourceProject) {
142
+ return columns;
143
+ }
144
+
145
+ const nextColumns: ProjectBoardColumns = {
146
+ planning: columns.planning.filter((project) => project.id !== projectId),
147
+ active: columns.active.filter((project) => project.id !== projectId),
148
+ paused: columns.paused.filter((project) => project.id !== projectId),
149
+ completed: columns.completed.filter((project) => project.id !== projectId),
150
+ };
151
+
152
+ return {
153
+ ...nextColumns,
154
+ [target]: [{ ...sourceProject, status: target }, ...nextColumns[target]],
155
+ };
156
+ }
157
+
158
+ function DraggableProjectCard({
159
+ project,
160
+ disabled,
161
+ children,
162
+ }: {
163
+ project: OperationsProject;
164
+ disabled?: boolean;
165
+ children: (isDragging: boolean) => ReactNode;
166
+ }) {
167
+ const { attributes, listeners, setNodeRef, transform, isDragging } =
168
+ useDraggable({
169
+ id: projectDragId(project.id),
170
+ disabled,
171
+ });
172
+
173
+ return (
174
+ <div
175
+ ref={setNodeRef}
176
+ style={{ transform: CSS.Translate.toString(transform) }}
177
+ {...listeners}
178
+ {...attributes}
179
+ className={isDragging ? 'z-20 touch-none' : 'touch-none'}
180
+ >
181
+ {children(isDragging)}
182
+ </div>
183
+ );
184
+ }
185
+
186
+ function DroppableProjectColumn({
187
+ columnId,
188
+ children,
189
+ }: {
190
+ columnId: ProjectBoardStatus;
191
+ children: (isOver: boolean) => ReactNode;
192
+ }) {
193
+ const { setNodeRef, isOver } = useDroppable({ id: columnDropId(columnId) });
194
+ return <div ref={setNodeRef}>{children(isOver)}</div>;
195
+ }
58
196
 
59
197
  function getPersonAvatarUrl(avatarId?: number | null): string {
60
198
  return typeof avatarId === 'number' && avatarId > 0
@@ -104,7 +242,11 @@ export default function OperationsProjectsPage() {
104
242
  const [search, setSearch] = useState('');
105
243
  const [statusFilter, setStatusFilter] = useState('all');
106
244
  const [page, setPage] = useState(1);
107
- const [pageSize, setPageSize] = useState(12);
245
+ const [pageSize, setPageSize] = usePersistedPageSize({
246
+ storageKey: 'pagination:operations-projects:pageSize',
247
+ defaultValue: 12,
248
+ allowedValues: [12, 24, 48],
249
+ });
108
250
  const [viewMode, setViewMode] = useState<ProjectViewMode>(() => {
109
251
  if (typeof window === 'undefined') {
110
252
  return 'table';
@@ -112,8 +254,20 @@ export default function OperationsProjectsPage() {
112
254
 
113
255
  const savedViewMode = window.localStorage.getItem(PROJECT_VIEW_STORAGE_KEY);
114
256
 
115
- return savedViewMode === 'cards' ? 'cards' : 'table';
257
+ if (
258
+ savedViewMode === 'table' ||
259
+ savedViewMode === 'cards' ||
260
+ savedViewMode === 'board'
261
+ ) {
262
+ return savedViewMode;
263
+ }
264
+
265
+ return 'table';
116
266
  });
267
+ const [boardOverride, setBoardOverride] =
268
+ useState<ProjectBoardColumns | null>(null);
269
+ const activeViewMode: ProjectViewMode =
270
+ statusFilter === 'archived' && viewMode === 'board' ? 'table' : viewMode;
117
271
 
118
272
  const createParam = searchParams.get('create');
119
273
  const editProjectId = parseEditProjectId(searchParams.get('edit'));
@@ -154,6 +308,8 @@ export default function OperationsProjectsPage() {
154
308
  updateSheetQuery({ editId: projectId });
155
309
  };
156
310
 
311
+ const { valuesVisible, toggleValuesVisible } = useValuesVisibility();
312
+
157
313
  const { data: projectsResponse, refetch } = useQuery<
158
314
  PaginatedResponse<OperationsProject>
159
315
  >({
@@ -186,8 +342,57 @@ export default function OperationsProjectsPage() {
186
342
  },
187
343
  placeholderData: (previous) => previous,
188
344
  });
189
- const projects = projectsResponse?.data ?? [];
345
+ const { data: boardProjectsResponse, refetch: refetchBoard } = useQuery<
346
+ PaginatedResponse<OperationsProject>
347
+ >({
348
+ queryKey: [
349
+ 'operations-projects-board',
350
+ currentLocaleCode,
351
+ search,
352
+ statusFilter,
353
+ ],
354
+ queryFn: () => {
355
+ const params = new URLSearchParams({
356
+ page: '1',
357
+ pageSize: '1000',
358
+ });
359
+
360
+ if (search.trim()) {
361
+ params.set('search', search.trim());
362
+ }
363
+
364
+ if (statusFilter !== 'all') {
365
+ params.set('status', statusFilter);
366
+ }
367
+
368
+ return fetchOperations<PaginatedResponse<OperationsProject>>(
369
+ request,
370
+ `/operations/projects?${params.toString()}`
371
+ );
372
+ },
373
+ enabled: activeViewMode === 'board',
374
+ placeholderData: (previous) => previous,
375
+ });
376
+ const projects = useMemo(
377
+ () => projectsResponse?.data ?? [],
378
+ [projectsResponse?.data]
379
+ );
380
+ const boardProjects = useMemo(
381
+ () => boardProjectsResponse?.data ?? [],
382
+ [boardProjectsResponse?.data]
383
+ );
384
+ const boardColumns = useMemo(() => {
385
+ if (boardOverride) {
386
+ return boardOverride;
387
+ }
388
+
389
+ return splitProjectsByStatus(boardProjects);
390
+ }, [boardOverride, boardProjects]);
190
391
  const filteredRows = projects;
392
+ const hasRowsForCurrentView =
393
+ activeViewMode === 'board'
394
+ ? boardProjects.length > 0
395
+ : filteredRows.length > 0;
191
396
 
192
397
  const statsCards = useMemo(
193
398
  () => [
@@ -210,13 +415,13 @@ export default function OperationsProjectsPage() {
210
415
  iconContainerClassName: 'bg-green-50 text-green-600',
211
416
  },
212
417
  {
213
- key: 'atRisk',
214
- title: t('cards.atRisk'),
215
- description: t('cards.atRiskDescription'),
216
- value: projects.filter((item) => item.status === 'at_risk').length,
217
- icon: ShieldAlert,
218
- accentClassName: 'from-amber-500/20 via-orange-500/10 to-transparent',
219
- iconContainerClassName: 'bg-amber-50 text-amber-600',
418
+ key: 'completed',
419
+ title: t('cards.completed'),
420
+ description: t('cards.completedDescription'),
421
+ value: projects.filter((item) => item.status === 'completed').length,
422
+ icon: CheckCircle2,
423
+ accentClassName: 'from-sky-500/20 via-cyan-500/10 to-transparent',
424
+ iconContainerClassName: 'bg-sky-50 text-sky-600',
220
425
  },
221
426
  {
222
427
  key: 'upcomingDeliveries',
@@ -232,7 +437,7 @@ export default function OperationsProjectsPage() {
232
437
  );
233
438
 
234
439
  const handleViewModeChange = (value: string) => {
235
- if (value !== 'table' && value !== 'cards') {
440
+ if (value !== 'table' && value !== 'cards' && value !== 'board') {
236
441
  return;
237
442
  }
238
443
 
@@ -243,6 +448,83 @@ export default function OperationsProjectsPage() {
243
448
  }
244
449
  };
245
450
 
451
+ const sensors = useSensors(
452
+ useSensor(PointerSensor, { activationConstraint: { distance: 6 } })
453
+ );
454
+
455
+ const handleBoardDragEnd = async (event: DragEndEvent) => {
456
+ if (!access.isDirector) {
457
+ return;
458
+ }
459
+
460
+ const projectId = parseProjectId(event.active.id);
461
+ const targetColumn = parseBoardColumnId(event.over?.id);
462
+
463
+ if (!projectId || !targetColumn) {
464
+ return;
465
+ }
466
+
467
+ const sourceColumn = PROJECT_BOARD_COLUMNS.find((column) =>
468
+ boardColumns[column.id].some((project) => project.id === projectId)
469
+ )?.id;
470
+
471
+ if (!sourceColumn || sourceColumn === targetColumn) {
472
+ return;
473
+ }
474
+
475
+ setBoardOverride(
476
+ moveProjectToColumn(boardColumns, projectId, targetColumn)
477
+ );
478
+
479
+ try {
480
+ await mutateOperations(
481
+ request,
482
+ `/operations/projects/${projectId}`,
483
+ 'PATCH',
484
+ {
485
+ status: targetColumn,
486
+ }
487
+ );
488
+ showToastHandler?.('success', t('messages.statusSuccess'));
489
+ await Promise.all([refetch(), refetchBoard()]);
490
+ setBoardOverride(null);
491
+ } catch {
492
+ setBoardOverride(null);
493
+ showToastHandler?.('error', t('messages.statusError'));
494
+ await Promise.all([refetch(), refetchBoard()]);
495
+ }
496
+ };
497
+
498
+ const renderBoardCard = (project: OperationsProject, isDragging = false) => (
499
+ <Card
500
+ className={`cursor-pointer border-border/60 py-0 shadow-sm transition ${
501
+ isDragging
502
+ ? 'opacity-70 shadow-lg'
503
+ : 'hover:-translate-y-0.5 hover:shadow-md'
504
+ }`}
505
+ onDoubleClick={() => router.push(`/operations/projects/${project.id}`)}
506
+ >
507
+ <CardContent className="space-y-2 p-3">
508
+ <div className="truncate text-sm font-semibold">{project.name}</div>
509
+ <div className="truncate text-xs text-muted-foreground">
510
+ {[project.code, project.clientName].filter(Boolean).join(' • ') ||
511
+ commonT('labels.notAvailable')}
512
+ </div>
513
+ <div className="flex items-center justify-between gap-2 pt-1">
514
+ <StatusBadge
515
+ label={tProjectStatus(project.status)}
516
+ className={getStatusBadgeClass(project.status)}
517
+ />
518
+ <Button variant="outline" size="icon" asChild>
519
+ <Link href={`/operations/projects/${project.id}`}>
520
+ <Eye className="size-4" />
521
+ </Link>
522
+ </Button>
523
+ </div>
524
+ </CardContent>
525
+ </Card>
526
+ );
527
+
246
528
  const toggleArchived = async (project: OperationsProject) => {
247
529
  const nextStatus = project.status === 'archived' ? 'active' : 'archived';
248
530
 
@@ -256,7 +538,7 @@ export default function OperationsProjectsPage() {
256
538
  }
257
539
  );
258
540
  showToastHandler?.('success', t('messages.statusSuccess'));
259
- await refetch();
541
+ await Promise.all([refetch(), refetchBoard()]);
260
542
  } catch {
261
543
  showToastHandler?.('error', t('messages.statusError'));
262
544
  }
@@ -269,11 +551,27 @@ export default function OperationsProjectsPage() {
269
551
  description={t('description')}
270
552
  current={t('breadcrumb')}
271
553
  actions={
272
- access.isDirector ? (
273
- <Button size="sm" onClick={openCreateSheet}>
274
- {commonT('actions.create')}
554
+ <div className="flex flex-wrap items-center gap-2">
555
+ <Button
556
+ size="icon"
557
+ variant="ghost"
558
+ onClick={toggleValuesVisible}
559
+ title={commonT(
560
+ valuesVisible ? 'actions.hideValues' : 'actions.showValues'
561
+ )}
562
+ >
563
+ {valuesVisible ? (
564
+ <EyeOff className="h-4 w-4" />
565
+ ) : (
566
+ <Eye className="h-4 w-4" />
567
+ )}
275
568
  </Button>
276
- ) : undefined
569
+ {access.isDirector ? (
570
+ <Button size="sm" onClick={openCreateSheet}>
571
+ {commonT('actions.create')}
572
+ </Button>
573
+ ) : null}
574
+ </div>
277
575
  }
278
576
  />
279
577
 
@@ -305,7 +603,6 @@ export default function OperationsProjectsPage() {
305
603
  { value: 'all', label: commonT('filters.allStatuses') },
306
604
  { value: 'planning', label: tProjectStatus('planning') },
307
605
  { value: 'active', label: tProjectStatus('active') },
308
- { value: 'at_risk', label: tProjectStatus('at_risk') },
309
606
  { value: 'paused', label: tProjectStatus('paused') },
310
607
  { value: 'completed', label: tProjectStatus('completed') },
311
608
  { value: 'archived', label: tProjectStatus('archived') },
@@ -321,7 +618,7 @@ export default function OperationsProjectsPage() {
321
618
  </span>
322
619
  <ToggleGroup
323
620
  type="single"
324
- value={viewMode}
621
+ value={activeViewMode}
325
622
  onValueChange={handleViewModeChange}
326
623
  variant="outline"
327
624
  size="sm"
@@ -343,12 +640,86 @@ export default function OperationsProjectsPage() {
343
640
  <LayoutGrid className="h-4 w-4" />
344
641
  <span className="hidden sm:inline">{t('viewModeCards')}</span>
345
642
  </ToggleGroupItem>
643
+ <ToggleGroupItem
644
+ value="board"
645
+ className="gap-1.5 px-2.5"
646
+ aria-label={t('viewModeBoard')}
647
+ >
648
+ <KanbanSquare className="h-4 w-4" />
649
+ <span className="hidden sm:inline">{t('viewModeBoard')}</span>
650
+ </ToggleGroupItem>
346
651
  </ToggleGroup>
347
652
  </div>
348
653
  </div>
349
654
 
350
- {filteredRows.length > 0 ? (
351
- viewMode === 'cards' ? (
655
+ {hasRowsForCurrentView ? (
656
+ activeViewMode === 'board' ? (
657
+ <>
658
+ {!access.isDirector ? (
659
+ <p className="text-sm text-muted-foreground">
660
+ {t('board.directorOnly')}
661
+ </p>
662
+ ) : null}
663
+ <DndContext
664
+ sensors={access.isDirector ? sensors : undefined}
665
+ collisionDetection={closestCenter}
666
+ onDragEnd={(event) => void handleBoardDragEnd(event)}
667
+ >
668
+ <div className="grid gap-4 lg:grid-cols-2 xl:grid-cols-4">
669
+ {PROJECT_BOARD_COLUMNS.map((column) => (
670
+ <DroppableProjectColumn key={column.id} columnId={column.id}>
671
+ {(isOver) => (
672
+ <div
673
+ className={`flex min-h-72 flex-col gap-3 rounded-md border bg-muted/20 p-3 ${
674
+ isOver && access.isDirector
675
+ ? 'border-primary/60 bg-primary/5'
676
+ : 'border-border/60'
677
+ }`}
678
+ >
679
+ <div className="flex items-center gap-2">
680
+ <span
681
+ className={`size-2.5 rounded-full ${column.color}`}
682
+ />
683
+ <span className="text-sm font-semibold">
684
+ {t(`board.columns.${column.id}`)}
685
+ </span>
686
+ <span className="text-xs text-muted-foreground">
687
+ ({boardColumns[column.id].length})
688
+ </span>
689
+ </div>
690
+
691
+ <div className="space-y-2">
692
+ {boardColumns[column.id].length ? (
693
+ boardColumns[column.id].map((project) =>
694
+ access.isDirector ? (
695
+ <DraggableProjectCard
696
+ key={project.id}
697
+ project={project}
698
+ >
699
+ {(isDragging) =>
700
+ renderBoardCard(project, isDragging)
701
+ }
702
+ </DraggableProjectCard>
703
+ ) : (
704
+ <div key={project.id}>
705
+ {renderBoardCard(project)}
706
+ </div>
707
+ )
708
+ )
709
+ ) : (
710
+ <div className="rounded-md border border-dashed border-border/70 px-3 py-5 text-center text-xs text-muted-foreground">
711
+ {t('board.emptyColumn')}
712
+ </div>
713
+ )}
714
+ </div>
715
+ </div>
716
+ )}
717
+ </DroppableProjectColumn>
718
+ ))}
719
+ </div>
720
+ </DndContext>
721
+ </>
722
+ ) : activeViewMode === 'cards' ? (
352
723
  <div className="grid gap-4 md:grid-cols-2 2xl:grid-cols-3">
353
724
  {filteredRows.map((project) => (
354
725
  <Card
@@ -441,6 +812,20 @@ export default function OperationsProjectsPage() {
441
812
  currentLocaleCode
442
813
  )}
443
814
  </div>
815
+ <div>
816
+ <span className="font-medium text-foreground">
817
+ {commonT('labels.budget')}:
818
+ </span>{' '}
819
+ {project.budgetAmount != null
820
+ ? valuesVisible
821
+ ? formatCurrency(
822
+ project.budgetAmount,
823
+ getSettingValue,
824
+ currentLocaleCode
825
+ )
826
+ : MASKED_VALUE
827
+ : commonT('labels.notAvailable')}
828
+ </div>
444
829
  <div className="flex items-center gap-2">
445
830
  <span className="font-medium text-foreground">
446
831
  {commonT('labels.contractStatus')}:
@@ -538,6 +923,9 @@ export default function OperationsProjectsPage() {
538
923
  <TableHead className="hidden xl:table-cell">
539
924
  {commonT('labels.endDate')}
540
925
  </TableHead>
926
+ <TableHead className="hidden xl:table-cell">
927
+ {commonT('labels.budget')}
928
+ </TableHead>
541
929
  <TableHead className="hidden 2xl:table-cell">
542
930
  {commonT('labels.contractStatus')}
543
931
  </TableHead>
@@ -626,6 +1014,17 @@ export default function OperationsProjectsPage() {
626
1014
  currentLocaleCode
627
1015
  )}
628
1016
  </TableCell>
1017
+ <TableCell className="hidden xl:table-cell">
1018
+ {project.budgetAmount != null
1019
+ ? valuesVisible
1020
+ ? formatCurrency(
1021
+ project.budgetAmount,
1022
+ getSettingValue,
1023
+ currentLocaleCode
1024
+ )
1025
+ : MASKED_VALUE
1026
+ : commonT('labels.notAvailable')}
1027
+ </TableCell>
629
1028
  <TableCell className="hidden 2xl:table-cell">
630
1029
  {project.contractStatus ? (
631
1030
  <StatusBadge
@@ -714,17 +1113,19 @@ export default function OperationsProjectsPage() {
714
1113
  />
715
1114
  )}
716
1115
 
717
- <PaginationFooter
718
- currentPage={projectsResponse?.page ?? page}
719
- pageSize={projectsResponse?.pageSize ?? pageSize}
720
- totalItems={projectsResponse?.total ?? 0}
721
- onPageChange={setPage}
722
- onPageSizeChange={(value) => {
723
- setPageSize(value);
724
- setPage(1);
725
- }}
726
- pageSizeOptions={[12, 24, 48]}
727
- />
1116
+ {activeViewMode !== 'board' ? (
1117
+ <PaginationFooter
1118
+ currentPage={projectsResponse?.page ?? page}
1119
+ pageSize={projectsResponse?.pageSize ?? pageSize}
1120
+ totalItems={projectsResponse?.total ?? 0}
1121
+ onPageChange={setPage}
1122
+ onPageSizeChange={(value) => {
1123
+ setPageSize(value);
1124
+ setPage(1);
1125
+ }}
1126
+ pageSizeOptions={[12, 24, 48]}
1127
+ />
1128
+ ) : null}
728
1129
 
729
1130
  <Sheet
730
1131
  open={isSheetOpen}