@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,291 @@
1
+ 'use client';
2
+
3
+ import { EmptyState, Page } from '@/components/entity-list';
4
+ import { Button } from '@/components/ui/button';
5
+ import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
6
+ import {
7
+ Table,
8
+ TableBody,
9
+ TableCell,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ } from '@/components/ui/table';
14
+ import { useApp, useQuery } from '@hed-hog/next-app-provider';
15
+ import { FileText, FolderKanban, Pencil } from 'lucide-react';
16
+ import Link from 'next/link';
17
+ import { useTranslations } from 'next-intl';
18
+ import { OperationsHeader } from './operations-header';
19
+ import { SectionCard } from './section-card';
20
+ import { StatusBadge } from './status-badge';
21
+ import { fetchOperations } from '../_lib/api';
22
+ import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
23
+ import type { OperationsProjectDetails } from '../_lib/types';
24
+ import {
25
+ formatCurrency,
26
+ formatDate,
27
+ formatDateRange,
28
+ formatEnumLabel,
29
+ formatHours,
30
+ formatPercent,
31
+ getStatusBadgeClass,
32
+ } from '../_lib/utils/format';
33
+
34
+ export function ProjectDetailsScreen({ projectId }: { projectId: number }) {
35
+ const t = useTranslations('operations.ProjectDetailsPage');
36
+ const commonT = useTranslations('operations.Common');
37
+ const { request, currentLocaleCode } = useApp();
38
+ const access = useOperationsAccess();
39
+
40
+ const { data: project, refetch } = useQuery<OperationsProjectDetails>({
41
+ queryKey: ['operations-project-details', currentLocaleCode, projectId],
42
+ queryFn: () =>
43
+ fetchOperations<OperationsProjectDetails>(request, `/operations/projects/${projectId}`),
44
+ });
45
+
46
+ if (!project) {
47
+ return (
48
+ <Page>
49
+ <OperationsHeader
50
+ title={t('title')}
51
+ description={t('description')}
52
+ current={t('breadcrumb')}
53
+ />
54
+ <EmptyState
55
+ icon={<FolderKanban className="size-12" />}
56
+ title={commonT('states.emptyTitle')}
57
+ description={t('notFound')}
58
+ actionLabel={commonT('actions.refresh')}
59
+ onAction={() => void refetch()}
60
+ />
61
+ </Page>
62
+ );
63
+ }
64
+
65
+ const cards = [
66
+ {
67
+ key: 'teamSize',
68
+ title: t('cards.teamSize'),
69
+ value: project.teamSize ?? 0,
70
+ description: t('cards.teamSizeDescription'),
71
+ },
72
+ {
73
+ key: 'timesheets',
74
+ title: t('cards.timesheets'),
75
+ value: project.timesheetSummary.totalTimesheets,
76
+ description: t('cards.timesheetsDescription', {
77
+ pending: project.timesheetSummary.pendingTimesheets,
78
+ }),
79
+ },
80
+ {
81
+ key: 'hours',
82
+ title: t('cards.loggedHours'),
83
+ value: formatHours(project.timesheetSummary.totalHours),
84
+ description: t('cards.loggedHoursDescription'),
85
+ },
86
+ {
87
+ key: 'allocation',
88
+ title: t('cards.allocation'),
89
+ value: formatPercent(project.operationalIndicators.averageAllocation),
90
+ description: t('cards.allocationDescription'),
91
+ },
92
+ ];
93
+
94
+ return (
95
+ <Page>
96
+ <OperationsHeader
97
+ title={project.name}
98
+ description={t('description')}
99
+ current={t('breadcrumb')}
100
+ actions={
101
+ access.isDirector ? (
102
+ <div className="flex gap-2">
103
+ {project.contractId ? (
104
+ <Button variant="outline" size="sm" asChild>
105
+ <Link href={`/operations/contracts?edit=${project.contractId}`}>
106
+ <FileText className="size-4" />
107
+ {commonT('actions.openContract')}
108
+ </Link>
109
+ </Button>
110
+ ) : null}
111
+ <Button size="sm" asChild>
112
+ <Link href={`/operations/projects/${project.id}/edit`}>
113
+ <Pencil className="size-4" />
114
+ {commonT('actions.edit')}
115
+ </Link>
116
+ </Button>
117
+ </div>
118
+ ) : undefined
119
+ }
120
+ />
121
+
122
+ <div className="grid gap-4 xl:grid-cols-4">
123
+ <SectionCard title={t('sections.overview')} className="xl:col-span-2">
124
+ <dl className="grid gap-3 text-sm md:grid-cols-2">
125
+ <div>
126
+ <dt className="text-muted-foreground">{commonT('labels.project')}</dt>
127
+ <dd className="font-medium">{project.name}</dd>
128
+ </div>
129
+ <div>
130
+ <dt className="text-muted-foreground">{commonT('labels.client')}</dt>
131
+ <dd className="font-medium">
132
+ {project.clientName || commonT('labels.notAvailable')}
133
+ </dd>
134
+ </div>
135
+ <div>
136
+ <dt className="text-muted-foreground">{commonT('labels.manager')}</dt>
137
+ <dd className="font-medium">
138
+ {project.managerName || commonT('labels.notAssigned')}
139
+ </dd>
140
+ </div>
141
+ <div>
142
+ <dt className="text-muted-foreground">{commonT('labels.status')}</dt>
143
+ <dd className="font-medium">
144
+ <StatusBadge
145
+ label={formatEnumLabel(project.status)}
146
+ className={getStatusBadgeClass(project.status)}
147
+ />
148
+ </dd>
149
+ </div>
150
+ <div>
151
+ <dt className="text-muted-foreground">{commonT('labels.startDate')}</dt>
152
+ <dd className="font-medium">{formatDate(project.startDate)}</dd>
153
+ </div>
154
+ <div>
155
+ <dt className="text-muted-foreground">{commonT('labels.endDate')}</dt>
156
+ <dd className="font-medium">{formatDate(project.endDate)}</dd>
157
+ </div>
158
+ <div>
159
+ <dt className="text-muted-foreground">{commonT('labels.budget')}</dt>
160
+ <dd className="font-medium">
161
+ {project.budgetAmount
162
+ ? formatCurrency(project.budgetAmount)
163
+ : commonT('labels.notAvailable')}
164
+ </dd>
165
+ </div>
166
+ <div>
167
+ <dt className="text-muted-foreground">{commonT('labels.progress')}</dt>
168
+ <dd className="font-medium">{formatPercent(project.progressPercent)}</dd>
169
+ </div>
170
+ </dl>
171
+ {project.summary ? (
172
+ <p className="mt-4 text-sm text-muted-foreground">{project.summary}</p>
173
+ ) : null}
174
+ </SectionCard>
175
+
176
+ <SectionCard title={t('sections.contract')} className="xl:col-span-2">
177
+ {project.relatedContract ? (
178
+ <div className="space-y-3">
179
+ <div className="flex items-center justify-between rounded-lg border px-4 py-3">
180
+ <div>
181
+ <div className="font-medium">{project.relatedContract.name}</div>
182
+ <div className="text-sm text-muted-foreground">
183
+ {project.relatedContract.code} •{' '}
184
+ {formatEnumLabel(project.relatedContract.contractCategory)}
185
+ </div>
186
+ </div>
187
+ <StatusBadge
188
+ label={formatEnumLabel(project.relatedContract.status)}
189
+ className={getStatusBadgeClass(project.relatedContract.status)}
190
+ />
191
+ </div>
192
+ <dl className="grid gap-3 text-sm md:grid-cols-2">
193
+ <div>
194
+ <dt className="text-muted-foreground">{commonT('labels.billingModel')}</dt>
195
+ <dd className="font-medium">
196
+ {formatEnumLabel(project.relatedContract.billingModel)}
197
+ </dd>
198
+ </div>
199
+ <div>
200
+ <dt className="text-muted-foreground">{commonT('labels.timeline')}</dt>
201
+ <dd className="font-medium">
202
+ {formatDateRange(
203
+ project.relatedContract.startDate,
204
+ project.relatedContract.endDate
205
+ )}
206
+ </dd>
207
+ </div>
208
+ </dl>
209
+ </div>
210
+ ) : (
211
+ <p className="text-sm text-muted-foreground">{t('noContract')}</p>
212
+ )}
213
+ </SectionCard>
214
+ </div>
215
+
216
+ <KpiCardsGrid items={cards} />
217
+
218
+ <div className="grid gap-4 xl:grid-cols-2">
219
+ <SectionCard title={t('sections.team')} description={t('sections.teamDescription')}>
220
+ {project.assignments.length > 0 ? (
221
+ <div className="overflow-x-auto rounded-md border">
222
+ <Table>
223
+ <TableHeader>
224
+ <TableRow>
225
+ <TableHead>{commonT('labels.collaborator')}</TableHead>
226
+ <TableHead>{commonT('labels.role')}</TableHead>
227
+ <TableHead>{commonT('labels.weeklyCapacity')}</TableHead>
228
+ <TableHead>{commonT('labels.status')}</TableHead>
229
+ </TableRow>
230
+ </TableHeader>
231
+ <TableBody>
232
+ {project.assignments.map((assignment) => (
233
+ <TableRow key={assignment.id}>
234
+ <TableCell>{assignment.collaboratorName}</TableCell>
235
+ <TableCell>
236
+ {assignment.roleLabel || commonT('labels.notAssigned')}
237
+ </TableCell>
238
+ <TableCell>
239
+ {assignment.weeklyHours
240
+ ? formatHours(assignment.weeklyHours)
241
+ : commonT('labels.notAvailable')}
242
+ </TableCell>
243
+ <TableCell>
244
+ <StatusBadge
245
+ label={formatEnumLabel(assignment.status)}
246
+ className={getStatusBadgeClass(assignment.status)}
247
+ />
248
+ </TableCell>
249
+ </TableRow>
250
+ ))}
251
+ </TableBody>
252
+ </Table>
253
+ </div>
254
+ ) : (
255
+ <p className="text-sm text-muted-foreground">{t('noAssignments')}</p>
256
+ )}
257
+ </SectionCard>
258
+
259
+ <SectionCard
260
+ title={t('sections.indicators')}
261
+ description={t('sections.indicatorsDescription')}
262
+ >
263
+ <dl className="grid gap-3 text-sm md:grid-cols-2">
264
+ <div>
265
+ <dt className="text-muted-foreground">{t('indicators.activeAssignments')}</dt>
266
+ <dd className="font-medium">{project.operationalIndicators.activeAssignments}</dd>
267
+ </div>
268
+ <div>
269
+ <dt className="text-muted-foreground">{t('indicators.billableAssignments')}</dt>
270
+ <dd className="font-medium">
271
+ {project.operationalIndicators.billableAssignments}
272
+ </dd>
273
+ </div>
274
+ <div>
275
+ <dt className="text-muted-foreground">{t('indicators.averageAllocation')}</dt>
276
+ <dd className="font-medium">
277
+ {formatPercent(project.operationalIndicators.averageAllocation)}
278
+ </dd>
279
+ </div>
280
+ <div>
281
+ <dt className="text-muted-foreground">{t('indicators.totalWeeklyHours')}</dt>
282
+ <dd className="font-medium">
283
+ {formatHours(project.operationalIndicators.totalWeeklyHours)}
284
+ </dd>
285
+ </div>
286
+ </dl>
287
+ </SectionCard>
288
+ </div>
289
+ </Page>
290
+ );
291
+ }