@hed-hog/operations 0.0.331 → 0.0.332

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 (62) hide show
  1. package/dist/controllers/operations-collaborators.controller.d.ts +54 -0
  2. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-collaborators.controller.js +100 -0
  4. package/dist/controllers/operations-collaborators.controller.js.map +1 -1
  5. package/dist/dto/create-collaborator-invoice.dto.d.ts +11 -0
  6. package/dist/dto/create-collaborator-invoice.dto.d.ts.map +1 -0
  7. package/dist/dto/create-collaborator-invoice.dto.js +55 -0
  8. package/dist/dto/create-collaborator-invoice.dto.js.map +1 -0
  9. package/dist/dto/create-collaborator-payment.dto.d.ts +10 -0
  10. package/dist/dto/create-collaborator-payment.dto.d.ts.map +1 -0
  11. package/dist/dto/create-collaborator-payment.dto.js +50 -0
  12. package/dist/dto/create-collaborator-payment.dto.js.map +1 -0
  13. package/dist/dto/list-collaborator-invoice.dto.d.ts +4 -0
  14. package/dist/dto/list-collaborator-invoice.dto.d.ts.map +1 -0
  15. package/dist/dto/list-collaborator-invoice.dto.js +8 -0
  16. package/dist/dto/list-collaborator-invoice.dto.js.map +1 -0
  17. package/dist/dto/list-collaborator-payment.dto.d.ts +4 -0
  18. package/dist/dto/list-collaborator-payment.dto.d.ts.map +1 -0
  19. package/dist/dto/list-collaborator-payment.dto.js +8 -0
  20. package/dist/dto/list-collaborator-payment.dto.js.map +1 -0
  21. package/dist/dto/update-collaborator-invoice.dto.d.ts +6 -0
  22. package/dist/dto/update-collaborator-invoice.dto.d.ts.map +1 -0
  23. package/dist/dto/update-collaborator-invoice.dto.js +9 -0
  24. package/dist/dto/update-collaborator-invoice.dto.js.map +1 -0
  25. package/dist/dto/update-collaborator-payment.dto.d.ts +6 -0
  26. package/dist/dto/update-collaborator-payment.dto.d.ts.map +1 -0
  27. package/dist/dto/update-collaborator-payment.dto.js +9 -0
  28. package/dist/dto/update-collaborator-payment.dto.js.map +1 -0
  29. package/dist/operations.service.d.ts +76 -0
  30. package/dist/operations.service.d.ts.map +1 -1
  31. package/dist/operations.service.js +235 -5
  32. package/dist/operations.service.js.map +1 -1
  33. package/hedhog/data/menu.yaml +27 -8
  34. package/hedhog/data/route.yaml +72 -0
  35. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +39 -3
  36. package/hedhog/frontend/app/_components/collaborator-invoices-tab.tsx.ejs +443 -0
  37. package/hedhog/frontend/app/_components/collaborator-payment-history-tab.tsx.ejs +429 -0
  38. package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +86 -87
  39. package/hedhog/frontend/app/_components/project-assignments-tab.tsx.ejs +218 -10
  40. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +710 -26
  41. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +158 -38
  42. package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +807 -803
  43. package/hedhog/frontend/app/_lib/api.ts.ejs +631 -480
  44. package/hedhog/frontend/app/_lib/types.ts.ejs +6 -5
  45. package/hedhog/frontend/app/_lib/utils/task-ui.ts.ejs +18 -0
  46. package/hedhog/frontend/app/my-projects/page.tsx.ejs +16 -2
  47. package/hedhog/frontend/app/my-tasks/page.tsx.ejs +95 -157
  48. package/hedhog/frontend/app/projects/page.tsx.ejs +42 -6
  49. package/hedhog/frontend/app/tasks-gantt/page.tsx.ejs +953 -0
  50. package/hedhog/frontend/messages/en.json +96 -2
  51. package/hedhog/frontend/messages/pt.json +96 -2
  52. package/hedhog/table/operations_collaborator_invoice.yaml +35 -0
  53. package/hedhog/table/operations_collaborator_payment.yaml +32 -0
  54. package/package.json +5 -5
  55. package/src/controllers/operations-collaborators.controller.ts +117 -8
  56. package/src/dto/create-collaborator-invoice.dto.ts +39 -0
  57. package/src/dto/create-collaborator-payment.dto.ts +35 -0
  58. package/src/dto/list-collaborator-invoice.dto.ts +3 -0
  59. package/src/dto/list-collaborator-payment.dto.ts +3 -0
  60. package/src/dto/update-collaborator-invoice.dto.ts +6 -0
  61. package/src/dto/update-collaborator-payment.dto.ts +6 -0
  62. package/src/operations.service.ts +328 -5
@@ -1,480 +1,631 @@
1
- type RequestFn = (input: {
2
- url: string;
3
- method: string;
4
- data?: unknown;
5
- }) => Promise<{ data: unknown }>;
6
-
7
- export function getOperationsErrorMessage(error: unknown, fallback: string) {
8
- if (error && typeof error === 'object') {
9
- const requestError = error as {
10
- message?: string;
11
- response?: {
12
- data?: {
13
- message?: string | string[];
14
- };
15
- };
16
- };
17
-
18
- const message =
19
- requestError.response?.data?.message ?? requestError.message;
20
-
21
- if (Array.isArray(message)) {
22
- const combined = message.filter(Boolean).join(' ');
23
- if (combined.trim()) {
24
- return combined;
25
- }
26
- }
27
-
28
- if (typeof message === 'string' && message.trim()) {
29
- return message;
30
- }
31
- }
32
-
33
- return fallback;
34
- }
35
-
36
- export async function fetchOperations<T>(request: RequestFn, url: string) {
37
- const response = await request({
38
- url,
39
- method: 'GET',
40
- });
41
-
42
- return response.data as T;
43
- }
44
-
45
- export async function mutateOperations<T>(
46
- request: RequestFn,
47
- url: string,
48
- method: 'POST' | 'PATCH' | 'DELETE',
49
- data?: unknown
50
- ) {
51
- const response = await request({
52
- url,
53
- method,
54
- data,
55
- });
56
-
57
- return response.data as T;
58
- }
59
-
60
- // ─── Cost Types ───────────────────────────────────────────────────────────────
61
-
62
- export type CostType = {
63
- id: number;
64
- slug: string;
65
- name: string;
66
- code?: string | null;
67
- category?: string | null;
68
- description?: string | null;
69
- defaultRecurrence?: 'monthly' | 'one_time' | 'yearly' | null;
70
- isAllocatable?: boolean | null;
71
- isDepreciable?: boolean | null;
72
- isActive: boolean;
73
- };
74
-
75
- export function fetchCostTypes(
76
- request: RequestFn,
77
- args: {
78
- search?: string;
79
- active?: boolean;
80
- page?: number;
81
- pageSize?: number;
82
- } = {}
83
- ) {
84
- const { search, active, page, pageSize } = args;
85
- const params = new URLSearchParams();
86
- if (search?.trim()) {
87
- params.set('search', search.trim());
88
- }
89
- if (active !== undefined) {
90
- params.set('active', String(active));
91
- }
92
- if (page !== undefined) {
93
- params.set('page', String(page));
94
- }
95
- if (pageSize !== undefined) {
96
- params.set('pageSize', String(pageSize));
97
- }
98
- const qs = params.toString();
99
- return fetchOperations<CostType[]>(
100
- request,
101
- qs ? `/operations/cost-types?${qs}` : '/operations/cost-types'
102
- );
103
- }
104
-
105
- export function createCostType(
106
- request: RequestFn,
107
- data: {
108
- name: string;
109
- slug?: string | null;
110
- code?: string | null;
111
- category?: string | null;
112
- description?: string | null;
113
- defaultRecurrence?: string | null;
114
- isAllocatable?: boolean | null;
115
- isDepreciable?: boolean | null;
116
- isActive?: boolean;
117
- }
118
- ) {
119
- return mutateOperations<CostType>(
120
- request,
121
- '/operations/cost-types',
122
- 'POST',
123
- data
124
- );
125
- }
126
-
127
- // ─── Collaborators ────────────────────────────────────────────────────────────
128
-
129
- export function fetchCollaborators(request: RequestFn) {
130
- return fetchOperations<import('./types').OperationsCollaborator[]>(
131
- request,
132
- '/operations/collaborators'
133
- );
134
- }
135
-
136
- // ─── Collaborator Costs ───────────────────────────────────────────────────────
137
-
138
- export type CollaboratorCost = {
139
- id: number;
140
- collaboratorId: number;
141
- costTypeId: number;
142
- costTypeName: string;
143
- costTypeSlug: string;
144
- amount: string;
145
- currency: string;
146
- recurrence: 'one_time' | 'monthly' | 'yearly';
147
- allocatable?: boolean;
148
- /** ISO date string — mapped to startDate going forward */
149
- referenceDate: string | null;
150
- startDate?: string | null;
151
- endDate?: string | null;
152
- depreciationMonths?: number | null;
153
- description: string | null;
154
- notes?: string | null;
155
- createdAt: string;
156
- };
157
-
158
- export function fetchCollaboratorCosts(
159
- request: RequestFn,
160
- collaboratorId: number
161
- ) {
162
- return fetchOperations<CollaboratorCost[]>(
163
- request,
164
- `/operations/collaborators/${collaboratorId}/costs`
165
- );
166
- }
167
-
168
- export function createCollaboratorCost(
169
- request: RequestFn,
170
- collaboratorId: number,
171
- data: {
172
- costTypeId: number;
173
- amount: number;
174
- currency?: string;
175
- recurrence?: string;
176
- allocatable?: boolean;
177
- referenceDate?: string | null;
178
- startDate?: string | null;
179
- endDate?: string | null;
180
- depreciationMonths?: number | null;
181
- description?: string | null;
182
- notes?: string | null;
183
- }
184
- ) {
185
- return mutateOperations<CollaboratorCost>(
186
- request,
187
- `/operations/collaborators/${collaboratorId}/costs`,
188
- 'POST',
189
- data
190
- );
191
- }
192
-
193
- export function updateCollaboratorCost(
194
- request: RequestFn,
195
- collaboratorId: number,
196
- costId: number,
197
- data: Partial<{
198
- costTypeId: number;
199
- amount: number;
200
- currency: string;
201
- recurrence: string;
202
- allocatable: boolean;
203
- referenceDate: string | null;
204
- startDate: string | null;
205
- endDate: string | null;
206
- depreciationMonths: number | null;
207
- description: string | null;
208
- notes: string | null;
209
- }>
210
- ) {
211
- return mutateOperations<CollaboratorCost>(
212
- request,
213
- `/operations/collaborators/${collaboratorId}/costs/${costId}`,
214
- 'PATCH',
215
- data
216
- );
217
- }
218
-
219
- export function deleteCollaboratorCost(
220
- request: RequestFn,
221
- collaboratorId: number,
222
- costId: number
223
- ) {
224
- return mutateOperations<{ success: boolean }>(
225
- request,
226
- `/operations/collaborators/${collaboratorId}/costs/${costId}`,
227
- 'DELETE'
228
- );
229
- }
230
-
231
- // ─── Currencies ───────────────────────────────────────────────────────────────
232
-
233
- export type Currency = Record<string, unknown> & {
234
- id: number;
235
- code: string;
236
- name: string;
237
- symbol: string;
238
- status: 'active' | 'inactive';
239
- };
240
-
241
- export function listCurrencies(
242
- request: RequestFn,
243
- params?: Record<string, string>
244
- ) {
245
- const qs = params ? new URLSearchParams(params).toString() : '';
246
- return fetchOperations<Currency[]>(
247
- request,
248
- qs ? `/finance/currencies?${qs}` : '/finance/currencies'
249
- );
250
- }
251
-
252
- // ─── Project Cost Categories ──────────────────────────────────────────────────
253
-
254
- export function listProjectCostCategories(
255
- request: RequestFn,
256
- params?: Record<string, string>
257
- ) {
258
- const qs = params ? new URLSearchParams(params).toString() : '';
259
- return fetchOperations(
260
- request,
261
- qs
262
- ? `/operations/project-cost-categories?${qs}`
263
- : '/operations/project-cost-categories'
264
- );
265
- }
266
-
267
- export function createProjectCostCategory(request: RequestFn, data: unknown) {
268
- return mutateOperations(
269
- request,
270
- '/operations/project-cost-categories',
271
- 'POST',
272
- data
273
- );
274
- }
275
-
276
- export function updateProjectCostCategory(
277
- request: RequestFn,
278
- id: number,
279
- data: unknown
280
- ) {
281
- return mutateOperations(
282
- request,
283
- `/operations/project-cost-categories/${id}`,
284
- 'PATCH',
285
- data
286
- );
287
- }
288
-
289
- export function deleteProjectCostCategory(request: RequestFn, id: number) {
290
- return mutateOperations(
291
- request,
292
- `/operations/project-cost-categories/${id}`,
293
- 'DELETE'
294
- );
295
- }
296
-
297
- // ─── Project Cost Types ───────────────────────────────────────────────────────
298
-
299
- export function listProjectCostTypes(
300
- request: RequestFn,
301
- params?: Record<string, string>
302
- ) {
303
- const qs = params ? new URLSearchParams(params).toString() : '';
304
- return fetchOperations(
305
- request,
306
- qs
307
- ? `/operations/project-cost-types?${qs}`
308
- : '/operations/project-cost-types'
309
- );
310
- }
311
-
312
- export function createProjectCostType(request: RequestFn, data: unknown) {
313
- return mutateOperations(
314
- request,
315
- '/operations/project-cost-types',
316
- 'POST',
317
- data
318
- );
319
- }
320
-
321
- export function updateProjectCostType(
322
- request: RequestFn,
323
- id: number,
324
- data: unknown
325
- ) {
326
- return mutateOperations(
327
- request,
328
- `/operations/project-cost-types/${id}`,
329
- 'PATCH',
330
- data
331
- );
332
- }
333
-
334
- export function deleteProjectCostType(request: RequestFn, id: number) {
335
- return mutateOperations(
336
- request,
337
- `/operations/project-cost-types/${id}`,
338
- 'DELETE'
339
- );
340
- }
341
-
342
- // ─── Project Costs ────────────────────────────────────────────────────────────
343
-
344
- export function listProjectCosts(
345
- request: RequestFn,
346
- projectId: number,
347
- params?: Record<string, string>
348
- ) {
349
- const qs = params ? new URLSearchParams(params).toString() : '';
350
- return fetchOperations(
351
- request,
352
- qs
353
- ? `/operations/projects/${projectId}/costs?${qs}`
354
- : `/operations/projects/${projectId}/costs`
355
- );
356
- }
357
-
358
- export function getProjectCostsSummary(request: RequestFn, projectId: number) {
359
- return fetchOperations(
360
- request,
361
- `/operations/projects/${projectId}/costs/summary`
362
- );
363
- }
364
-
365
- export function getFullProjectCostSummary(
366
- request: RequestFn,
367
- projectId: number
368
- ) {
369
- return fetchOperations(
370
- request,
371
- `/operations/projects/${projectId}/cost-summary`
372
- );
373
- }
374
-
375
- export function createProjectCost(
376
- request: RequestFn,
377
- projectId: number,
378
- data: unknown
379
- ) {
380
- return mutateOperations(
381
- request,
382
- `/operations/projects/${projectId}/costs`,
383
- 'POST',
384
- data
385
- );
386
- }
387
-
388
- export function updateProjectCost(
389
- request: RequestFn,
390
- projectId: number,
391
- costId: number,
392
- data: unknown
393
- ) {
394
- return mutateOperations(
395
- request,
396
- `/operations/projects/${projectId}/costs/${costId}`,
397
- 'PATCH',
398
- data
399
- );
400
- }
401
-
402
- export function deleteProjectCost(
403
- request: RequestFn,
404
- projectId: number,
405
- costId: number
406
- ) {
407
- return mutateOperations(
408
- request,
409
- `/operations/projects/${projectId}/costs/${costId}`,
410
- 'DELETE'
411
- );
412
- }
413
-
414
- export function getProjectCostReport(
415
- request: RequestFn,
416
- projectId: number,
417
- filters?: Record<string, unknown>
418
- ) {
419
- const params = filters
420
- ? '?' +
421
- new URLSearchParams(
422
- Object.entries(filters)
423
- .filter(([, v]) => v !== undefined && v !== null)
424
- .map(([k, v]) => [k, String(v)])
425
- ).toString()
426
- : '';
427
- return fetchOperations(
428
- request,
429
- `/operations/projects/${projectId}/costs/report${params}`
430
- );
431
- }
432
-
433
- // ─── Task Comments ────────────────────────────────────────────────────────────
434
-
435
- export function fetchTaskActivities(request: RequestFn, taskId: number) {
436
- return fetchOperations(request, `/operations/tasks/${taskId}/activities`);
437
- }
438
-
439
- export function fetchTaskComments(request: RequestFn, taskId: number) {
440
- return fetchOperations(request, `/operations/tasks/${taskId}/comments`);
441
- }
442
-
443
- export function createTaskComment(
444
- request: RequestFn,
445
- taskId: number,
446
- content: string
447
- ) {
448
- return mutateOperations(
449
- request,
450
- `/operations/tasks/${taskId}/comments`,
451
- 'POST',
452
- { content }
453
- );
454
- }
455
-
456
- export function updateTaskComment(
457
- request: RequestFn,
458
- taskId: number,
459
- commentId: number,
460
- content: string
461
- ) {
462
- return mutateOperations(
463
- request,
464
- `/operations/tasks/${taskId}/comments/${commentId}`,
465
- 'PATCH',
466
- { content }
467
- );
468
- }
469
-
470
- export function deleteTaskComment(
471
- request: RequestFn,
472
- taskId: number,
473
- commentId: number
474
- ) {
475
- return mutateOperations(
476
- request,
477
- `/operations/tasks/${taskId}/comments/${commentId}`,
478
- 'DELETE'
479
- );
480
- }
1
+ type RequestFn = (input: {
2
+ url: string;
3
+ method: string;
4
+ data?: unknown;
5
+ }) => Promise<{ data: unknown }>;
6
+
7
+ export function getOperationsErrorMessage(error: unknown, fallback: string) {
8
+ if (error && typeof error === 'object') {
9
+ const requestError = error as {
10
+ message?: string;
11
+ response?: {
12
+ data?: {
13
+ message?: string | string[];
14
+ };
15
+ };
16
+ };
17
+
18
+ const message =
19
+ requestError.response?.data?.message ?? requestError.message;
20
+
21
+ if (Array.isArray(message)) {
22
+ const combined = message.filter(Boolean).join(' ');
23
+ if (combined.trim()) {
24
+ return combined;
25
+ }
26
+ }
27
+
28
+ if (typeof message === 'string' && message.trim()) {
29
+ return message;
30
+ }
31
+ }
32
+
33
+ return fallback;
34
+ }
35
+
36
+ export async function fetchOperations<T>(request: RequestFn, url: string) {
37
+ const response = await request({
38
+ url,
39
+ method: 'GET',
40
+ });
41
+
42
+ return response.data as T;
43
+ }
44
+
45
+ export async function mutateOperations<T>(
46
+ request: RequestFn,
47
+ url: string,
48
+ method: 'POST' | 'PATCH' | 'DELETE',
49
+ data?: unknown
50
+ ) {
51
+ const response = await request({
52
+ url,
53
+ method,
54
+ data,
55
+ });
56
+
57
+ return response.data as T;
58
+ }
59
+
60
+ // ─── Cost Types ───────────────────────────────────────────────────────────────
61
+
62
+ export type CostType = {
63
+ id: number;
64
+ slug: string;
65
+ name: string;
66
+ code?: string | null;
67
+ category?: string | null;
68
+ description?: string | null;
69
+ defaultRecurrence?: 'monthly' | 'one_time' | 'yearly' | null;
70
+ isAllocatable?: boolean | null;
71
+ isDepreciable?: boolean | null;
72
+ isActive: boolean;
73
+ };
74
+
75
+ export function fetchCostTypes(
76
+ request: RequestFn,
77
+ args: {
78
+ search?: string;
79
+ active?: boolean;
80
+ page?: number;
81
+ pageSize?: number;
82
+ } = {}
83
+ ) {
84
+ const { search, active, page, pageSize } = args;
85
+ const params = new URLSearchParams();
86
+ if (search?.trim()) {
87
+ params.set('search', search.trim());
88
+ }
89
+ if (active !== undefined) {
90
+ params.set('active', String(active));
91
+ }
92
+ if (page !== undefined) {
93
+ params.set('page', String(page));
94
+ }
95
+ if (pageSize !== undefined) {
96
+ params.set('pageSize', String(pageSize));
97
+ }
98
+ const qs = params.toString();
99
+ return fetchOperations<CostType[]>(
100
+ request,
101
+ qs ? `/operations/cost-types?${qs}` : '/operations/cost-types'
102
+ );
103
+ }
104
+
105
+ export function createCostType(
106
+ request: RequestFn,
107
+ data: {
108
+ name: string;
109
+ slug?: string | null;
110
+ code?: string | null;
111
+ category?: string | null;
112
+ description?: string | null;
113
+ defaultRecurrence?: string | null;
114
+ isAllocatable?: boolean | null;
115
+ isDepreciable?: boolean | null;
116
+ isActive?: boolean;
117
+ }
118
+ ) {
119
+ return mutateOperations<CostType>(
120
+ request,
121
+ '/operations/cost-types',
122
+ 'POST',
123
+ data
124
+ );
125
+ }
126
+
127
+ // ─── Collaborators ────────────────────────────────────────────────────────────
128
+
129
+ export function fetchCollaborators(request: RequestFn) {
130
+ return fetchOperations<import('./types').OperationsCollaborator[]>(
131
+ request,
132
+ '/operations/collaborators'
133
+ );
134
+ }
135
+
136
+ // ─── Collaborator Costs ───────────────────────────────────────────────────────
137
+
138
+ export type CollaboratorCost = {
139
+ id: number;
140
+ collaboratorId: number;
141
+ costTypeId: number;
142
+ costTypeName: string;
143
+ costTypeSlug: string;
144
+ amount: string;
145
+ currency: string;
146
+ recurrence: 'one_time' | 'monthly' | 'yearly';
147
+ allocatable?: boolean;
148
+ /** ISO date string — mapped to startDate going forward */
149
+ referenceDate: string | null;
150
+ startDate?: string | null;
151
+ endDate?: string | null;
152
+ depreciationMonths?: number | null;
153
+ description: string | null;
154
+ notes?: string | null;
155
+ createdAt: string;
156
+ };
157
+
158
+ export function fetchCollaboratorCosts(
159
+ request: RequestFn,
160
+ collaboratorId: number
161
+ ) {
162
+ return fetchOperations<CollaboratorCost[]>(
163
+ request,
164
+ `/operations/collaborators/${collaboratorId}/costs`
165
+ );
166
+ }
167
+
168
+ export function createCollaboratorCost(
169
+ request: RequestFn,
170
+ collaboratorId: number,
171
+ data: {
172
+ costTypeId: number;
173
+ amount: number;
174
+ currency?: string;
175
+ recurrence?: string;
176
+ allocatable?: boolean;
177
+ referenceDate?: string | null;
178
+ startDate?: string | null;
179
+ endDate?: string | null;
180
+ depreciationMonths?: number | null;
181
+ description?: string | null;
182
+ notes?: string | null;
183
+ }
184
+ ) {
185
+ return mutateOperations<CollaboratorCost>(
186
+ request,
187
+ `/operations/collaborators/${collaboratorId}/costs`,
188
+ 'POST',
189
+ data
190
+ );
191
+ }
192
+
193
+ export function updateCollaboratorCost(
194
+ request: RequestFn,
195
+ collaboratorId: number,
196
+ costId: number,
197
+ data: Partial<{
198
+ costTypeId: number;
199
+ amount: number;
200
+ currency: string;
201
+ recurrence: string;
202
+ allocatable: boolean;
203
+ referenceDate: string | null;
204
+ startDate: string | null;
205
+ endDate: string | null;
206
+ depreciationMonths: number | null;
207
+ description: string | null;
208
+ notes: string | null;
209
+ }>
210
+ ) {
211
+ return mutateOperations<CollaboratorCost>(
212
+ request,
213
+ `/operations/collaborators/${collaboratorId}/costs/${costId}`,
214
+ 'PATCH',
215
+ data
216
+ );
217
+ }
218
+
219
+ export function deleteCollaboratorCost(
220
+ request: RequestFn,
221
+ collaboratorId: number,
222
+ costId: number
223
+ ) {
224
+ return mutateOperations<{ success: boolean }>(
225
+ request,
226
+ `/operations/collaborators/${collaboratorId}/costs/${costId}`,
227
+ 'DELETE'
228
+ );
229
+ }
230
+
231
+ // ─── Currencies ───────────────────────────────────────────────────────────────
232
+
233
+ export type Currency = Record<string, unknown> & {
234
+ id: number;
235
+ code: string;
236
+ name: string;
237
+ symbol: string;
238
+ status: 'active' | 'inactive';
239
+ };
240
+
241
+ export function listCurrencies(
242
+ request: RequestFn,
243
+ params?: Record<string, string>
244
+ ) {
245
+ const qs = params ? new URLSearchParams(params).toString() : '';
246
+ return fetchOperations<Currency[]>(
247
+ request,
248
+ qs ? `/finance/currencies?${qs}` : '/finance/currencies'
249
+ );
250
+ }
251
+
252
+ // ─── Project Cost Categories ──────────────────────────────────────────────────
253
+
254
+ export function listProjectCostCategories(
255
+ request: RequestFn,
256
+ params?: Record<string, string>
257
+ ) {
258
+ const qs = params ? new URLSearchParams(params).toString() : '';
259
+ return fetchOperations(
260
+ request,
261
+ qs
262
+ ? `/operations/project-cost-categories?${qs}`
263
+ : '/operations/project-cost-categories'
264
+ );
265
+ }
266
+
267
+ export function createProjectCostCategory(request: RequestFn, data: unknown) {
268
+ return mutateOperations(
269
+ request,
270
+ '/operations/project-cost-categories',
271
+ 'POST',
272
+ data
273
+ );
274
+ }
275
+
276
+ export function updateProjectCostCategory(
277
+ request: RequestFn,
278
+ id: number,
279
+ data: unknown
280
+ ) {
281
+ return mutateOperations(
282
+ request,
283
+ `/operations/project-cost-categories/${id}`,
284
+ 'PATCH',
285
+ data
286
+ );
287
+ }
288
+
289
+ export function deleteProjectCostCategory(request: RequestFn, id: number) {
290
+ return mutateOperations(
291
+ request,
292
+ `/operations/project-cost-categories/${id}`,
293
+ 'DELETE'
294
+ );
295
+ }
296
+
297
+ // ─── Project Cost Types ───────────────────────────────────────────────────────
298
+
299
+ export function listProjectCostTypes(
300
+ request: RequestFn,
301
+ params?: Record<string, string>
302
+ ) {
303
+ const qs = params ? new URLSearchParams(params).toString() : '';
304
+ return fetchOperations(
305
+ request,
306
+ qs
307
+ ? `/operations/project-cost-types?${qs}`
308
+ : '/operations/project-cost-types'
309
+ );
310
+ }
311
+
312
+ export function createProjectCostType(request: RequestFn, data: unknown) {
313
+ return mutateOperations(
314
+ request,
315
+ '/operations/project-cost-types',
316
+ 'POST',
317
+ data
318
+ );
319
+ }
320
+
321
+ export function updateProjectCostType(
322
+ request: RequestFn,
323
+ id: number,
324
+ data: unknown
325
+ ) {
326
+ return mutateOperations(
327
+ request,
328
+ `/operations/project-cost-types/${id}`,
329
+ 'PATCH',
330
+ data
331
+ );
332
+ }
333
+
334
+ export function deleteProjectCostType(request: RequestFn, id: number) {
335
+ return mutateOperations(
336
+ request,
337
+ `/operations/project-cost-types/${id}`,
338
+ 'DELETE'
339
+ );
340
+ }
341
+
342
+ // ─── Project Costs ────────────────────────────────────────────────────────────
343
+
344
+ export function listProjectCosts(
345
+ request: RequestFn,
346
+ projectId: number,
347
+ params?: Record<string, string>
348
+ ) {
349
+ const qs = params ? new URLSearchParams(params).toString() : '';
350
+ return fetchOperations(
351
+ request,
352
+ qs
353
+ ? `/operations/projects/${projectId}/costs?${qs}`
354
+ : `/operations/projects/${projectId}/costs`
355
+ );
356
+ }
357
+
358
+ export function getProjectCostsSummary(request: RequestFn, projectId: number) {
359
+ return fetchOperations(
360
+ request,
361
+ `/operations/projects/${projectId}/costs/summary`
362
+ );
363
+ }
364
+
365
+ export function getFullProjectCostSummary(
366
+ request: RequestFn,
367
+ projectId: number
368
+ ) {
369
+ return fetchOperations(
370
+ request,
371
+ `/operations/projects/${projectId}/cost-summary`
372
+ );
373
+ }
374
+
375
+ export function createProjectCost(
376
+ request: RequestFn,
377
+ projectId: number,
378
+ data: unknown
379
+ ) {
380
+ return mutateOperations(
381
+ request,
382
+ `/operations/projects/${projectId}/costs`,
383
+ 'POST',
384
+ data
385
+ );
386
+ }
387
+
388
+ export function updateProjectCost(
389
+ request: RequestFn,
390
+ projectId: number,
391
+ costId: number,
392
+ data: unknown
393
+ ) {
394
+ return mutateOperations(
395
+ request,
396
+ `/operations/projects/${projectId}/costs/${costId}`,
397
+ 'PATCH',
398
+ data
399
+ );
400
+ }
401
+
402
+ export function deleteProjectCost(
403
+ request: RequestFn,
404
+ projectId: number,
405
+ costId: number
406
+ ) {
407
+ return mutateOperations(
408
+ request,
409
+ `/operations/projects/${projectId}/costs/${costId}`,
410
+ 'DELETE'
411
+ );
412
+ }
413
+
414
+ export function getProjectCostReport(
415
+ request: RequestFn,
416
+ projectId: number,
417
+ filters?: Record<string, unknown>
418
+ ) {
419
+ const params = filters
420
+ ? '?' +
421
+ new URLSearchParams(
422
+ Object.entries(filters)
423
+ .filter(([, v]) => v !== undefined && v !== null)
424
+ .map(([k, v]) => [k, String(v)])
425
+ ).toString()
426
+ : '';
427
+ return fetchOperations(
428
+ request,
429
+ `/operations/projects/${projectId}/costs/report${params}`
430
+ );
431
+ }
432
+
433
+ // ─── Task Comments ────────────────────────────────────────────────────────────
434
+
435
+ export function fetchTaskActivities(request: RequestFn, taskId: number) {
436
+ return fetchOperations(request, `/operations/tasks/${taskId}/activities`);
437
+ }
438
+
439
+ export function fetchTaskComments(request: RequestFn, taskId: number) {
440
+ return fetchOperations(request, `/operations/tasks/${taskId}/comments`);
441
+ }
442
+
443
+ export function createTaskComment(
444
+ request: RequestFn,
445
+ taskId: number,
446
+ content: string
447
+ ) {
448
+ return mutateOperations(
449
+ request,
450
+ `/operations/tasks/${taskId}/comments`,
451
+ 'POST',
452
+ { content }
453
+ );
454
+ }
455
+
456
+ export function updateTaskComment(
457
+ request: RequestFn,
458
+ taskId: number,
459
+ commentId: number,
460
+ content: string
461
+ ) {
462
+ return mutateOperations(
463
+ request,
464
+ `/operations/tasks/${taskId}/comments/${commentId}`,
465
+ 'PATCH',
466
+ { content }
467
+ );
468
+ }
469
+
470
+ export function deleteTaskComment(
471
+ request: RequestFn,
472
+ taskId: number,
473
+ commentId: number
474
+ ) {
475
+ return mutateOperations(
476
+ request,
477
+ `/operations/tasks/${taskId}/comments/${commentId}`,
478
+ 'DELETE'
479
+ );
480
+ }
481
+
482
+ // ─── Collaborator Payments ────────────────────────────────────────────────────
483
+
484
+ export type CollaboratorPayment = {
485
+ id: number;
486
+ collaboratorId: number;
487
+ amount: string;
488
+ paymentDate: string;
489
+ referenceMonth: string | null;
490
+ paymentMethod: string;
491
+ notes: string | null;
492
+ createdAt: string;
493
+ };
494
+
495
+ export function fetchCollaboratorPayments(
496
+ request: RequestFn,
497
+ collaboratorId: number
498
+ ) {
499
+ return fetchOperations<CollaboratorPayment[]>(
500
+ request,
501
+ `/operations/collaborators/${collaboratorId}/payment-history`
502
+ );
503
+ }
504
+
505
+ export function createCollaboratorPayment(
506
+ request: RequestFn,
507
+ collaboratorId: number,
508
+ data: {
509
+ amount: number;
510
+ paymentDate: string;
511
+ referenceMonth?: string | null;
512
+ paymentMethod?: string;
513
+ notes?: string | null;
514
+ }
515
+ ) {
516
+ return mutateOperations<CollaboratorPayment>(
517
+ request,
518
+ `/operations/collaborators/${collaboratorId}/payment-history`,
519
+ 'POST',
520
+ data
521
+ );
522
+ }
523
+
524
+ export function updateCollaboratorPayment(
525
+ request: RequestFn,
526
+ collaboratorId: number,
527
+ paymentId: number,
528
+ data: Partial<{
529
+ amount: number;
530
+ paymentDate: string;
531
+ referenceMonth: string | null;
532
+ paymentMethod: string;
533
+ notes: string | null;
534
+ }>
535
+ ) {
536
+ return mutateOperations<CollaboratorPayment>(
537
+ request,
538
+ `/operations/collaborators/${collaboratorId}/payment-history/${paymentId}`,
539
+ 'PATCH',
540
+ data
541
+ );
542
+ }
543
+
544
+ export function deleteCollaboratorPayment(
545
+ request: RequestFn,
546
+ collaboratorId: number,
547
+ paymentId: number
548
+ ) {
549
+ return mutateOperations<{ success: boolean }>(
550
+ request,
551
+ `/operations/collaborators/${collaboratorId}/payment-history/${paymentId}`,
552
+ 'DELETE'
553
+ );
554
+ }
555
+
556
+ // ─── Collaborator Invoices ────────────────────────────────────────────────────
557
+
558
+ export type CollaboratorInvoice = {
559
+ id: number;
560
+ collaboratorId: number;
561
+ invoiceNumber: string | null;
562
+ amount: string;
563
+ issueDate: string;
564
+ dueDate: string | null;
565
+ status: string;
566
+ description: string | null;
567
+ createdAt: string;
568
+ };
569
+
570
+ export function fetchCollaboratorInvoices(
571
+ request: RequestFn,
572
+ collaboratorId: number
573
+ ) {
574
+ return fetchOperations<CollaboratorInvoice[]>(
575
+ request,
576
+ `/operations/collaborators/${collaboratorId}/invoices`
577
+ );
578
+ }
579
+
580
+ export function createCollaboratorInvoice(
581
+ request: RequestFn,
582
+ collaboratorId: number,
583
+ data: {
584
+ invoiceNumber?: string | null;
585
+ amount: number;
586
+ issueDate: string;
587
+ dueDate?: string | null;
588
+ status?: string;
589
+ description?: string | null;
590
+ }
591
+ ) {
592
+ return mutateOperations<CollaboratorInvoice>(
593
+ request,
594
+ `/operations/collaborators/${collaboratorId}/invoices`,
595
+ 'POST',
596
+ data
597
+ );
598
+ }
599
+
600
+ export function updateCollaboratorInvoice(
601
+ request: RequestFn,
602
+ collaboratorId: number,
603
+ invoiceId: number,
604
+ data: Partial<{
605
+ invoiceNumber: string | null;
606
+ amount: number;
607
+ issueDate: string;
608
+ dueDate: string | null;
609
+ status: string;
610
+ description: string | null;
611
+ }>
612
+ ) {
613
+ return mutateOperations<CollaboratorInvoice>(
614
+ request,
615
+ `/operations/collaborators/${collaboratorId}/invoices/${invoiceId}`,
616
+ 'PATCH',
617
+ data
618
+ );
619
+ }
620
+
621
+ export function deleteCollaboratorInvoice(
622
+ request: RequestFn,
623
+ collaboratorId: number,
624
+ invoiceId: number
625
+ ) {
626
+ return mutateOperations<{ success: boolean }>(
627
+ request,
628
+ `/operations/collaborators/${collaboratorId}/invoices/${invoiceId}`,
629
+ 'DELETE'
630
+ );
631
+ }