@hed-hog/operations 0.0.294 → 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 +7 -6
  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
@@ -1,278 +0,0 @@
1
- 'use client';
2
-
3
- import { Page, PaginationFooter, SearchBar } from '@/components/entity-list';
4
- import { Button } from '@/components/ui/button';
5
- import { Progress } from '@/components/ui/progress';
6
- import {
7
- Table,
8
- TableBody,
9
- TableCell,
10
- TableHead,
11
- TableHeader,
12
- TableRow,
13
- } from '@/components/ui/table';
14
- import { FilePlus2 } from 'lucide-react';
15
- import { useTranslations } from 'next-intl';
16
- import { useMemo, useState } from 'react';
17
- import { OperationsHeader } from '../_components/operations-header';
18
- import { SectionCard } from '../_components/section-card';
19
- import { StatusBadge } from '../_components/status-badge';
20
- import { useOperationsGrowthData } from '../_lib/hooks/use-operations-growth-data';
21
- import { formatDate } from '../_lib/utils/format';
22
- import {
23
- getGrowthEvaluationBadgeClasses,
24
- humanizeGrowthStatus,
25
- } from '../_lib/utils/growth';
26
-
27
- const PAGE_SIZE_OPTIONS = [4, 8, 12];
28
- const DEFAULT_PAGE_SIZE = PAGE_SIZE_OPTIONS[0] ?? 4;
29
-
30
- export default function OperationsEvaluationsPage() {
31
- const t = useTranslations('operations.EvaluationsPage');
32
- const { evaluations, users, projects, tasks, evaluationCycles, performanceCriteria } =
33
- useOperationsGrowthData();
34
- const [searchInput, setSearchInput] = useState('');
35
- const [search, setSearch] = useState('');
36
- const [employeeFilter, setEmployeeFilter] = useState('all');
37
- const [cycleFilter, setCycleFilter] = useState('all');
38
- const [projectFilter, setProjectFilter] = useState('all');
39
- const [currentPage, setCurrentPage] = useState(1);
40
- const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
41
- const [selectedEvaluationId, setSelectedEvaluationId] = useState(evaluations[0]?.id ?? '');
42
-
43
- const filteredEvaluations = useMemo(
44
- () =>
45
- evaluations.filter((evaluation) => {
46
- const employee = users.find((item) => item.id === evaluation.employeeId);
47
- const evaluator = users.find((item) => item.id === evaluation.evaluatorId);
48
- const project = projects.find((item) => item.id === evaluation.projectId);
49
- const cycle = evaluationCycles.find((item) => item.id === evaluation.cycleId);
50
- const haystack = `${employee?.name ?? ''} ${evaluator?.name ?? ''} ${project?.name ?? ''} ${cycle?.name ?? ''}`;
51
-
52
- const matchesSearch = haystack.toLowerCase().includes(search.toLowerCase());
53
- const matchesEmployee =
54
- employeeFilter === 'all' || evaluation.employeeId === employeeFilter;
55
- const matchesCycle = cycleFilter === 'all' || evaluation.cycleId === cycleFilter;
56
- const matchesProject =
57
- projectFilter === 'all' || evaluation.projectId === projectFilter;
58
-
59
- return matchesSearch && matchesEmployee && matchesCycle && matchesProject;
60
- }),
61
- [cycleFilter, employeeFilter, evaluationCycles, evaluations, projectFilter, projects, search, users]
62
- );
63
-
64
- const totalPages = Math.max(1, Math.ceil(filteredEvaluations.length / pageSize));
65
- const safePage = Math.min(Math.max(currentPage, 1), totalPages);
66
- const paginatedEvaluations = filteredEvaluations.slice(
67
- (safePage - 1) * pageSize,
68
- safePage * pageSize
69
- );
70
- const selectedEvaluation =
71
- filteredEvaluations.find((item) => item.id === selectedEvaluationId) ??
72
- paginatedEvaluations[0] ??
73
- null;
74
-
75
- const selectedEmployee = users.find(
76
- (item) => item.id === selectedEvaluation?.employeeId
77
- );
78
- const selectedEvaluator = users.find(
79
- (item) => item.id === selectedEvaluation?.evaluatorId
80
- );
81
- const selectedProject = projects.find(
82
- (item) => item.id === selectedEvaluation?.projectId
83
- );
84
- const selectedTask = tasks.find((item) => item.id === selectedEvaluation?.taskId);
85
-
86
- return (
87
- <Page>
88
- <OperationsHeader
89
- title={t('title')}
90
- description={t('description')}
91
- current={t('breadcrumb')}
92
- actions={
93
- <Button className="gap-2" size="sm">
94
- <FilePlus2 className="h-4 w-4" />
95
- {t('newAction')}
96
- </Button>
97
- }
98
- />
99
-
100
- <SearchBar
101
- className="mb-6"
102
- searchQuery={searchInput}
103
- onSearchChange={setSearchInput}
104
- onSearch={() => {
105
- setSearch(searchInput);
106
- setCurrentPage(1);
107
- }}
108
- placeholder={t('searchPlaceholder')}
109
- controls={[
110
- {
111
- id: 'employee-filter',
112
- type: 'select',
113
- value: employeeFilter,
114
- onChange: (value) => {
115
- setEmployeeFilter(value);
116
- setCurrentPage(1);
117
- },
118
- placeholder: t('filters.allEmployees'),
119
- options: [
120
- { value: 'all', label: t('filters.allEmployees') },
121
- ...users.map((item) => ({ value: item.id, label: item.name })),
122
- ],
123
- },
124
- {
125
- id: 'cycle-filter',
126
- type: 'select',
127
- value: cycleFilter,
128
- onChange: (value) => {
129
- setCycleFilter(value);
130
- setCurrentPage(1);
131
- },
132
- placeholder: t('filters.allCycles'),
133
- options: [
134
- { value: 'all', label: t('filters.allCycles') },
135
- ...evaluationCycles.map((item) => ({ value: item.id, label: item.name })),
136
- ],
137
- },
138
- {
139
- id: 'project-filter',
140
- type: 'select',
141
- value: projectFilter,
142
- onChange: (value) => {
143
- setProjectFilter(value);
144
- setCurrentPage(1);
145
- },
146
- placeholder: t('filters.allProjects'),
147
- options: [
148
- { value: 'all', label: t('filters.allProjects') },
149
- ...projects.map((item) => ({ value: item.id, label: item.name })),
150
- ],
151
- },
152
- ]}
153
- />
154
-
155
- <div className="grid gap-4 xl:grid-cols-[1.25fr_0.95fr]">
156
- <SectionCard title={t('tableTitle')} description={t('tableDescription')}>
157
- <div className="space-y-4">
158
- <Table>
159
- <TableHeader>
160
- <TableRow>
161
- <TableHead>{t('columns.employee')}</TableHead>
162
- <TableHead>{t('columns.evaluator')}</TableHead>
163
- <TableHead>{t('columns.project')}</TableHead>
164
- <TableHead>{t('columns.cycle')}</TableHead>
165
- <TableHead>{t('columns.score')}</TableHead>
166
- <TableHead>{t('columns.status')}</TableHead>
167
- </TableRow>
168
- </TableHeader>
169
- <TableBody>
170
- {paginatedEvaluations.map((evaluation) => {
171
- const employee = users.find((item) => item.id === evaluation.employeeId);
172
- const evaluator = users.find((item) => item.id === evaluation.evaluatorId);
173
- const project = projects.find((item) => item.id === evaluation.projectId);
174
- const cycle = evaluationCycles.find((item) => item.id === evaluation.cycleId);
175
-
176
- return (
177
- <TableRow
178
- key={evaluation.id}
179
- className="cursor-pointer"
180
- onClick={() => setSelectedEvaluationId(evaluation.id)}
181
- >
182
- <TableCell className="font-medium">{employee?.name}</TableCell>
183
- <TableCell>{evaluator?.name}</TableCell>
184
- <TableCell>{project?.name ?? '-'}</TableCell>
185
- <TableCell>{cycle?.name}</TableCell>
186
- <TableCell>{evaluation.generatedScore}</TableCell>
187
- <TableCell>
188
- <StatusBadge
189
- label={humanizeGrowthStatus(evaluation.status)}
190
- className={getGrowthEvaluationBadgeClasses(evaluation.status)}
191
- />
192
- </TableCell>
193
- </TableRow>
194
- );
195
- })}
196
- </TableBody>
197
- </Table>
198
- <PaginationFooter
199
- currentPage={safePage}
200
- pageSize={pageSize}
201
- totalItems={filteredEvaluations.length}
202
- onPageChange={setCurrentPage}
203
- onPageSizeChange={(value) => {
204
- setPageSize(value);
205
- setCurrentPage(1);
206
- }}
207
- pageSizeOptions={PAGE_SIZE_OPTIONS}
208
- />
209
- </div>
210
- </SectionCard>
211
-
212
- <SectionCard title={t('detailTitle')} description={t('detailDescription')}>
213
- {selectedEvaluation ? (
214
- <div className="space-y-5">
215
- <div className="grid gap-3 rounded-lg border p-4 text-sm">
216
- <div className="flex items-center justify-between">
217
- <span className="text-muted-foreground">{t('detail.employee')}</span>
218
- <span className="font-medium">{selectedEmployee?.name}</span>
219
- </div>
220
- <div className="flex items-center justify-between">
221
- <span className="text-muted-foreground">{t('detail.evaluator')}</span>
222
- <span className="font-medium">{selectedEvaluator?.name}</span>
223
- </div>
224
- <div className="flex items-center justify-between">
225
- <span className="text-muted-foreground">{t('detail.project')}</span>
226
- <span className="font-medium">{selectedProject?.name ?? '-'}</span>
227
- </div>
228
- <div className="flex items-center justify-between">
229
- <span className="text-muted-foreground">{t('detail.task')}</span>
230
- <span className="font-medium">{selectedTask?.title ?? '-'}</span>
231
- </div>
232
- <div className="flex items-center justify-between">
233
- <span className="text-muted-foreground">{t('detail.date')}</span>
234
- <span className="font-medium">{formatDate(selectedEvaluation.evaluationDate)}</span>
235
- </div>
236
- </div>
237
-
238
- <div>
239
- <p className="mb-3 text-sm font-medium">{t('detail.criteria')}</p>
240
- <div className="space-y-4">
241
- {selectedEvaluation.criteria.map((criterion) => {
242
- const item = performanceCriteria.find(
243
- (entry) => entry.id === criterion.criterionId
244
- );
245
-
246
- return (
247
- <div key={criterion.criterionId} className="rounded-lg border p-3">
248
- <div className="mb-2 flex items-center justify-between gap-3 text-sm">
249
- <span className="font-medium">{item?.name}</span>
250
- <span>{criterion.generatedScore} pts</span>
251
- </div>
252
- <Progress value={criterion.rating * 20} />
253
- </div>
254
- );
255
- })}
256
- </div>
257
- </div>
258
-
259
- <div className="rounded-lg border p-4">
260
- <p className="text-sm font-medium">{t('detail.publicComment')}</p>
261
- <p className="text-muted-foreground mt-2 text-sm">
262
- {selectedEvaluation.publicComment}
263
- </p>
264
- </div>
265
-
266
- <div className="rounded-lg border p-4">
267
- <p className="text-sm font-medium">{t('detail.privateComment')}</p>
268
- <p className="text-muted-foreground mt-2 text-sm">
269
- {selectedEvaluation.privateComment}
270
- </p>
271
- </div>
272
- </div>
273
- ) : null}
274
- </SectionCard>
275
- </div>
276
- </Page>
277
- );
278
- }
@@ -1,171 +0,0 @@
1
- 'use client';
2
-
3
- import { Page, PaginationFooter, SearchBar } from '@/components/entity-list';
4
- import { Badge } from '@/components/ui/badge';
5
- import { Progress } from '@/components/ui/progress';
6
- import { Target } from 'lucide-react';
7
- import { useTranslations } from 'next-intl';
8
- import { useMemo, useState } from 'react';
9
- import { OperationsHeader } from '../_components/operations-header';
10
- import { SectionCard } from '../_components/section-card';
11
- import { StatusBadge } from '../_components/status-badge';
12
- import { useOperationsGrowthData } from '../_lib/hooks/use-operations-growth-data';
13
- import { getGrowthGoalBadgeClasses, humanizeGrowthStatus } from '../_lib/utils/growth';
14
-
15
- const PAGE_SIZE_OPTIONS = [4, 8, 12];
16
- const DEFAULT_PAGE_SIZE = PAGE_SIZE_OPTIONS[0] ?? 4;
17
-
18
- export default function OperationsGoalsPage() {
19
- const t = useTranslations('operations.GoalsPage');
20
- const { goals, users, projects } = useOperationsGrowthData();
21
- const [searchInput, setSearchInput] = useState('');
22
- const [search, setSearch] = useState('');
23
- const [statusFilter, setStatusFilter] = useState('all');
24
- const [currentPage, setCurrentPage] = useState(1);
25
- const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
26
-
27
- const filteredGoals = useMemo(
28
- () =>
29
- goals.filter((goal) => {
30
- const owner = users.find((item) => item.id === goal.employeeId)?.name;
31
- const project = projects.find((item) => item.id === goal.projectId)?.name;
32
- const haystack = `${goal.title} ${goal.description} ${owner ?? ''} ${project ?? ''} ${goal.teamName ?? ''}`;
33
- const matchesSearch = haystack.toLowerCase().includes(search.toLowerCase());
34
- const matchesStatus = statusFilter === 'all' || goal.status === statusFilter;
35
-
36
- return matchesSearch && matchesStatus;
37
- }),
38
- [goals, projects, search, statusFilter, users]
39
- );
40
-
41
- const totalPages = Math.max(1, Math.ceil(filteredGoals.length / pageSize));
42
- const safePage = Math.min(Math.max(currentPage, 1), totalPages);
43
- const paginatedGoals = filteredGoals.slice(
44
- (safePage - 1) * pageSize,
45
- safePage * pageSize
46
- );
47
-
48
- return (
49
- <Page>
50
- <OperationsHeader
51
- title={t('title')}
52
- description={t('description')}
53
- current={t('breadcrumb')}
54
- />
55
-
56
- <SearchBar
57
- className="mb-6"
58
- searchQuery={searchInput}
59
- onSearchChange={setSearchInput}
60
- onSearch={() => {
61
- setSearch(searchInput);
62
- setCurrentPage(1);
63
- }}
64
- placeholder={t('searchPlaceholder')}
65
- controls={[
66
- {
67
- id: 'status-filter',
68
- type: 'select',
69
- value: statusFilter,
70
- onChange: (value) => {
71
- setStatusFilter(value);
72
- setCurrentPage(1);
73
- },
74
- placeholder: t('filters.allStatuses'),
75
- options: [
76
- { value: 'all', label: t('filters.allStatuses') },
77
- { value: 'active', label: t('filters.active') },
78
- { value: 'at_risk', label: t('filters.atRisk') },
79
- { value: 'completed', label: t('filters.completed') },
80
- { value: 'draft', label: t('filters.draft') },
81
- ],
82
- },
83
- ]}
84
- />
85
-
86
- <SectionCard title={t('sectionTitle')} description={t('sectionDescription')}>
87
- <div className="space-y-4">
88
- <div className="grid gap-4 lg:grid-cols-2">
89
- {paginatedGoals.map((goal) => {
90
- const owner = users.find((item) => item.id === goal.employeeId)?.name;
91
- const project = projects.find((item) => item.id === goal.projectId)?.name;
92
-
93
- return (
94
- <div key={goal.id} className="rounded-lg border p-4">
95
- <div className="mb-3 flex items-start justify-between gap-3">
96
- <div>
97
- <p className="font-medium">{goal.title}</p>
98
- <p className="text-muted-foreground text-sm">{goal.description}</p>
99
- </div>
100
- <StatusBadge
101
- label={humanizeGrowthStatus(goal.status)}
102
- className={getGrowthGoalBadgeClasses(goal.status)}
103
- />
104
- </div>
105
- <div className="mb-3 flex flex-wrap gap-2">
106
- <Badge variant="outline">{goal.periodLabel}</Badge>
107
- <Badge variant="secondary">{owner ?? goal.teamName ?? t('sharedTeam')}</Badge>
108
- {project ? <Badge variant="outline">{project}</Badge> : null}
109
- </div>
110
- <div className="space-y-2">
111
- <div className="flex items-center justify-between text-sm">
112
- <span>{t('progress')}</span>
113
- <span>{goal.progressPercent}%</span>
114
- </div>
115
- <Progress value={goal.progressPercent} />
116
- <div className="flex items-center justify-between text-sm">
117
- <span className="text-muted-foreground">{t('scoreGenerated')}</span>
118
- <span>
119
- {goal.currentScore} / {goal.targetScore}
120
- </span>
121
- </div>
122
- </div>
123
- <div className="mt-4 rounded-md bg-slate-50 p-3 text-sm">
124
- <span className="font-medium">{t('completionRule')}:</span> {goal.completionRule}
125
- </div>
126
- </div>
127
- );
128
- })}
129
- </div>
130
- <PaginationFooter
131
- currentPage={safePage}
132
- pageSize={pageSize}
133
- totalItems={filteredGoals.length}
134
- onPageChange={setCurrentPage}
135
- onPageSizeChange={(value) => {
136
- setPageSize(value);
137
- setCurrentPage(1);
138
- }}
139
- pageSizeOptions={PAGE_SIZE_OPTIONS}
140
- />
141
- </div>
142
- </SectionCard>
143
-
144
- <SectionCard title={t('highlightsTitle')} description={t('highlightsDescription')}>
145
- <div className="grid gap-4 md:grid-cols-3">
146
- <div className="rounded-lg border border-dashed border-slate-300 p-4">
147
- <Target className="mb-3 h-5 w-5 text-blue-700" />
148
- <p className="font-medium">{t('highlights.activeGoals')}</p>
149
- <p className="mt-2 text-2xl font-bold">
150
- {goals.filter((item) => item.status === 'active').length}
151
- </p>
152
- </div>
153
- <div className="rounded-lg border border-dashed border-slate-300 p-4">
154
- <Target className="mb-3 h-5 w-5 text-orange-700" />
155
- <p className="font-medium">{t('highlights.atRiskGoals')}</p>
156
- <p className="mt-2 text-2xl font-bold">
157
- {goals.filter((item) => item.status === 'at_risk').length}
158
- </p>
159
- </div>
160
- <div className="rounded-lg border border-dashed border-slate-300 p-4">
161
- <Target className="mb-3 h-5 w-5 text-emerald-700" />
162
- <p className="font-medium">{t('highlights.completedGoals')}</p>
163
- <p className="mt-2 text-2xl font-bold">
164
- {goals.filter((item) => item.status === 'completed').length}
165
- </p>
166
- </div>
167
- </div>
168
- </SectionCard>
169
- </Page>
170
- );
171
- }