@hed-hog/operations 0.0.295 → 0.0.296

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 (126) hide show
  1. package/dist/operations.controller.d.ts +415 -0
  2. package/dist/operations.controller.d.ts.map +1 -0
  3. package/dist/operations.controller.js +333 -0
  4. package/dist/operations.controller.js.map +1 -0
  5. package/dist/operations.module.d.ts.map +1 -1
  6. package/dist/operations.module.js +4 -3
  7. package/dist/operations.module.js.map +1 -1
  8. package/dist/operations.service.d.ts +589 -153
  9. package/dist/operations.service.d.ts.map +1 -1
  10. package/dist/operations.service.js +2229 -100
  11. package/dist/operations.service.js.map +1 -1
  12. package/hedhog/data/menu.yaml +198 -251
  13. package/hedhog/data/role.yaml +23 -14
  14. package/hedhog/data/route.yaml +317 -143
  15. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +310 -0
  16. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +631 -0
  17. package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +132 -0
  18. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +558 -0
  19. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +291 -0
  20. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +689 -0
  21. package/hedhog/frontend/app/_lib/api.ts.ejs +32 -0
  22. package/hedhog/frontend/app/_lib/hooks/use-operations-access.ts.ejs +44 -0
  23. package/hedhog/frontend/app/_lib/types.ts.ejs +360 -0
  24. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +129 -25
  25. package/hedhog/frontend/app/_lib/utils/forms.ts.ejs +14 -0
  26. package/hedhog/frontend/app/approvals/page.tsx.ejs +386 -147
  27. package/hedhog/frontend/app/collaborators/[id]/edit/page.tsx.ejs +11 -0
  28. package/hedhog/frontend/app/collaborators/[id]/page.tsx.ejs +11 -0
  29. package/hedhog/frontend/app/collaborators/new/page.tsx.ejs +5 -0
  30. package/hedhog/frontend/app/collaborators/page.tsx.ejs +261 -0
  31. package/hedhog/frontend/app/contracts/[id]/edit/page.tsx.ejs +11 -0
  32. package/hedhog/frontend/app/contracts/[id]/page.tsx.ejs +11 -108
  33. package/hedhog/frontend/app/contracts/new/page.tsx.ejs +17 -0
  34. package/hedhog/frontend/app/contracts/page.tsx.ejs +262 -181
  35. package/hedhog/frontend/app/page.tsx.ejs +319 -177
  36. package/hedhog/frontend/app/projects/[id]/edit/page.tsx.ejs +11 -0
  37. package/hedhog/frontend/app/projects/[id]/page.tsx.ejs +11 -936
  38. package/hedhog/frontend/app/projects/new/page.tsx.ejs +5 -0
  39. package/hedhog/frontend/app/projects/page.tsx.ejs +236 -1074
  40. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +418 -0
  41. package/hedhog/frontend/app/team/page.tsx.ejs +339 -0
  42. package/hedhog/frontend/app/time-off/page.tsx.ejs +328 -0
  43. package/hedhog/frontend/app/timesheets/page.tsx.ejs +636 -126
  44. package/hedhog/frontend/messages/en.json +648 -454
  45. package/hedhog/frontend/messages/pt.json +647 -454
  46. package/hedhog/table/operations_approval.yaml +49 -0
  47. package/hedhog/table/operations_approval_history.yaml +29 -0
  48. package/hedhog/table/{operations_employee.yaml → operations_collaborator.yaml} +67 -64
  49. package/hedhog/table/operations_collaborator_schedule_day.yaml +34 -0
  50. package/hedhog/table/operations_contract.yaml +100 -48
  51. package/hedhog/table/operations_contract_document.yaml +39 -0
  52. package/hedhog/table/operations_contract_financial_term.yaml +40 -0
  53. package/hedhog/table/operations_contract_history.yaml +27 -0
  54. package/hedhog/table/operations_contract_party.yaml +46 -0
  55. package/hedhog/table/operations_contract_revision.yaml +38 -0
  56. package/hedhog/table/operations_contract_signature.yaml +38 -0
  57. package/hedhog/table/operations_project.yaml +54 -50
  58. package/hedhog/table/{operations_allocation.yaml → operations_project_assignment.yaml} +55 -52
  59. package/hedhog/table/operations_schedule_adjustment_day.yaml +34 -0
  60. package/hedhog/table/operations_schedule_adjustment_request.yaml +53 -0
  61. package/hedhog/table/operations_time_off_request.yaml +57 -0
  62. package/hedhog/table/operations_timesheet.yaml +41 -36
  63. package/hedhog/table/operations_timesheet_entry.yaml +40 -50
  64. package/package.json +8 -7
  65. package/src/operations.controller.ts +182 -0
  66. package/src/operations.module.ts +22 -21
  67. package/src/operations.service.ts +3595 -137
  68. package/hedhog/data/operations_career_level.yaml +0 -102
  69. package/hedhog/data/operations_career_track.yaml +0 -8
  70. package/hedhog/data/operations_certification.yaml +0 -38
  71. package/hedhog/data/operations_evaluation_cycle.yaml +0 -18
  72. package/hedhog/data/operations_performance_criterion.yaml +0 -48
  73. package/hedhog/frontend/app/_components/allocation-calendar.tsx.ejs +0 -56
  74. package/hedhog/frontend/app/_components/kanban-board.tsx.ejs +0 -626
  75. package/hedhog/frontend/app/_components/timesheet-entry-dialog.tsx.ejs +0 -142
  76. package/hedhog/frontend/app/_lib/hooks/use-operations-data.ts.ejs +0 -41
  77. package/hedhog/frontend/app/_lib/hooks/use-operations-growth-data.ts.ejs +0 -63
  78. package/hedhog/frontend/app/_lib/mocks/allocations.mock.ts.ejs +0 -74
  79. package/hedhog/frontend/app/_lib/mocks/contracts.mock.ts.ejs +0 -74
  80. package/hedhog/frontend/app/_lib/mocks/operations-growth.mock.ts.ejs +0 -824
  81. package/hedhog/frontend/app/_lib/mocks/projects.mock.ts.ejs +0 -455
  82. package/hedhog/frontend/app/_lib/mocks/tasks.mock.ts.ejs +0 -117
  83. package/hedhog/frontend/app/_lib/mocks/timesheets.mock.ts.ejs +0 -84
  84. package/hedhog/frontend/app/_lib/mocks/users.mock.ts.ejs +0 -67
  85. package/hedhog/frontend/app/_lib/services/contracts.service.ts.ejs +0 -10
  86. package/hedhog/frontend/app/_lib/services/operations-growth.service.ts.ejs +0 -31
  87. package/hedhog/frontend/app/_lib/services/projects.service.ts.ejs +0 -10
  88. package/hedhog/frontend/app/_lib/services/tasks.service.ts.ejs +0 -10
  89. package/hedhog/frontend/app/_lib/services/timesheets.service.ts.ejs +0 -10
  90. package/hedhog/frontend/app/_lib/types/operations-growth.ts.ejs +0 -209
  91. package/hedhog/frontend/app/_lib/types/operations.ts.ejs +0 -156
  92. package/hedhog/frontend/app/_lib/utils/growth.ts.ejs +0 -62
  93. package/hedhog/frontend/app/_lib/utils/metrics.ts.ejs +0 -103
  94. package/hedhog/frontend/app/_lib/utils/status.ts.ejs +0 -80
  95. package/hedhog/frontend/app/allocations/page.tsx.ejs +0 -155
  96. package/hedhog/frontend/app/career/page.tsx.ejs +0 -143
  97. package/hedhog/frontend/app/certifications/page.tsx.ejs +0 -202
  98. package/hedhog/frontend/app/evaluations/page.tsx.ejs +0 -278
  99. package/hedhog/frontend/app/goals/page.tsx.ejs +0 -171
  100. package/hedhog/frontend/app/growth/page.tsx.ejs +0 -288
  101. package/hedhog/frontend/app/manager/page.tsx.ejs +0 -175
  102. package/hedhog/frontend/app/rewards/page.tsx.ejs +0 -196
  103. package/hedhog/frontend/app/tasks/page.tsx.ejs +0 -999
  104. package/hedhog/table/operations_calibration_item.yaml +0 -61
  105. package/hedhog/table/operations_calibration_session.yaml +0 -25
  106. package/hedhog/table/operations_career_level.yaml +0 -75
  107. package/hedhog/table/operations_career_track.yaml +0 -21
  108. package/hedhog/table/operations_certification.yaml +0 -48
  109. package/hedhog/table/operations_employee_certification.yaml +0 -43
  110. package/hedhog/table/operations_employee_connect.yaml +0 -61
  111. package/hedhog/table/operations_employee_evaluation.yaml +0 -113
  112. package/hedhog/table/operations_employee_evaluation_item.yaml +0 -39
  113. package/hedhog/table/operations_employee_profile.yaml +0 -80
  114. package/hedhog/table/operations_employee_skill_matrix.yaml +0 -30
  115. package/hedhog/table/operations_evaluation_cycle.yaml +0 -31
  116. package/hedhog/table/operations_goal.yaml +0 -67
  117. package/hedhog/table/operations_goal_progress.yaml +0 -31
  118. package/hedhog/table/operations_performance_criterion.yaml +0 -29
  119. package/hedhog/table/operations_promotion_readiness.yaml +0 -49
  120. package/hedhog/table/operations_promotion_recommendation.yaml +0 -63
  121. package/hedhog/table/operations_public_recognition.yaml +0 -46
  122. package/hedhog/table/operations_reward.yaml +0 -100
  123. package/hedhog/table/operations_score_event.yaml +0 -81
  124. package/hedhog/table/operations_task.yaml +0 -60
  125. package/src/operations-data.controller.ts +0 -54
  126. package/src/operations-growth.controller.ts +0 -44
@@ -0,0 +1,32 @@
1
+ type RequestFn = (input: {
2
+ url: string;
3
+ method: string;
4
+ data?: unknown;
5
+ }) => Promise<{ data: any }>;
6
+
7
+ export async function fetchOperations<T>(
8
+ request: RequestFn,
9
+ url: string
10
+ ) {
11
+ const response = await request({
12
+ url,
13
+ method: 'GET',
14
+ });
15
+
16
+ return response.data as T;
17
+ }
18
+
19
+ export async function mutateOperations<T>(
20
+ request: RequestFn,
21
+ url: string,
22
+ method: 'POST' | 'PATCH',
23
+ data?: unknown
24
+ ) {
25
+ const response = await request({
26
+ url,
27
+ method,
28
+ data,
29
+ });
30
+
31
+ return response.data as T;
32
+ }
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+
3
+ import { useApp, useQuery } from '@hed-hog/next-app-provider';
4
+
5
+ type Role = {
6
+ slug?: string;
7
+ };
8
+
9
+ export function useOperationsAccess() {
10
+ const { request, accessToken } = useApp();
11
+
12
+ const { data, isLoading } = useQuery<{ roles: Role[] }>({
13
+ queryKey: ['operations-roles', accessToken],
14
+ enabled: !!accessToken,
15
+ queryFn: async () => {
16
+ const response = await request<{ roles: Role[] }>({
17
+ url: '/auth/roles',
18
+ });
19
+
20
+ return response.data;
21
+ },
22
+ });
23
+
24
+ const roleSlugs = (data?.roles ?? [])
25
+ .map((role) => role.slug || '')
26
+ .filter(Boolean);
27
+
28
+ return {
29
+ isLoading,
30
+ roleSlugs,
31
+ isDirector:
32
+ roleSlugs.includes('admin') ||
33
+ roleSlugs.includes('admin-operations-director'),
34
+ isSupervisor:
35
+ roleSlugs.includes('admin') ||
36
+ roleSlugs.includes('admin-operations-director') ||
37
+ roleSlugs.includes('admin-operations-supervisor'),
38
+ isCollaborator:
39
+ roleSlugs.includes('admin') ||
40
+ roleSlugs.includes('admin-operations-director') ||
41
+ roleSlugs.includes('admin-operations-supervisor') ||
42
+ roleSlugs.includes('admin-operations-collaborator'),
43
+ };
44
+ }
@@ -0,0 +1,360 @@
1
+ export type OperationsDashboard = {
2
+ actor: {
3
+ roleScope: 'self' | 'team' | 'full';
4
+ collaboratorId: number | null;
5
+ collaboratorName: string | null;
6
+ teamSize: number;
7
+ };
8
+ cards: {
9
+ projectsTotal: number;
10
+ activeProjects: number;
11
+ visibleTimesheets: number;
12
+ pendingTimesheets: number;
13
+ timeOffRequests: number;
14
+ scheduleAdjustmentRequests: number;
15
+ pendingApprovals: number;
16
+ };
17
+ recentTimesheets: Array<{
18
+ id: number;
19
+ collaboratorName: string;
20
+ weekStartDate: string;
21
+ weekEndDate: string;
22
+ totalHours: number | null;
23
+ status: string;
24
+ }>;
25
+ };
26
+
27
+ export type OperationsCollaborator = {
28
+ id: number;
29
+ userId?: number;
30
+ code: string;
31
+ collaboratorType?: string;
32
+ displayName: string;
33
+ department?: string | null;
34
+ title?: string | null;
35
+ levelLabel?: string | null;
36
+ weeklyCapacityHours?: number | null;
37
+ status: string;
38
+ joinedAt?: string | null;
39
+ leftAt?: string | null;
40
+ notes?: string | null;
41
+ supervisorId?: number | null;
42
+ supervisorName?: string | null;
43
+ contractId?: number | null;
44
+ contractStatus?: string | null;
45
+ activeAssignments?: number;
46
+ pendingApprovals?: number;
47
+ pendingTimeOffRequests?: number;
48
+ pendingScheduleAdjustmentRequests?: number;
49
+ };
50
+
51
+ export type OperationsProject = {
52
+ id: number;
53
+ contractId?: number | null;
54
+ managerCollaboratorId?: number | null;
55
+ code: string;
56
+ name: string;
57
+ clientName?: string | null;
58
+ summary?: string | null;
59
+ status: string;
60
+ progressPercent?: number | null;
61
+ deliveryModel?: string | null;
62
+ budgetAmount?: number | null;
63
+ startDate?: string | null;
64
+ endDate?: string | null;
65
+ contractName?: string | null;
66
+ contractStatus?: string | null;
67
+ contractCategory?: string | null;
68
+ managerName?: string | null;
69
+ myAssignmentId?: number | null;
70
+ myRoleLabel?: string | null;
71
+ teamSize?: number;
72
+ };
73
+
74
+ export type OperationsContract = {
75
+ id: number;
76
+ code: string;
77
+ name: string;
78
+ contractCategory?: string;
79
+ contractType?: string;
80
+ clientName: string;
81
+ signatureStatus?: string;
82
+ isActive?: boolean;
83
+ billingModel: string;
84
+ accountManagerCollaboratorId?: number | null;
85
+ accountManagerName?: string | null;
86
+ relatedCollaboratorId?: number | null;
87
+ relatedCollaboratorName?: string | null;
88
+ mainRelatedPartyName?: string | null;
89
+ originType?: string | null;
90
+ originId?: number | null;
91
+ startDate: string;
92
+ endDate?: string | null;
93
+ signedAt?: string | null;
94
+ effectiveDate?: string | null;
95
+ budgetAmount?: number | null;
96
+ monthlyHourCap?: number | null;
97
+ valueAmount?: number | null;
98
+ paymentAmount?: number | null;
99
+ revenueAmount?: number | null;
100
+ fineAmount?: number | null;
101
+ status: string;
102
+ description?: string | null;
103
+ contentHtml?: string | null;
104
+ projectCount?: number;
105
+ currentPdfFileName?: string | null;
106
+ scheduleSummary?: OperationsWeeklyScheduleDay[];
107
+ };
108
+
109
+ export type OperationsWeeklyScheduleDay = {
110
+ weekday: string;
111
+ isWorkingDay: boolean;
112
+ startTime?: string | null;
113
+ endTime?: string | null;
114
+ breakMinutes?: number | null;
115
+ };
116
+
117
+ export type OperationsTimesheetEntry = {
118
+ id: number;
119
+ projectAssignmentId?: number | null;
120
+ projectId?: number | null;
121
+ projectName?: string | null;
122
+ roleLabel?: string | null;
123
+ activityLabel?: string | null;
124
+ workDate: string;
125
+ hours: number;
126
+ description?: string | null;
127
+ };
128
+
129
+ export type OperationsTimesheet = {
130
+ id: number;
131
+ collaboratorId: number;
132
+ collaboratorName: string;
133
+ approverCollaboratorId?: number | null;
134
+ approverName?: string | null;
135
+ weekStartDate: string;
136
+ weekEndDate: string;
137
+ totalHours?: number | null;
138
+ status: string;
139
+ submittedAt?: string | null;
140
+ reviewedAt?: string | null;
141
+ notes?: string | null;
142
+ decisionNote?: string | null;
143
+ entries?: OperationsTimesheetEntry[];
144
+ };
145
+
146
+ export type OperationsTimeOffRequest = {
147
+ id: number;
148
+ collaboratorId: number;
149
+ collaboratorName?: string;
150
+ approverCollaboratorId?: number | null;
151
+ approverName?: string | null;
152
+ requestType: string;
153
+ startDate: string;
154
+ endDate: string;
155
+ totalDays?: number | null;
156
+ status: string;
157
+ reason?: string | null;
158
+ submittedAt?: string | null;
159
+ reviewedAt?: string | null;
160
+ approverNote?: string | null;
161
+ };
162
+
163
+ export type OperationsScheduleAdjustmentDay = {
164
+ requestId: number;
165
+ weekday: string;
166
+ isWorkingDay: boolean;
167
+ startTime?: string | null;
168
+ endTime?: string | null;
169
+ breakMinutes?: number | null;
170
+ };
171
+
172
+ export type OperationsScheduleAdjustmentRequest = {
173
+ id: number;
174
+ collaboratorId: number;
175
+ collaboratorName?: string;
176
+ approverCollaboratorId?: number | null;
177
+ approverName?: string | null;
178
+ requestScope: string;
179
+ effectiveStartDate: string;
180
+ effectiveEndDate?: string | null;
181
+ status: string;
182
+ reason?: string | null;
183
+ submittedAt?: string | null;
184
+ reviewedAt?: string | null;
185
+ approverNote?: string | null;
186
+ currentSchedule?: OperationsWeeklyScheduleDay[];
187
+ days?: OperationsScheduleAdjustmentDay[];
188
+ };
189
+
190
+ export type OperationsApproval = {
191
+ id: number;
192
+ targetType: string;
193
+ targetId: number;
194
+ requesterCollaboratorId: number;
195
+ requesterName: string;
196
+ approverCollaboratorId?: number | null;
197
+ approverName?: string | null;
198
+ status: string;
199
+ submittedAt?: string | null;
200
+ decidedAt?: string | null;
201
+ decisionNote?: string | null;
202
+ timesheetWeekStartDate?: string | null;
203
+ timesheetWeekEndDate?: string | null;
204
+ timesheetTotalHours?: number | null;
205
+ timesheetProjectNames?: string | null;
206
+ timeOffType?: string | null;
207
+ timeOffStartDate?: string | null;
208
+ timeOffEndDate?: string | null;
209
+ timeOffReason?: string | null;
210
+ scheduleRequestScope?: string | null;
211
+ scheduleStartDate?: string | null;
212
+ scheduleEndDate?: string | null;
213
+ scheduleReason?: string | null;
214
+ };
215
+
216
+ export type OperationsTeamOverview = {
217
+ teamMembers: OperationsCollaborator[];
218
+ projectCount: number;
219
+ pendingApprovals: number;
220
+ pendingItems: {
221
+ timesheets: number;
222
+ timeOffRequests: number;
223
+ scheduleAdjustmentRequests: number;
224
+ };
225
+ teamProjects: Array<{
226
+ id: number;
227
+ code: string;
228
+ name: string;
229
+ clientName?: string | null;
230
+ status: string;
231
+ teamSize: number;
232
+ pendingTimesheets: number;
233
+ }>;
234
+ pendingApprovalQueue: OperationsApproval[];
235
+ pendingTimeOffRequests: OperationsTimeOffRequest[];
236
+ pendingScheduleAdjustmentRequests: OperationsScheduleAdjustmentRequest[];
237
+ };
238
+
239
+ export type OperationsCollaboratorDetails = OperationsCollaborator & {
240
+ assignedProjects: Array<{
241
+ id: number;
242
+ code: string;
243
+ name: string;
244
+ status: string;
245
+ roleLabel?: string | null;
246
+ allocationPercent?: number | null;
247
+ weeklyHours?: number | null;
248
+ startDate?: string | null;
249
+ endDate?: string | null;
250
+ }>;
251
+ relatedContracts: OperationsContract[];
252
+ weeklySchedule: OperationsWeeklyScheduleDay[];
253
+ timesheetSummary: {
254
+ totalTimesheets: number;
255
+ pendingTimesheets: number;
256
+ totalHours: number;
257
+ };
258
+ timeOffSummary: {
259
+ totalRequests: number;
260
+ pendingRequests: number;
261
+ approvedRequests: number;
262
+ };
263
+ scheduleAdjustmentRequests: Array<{
264
+ id: number;
265
+ requestScope: string;
266
+ effectiveStartDate: string;
267
+ effectiveEndDate?: string | null;
268
+ status: string;
269
+ reason?: string | null;
270
+ }>;
271
+ };
272
+
273
+ export type OperationsProjectDetails = OperationsProject & {
274
+ assignments: Array<{
275
+ id: number;
276
+ collaboratorId: number;
277
+ collaboratorName: string;
278
+ roleLabel?: string | null;
279
+ allocationPercent?: number | null;
280
+ weeklyHours?: number | null;
281
+ isBillable?: boolean;
282
+ startDate?: string | null;
283
+ endDate?: string | null;
284
+ status: string;
285
+ }>;
286
+ relatedContract?: OperationsContract | null;
287
+ timesheetSummary: {
288
+ totalTimesheets: number;
289
+ pendingTimesheets: number;
290
+ totalHours: number;
291
+ };
292
+ operationalIndicators: {
293
+ activeAssignments: number;
294
+ billableAssignments: number;
295
+ averageAllocation: number;
296
+ totalWeeklyHours: number;
297
+ };
298
+ };
299
+
300
+ export type OperationsContractDetails = OperationsContract & {
301
+ projects: Array<{
302
+ id: number;
303
+ code: string;
304
+ name: string;
305
+ status: string;
306
+ }>;
307
+ parties: Array<{
308
+ id: number;
309
+ partyRole: string;
310
+ partyType: string;
311
+ displayName: string;
312
+ documentNumber?: string | null;
313
+ email?: string | null;
314
+ phone?: string | null;
315
+ isPrimary?: boolean;
316
+ }>;
317
+ signatures: Array<{
318
+ id: number;
319
+ signerName: string;
320
+ signerRole?: string | null;
321
+ signerEmail?: string | null;
322
+ status: string;
323
+ signedAt?: string | null;
324
+ }>;
325
+ financialTerms: Array<{
326
+ id: number;
327
+ termType: string;
328
+ label: string;
329
+ amount: number;
330
+ recurrence: string;
331
+ dueDay?: number | null;
332
+ notes?: string | null;
333
+ }>;
334
+ documents: Array<{
335
+ id: number;
336
+ documentType: string;
337
+ fileName: string;
338
+ mimeType: string;
339
+ fileContentBase64?: string | null;
340
+ isCurrent: boolean;
341
+ notes?: string | null;
342
+ createdAt: string;
343
+ }>;
344
+ revisions: Array<{
345
+ id: number;
346
+ revisionType: string;
347
+ title: string;
348
+ effectiveDate?: string | null;
349
+ status: string;
350
+ summary?: string | null;
351
+ }>;
352
+ history: Array<{
353
+ id: number;
354
+ actorUserId?: number | null;
355
+ action: string;
356
+ note?: string | null;
357
+ metadataJson?: string | null;
358
+ createdAt: string;
359
+ }>;
360
+ };
@@ -1,25 +1,129 @@
1
- export const operationsCurrency = new Intl.NumberFormat('en-US', {
2
- style: 'currency',
3
- currency: 'USD',
4
- maximumFractionDigits: 0,
5
- });
6
-
7
- export function formatCurrency(value: number) {
8
- return operationsCurrency.format(value);
9
- }
10
-
11
- export function formatDate(value: string) {
12
- return new Date(`${value}T00:00:00`).toLocaleDateString('en-US', {
13
- month: 'short',
14
- day: 'numeric',
15
- year: 'numeric',
16
- });
17
- }
18
-
19
- export function formatPercent(value: number) {
20
- return `${value}%`;
21
- }
22
-
23
- export function formatHours(value: number) {
24
- return `${value.toFixed(1)}h`;
25
- }
1
+ export const operationsCurrency = new Intl.NumberFormat('en-US', {
2
+ style: 'currency',
3
+ currency: 'USD',
4
+ maximumFractionDigits: 0,
5
+ });
6
+
7
+ export function formatCurrency(value: number) {
8
+ return operationsCurrency.format(value);
9
+ }
10
+
11
+ export function formatDate(value?: string | null) {
12
+ if (!value) {
13
+ return '-';
14
+ }
15
+
16
+ return new Date(`${value}T00:00:00`).toLocaleDateString('en-US', {
17
+ month: 'short',
18
+ day: 'numeric',
19
+ year: 'numeric',
20
+ });
21
+ }
22
+
23
+ export function formatDateTime(value?: string | null) {
24
+ if (!value) {
25
+ return '-';
26
+ }
27
+
28
+ return new Date(value).toLocaleString('en-US', {
29
+ month: 'short',
30
+ day: 'numeric',
31
+ year: 'numeric',
32
+ hour: '2-digit',
33
+ minute: '2-digit',
34
+ });
35
+ }
36
+
37
+ export function formatPercent(value?: number | null) {
38
+ if (value === null || value === undefined || Number.isNaN(value)) {
39
+ return '-';
40
+ }
41
+
42
+ return `${value}%`;
43
+ }
44
+
45
+ export function formatHours(value?: number | null) {
46
+ if (value === null || value === undefined || Number.isNaN(value)) {
47
+ return '-';
48
+ }
49
+
50
+ return `${Number(value).toFixed(1)}h`;
51
+ }
52
+
53
+ export function formatEnumLabel(value?: string | null) {
54
+ if (!value) {
55
+ return '-';
56
+ }
57
+
58
+ return value
59
+ .replace(/[_-]+/g, ' ')
60
+ .replace(/\b\w/g, (match) => match.toUpperCase());
61
+ }
62
+
63
+ export function formatDateRange(
64
+ startDate?: string | null,
65
+ endDate?: string | null
66
+ ) {
67
+ if (!startDate && !endDate) {
68
+ return '-';
69
+ }
70
+
71
+ if (!endDate || startDate === endDate) {
72
+ return formatDate(startDate);
73
+ }
74
+
75
+ return `${formatDate(startDate)} to ${formatDate(endDate)}`;
76
+ }
77
+
78
+ export function getStatusBadgeClass(value?: string | null) {
79
+ switch (value) {
80
+ case 'active':
81
+ case 'approved':
82
+ case 'completed':
83
+ return 'bg-emerald-100 text-emerald-800';
84
+ case 'submitted':
85
+ case 'pending':
86
+ case 'renewal':
87
+ case 'planning':
88
+ return 'bg-amber-100 text-amber-900';
89
+ case 'rejected':
90
+ case 'cancelled':
91
+ case 'archived':
92
+ case 'closed':
93
+ return 'bg-rose-100 text-rose-800';
94
+ case 'draft':
95
+ case 'paused':
96
+ case 'inactive':
97
+ case 'on_leave':
98
+ return 'bg-slate-100 text-slate-800';
99
+ case 'at_risk':
100
+ return 'bg-orange-100 text-orange-900';
101
+ default:
102
+ return 'bg-sky-100 text-sky-800';
103
+ }
104
+ }
105
+
106
+ export function summarizeScheduleDays(
107
+ days: Array<{
108
+ weekday: string;
109
+ isWorkingDay: boolean;
110
+ startTime?: string | null;
111
+ endTime?: string | null;
112
+ }>
113
+ ) {
114
+ if (!days.length) {
115
+ return '-';
116
+ }
117
+
118
+ return days
119
+ .filter((day) => day.isWorkingDay)
120
+ .map((day) => {
121
+ const label = formatEnumLabel(day.weekday);
122
+ if (day.startTime && day.endTime) {
123
+ return `${label}: ${day.startTime}-${day.endTime}`;
124
+ }
125
+
126
+ return label;
127
+ })
128
+ .join(', ');
129
+ }
@@ -0,0 +1,14 @@
1
+ export function parseNumberInput(value: string) {
2
+ const trimmed = value.trim();
3
+ if (!trimmed) {
4
+ return null;
5
+ }
6
+
7
+ const parsed = Number(trimmed);
8
+ return Number.isFinite(parsed) ? parsed : null;
9
+ }
10
+
11
+ export function trimToNull(value?: string | null) {
12
+ const trimmed = value?.trim() ?? '';
13
+ return trimmed ? trimmed : null;
14
+ }