@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
@@ -69,14 +69,14 @@ import {
69
69
  import { useTranslations } from 'next-intl';
70
70
  import Link from 'next/link';
71
71
  import { useRouter } from 'next/navigation';
72
- import { useEffect, useMemo, useState } from 'react';
72
+ import { useEffect, useMemo, useRef, useState } from 'react';
73
73
  import {
74
74
  CartesianGrid,
75
75
  Legend,
76
76
  Line,
77
77
  LineChart,
78
- ResponsiveContainer,
79
78
  Tooltip as RechartsTooltip,
79
+ ResponsiveContainer,
80
80
  XAxis,
81
81
  YAxis,
82
82
  } from 'recharts';
@@ -117,10 +117,6 @@ import { DepartmentPicker } from './department-picker';
117
117
  import { OperationsHeader } from './operations-header';
118
118
  import { ProjectAssignmentsTab } from './project-assignments-tab';
119
119
  import { StatusBadge } from './status-badge';
120
- import {
121
- SystemUserPicker,
122
- type SystemUserPickerOption as SystemUserOption,
123
- } from './system-user-picker';
124
120
 
125
121
  const weekdays = [
126
122
  'monday',
@@ -251,7 +247,6 @@ const EQUITY_ENABLED_TYPE_SLUGS = new Set([
251
247
  const HOURLY_RATE_SLUGS = new Set(['pj', 'freelancer']);
252
248
 
253
249
  type CollaboratorFormState = {
254
- userId: string;
255
250
  personId: string;
256
251
  displayName: string;
257
252
  collaboratorTypeId: string;
@@ -299,7 +294,6 @@ function defaultSchedule(): CollaboratorFormState['weeklySchedule'] {
299
294
 
300
295
  function buildEmptyForm(): CollaboratorFormState {
301
296
  return {
302
- userId: '',
303
297
  personId: '',
304
298
  displayName: '',
305
299
  collaboratorTypeId: '',
@@ -358,7 +352,6 @@ function toFormState(
358
352
  collaborator: OperationsCollaboratorDetails
359
353
  ): CollaboratorFormState {
360
354
  return {
361
- userId: collaborator.userId ? String(collaborator.userId) : '',
362
355
  personId: collaborator.personId ? String(collaborator.personId) : '',
363
356
  displayName: collaborator.displayName ?? '',
364
357
  collaboratorTypeId: collaborator.collaboratorTypeId
@@ -637,38 +630,6 @@ export function CollaboratorFormScreen({
637
630
  ),
638
631
  });
639
632
 
640
- const { data: systemUsers = [], refetch: refetchSystemUsers } = useQuery<
641
- SystemUserOption[]
642
- >({
643
- queryKey: ['operations-collaborator-form-system-users', currentLocaleCode],
644
- enabled: access.isDirector,
645
- staleTime: 0,
646
- refetchOnMount: 'always',
647
- queryFn: async () => {
648
- const response = await request<{
649
- paginate: {
650
- data: Array<{
651
- id: number;
652
- name: string | null;
653
- photo_id?: number | null;
654
- }>;
655
- };
656
- }>({
657
- url: '/user?pageSize=100',
658
- method: 'GET',
659
- });
660
-
661
- const raw = response.data?.paginate?.data ?? [];
662
- const options: SystemUserOption[] = raw.map((u) => ({
663
- id: u.id,
664
- name: u.name || `#${u.id}`,
665
- photoId: u.photo_id ?? null,
666
- }));
667
-
668
- return options.sort((left, right) => left.name.localeCompare(right.name));
669
- },
670
- });
671
-
672
633
  const {
673
634
  data: collaboratorTypes = [],
674
635
  refetch: refetchCollaboratorTypes,
@@ -726,6 +687,7 @@ export function CollaboratorFormScreen({
726
687
 
727
688
  const [personSheetOpen, setPersonSheetOpen] = useState(false);
728
689
  const [personToEdit, setPersonToEdit] = useState<Person | null>(null);
690
+ const personSheetModeRef = useRef<'edit' | 'create'>('edit');
729
691
 
730
692
  const { data: contactTypes = [] } = useQuery<ContactTypeOption[]>({
731
693
  queryKey: ['contact-person-contact-types', currentLocaleCode],
@@ -781,13 +743,22 @@ export function CollaboratorFormScreen({
781
743
  placeholderData: (previous) => previous ?? [],
782
744
  });
783
745
 
784
- const handleOpenPersonSheet = async () => {
785
- if (!collaborator?.personId) return;
746
+ const handleOpenPersonSheet = async (personId?: number | null) => {
747
+ const targetPersonId =
748
+ personId && personId > 0
749
+ ? personId
750
+ : (parseNumberInput(form.personId) ?? collaborator?.personId ?? null);
751
+
752
+ if (!targetPersonId) {
753
+ return;
754
+ }
755
+
786
756
  try {
787
757
  const response = await request<Person>({
788
- url: `/person/${collaborator.personId}`,
758
+ url: `/person/${targetPersonId}`,
789
759
  method: 'GET',
790
760
  });
761
+ personSheetModeRef.current = 'edit';
791
762
  setPersonToEdit(response.data);
792
763
  setPersonSheetOpen(true);
793
764
  } catch {
@@ -795,6 +766,12 @@ export function CollaboratorFormScreen({
795
766
  }
796
767
  };
797
768
 
769
+ const handleOpenPersonCreateSheet = () => {
770
+ personSheetModeRef.current = 'create';
771
+ setPersonToEdit(null);
772
+ setPersonSheetOpen(true);
773
+ };
774
+
798
775
  useEffect(() => {
799
776
  if (collaborator) {
800
777
  // eslint-disable-next-line react-hooks/set-state-in-effect
@@ -1016,37 +993,6 @@ export function CollaboratorFormScreen({
1016
993
  }
1017
994
  };
1018
995
 
1019
- const createSystemUser = async (
1020
- name: string,
1021
- email: string,
1022
- password: string
1023
- ): Promise<SystemUserOption | null> => {
1024
- try {
1025
- const response = await request<{
1026
- user: { id: number; name: string | null; photo_id?: number | null };
1027
- challenge: unknown;
1028
- }>({
1029
- url: '/user',
1030
- method: 'POST',
1031
- data: { name, email, password },
1032
- });
1033
- await refetchSystemUsers();
1034
- showToastHandler?.('success', t('messages.createUserSuccess'));
1035
- const user = response.data.user;
1036
- return {
1037
- id: user.id,
1038
- name: user.name || `#${user.id}`,
1039
- photoId: user.photo_id ?? null,
1040
- };
1041
- } catch (error) {
1042
- showToastHandler?.(
1043
- 'error',
1044
- getOperationsErrorMessage(error, t('messages.createUserError'))
1045
- );
1046
- return null;
1047
- }
1048
- };
1049
-
1050
996
  const updateScheduleDay = (
1051
997
  weekday: string,
1052
998
  patch: Partial<CollaboratorFormState['weeklySchedule'][number]>
@@ -1081,7 +1027,6 @@ export function CollaboratorFormScreen({
1081
1027
  };
1082
1028
 
1083
1029
  const onSubmit = async () => {
1084
- const userId = parseNumberInput(form.userId);
1085
1030
  const personId = parseNumberInput(form.personId);
1086
1031
  const compensationAmount = isHourlyType
1087
1032
  ? null
@@ -1103,7 +1048,6 @@ export function CollaboratorFormScreen({
1103
1048
  }
1104
1049
 
1105
1050
  const payload = {
1106
- userId: userId ?? undefined,
1107
1051
  personId: personId ?? undefined,
1108
1052
  displayName: trimToNull(form.displayName),
1109
1053
  collaboratorTypeId,
@@ -1380,6 +1324,10 @@ export function CollaboratorFormScreen({
1380
1324
  value={form.personId ? Number(form.personId) : null}
1381
1325
  initialSelectedLabel={collaborator?.personName ?? form.displayName}
1382
1326
  selectPlaceholder={t('placeholders.person')}
1327
+ showEditButton
1328
+ editAriaLabel={t('actions.editPersonCrm')}
1329
+ onEditSelection={(personId) => void handleOpenPersonSheet(personId)}
1330
+ onCreateNew={() => handleOpenPersonCreateSheet()}
1383
1331
  onChange={(personId, personName) =>
1384
1332
  setForm((current) => ({
1385
1333
  ...current,
@@ -1389,25 +1337,6 @@ export function CollaboratorFormScreen({
1389
1337
  }
1390
1338
  />
1391
1339
  </div>
1392
- <div className="space-y-2 md:col-span-2 xl:col-span-2">
1393
- <SystemUserPicker
1394
- label={t('fields.userIdOptional')}
1395
- value={form.userId}
1396
- options={systemUsers}
1397
- placeholder={t('placeholders.userIdOptional')}
1398
- emptyLabel={commonT('labels.notAssigned')}
1399
- onChange={(value) =>
1400
- setForm((current) => ({
1401
- ...current,
1402
- userId: value,
1403
- }))
1404
- }
1405
- onCreate={createSystemUser}
1406
- />
1407
- <p className="text-xs text-muted-foreground">
1408
- {t('fields.userIdDescription')}
1409
- </p>
1410
- </div>
1411
1340
 
1412
1341
  {!isCreateMode ? (
1413
1342
  <div className="space-y-2">
@@ -2128,7 +2057,7 @@ export function CollaboratorFormScreen({
2128
2057
  </div>
2129
2058
  );
2130
2059
 
2131
- const salaryChartData = useMemo(() => {
2060
+ const salaryChartData = (() => {
2132
2061
  if (!compensationHistory.length) return [];
2133
2062
 
2134
2063
  const sorted = [...compensationHistory].sort(
@@ -2178,7 +2107,7 @@ export function CollaboratorFormScreen({
2178
2107
  },
2179
2108
  ...projectionPoints,
2180
2109
  ];
2181
- }, [compensationHistory]);
2110
+ })();
2182
2111
 
2183
2112
  const costsSection = (
2184
2113
  <Accordion
@@ -2488,7 +2417,18 @@ export function CollaboratorFormScreen({
2488
2417
  setPersonSheetOpen(nextOpen);
2489
2418
  if (!nextOpen) setPersonToEdit(null);
2490
2419
  }}
2491
- onSuccess={() => void refetchCollaborator()}
2420
+ onSuccess={(person) => {
2421
+ if (personSheetModeRef.current === 'create' && person) {
2422
+ setForm((current) => ({
2423
+ ...current,
2424
+ personId: String(person.id),
2425
+ displayName: person.name,
2426
+ }));
2427
+ } else {
2428
+ void refetchCollaborator();
2429
+ }
2430
+ personSheetModeRef.current = 'edit';
2431
+ }}
2492
2432
  />
2493
2433
  </div>
2494
2434
  );
@@ -0,0 +1,158 @@
1
+ 'use client';
2
+
3
+ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
4
+ import { EntityPicker } from '@/components/ui/entity-picker';
5
+ import { useApp } from '@hed-hog/next-app-provider';
6
+ import { useTranslations } from 'next-intl';
7
+ import type { Dispatch, SetStateAction } from 'react';
8
+
9
+ export type CollaboratorPickerOption = {
10
+ id: number;
11
+ displayName: string;
12
+ userPhotoId?: number | null;
13
+ personAvatarId?: number | null;
14
+ code?: string | null;
15
+ department?: string | null;
16
+ };
17
+
18
+ export type CollaboratorPickerProps = {
19
+ label?: string;
20
+ value: string;
21
+ placeholder: string;
22
+ onChange: (value: string) => void;
23
+ setFormData: Dispatch<SetStateAction<any>>;
24
+ };
25
+
26
+ type CollaboratorListPayload = {
27
+ data: CollaboratorPickerOption[];
28
+ total: number;
29
+ page: number;
30
+ pageSize: number;
31
+ lastPage: number;
32
+ };
33
+
34
+ const PAGE_SIZE = 20;
35
+
36
+ function getCollaboratorPhotoUrl(option: CollaboratorPickerOption) {
37
+ // Priority: userPhotoId > personAvatarId
38
+ if (typeof option.userPhotoId === 'number' && option.userPhotoId > 0) {
39
+ return `${process.env.NEXT_PUBLIC_API_BASE_URL}/file/open/${option.userPhotoId}`;
40
+ }
41
+ if (typeof option.personAvatarId === 'number' && option.personAvatarId > 0) {
42
+ return `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${option.personAvatarId}`;
43
+ }
44
+ return undefined;
45
+ }
46
+
47
+ function getInitials(value?: string | null) {
48
+ const parts = String(value ?? '')
49
+ .trim()
50
+ .split(/\s+/)
51
+ .filter(Boolean)
52
+ .slice(0, 2);
53
+
54
+ if (!parts.length) {
55
+ return '??';
56
+ }
57
+
58
+ return parts.map((part) => part[0]?.toUpperCase() ?? '').join('');
59
+ }
60
+
61
+ export function CollaboratorPicker({
62
+ label,
63
+ value,
64
+ placeholder,
65
+ onChange,
66
+ setFormData,
67
+ }: CollaboratorPickerProps) {
68
+ const { request } = useApp();
69
+ const commonT = useTranslations('operations.Common');
70
+ const taskT = useTranslations('operations.ProjectDetailsPage');
71
+
72
+ return (
73
+ <EntityPicker<CollaboratorPickerOption>
74
+ label={label}
75
+ value={value || ''}
76
+ onChange={(nextValue) => {
77
+ const normalizedValue = nextValue ? String(nextValue) : '';
78
+ onChange(normalizedValue);
79
+ setFormData((prev: any) => ({
80
+ ...prev,
81
+ assigneeCollaboratorId: normalizedValue,
82
+ }));
83
+ }}
84
+ placeholder={commonT('labels.notAssigned')}
85
+ emptySelectionLabel={commonT('labels.notAssigned')}
86
+ searchPlaceholder={placeholder}
87
+ emptyStateDescription={commonT('states.emptyDescription')}
88
+ noResultsLabel={commonT('states.emptyDescription')}
89
+ loadingLabel={commonT('states.loading')}
90
+ clearable
91
+ allowEmptySelection
92
+ valueType="string"
93
+ showCreateButton={false}
94
+ getOptionValue={(collab) => String(collab.id)}
95
+ getOptionLabel={(collab) => collab.displayName}
96
+ getOptionDescription={(collab) =>
97
+ [collab.code, collab.department].filter(Boolean).join(' • ') || undefined
98
+ }
99
+ renderOption={({ option }) => (
100
+ <div className="flex min-w-0 items-center gap-2.5">
101
+ <Avatar className="h-7 w-7 shrink-0 rounded-full">
102
+ <AvatarImage src={getCollaboratorPhotoUrl(option)} />
103
+ <AvatarFallback className="rounded-full bg-muted text-[10px] font-semibold text-muted-foreground">
104
+ {getInitials(option.displayName)}
105
+ </AvatarFallback>
106
+ </Avatar>
107
+ <div className="min-w-0 flex-1">
108
+ <div className="truncate text-sm font-medium">{option.displayName}</div>
109
+ {option.code || option.department ? (
110
+ <div className="truncate text-xs text-muted-foreground">
111
+ {[option.code, option.department].filter(Boolean).join(' • ')}
112
+ </div>
113
+ ) : null}
114
+ </div>
115
+ </div>
116
+ )}
117
+ renderSelectedValue={({ option, label: fallbackLabel }) => {
118
+ if (!option) {
119
+ return fallbackLabel;
120
+ }
121
+ return (
122
+ <div className="flex min-w-0 items-center gap-2">
123
+ <Avatar className="h-5 w-5 shrink-0 rounded-full">
124
+ <AvatarImage src={getCollaboratorPhotoUrl(option)} />
125
+ <AvatarFallback className="rounded-full bg-muted text-[9px] font-semibold text-muted-foreground">
126
+ {getInitials(option.displayName)}
127
+ </AvatarFallback>
128
+ </Avatar>
129
+ <span className="truncate">{option.displayName}</span>
130
+ </div>
131
+ );
132
+ }}
133
+ loadOptions={async ({ page, pageSize, search }) => {
134
+ const params = new URLSearchParams();
135
+ params.set('page', String(page));
136
+ params.set('pageSize', String(pageSize || PAGE_SIZE));
137
+ params.set('sortField', 'displayName');
138
+ params.set('sortOrder', 'asc');
139
+
140
+ if (search.trim()) {
141
+ params.set('search', search.trim());
142
+ }
143
+
144
+ const response = await request<CollaboratorListPayload>({
145
+ url: `/operations/collaborators?${params.toString()}`,
146
+ method: 'GET',
147
+ });
148
+
149
+ const payload = response?.data;
150
+
151
+ return {
152
+ items: (payload?.data ?? []) as CollaboratorPickerOption[],
153
+ hasMore: (payload?.page ?? 1) < (payload?.lastPage ?? 1),
154
+ };
155
+ }}
156
+ />
157
+ );
158
+ }