@hed-hog/operations 0.0.318 → 0.0.319

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 (137) hide show
  1. package/dist/controllers/operations-collaborator-costs.controller.d.ts +144 -0
  2. package/dist/controllers/operations-collaborator-costs.controller.d.ts.map +1 -0
  3. package/dist/controllers/operations-collaborator-costs.controller.js +162 -0
  4. package/dist/controllers/operations-collaborator-costs.controller.js.map +1 -0
  5. package/dist/controllers/operations-collaborators.controller.d.ts +14 -0
  6. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  7. package/dist/controllers/operations-collaborators.controller.js +11 -0
  8. package/dist/controllers/operations-collaborators.controller.js.map +1 -1
  9. package/dist/controllers/operations-projects.controller.d.ts +31 -0
  10. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  11. package/dist/controllers/operations-projects.controller.js +23 -0
  12. package/dist/controllers/operations-projects.controller.js.map +1 -1
  13. package/dist/controllers/operations-reports.controller.d.ts +199 -0
  14. package/dist/controllers/operations-reports.controller.d.ts.map +1 -0
  15. package/dist/controllers/operations-reports.controller.js +53 -0
  16. package/dist/controllers/operations-reports.controller.js.map +1 -0
  17. package/dist/controllers/operations-tasks.controller.d.ts +41 -2
  18. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -1
  19. package/dist/controllers/operations-tasks.controller.js +17 -5
  20. package/dist/controllers/operations-tasks.controller.js.map +1 -1
  21. package/dist/dto/create-collaborator-cost.dto.d.ts +16 -0
  22. package/dist/dto/create-collaborator-cost.dto.d.ts.map +1 -0
  23. package/dist/dto/create-collaborator-cost.dto.js +88 -0
  24. package/dist/dto/create-collaborator-cost.dto.js.map +1 -0
  25. package/dist/dto/create-collaborator.dto.d.ts +0 -1
  26. package/dist/dto/create-collaborator.dto.d.ts.map +1 -1
  27. package/dist/dto/create-collaborator.dto.js +0 -6
  28. package/dist/dto/create-collaborator.dto.js.map +1 -1
  29. package/dist/dto/create-cost-type.dto.d.ts +13 -0
  30. package/dist/dto/create-cost-type.dto.d.ts.map +1 -0
  31. package/dist/dto/create-cost-type.dto.js +87 -0
  32. package/dist/dto/create-cost-type.dto.js.map +1 -0
  33. package/dist/dto/list-approvals.dto.d.ts +2 -0
  34. package/dist/dto/list-approvals.dto.d.ts.map +1 -1
  35. package/dist/dto/list-approvals.dto.js +10 -0
  36. package/dist/dto/list-approvals.dto.js.map +1 -1
  37. package/dist/dto/list-collaborator-costs.dto.d.ts +5 -0
  38. package/dist/dto/list-collaborator-costs.dto.d.ts.map +1 -0
  39. package/dist/dto/list-collaborator-costs.dto.js +23 -0
  40. package/dist/dto/list-collaborator-costs.dto.js.map +1 -0
  41. package/dist/dto/list-cost-types.dto.d.ts +6 -0
  42. package/dist/dto/list-cost-types.dto.d.ts.map +1 -0
  43. package/dist/dto/list-cost-types.dto.js +35 -0
  44. package/dist/dto/list-cost-types.dto.js.map +1 -0
  45. package/dist/dto/list-my-projects.dto.d.ts +5 -0
  46. package/dist/dto/list-my-projects.dto.d.ts.map +1 -0
  47. package/dist/dto/list-my-projects.dto.js +23 -0
  48. package/dist/dto/list-my-projects.dto.js.map +1 -0
  49. package/dist/dto/list-my-tasks.dto.d.ts +6 -0
  50. package/dist/dto/list-my-tasks.dto.d.ts.map +1 -0
  51. package/dist/dto/list-my-tasks.dto.js +33 -0
  52. package/dist/dto/list-my-tasks.dto.js.map +1 -0
  53. package/dist/dto/list-projects.dto.d.ts +1 -0
  54. package/dist/dto/list-projects.dto.d.ts.map +1 -1
  55. package/dist/dto/list-projects.dto.js +7 -0
  56. package/dist/dto/list-projects.dto.js.map +1 -1
  57. package/dist/dto/list-reports.dto.d.ts +16 -0
  58. package/dist/dto/list-reports.dto.d.ts.map +1 -0
  59. package/dist/dto/list-reports.dto.js +75 -0
  60. package/dist/dto/list-reports.dto.js.map +1 -0
  61. package/dist/dto/list-tasks.dto.d.ts +2 -0
  62. package/dist/dto/list-tasks.dto.d.ts.map +1 -1
  63. package/dist/dto/list-tasks.dto.js +12 -0
  64. package/dist/dto/list-tasks.dto.js.map +1 -1
  65. package/dist/dto/list-timesheets.dto.d.ts +2 -0
  66. package/dist/dto/list-timesheets.dto.d.ts.map +1 -1
  67. package/dist/dto/list-timesheets.dto.js +10 -0
  68. package/dist/dto/list-timesheets.dto.js.map +1 -1
  69. package/dist/dto/update-collaborator-cost.dto.d.ts +6 -0
  70. package/dist/dto/update-collaborator-cost.dto.d.ts.map +1 -0
  71. package/dist/dto/update-collaborator-cost.dto.js +9 -0
  72. package/dist/dto/update-collaborator-cost.dto.js.map +1 -0
  73. package/dist/dto/update-task.dto.d.ts +1 -0
  74. package/dist/dto/update-task.dto.d.ts.map +1 -1
  75. package/dist/dto/update-task.dto.js +6 -0
  76. package/dist/dto/update-task.dto.js.map +1 -1
  77. package/dist/operations.module.d.ts.map +1 -1
  78. package/dist/operations.module.js +4 -0
  79. package/dist/operations.module.js.map +1 -1
  80. package/dist/operations.service.d.ts +457 -3
  81. package/dist/operations.service.d.ts.map +1 -1
  82. package/dist/operations.service.js +1445 -208
  83. package/dist/operations.service.js.map +1 -1
  84. package/dist/operations.service.spec.js +31 -7
  85. package/dist/operations.service.spec.js.map +1 -1
  86. package/hedhog/data/menu.yaml +112 -7
  87. package/hedhog/data/operations_cost_type.yaml +166 -0
  88. package/hedhog/data/route.yaml +185 -0
  89. package/hedhog/frontend/app/_components/collaborator-costs-section.tsx.ejs +884 -0
  90. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +94 -15
  91. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +219 -94
  92. package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +21 -32
  93. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +178 -89
  94. package/hedhog/frontend/app/_components/my-project-summary-screen.tsx.ejs +1185 -0
  95. package/hedhog/frontend/app/_components/operations-calendar-view.tsx.ejs +306 -0
  96. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +943 -782
  97. package/hedhog/frontend/app/_components/task-detail-sheet.tsx.ejs +223 -0
  98. package/hedhog/frontend/app/_lib/api.ts.ejs +162 -0
  99. package/hedhog/frontend/app/_lib/types.ts.ejs +229 -3
  100. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +11 -3
  101. package/hedhog/frontend/app/approvals/page.tsx.ejs +191 -46
  102. package/hedhog/frontend/app/collaborators/page.tsx.ejs +133 -25
  103. package/hedhog/frontend/app/my-projects/[id]/page.tsx.ejs +11 -0
  104. package/hedhog/frontend/app/my-projects/page.tsx.ejs +440 -0
  105. package/hedhog/frontend/app/my-tasks/page.tsx.ejs +1304 -0
  106. package/hedhog/frontend/app/reports/collaborators/page.tsx.ejs +771 -0
  107. package/hedhog/frontend/app/reports/projects/page.tsx.ejs +809 -0
  108. package/hedhog/frontend/app/timesheets/page.tsx.ejs +322 -58
  109. package/hedhog/frontend/messages/en.json +234 -25
  110. package/hedhog/frontend/messages/pt.json +234 -25
  111. package/hedhog/table/operations_collaborator.yaml +0 -4
  112. package/hedhog/table/operations_collaborator_compensation_history.yaml +28 -0
  113. package/hedhog/table/operations_collaborator_cost.yaml +56 -0
  114. package/hedhog/table/operations_cost_type.yaml +38 -0
  115. package/package.json +6 -6
  116. package/src/controllers/operations-collaborator-costs.controller.ts +147 -0
  117. package/src/controllers/operations-collaborators.controller.ts +19 -8
  118. package/src/controllers/operations-projects.controller.ts +19 -8
  119. package/src/controllers/operations-reports.controller.ts +32 -0
  120. package/src/controllers/operations-tasks.controller.ts +32 -12
  121. package/src/dto/create-collaborator-cost.dto.ts +78 -0
  122. package/src/dto/create-collaborator.dto.ts +9 -14
  123. package/src/dto/create-cost-type.dto.ts +62 -0
  124. package/src/dto/list-approvals.dto.ts +8 -0
  125. package/src/dto/list-collaborator-costs.dto.ts +8 -0
  126. package/src/dto/list-cost-types.dto.ts +19 -0
  127. package/src/dto/list-my-projects.dto.ts +8 -0
  128. package/src/dto/list-my-tasks.dto.ts +17 -0
  129. package/src/dto/list-projects.dto.ts +7 -1
  130. package/src/dto/list-reports.dto.ts +51 -0
  131. package/src/dto/list-tasks.dto.ts +11 -1
  132. package/src/dto/list-timesheets.dto.ts +8 -0
  133. package/src/dto/update-collaborator-cost.dto.ts +4 -0
  134. package/src/dto/update-task.dto.ts +6 -0
  135. package/src/operations.module.ts +7 -3
  136. package/src/operations.service.spec.ts +45 -7
  137. package/src/operations.service.ts +1992 -225
@@ -0,0 +1,223 @@
1
+ 'use client';
2
+
3
+ import type { ReactNode } from 'react';
4
+ import {
5
+ Sheet,
6
+ SheetContent,
7
+ SheetHeader,
8
+ SheetTitle,
9
+ } from '@/components/ui/sheet';
10
+ import { useApp } from '@hed-hog/next-app-provider';
11
+ import { AlarmClock, Calendar, Tag, User } from 'lucide-react';
12
+ import { useTranslations } from 'next-intl';
13
+ import { formatDate, getStatusBadgeClass } from '../_lib/utils/format';
14
+ import { StatusBadge } from './status-badge';
15
+
16
+ export type TaskDetailSheetData = {
17
+ id: number;
18
+ name: string;
19
+ description?: string | null;
20
+ status: string;
21
+ priority?: string | null;
22
+ dueDate?: string | null;
23
+ estimateHours?: number | null;
24
+ tags?: string | null;
25
+ projectName?: string | null;
26
+ projectCode?: string | null;
27
+ assigneeName?: string | null;
28
+ assigneeUserPhotoId?: number | null;
29
+ assigneePersonAvatarId?: number | null;
30
+ };
31
+
32
+ type Props = {
33
+ task: TaskDetailSheetData | null;
34
+ open: boolean;
35
+ onOpenChange: (open: boolean) => void;
36
+ statusLabel?: (status: string) => string;
37
+ footer?: ReactNode;
38
+ };
39
+
40
+ function getPriorityLabel(value?: string | null) {
41
+ const labels: Record<string, string> = {
42
+ low: 'Baixa',
43
+ medium: 'Média',
44
+ high: 'Alta',
45
+ };
46
+ return labels[value ?? ''] ?? value ?? '—';
47
+ }
48
+
49
+ function getPriorityClassName(value?: string | null) {
50
+ if (value === 'high') return 'bg-rose-100 text-rose-700 border-rose-200';
51
+ if (value === 'medium') return 'bg-amber-100 text-amber-700 border-amber-200';
52
+ return 'bg-emerald-100 text-emerald-700 border-emerald-200';
53
+ }
54
+
55
+ function getAvatarSrc(task: TaskDetailSheetData | null) {
56
+ if (!task) return null;
57
+ if (
58
+ typeof task.assigneeUserPhotoId === 'number' &&
59
+ task.assigneeUserPhotoId > 0
60
+ )
61
+ return `${process.env.NEXT_PUBLIC_API_BASE_URL}/file/open/${task.assigneeUserPhotoId}`;
62
+ if (
63
+ typeof task.assigneePersonAvatarId === 'number' &&
64
+ task.assigneePersonAvatarId > 0
65
+ )
66
+ return `${process.env.NEXT_PUBLIC_API_BASE_URL}/person/avatar/${task.assigneePersonAvatarId}`;
67
+ return null;
68
+ }
69
+
70
+ function getInitials(value?: string | null) {
71
+ const parts = String(value ?? '')
72
+ .trim()
73
+ .split(/\s+/)
74
+ .filter(Boolean)
75
+ .slice(0, 2);
76
+ if (!parts.length) return '??';
77
+ return parts.map((p) => p[0]?.toUpperCase() ?? '').join('');
78
+ }
79
+
80
+ export function TaskDetailSheet({
81
+ task,
82
+ open,
83
+ onOpenChange,
84
+ statusLabel,
85
+ footer,
86
+ }: Props) {
87
+ const commonT = useTranslations('operations.Common');
88
+ const detailT = useTranslations('operations.ProjectDetailsPage');
89
+ const { getSettingValue, currentLocaleCode } = useApp();
90
+
91
+ const avatarSrc = getAvatarSrc(task);
92
+
93
+ return (
94
+ <Sheet open={open} onOpenChange={onOpenChange}>
95
+ <SheetContent className="flex w-full flex-col gap-0 overflow-y-auto sm:max-w-md">
96
+ {task ? (
97
+ <>
98
+ <SheetHeader className="pb-2">
99
+ <SheetTitle className="pr-6 text-base font-semibold leading-snug">
100
+ {task.name}
101
+ </SheetTitle>
102
+ <div className="flex flex-wrap gap-2 pt-1">
103
+ <StatusBadge
104
+ label={statusLabel?.(task.status) ?? task.status}
105
+ className={getStatusBadgeClass(task.status)}
106
+ />
107
+ {task.priority ? (
108
+ <span
109
+ className={`inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold ${getPriorityClassName(task.priority)}`}
110
+ >
111
+ {getPriorityLabel(task.priority)}
112
+ </span>
113
+ ) : null}
114
+ </div>
115
+ </SheetHeader>
116
+
117
+ <div className="flex flex-col gap-5 p-4 pt-3">
118
+ {task.description ? (
119
+ <div>
120
+ <p className="mb-1 text-xs font-medium uppercase tracking-wide text-muted-foreground">
121
+ {detailT('taskForm.descriptionLabel')}
122
+ </p>
123
+ <p className="text-sm leading-relaxed">{task.description}</p>
124
+ </div>
125
+ ) : null}
126
+
127
+ {task.dueDate || task.estimateHours != null ? (
128
+ <div className="grid grid-cols-2 gap-3">
129
+ {task.dueDate ? (
130
+ <div className="flex items-start gap-2 text-sm">
131
+ <Calendar className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
132
+ <div>
133
+ <p className="text-[11px] text-muted-foreground">
134
+ {detailT('taskForm.deadlineLabel')}
135
+ </p>
136
+ <p className="font-medium">
137
+ {formatDate(
138
+ task.dueDate,
139
+ getSettingValue,
140
+ currentLocaleCode
141
+ )}
142
+ </p>
143
+ </div>
144
+ </div>
145
+ ) : null}
146
+ {task.estimateHours != null ? (
147
+ <div className="flex items-start gap-2 text-sm">
148
+ <AlarmClock className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
149
+ <div>
150
+ <p className="text-[11px] text-muted-foreground">
151
+ {detailT('taskForm.estimateLabel')}
152
+ </p>
153
+ <p className="font-medium">{task.estimateHours}h</p>
154
+ </div>
155
+ </div>
156
+ ) : null}
157
+ </div>
158
+ ) : null}
159
+
160
+ {task.tags ? (
161
+ <div>
162
+ <div className="mb-1.5 flex items-center gap-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
163
+ <Tag className="size-3" />
164
+ {detailT('taskForm.tagsLabel')}
165
+ </div>
166
+ <div className="flex flex-wrap gap-1.5">
167
+ {task.tags.split(',').map((tag) => (
168
+ <span
169
+ key={tag.trim()}
170
+ className="rounded-md bg-muted px-2 py-0.5 text-xs text-muted-foreground"
171
+ >
172
+ {tag.trim()}
173
+ </span>
174
+ ))}
175
+ </div>
176
+ </div>
177
+ ) : null}
178
+
179
+ {task.assigneeName ? (
180
+ <div>
181
+ <div className="mb-1.5 flex items-center gap-1.5 text-xs font-medium uppercase tracking-wide text-muted-foreground">
182
+ <User className="size-3" />
183
+ {commonT('labels.collaborator')}
184
+ </div>
185
+ <div className="flex items-center gap-2.5">
186
+ <div className="flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted text-xs font-semibold uppercase text-muted-foreground ring-1 ring-border">
187
+ {avatarSrc ? (
188
+ // eslint-disable-next-line @next/next/no-img-element
189
+ <img
190
+ src={avatarSrc}
191
+ alt={task.assigneeName}
192
+ className="size-full object-cover"
193
+ />
194
+ ) : (
195
+ getInitials(task.assigneeName)
196
+ )}
197
+ </div>
198
+ <span className="text-sm">{task.assigneeName}</span>
199
+ </div>
200
+ </div>
201
+ ) : null}
202
+
203
+ {task.projectName || task.projectCode ? (
204
+ <div className="rounded-lg border bg-muted/20 px-3 py-2.5">
205
+ <p className="text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
206
+ {commonT('labels.project')}
207
+ </p>
208
+ <p className="mt-0.5 text-sm font-medium">
209
+ {[task.projectName, task.projectCode]
210
+ .filter(Boolean)
211
+ .join(' • ')}
212
+ </p>
213
+ </div>
214
+ ) : null}
215
+
216
+ {footer ? <div className="border-t pt-5">{footer}</div> : null}
217
+ </div>
218
+ </>
219
+ ) : null}
220
+ </SheetContent>
221
+ </Sheet>
222
+ );
223
+ }
@@ -56,3 +56,165 @@ export async function mutateOperations<T>(
56
56
 
57
57
  return response.data as T;
58
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
+ // ─── Collaborator Costs ───────────────────────────────────────────────────────
128
+
129
+ export type CollaboratorCost = {
130
+ id: number;
131
+ collaboratorId: number;
132
+ costTypeId: number;
133
+ costTypeName: string;
134
+ costTypeSlug: string;
135
+ amount: string;
136
+ currency: string;
137
+ recurrence: 'one_time' | 'monthly' | 'yearly';
138
+ allocatable?: boolean;
139
+ /** ISO date string — mapped to startDate going forward */
140
+ referenceDate: string | null;
141
+ startDate?: string | null;
142
+ endDate?: string | null;
143
+ depreciationMonths?: number | null;
144
+ description: string | null;
145
+ notes?: string | null;
146
+ createdAt: string;
147
+ };
148
+
149
+ export function fetchCollaboratorCosts(
150
+ request: RequestFn,
151
+ collaboratorId: number
152
+ ) {
153
+ return fetchOperations<CollaboratorCost[]>(
154
+ request,
155
+ `/operations/collaborators/${collaboratorId}/costs`
156
+ );
157
+ }
158
+
159
+ export function createCollaboratorCost(
160
+ request: RequestFn,
161
+ collaboratorId: number,
162
+ data: {
163
+ costTypeId: number;
164
+ amount: number;
165
+ currency?: string;
166
+ recurrence?: string;
167
+ allocatable?: boolean;
168
+ referenceDate?: string | null;
169
+ startDate?: string | null;
170
+ endDate?: string | null;
171
+ depreciationMonths?: number | null;
172
+ description?: string | null;
173
+ notes?: string | null;
174
+ }
175
+ ) {
176
+ return mutateOperations<CollaboratorCost>(
177
+ request,
178
+ `/operations/collaborators/${collaboratorId}/costs`,
179
+ 'POST',
180
+ data
181
+ );
182
+ }
183
+
184
+ export function updateCollaboratorCost(
185
+ request: RequestFn,
186
+ collaboratorId: number,
187
+ costId: number,
188
+ data: Partial<{
189
+ costTypeId: number;
190
+ amount: number;
191
+ currency: string;
192
+ recurrence: string;
193
+ allocatable: boolean;
194
+ referenceDate: string | null;
195
+ startDate: string | null;
196
+ endDate: string | null;
197
+ depreciationMonths: number | null;
198
+ description: string | null;
199
+ notes: string | null;
200
+ }>
201
+ ) {
202
+ return mutateOperations<CollaboratorCost>(
203
+ request,
204
+ `/operations/collaborators/${collaboratorId}/costs/${costId}`,
205
+ 'PATCH',
206
+ data
207
+ );
208
+ }
209
+
210
+ export function deleteCollaboratorCost(
211
+ request: RequestFn,
212
+ collaboratorId: number,
213
+ costId: number
214
+ ) {
215
+ return mutateOperations<{ success: boolean }>(
216
+ request,
217
+ `/operations/collaborators/${collaboratorId}/costs/${costId}`,
218
+ 'DELETE'
219
+ );
220
+ }
@@ -8,7 +8,7 @@ export type PaginatedResponse<T> = {
8
8
  data: T[];
9
9
  };
10
10
 
11
- export type OperationsDashboard = {
11
+ export type OperationsDashboard = {
12
12
  actor: {
13
13
  roleScope: 'self' | 'team' | 'full';
14
14
  collaboratorId: number | null;
@@ -32,7 +32,183 @@ export type OperationsDashboard = {
32
32
  totalHours: number | null;
33
33
  status: string;
34
34
  }>;
35
- };
35
+ };
36
+
37
+ export type OperationsReportScenario = 'base' | 'growth' | 'conservative';
38
+
39
+ export type OperationsReportPlanningCard = {
40
+ title: string;
41
+ value: string;
42
+ description: string;
43
+ };
44
+
45
+ export type OperationsProjectsReport = {
46
+ filters: {
47
+ from: string;
48
+ to: string;
49
+ status: string;
50
+ client: string;
51
+ scenario: OperationsReportScenario;
52
+ clients: string[];
53
+ };
54
+ summary: {
55
+ contractedRevenue: number;
56
+ recognizedRevenue: number;
57
+ realizedCost: number;
58
+ forecastCost: number;
59
+ profit: number;
60
+ margin: number;
61
+ plannedHours: number;
62
+ actualHours: number;
63
+ billableHours: number;
64
+ reworkHours: number;
65
+ backlogValue: number;
66
+ avgDeadline: number;
67
+ avgAllocation: number;
68
+ atRisk: number;
69
+ burnRate: number;
70
+ };
71
+ forecast: Array<{
72
+ month: string;
73
+ revenue: number;
74
+ cost: number;
75
+ profit: number;
76
+ backlog: number;
77
+ planned: number;
78
+ actual: number;
79
+ }>;
80
+ costComposition: Array<{ name: string; value: number }>;
81
+ hoursByProject: Array<{
82
+ project: string;
83
+ Faturável: number;
84
+ Interno: number;
85
+ Retrabalho: number;
86
+ Livre: number;
87
+ }>;
88
+ health: Array<{
89
+ project: string;
90
+ margem: number;
91
+ prazo: number;
92
+ alocacao: number;
93
+ saude: number;
94
+ }>;
95
+ ranking: Array<{ name: string; Lucro: number; Custo: number }>;
96
+ progress: Array<{ month: string; Planejado: number; Realizado: number }>;
97
+ planningCards: OperationsReportPlanningCard[];
98
+ rows: Array<{
99
+ id: number;
100
+ name: string;
101
+ client: string;
102
+ manager: string;
103
+ squad: string;
104
+ status: 'on_track' | 'attention' | 'late' | 'paused';
105
+ contractType: 'fixed_price' | 'time_materials' | 'retainer';
106
+ priority: string;
107
+ startDate: string | null;
108
+ endDate: string | null;
109
+ contractedRevenue: number;
110
+ recognizedRevenue: number;
111
+ realizedCost: number;
112
+ forecastCost: number;
113
+ teamCost: number;
114
+ infraCost: number;
115
+ licenseCost: number;
116
+ thirdPartyCost: number;
117
+ reworkCost: number;
118
+ plannedHours: number;
119
+ actualHours: number;
120
+ billableHours: number;
121
+ reworkHours: number;
122
+ internalHours: number;
123
+ allocatedCapacity: number;
124
+ physicalProgress: number;
125
+ financialProgress: number;
126
+ backlogValue: number;
127
+ futureDeliveries: number;
128
+ risk: 'baixo' | 'médio' | 'alto';
129
+ recommendation: string;
130
+ }>;
131
+ };
132
+
133
+ export type OperationsCollaboratorsReport = {
134
+ filters: {
135
+ from: string;
136
+ to: string;
137
+ department: string;
138
+ contractType: string;
139
+ scenario: OperationsReportScenario;
140
+ departments: string[];
141
+ contractTypes: string[];
142
+ };
143
+ summary: {
144
+ cost: number;
145
+ salary: number;
146
+ benefits: number;
147
+ taxes: number;
148
+ tools: number;
149
+ billableValue: number;
150
+ profit: number;
151
+ margin: number;
152
+ availableHours: number;
153
+ allocatedHours: number;
154
+ billableHours: number;
155
+ internalHours: number;
156
+ overtimeHours: number;
157
+ freeHours: number;
158
+ allocation: number;
159
+ utilization: number;
160
+ overloadCount: number;
161
+ hourlyCost: number;
162
+ };
163
+ forecast: Array<{
164
+ month: string;
165
+ revenue: number;
166
+ cost: number;
167
+ profit: number;
168
+ margin: number;
169
+ capacity: number;
170
+ }>;
171
+ costComposition: Array<{ name: string; value: number }>;
172
+ capacityByDepartment: Array<{
173
+ department: string;
174
+ Faturável: number;
175
+ Interno: number;
176
+ Livre: number;
177
+ Sobrecarga: number;
178
+ }>;
179
+ health: Array<{
180
+ department: string;
181
+ margem: number;
182
+ alocacao: number;
183
+ utilizacao: number;
184
+ saude: number;
185
+ }>;
186
+ ranking: Array<{ name: string; Custo: number; Lucro: number }>;
187
+ planningCards: OperationsReportPlanningCard[];
188
+ rows: Array<{
189
+ id: number;
190
+ name: string;
191
+ role: string;
192
+ seniority: string;
193
+ department: string;
194
+ contractType: string;
195
+ startDate: string | null;
196
+ endDate: string | null;
197
+ salaryCost: number;
198
+ benefitsCost: number;
199
+ taxesCost: number;
200
+ toolsCost: number;
201
+ billableValue: number;
202
+ availableHours: number;
203
+ allocatedHours: number;
204
+ billableHours: number;
205
+ internalHours: number;
206
+ overtimeHours: number;
207
+ projects: number;
208
+ risk: 'baixo' | 'médio' | 'alto';
209
+ recommendation: string;
210
+ }>;
211
+ };
36
212
 
37
213
  export type OperationsCollaborator = {
38
214
  id: number;
@@ -72,6 +248,10 @@ export type OperationsCollaboratorStats = {
72
248
  active: number;
73
249
  onLeave: number;
74
250
  withContracts: number;
251
+ totalSalary: number;
252
+ totalCosts: number;
253
+ avgSalaryPlusCosts: number;
254
+ avgSalaryPlusCostsPerCollaborator: number;
75
255
  };
76
256
 
77
257
  export type OperationsContractStats = {
@@ -150,11 +330,19 @@ export type OperationsTaskOption = {
150
330
  name: string;
151
331
  description?: string | null;
152
332
  status: string;
333
+ priority?: string | null;
153
334
  projectId: number;
154
- projectAssignmentId: number;
335
+ projectAssignmentId: number | null;
155
336
  projectName: string;
156
337
  projectCode?: string | null;
338
+ dueDate?: string | null;
339
+ estimateHours?: number | null;
340
+ tags?: string | null;
341
+ assigneeName?: string | null;
342
+ assigneeUserPhotoId?: number | null;
343
+ assigneePersonAvatarId?: number | null;
157
344
  createdAt?: string | null;
345
+ deletedAt?: string | null;
158
346
  };
159
347
 
160
348
  export type OperationsProject = {
@@ -410,6 +598,44 @@ export type OperationsCollaboratorDetails = OperationsCollaborator & {
410
598
  }>;
411
599
  };
412
600
 
601
+ export type OperationsMyProjectSummary = {
602
+ id: number;
603
+ code: string;
604
+ name: string;
605
+ status: string;
606
+ summary: string | null;
607
+ startDate: string | null;
608
+ endDate: string | null;
609
+ myAssignmentId: number | null;
610
+ myRoleLabel: string | null;
611
+ assignments: Array<{
612
+ id: number;
613
+ collaboratorId: number;
614
+ collaboratorName: string;
615
+ roleLabel: string | null;
616
+ status: string;
617
+ avatarId: number | null;
618
+ userPhotoId: number | null;
619
+ }>;
620
+ tasks: Array<{
621
+ id: number;
622
+ name: string;
623
+ description: string | null;
624
+ priority: string;
625
+ status: string;
626
+ dueDate: string | null;
627
+ estimateHours: number | null;
628
+ position: number;
629
+ tags: string | null;
630
+ assigneeCollaboratorId: number | null;
631
+ assigneeName: string | null;
632
+ assigneeUserPhotoId: number | null;
633
+ assigneePersonAvatarId: number | null;
634
+ projectAssignmentId: number | null;
635
+ createdAt: string;
636
+ }>;
637
+ };
638
+
413
639
  export type OperationsProjectDetails = OperationsProject & {
414
640
  assignments: Array<{
415
641
  id: number;
@@ -22,6 +22,16 @@ function resolveLocale(currentLocaleCode: string) {
22
22
  return currentLocaleCode.startsWith('pt') ? 'pt-BR' : 'en-US';
23
23
  }
24
24
 
25
+ function parseDateValue(value: string) {
26
+ const dateMatch = String(value).match(/(\d{4}-\d{2}-\d{2})/);
27
+
28
+ if (dateMatch?.[1]) {
29
+ return new Date(`${dateMatch[1]}T12:00:00`);
30
+ }
31
+
32
+ return new Date(value);
33
+ }
34
+
25
35
  export function formatDate(
26
36
  value?: string | null,
27
37
  getSettingValue?: ((key: string) => unknown) | null,
@@ -31,9 +41,7 @@ export function formatDate(
31
41
  return '-';
32
42
  }
33
43
 
34
- const parsedDate = new Date(
35
- value.includes('T') ? value : `${value}T00:00:00`
36
- );
44
+ const parsedDate = parseDateValue(value);
37
45
 
38
46
  if (Number.isNaN(parsedDate.getTime())) {
39
47
  return '-';