@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
@@ -17,6 +17,7 @@ import {
17
17
  Trash2,
18
18
  UploadCloud,
19
19
  } from 'lucide-react';
20
+ import { useTranslations } from 'next-intl';
20
21
  import { useCallback, useEffect, useRef, useState } from 'react';
21
22
 
22
23
  // ─── Types ────────────────────────────────────────────────────────────────────
@@ -114,9 +115,11 @@ function getFileOpenUrl(fileId: number) {
114
115
 
115
116
  type Props = {
116
117
  taskId: number | null;
118
+ onChanged?: () => void;
117
119
  };
118
120
 
119
- export function TaskFileAttachments({ taskId }: Props) {
121
+ export function TaskFileAttachments({ taskId, onChanged }: Props) {
122
+ const t = useTranslations('operations.TaskFileAttachments');
120
123
  const { request } = useApp();
121
124
 
122
125
  const [files, setFiles] = useState<TaskFile[]>([]);
@@ -181,11 +184,12 @@ export function TaskFileAttachments({ taskId }: Props) {
181
184
 
182
185
  if (res?.data) {
183
186
  await fetchFiles();
187
+ onChanged?.();
184
188
  }
185
189
  } catch {
186
190
  setUploading((prev) =>
187
191
  prev.map((u) =>
188
- u.key === key ? { ...u, error: 'Erro no upload' } : u
192
+ u.key === key ? { ...u, error: t('uploadError') } : u
189
193
  )
190
194
  );
191
195
  setTimeout(() => {
@@ -220,6 +224,7 @@ export function TaskFileAttachments({ taskId }: Props) {
220
224
  method: 'DELETE',
221
225
  });
222
226
  setFiles((prev) => prev.filter((f) => f.id !== relationId));
227
+ onChanged?.();
223
228
  } catch {
224
229
  // ignore
225
230
  } finally {
@@ -254,7 +259,7 @@ export function TaskFileAttachments({ taskId }: Props) {
254
259
  <div
255
260
  role="button"
256
261
  tabIndex={0}
257
- aria-label="Clique ou solte arquivos para anexar"
262
+ aria-label={t('dropzone.ariaLabel')}
258
263
  className={[
259
264
  'flex cursor-pointer flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed px-4 py-5 text-sm transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50',
260
265
  isDraggingOver
@@ -275,8 +280,8 @@ export function TaskFileAttachments({ taskId }: Props) {
275
280
  <UploadCloud className="size-6 shrink-0 opacity-60" />
276
281
  <span className="text-center text-xs leading-relaxed">
277
282
  {isDraggingOver
278
- ? 'Solte os arquivos aqui'
279
- : 'Clique ou arraste arquivos para anexar'}
283
+ ? t('dropzone.dragging')
284
+ : t('dropzone.idle')}
280
285
  </span>
281
286
  <input
282
287
  ref={inputRef}
@@ -308,7 +313,7 @@ export function TaskFileAttachments({ taskId }: Props) {
308
313
  )}
309
314
  </div>
310
315
  <span className="shrink-0 text-[10px] tabular-nums text-muted-foreground">
311
- {u.error ? 'Erro' : `${u.progress}%`}
316
+ {u.error ? t('error') : `${u.progress}%`}
312
317
  </span>
313
318
  </div>
314
319
  ))}
@@ -329,7 +334,7 @@ export function TaskFileAttachments({ taskId }: Props) {
329
334
  {!loadingFiles && isEmpty ? (
330
335
  <div className="flex items-center gap-2 rounded-xl border border-dashed bg-muted/10 px-3 py-3 text-xs text-muted-foreground">
331
336
  <Paperclip className="size-3.5 shrink-0" />
332
- Nenhum arquivo anexado
337
+ {t('empty')}
333
338
  </div>
334
339
  ) : null}
335
340
 
@@ -357,7 +362,7 @@ export function TaskFileAttachments({ taskId }: Props) {
357
362
  variant="ghost"
358
363
  size="icon"
359
364
  className="size-7 rounded-lg"
360
- title="Abrir em nova aba"
365
+ title={t('actions.openInNewTab')}
361
366
  onClick={() =>
362
367
  window.open(getFileOpenUrl(file.file_id), '_blank')
363
368
  }
@@ -369,7 +374,7 @@ export function TaskFileAttachments({ taskId }: Props) {
369
374
  variant="ghost"
370
375
  size="icon"
371
376
  className="size-7 rounded-lg text-destructive hover:bg-destructive/10 hover:text-destructive"
372
- title="Remover arquivo"
377
+ title={t('actions.removeFile')}
373
378
  disabled={isDeleting}
374
379
  onClick={() => void handleDelete(file.id)}
375
380
  >
@@ -0,0 +1,406 @@
1
+ 'use client';
2
+
3
+ import {
4
+ RichTextEditor,
5
+ type MentionItem,
6
+ } from '@/components/rich-text-editor';
7
+ import { Button } from '@/components/ui/button';
8
+ import { Input } from '@/components/ui/input';
9
+ import { Label } from '@/components/ui/label';
10
+ import {
11
+ Select,
12
+ SelectContent,
13
+ SelectItem,
14
+ SelectTrigger,
15
+ SelectValue,
16
+ } from '@/components/ui/select';
17
+ import { History, Paperclip, Plus, X } from 'lucide-react';
18
+ import { useTranslations } from 'next-intl';
19
+ import { useState, type Dispatch, type SetStateAction } from 'react';
20
+ import { Badge } from '@/components/ui/badge';
21
+ import type { OperationsProjectOption } from '../_lib/types';
22
+ import {
23
+ formatDurationMinutes,
24
+ getTaskPriorityLabel,
25
+ } from '../_lib/utils/task-ui';
26
+ import { CollaboratorPicker } from './collaborator-picker';
27
+ import { TaskFileAttachments } from './task-file-attachments';
28
+
29
+ type BaseTaskFormState = {
30
+ projectId: string;
31
+ name: string;
32
+ description: string;
33
+ priority: 'low' | 'medium' | 'high';
34
+ status: string;
35
+ dueDate: string;
36
+ estimateHours: string;
37
+ tags: string;
38
+ assigneeCollaboratorId: string;
39
+ };
40
+
41
+ type TaskFormFieldsProps<TFormState extends BaseTaskFormState> = {
42
+ formData: TFormState;
43
+ setFormData: Dispatch<SetStateAction<TFormState>>;
44
+ statusOptions: Array<{ id: TFormState['status']; label: string }>;
45
+ mentionItems: MentionItem[];
46
+ projectOptions: OperationsProjectOption[];
47
+ showProject?: boolean;
48
+ projectRequired?: boolean;
49
+ allowUnassignedProject?: boolean;
50
+ onOpenCreateProject?: () => void;
51
+ showDoingTime?: boolean;
52
+ doingMinutes?: number | null;
53
+ showAttachments?: boolean;
54
+ attachmentTaskId?: number | null;
55
+ /** Which field group to render. 'main' = project/name/description. 'aside' = metadata+files. 'all' = everything (default). */
56
+ section?: 'all' | 'main' | 'aside';
57
+ /** Hide the assignee field entirely. */
58
+ showAssignee?: boolean;
59
+ /** When provided, renders a static Select instead of the CollaboratorPicker API widget. */
60
+ assigneeOptions?: Array<{ id: string; label: string }>;
61
+ /** Called immediately when a tag is added or removed (for auto-save in edit mode). */
62
+ onTagsChange?: (tags: string) => void;
63
+ /** Called after a file is uploaded or deleted (for refreshing external counts). */
64
+ onFileChanged?: () => void;
65
+ };
66
+
67
+ export function TaskFormFields<TFormState extends BaseTaskFormState>({
68
+ formData,
69
+ setFormData,
70
+ statusOptions,
71
+ mentionItems,
72
+ projectOptions,
73
+ showProject = true,
74
+ projectRequired = false,
75
+ allowUnassignedProject = false,
76
+ onOpenCreateProject,
77
+ showDoingTime = false,
78
+ doingMinutes,
79
+ showAttachments = false,
80
+ attachmentTaskId,
81
+ section = 'all',
82
+ showAssignee = true,
83
+ assigneeOptions,
84
+ onTagsChange,
85
+ onFileChanged,
86
+ }: TaskFormFieldsProps<TFormState>) {
87
+ const commonT = useTranslations('operations.Common');
88
+ const taskT = useTranslations('operations.ProjectDetailsPage');
89
+ const projectsT = useTranslations('operations.ProjectsPage');
90
+
91
+ const [tagInput, setTagInput] = useState('');
92
+
93
+ const showMain = section === 'all' || section === 'main';
94
+ const showAside = section === 'all' || section === 'aside';
95
+
96
+ const tagList = formData.tags
97
+ ? formData.tags.split(',').map((t) => t.trim()).filter(Boolean)
98
+ : [];
99
+
100
+ const commitTag = (value: string) => {
101
+ const trimmed = value.trim();
102
+ if (!trimmed || tagList.includes(trimmed)) {
103
+ setTagInput('');
104
+ return;
105
+ }
106
+ const next = [...tagList, trimmed].join(',');
107
+ setFormData((prev) => ({ ...prev, tags: next }));
108
+ onTagsChange?.(next);
109
+ setTagInput('');
110
+ };
111
+
112
+ const removeTag = (tag: string) => {
113
+ const next = tagList.filter((t) => t !== tag).join(',');
114
+ setFormData((prev) => ({ ...prev, tags: next }));
115
+ onTagsChange?.(next);
116
+ };
117
+
118
+ const doingTimeClass = (() => {
119
+ const estimate = formData.estimateHours
120
+ ? Number(formData.estimateHours) * 60
121
+ : null;
122
+ if (!estimate || doingMinutes == null)
123
+ return 'border bg-muted/30';
124
+ if (doingMinutes > estimate * 1.1)
125
+ return 'border-red-500/30 bg-red-500/10 text-red-600 dark:text-red-400';
126
+ if (doingMinutes > estimate)
127
+ return 'border-amber-500/30 bg-amber-500/10 text-amber-600 dark:text-amber-400';
128
+ return 'border-emerald-500/30 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400';
129
+ })();
130
+
131
+ return (
132
+ <>
133
+ {showMain && showProject ? (
134
+ <div className="space-y-1.5">
135
+ <Label htmlFor="task-form-project">
136
+ {commonT('labels.project')}
137
+ {projectRequired ? ' *' : ''}
138
+ </Label>
139
+ <div className="flex gap-2">
140
+ <Select
141
+ value={formData.projectId}
142
+ onValueChange={(value) =>
143
+ setFormData((prev) => ({ ...prev, projectId: value }))
144
+ }
145
+ >
146
+ <SelectTrigger className="w-full" id="task-form-project">
147
+ <SelectValue placeholder={commonT('labels.project')} />
148
+ </SelectTrigger>
149
+ <SelectContent>
150
+ {allowUnassignedProject ? (
151
+ <SelectItem value="none">
152
+ {commonT('labels.notAssigned')}
153
+ </SelectItem>
154
+ ) : null}
155
+ {projectOptions.map((project) => (
156
+ <SelectItem key={project.id} value={String(project.id)}>
157
+ {project.label}
158
+ </SelectItem>
159
+ ))}
160
+ </SelectContent>
161
+ </Select>
162
+ {onOpenCreateProject ? (
163
+ <Button
164
+ type="button"
165
+ variant="outline"
166
+ size="icon"
167
+ className="shrink-0"
168
+ title={projectsT('sheet.createTitle')}
169
+ onClick={onOpenCreateProject}
170
+ >
171
+ <Plus className="size-4" />
172
+ </Button>
173
+ ) : null}
174
+ </div>
175
+ </div>
176
+ ) : null}
177
+
178
+ {showMain ? (
179
+ <div className="space-y-1.5">
180
+ <Label htmlFor="task-form-name">{taskT('taskForm.nameLabel')} *</Label>
181
+ <Input
182
+ id="task-form-name"
183
+ placeholder={taskT('taskForm.namePlaceholder')}
184
+ value={formData.name}
185
+ onChange={(event) =>
186
+ setFormData((prev) => ({ ...prev, name: event.target.value }))
187
+ }
188
+ />
189
+ </div>
190
+ ) : null}
191
+
192
+ {showMain ? (
193
+ <div className="space-y-1.5">
194
+ <Label htmlFor="task-form-description">
195
+ {taskT('taskForm.descriptionLabel')}
196
+ </Label>
197
+ <RichTextEditor
198
+ value={formData.description}
199
+ onChange={(value) =>
200
+ setFormData((prev) => ({ ...prev, description: value }))
201
+ }
202
+ mentions={mentionItems}
203
+ />
204
+ </div>
205
+ ) : null}
206
+
207
+ {showAside ? (
208
+ <div className="space-y-1.5">
209
+ <Label>{taskT('taskForm.priorityLabel')}</Label>
210
+ <Select
211
+ value={formData.priority}
212
+ onValueChange={(value) =>
213
+ setFormData((prev) => ({
214
+ ...prev,
215
+ priority: value as TFormState['priority'],
216
+ }))
217
+ }
218
+ >
219
+ <SelectTrigger className="w-full">
220
+ <SelectValue />
221
+ </SelectTrigger>
222
+ <SelectContent>
223
+ <SelectItem value="low">{getTaskPriorityLabel('low')}</SelectItem>
224
+ <SelectItem value="medium">
225
+ {getTaskPriorityLabel('medium')}
226
+ </SelectItem>
227
+ <SelectItem value="high">
228
+ {getTaskPriorityLabel('high')}
229
+ </SelectItem>
230
+ </SelectContent>
231
+ </Select>
232
+ </div>
233
+ ) : null}
234
+
235
+ {showAside ? (
236
+ <div className="space-y-1.5">
237
+ <Label>{taskT('taskForm.columnLabel')}</Label>
238
+ <Select
239
+ value={formData.status}
240
+ onValueChange={(value) =>
241
+ setFormData((prev) => ({
242
+ ...prev,
243
+ status: value as TFormState['status'],
244
+ }))
245
+ }
246
+ >
247
+ <SelectTrigger className="w-full">
248
+ <SelectValue />
249
+ </SelectTrigger>
250
+ <SelectContent>
251
+ {statusOptions.map((column) => (
252
+ <SelectItem key={column.id} value={column.id}>
253
+ {column.label}
254
+ </SelectItem>
255
+ ))}
256
+ </SelectContent>
257
+ </Select>
258
+ </div>
259
+ ) : null}
260
+
261
+ {showAside && showAssignee ? (
262
+ <div className="space-y-1.5">
263
+ <Label htmlFor="task-form-assignee">
264
+ {taskT('taskForm.assigneeLabel')}
265
+ </Label>
266
+ {assigneeOptions ? (
267
+ <Select
268
+ value={formData.assigneeCollaboratorId || 'none'}
269
+ onValueChange={(value) =>
270
+ setFormData((prev) => ({
271
+ ...prev,
272
+ assigneeCollaboratorId: value === 'none' ? '' : value,
273
+ }))
274
+ }
275
+ >
276
+ <SelectTrigger className="w-full" id="task-form-assignee">
277
+ <SelectValue placeholder={taskT('taskForm.assigneeLabel')} />
278
+ </SelectTrigger>
279
+ <SelectContent>
280
+ <SelectItem value="none">
281
+ {commonT('labels.notAssigned')}
282
+ </SelectItem>
283
+ {assigneeOptions.map((option) => (
284
+ <SelectItem key={option.id} value={option.id}>
285
+ {option.label}
286
+ </SelectItem>
287
+ ))}
288
+ </SelectContent>
289
+ </Select>
290
+ ) : (
291
+ <CollaboratorPicker
292
+ value={formData.assigneeCollaboratorId ?? ''}
293
+ placeholder={taskT('taskForm.assigneeLabel')}
294
+ onChange={(newValue) =>
295
+ setFormData((prev) => ({
296
+ ...prev,
297
+ assigneeCollaboratorId: newValue,
298
+ }))
299
+ }
300
+ setFormData={setFormData}
301
+ />
302
+ )}
303
+ </div>
304
+ ) : null}
305
+
306
+ {showAside ? (
307
+ <div className="grid grid-cols-2 gap-3">
308
+ <div className="space-y-1.5">
309
+ <Label htmlFor="task-form-due-date">
310
+ {taskT('taskForm.deadlineLabel')}
311
+ </Label>
312
+ <Input
313
+ id="task-form-due-date"
314
+ type="date"
315
+ value={formData.dueDate}
316
+ onChange={(event) =>
317
+ setFormData((prev) => ({ ...prev, dueDate: event.target.value }))
318
+ }
319
+ />
320
+ </div>
321
+
322
+ <div className="space-y-1.5">
323
+ <Label htmlFor="task-form-estimate">
324
+ {taskT('taskForm.estimateLabel')}
325
+ </Label>
326
+ <Input
327
+ id="task-form-estimate"
328
+ type="number"
329
+ min="0"
330
+ step="0.5"
331
+ placeholder="0"
332
+ value={formData.estimateHours}
333
+ onChange={(event) =>
334
+ setFormData((prev) => ({
335
+ ...prev,
336
+ estimateHours: event.target.value,
337
+ }))
338
+ }
339
+ />
340
+ </div>
341
+ </div>
342
+ ) : null}
343
+
344
+ {showAside && showDoingTime ? (
345
+ <div className="space-y-1.5">
346
+ <Label className="flex items-center gap-1.5">
347
+ <History className="size-3.5" />
348
+ {taskT('taskForm.doingTimeLabel')}
349
+ </Label>
350
+ <div
351
+ className={`rounded-md px-3 py-2 text-sm font-medium ${doingTimeClass}`}
352
+ >
353
+ {formatDurationMinutes(doingMinutes)}
354
+ </div>
355
+ </div>
356
+ ) : null}
357
+
358
+ {showAside ? (
359
+ <div className="space-y-1.5">
360
+ <Label>{taskT('taskForm.tagsLabel')}</Label>
361
+ {tagList.length > 0 ? (
362
+ <div className="flex flex-wrap gap-1.5">
363
+ {tagList.map((tag) => (
364
+ <Badge key={tag} variant="secondary" className="gap-1 pr-1">
365
+ {tag}
366
+ <button
367
+ type="button"
368
+ onClick={() => removeTag(tag)}
369
+ className="rounded-sm opacity-60 hover:opacity-100 focus:outline-none"
370
+ >
371
+ <X className="size-3" />
372
+ </button>
373
+ </Badge>
374
+ ))}
375
+ </div>
376
+ ) : null}
377
+ <Input
378
+ id="task-form-tags"
379
+ placeholder={taskT('taskForm.tagsPlaceholder')}
380
+ value={tagInput}
381
+ onChange={(e) => setTagInput(e.target.value)}
382
+ onKeyDown={(e) => {
383
+ if (e.key === 'Enter' || e.key === ',') {
384
+ e.preventDefault();
385
+ commitTag(tagInput);
386
+ } else if (e.key === 'Backspace' && !tagInput && tagList.length > 0) {
387
+ removeTag(tagList.at(-1)!);
388
+ }
389
+ }}
390
+ onBlur={() => { if (tagInput.trim()) commitTag(tagInput); }}
391
+ />
392
+ </div>
393
+ ) : null}
394
+
395
+ {showAside && showAttachments && attachmentTaskId ? (
396
+ <div className="space-y-1.5">
397
+ <Label className="flex items-center gap-1.5">
398
+ <Paperclip className="size-3.5" />
399
+ {taskT('taskForm.attachmentsLabel')}
400
+ </Label>
401
+ <TaskFileAttachments taskId={attachmentTaskId} onChanged={onFileChanged} />
402
+ </div>
403
+ ) : null}
404
+ </>
405
+ );
406
+ }