@hed-hog/operations 0.0.303 → 0.0.304

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 (166) hide show
  1. package/README.md +200 -43
  2. package/dist/controllers/operations-approvals.controller.d.ts +9 -0
  3. package/dist/controllers/operations-approvals.controller.d.ts.map +1 -0
  4. package/dist/controllers/operations-approvals.controller.js +64 -0
  5. package/dist/controllers/operations-approvals.controller.js.map +1 -0
  6. package/dist/controllers/operations-collaborators.controller.d.ts +223 -0
  7. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -0
  8. package/dist/controllers/operations-collaborators.controller.js +96 -0
  9. package/dist/controllers/operations-collaborators.controller.js.map +1 -0
  10. package/dist/controllers/operations-contracts.controller.d.ts +683 -0
  11. package/dist/controllers/operations-contracts.controller.d.ts.map +1 -0
  12. package/dist/controllers/operations-contracts.controller.js +198 -0
  13. package/dist/controllers/operations-contracts.controller.js.map +1 -0
  14. package/dist/controllers/operations-org-structure.controller.d.ts +108 -0
  15. package/dist/controllers/operations-org-structure.controller.d.ts.map +1 -0
  16. package/dist/controllers/operations-org-structure.controller.js +143 -0
  17. package/dist/controllers/operations-org-structure.controller.js.map +1 -0
  18. package/dist/controllers/operations-projects.controller.d.ts +169 -0
  19. package/dist/controllers/operations-projects.controller.d.ts.map +1 -0
  20. package/dist/controllers/operations-projects.controller.js +87 -0
  21. package/dist/controllers/operations-projects.controller.js.map +1 -0
  22. package/dist/controllers/operations-tasks.controller.d.ts +54 -0
  23. package/dist/controllers/operations-tasks.controller.d.ts.map +1 -0
  24. package/dist/controllers/operations-tasks.controller.js +79 -0
  25. package/dist/controllers/operations-tasks.controller.js.map +1 -0
  26. package/dist/controllers/operations-timesheets.controller.d.ts +99 -0
  27. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -0
  28. package/dist/controllers/operations-timesheets.controller.js +154 -0
  29. package/dist/controllers/operations-timesheets.controller.js.map +1 -0
  30. package/dist/dto/create-collaborator-type.dto.d.ts +10 -0
  31. package/dist/dto/create-collaborator-type.dto.d.ts.map +1 -0
  32. package/dist/dto/create-collaborator-type.dto.js +56 -0
  33. package/dist/dto/create-collaborator-type.dto.js.map +1 -0
  34. package/dist/dto/create-collaborator.dto.d.ts +42 -0
  35. package/dist/dto/create-collaborator.dto.d.ts.map +1 -0
  36. package/dist/dto/create-collaborator.dto.js +228 -0
  37. package/dist/dto/create-collaborator.dto.js.map +1 -0
  38. package/dist/dto/create-schedule-adjustment-request.dto.d.ts +17 -0
  39. package/dist/dto/create-schedule-adjustment-request.dto.d.ts.map +1 -0
  40. package/dist/dto/create-schedule-adjustment-request.dto.js +89 -0
  41. package/dist/dto/create-schedule-adjustment-request.dto.js.map +1 -0
  42. package/dist/dto/create-task.dto.d.ts +8 -0
  43. package/dist/dto/create-task.dto.d.ts.map +1 -0
  44. package/dist/dto/create-task.dto.js +50 -0
  45. package/dist/dto/create-task.dto.js.map +1 -0
  46. package/dist/dto/create-time-off-request.dto.d.ts +9 -0
  47. package/dist/dto/create-time-off-request.dto.d.ts.map +1 -0
  48. package/dist/dto/create-time-off-request.dto.js +54 -0
  49. package/dist/dto/create-time-off-request.dto.js.map +1 -0
  50. package/dist/dto/create-timesheet-entry.dto.d.ts +12 -0
  51. package/dist/dto/create-timesheet-entry.dto.d.ts.map +1 -0
  52. package/dist/dto/create-timesheet-entry.dto.js +75 -0
  53. package/dist/dto/create-timesheet-entry.dto.js.map +1 -0
  54. package/dist/dto/list-collaborator-types.dto.d.ts +4 -0
  55. package/dist/dto/list-collaborator-types.dto.d.ts.map +1 -0
  56. package/dist/dto/list-collaborator-types.dto.js +29 -0
  57. package/dist/dto/list-collaborator-types.dto.js.map +1 -0
  58. package/dist/dto/list-collaborators.dto.d.ts +8 -0
  59. package/dist/dto/list-collaborators.dto.d.ts.map +1 -0
  60. package/dist/dto/list-collaborators.dto.js +42 -0
  61. package/dist/dto/list-collaborators.dto.js.map +1 -0
  62. package/dist/dto/list-project-options.dto.d.ts +4 -0
  63. package/dist/dto/list-project-options.dto.d.ts.map +1 -0
  64. package/dist/dto/list-project-options.dto.js +8 -0
  65. package/dist/dto/list-project-options.dto.js.map +1 -0
  66. package/dist/dto/list-tasks.dto.d.ts +7 -0
  67. package/dist/dto/list-tasks.dto.d.ts.map +1 -0
  68. package/dist/dto/list-tasks.dto.js +38 -0
  69. package/dist/dto/list-tasks.dto.js.map +1 -0
  70. package/dist/dto/list-timesheet-entries.dto.d.ts +10 -0
  71. package/dist/dto/list-timesheet-entries.dto.d.ts.map +1 -0
  72. package/dist/dto/list-timesheet-entries.dto.js +54 -0
  73. package/dist/dto/list-timesheet-entries.dto.js.map +1 -0
  74. package/dist/dto/update-collaborator-type.dto.d.ts +4 -0
  75. package/dist/dto/update-collaborator-type.dto.d.ts.map +1 -0
  76. package/dist/dto/update-collaborator-type.dto.js +8 -0
  77. package/dist/dto/update-collaborator-type.dto.js.map +1 -0
  78. package/dist/dto/update-collaborator.dto.d.ts +4 -0
  79. package/dist/dto/update-collaborator.dto.d.ts.map +1 -0
  80. package/dist/dto/update-collaborator.dto.js +8 -0
  81. package/dist/dto/update-collaborator.dto.js.map +1 -0
  82. package/dist/dto/update-task.dto.d.ts +8 -0
  83. package/dist/dto/update-task.dto.d.ts.map +1 -0
  84. package/dist/dto/update-task.dto.js +51 -0
  85. package/dist/dto/update-task.dto.js.map +1 -0
  86. package/dist/operations.controller.d.ts +0 -1045
  87. package/dist/operations.controller.d.ts.map +1 -1
  88. package/dist/operations.controller.js +0 -429
  89. package/dist/operations.controller.js.map +1 -1
  90. package/dist/operations.module.d.ts.map +1 -1
  91. package/dist/operations.module.js +23 -2
  92. package/dist/operations.module.js.map +1 -1
  93. package/dist/operations.service.d.ts +373 -8
  94. package/dist/operations.service.d.ts.map +1 -1
  95. package/dist/operations.service.js +1598 -111
  96. package/dist/operations.service.js.map +1 -1
  97. package/dist/operations.service.spec.js +315 -1
  98. package/dist/operations.service.spec.js.map +1 -1
  99. package/dist/services/shared/operations-access.service.d.ts +16 -0
  100. package/dist/services/shared/operations-access.service.d.ts.map +1 -0
  101. package/dist/services/shared/operations-access.service.js +48 -0
  102. package/dist/services/shared/operations-access.service.js.map +1 -0
  103. package/hedhog/data/dashboard.yaml +20 -0
  104. package/hedhog/data/dashboard_component.yaml +274 -0
  105. package/hedhog/data/dashboard_component_role.yaml +174 -0
  106. package/hedhog/data/dashboard_item.yaml +299 -0
  107. package/hedhog/data/dashboard_role.yaml +20 -0
  108. package/hedhog/data/menu.yaml +30 -13
  109. package/hedhog/data/operations_collaborator_type.yaml +76 -0
  110. package/hedhog/data/route.yaml +183 -0
  111. package/hedhog/frontend/app/_components/async-options-combobox.tsx.ejs +231 -0
  112. package/hedhog/frontend/app/_components/collaborator-details-screen.tsx.ejs +134 -49
  113. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +772 -93
  114. package/hedhog/frontend/app/_components/department-select-with-create.tsx.ejs +38 -16
  115. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +875 -632
  116. package/hedhog/frontend/app/_components/timesheet-task-create-sheet.tsx.ejs +213 -0
  117. package/hedhog/frontend/app/_lib/api.ts.ejs +30 -1
  118. package/hedhog/frontend/app/_lib/types.ts.ejs +142 -39
  119. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +33 -2
  120. package/hedhog/frontend/app/approvals/page.tsx.ejs +116 -98
  121. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +502 -0
  122. package/hedhog/frontend/app/collaborators/page.tsx.ejs +109 -68
  123. package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +11 -9
  124. package/hedhog/frontend/app/departments/page.tsx.ejs +1 -1
  125. package/hedhog/frontend/app/projects/page.tsx.ejs +5 -1
  126. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +244 -120
  127. package/hedhog/frontend/app/team/page.tsx.ejs +15 -2
  128. package/hedhog/frontend/app/time-off/page.tsx.ejs +158 -82
  129. package/hedhog/frontend/app/timesheets/page.tsx.ejs +814 -357
  130. package/hedhog/frontend/messages/en.json +243 -51
  131. package/hedhog/frontend/messages/pt.json +458 -268
  132. package/hedhog/table/operations_collaborator.yaml +26 -13
  133. package/hedhog/table/operations_collaborator_equity_participation.yaml +43 -0
  134. package/hedhog/table/operations_collaborator_type.yaml +33 -0
  135. package/hedhog/table/operations_job_title.yaml +24 -0
  136. package/hedhog/table/operations_project_assignment.yaml +9 -0
  137. package/hedhog/table/operations_project_role.yaml +39 -0
  138. package/hedhog/table/operations_task.yaml +30 -0
  139. package/hedhog/table/operations_timesheet_entry.yaml +12 -0
  140. package/package.json +6 -6
  141. package/src/controllers/operations-approvals.controller.ts +24 -0
  142. package/src/controllers/operations-collaborators.controller.ts +60 -0
  143. package/src/controllers/operations-contracts.controller.ts +138 -0
  144. package/src/controllers/operations-org-structure.controller.ts +92 -0
  145. package/src/controllers/operations-projects.controller.ts +50 -0
  146. package/src/controllers/operations-tasks.controller.ts +52 -0
  147. package/src/controllers/operations-timesheets.controller.ts +100 -0
  148. package/src/dto/create-collaborator-type.dto.ts +43 -0
  149. package/src/dto/create-collaborator.dto.ts +223 -0
  150. package/src/dto/create-schedule-adjustment-request.dto.ts +91 -0
  151. package/src/dto/create-task.dto.ts +35 -0
  152. package/src/dto/create-time-off-request.dto.ts +53 -0
  153. package/src/dto/create-timesheet-entry.dto.ts +67 -0
  154. package/src/dto/list-collaborator-types.dto.ts +15 -0
  155. package/src/dto/list-collaborators.dto.ts +30 -0
  156. package/src/dto/list-project-options.dto.ts +3 -0
  157. package/src/dto/list-tasks.dto.ts +25 -0
  158. package/src/dto/list-timesheet-entries.dto.ts +40 -0
  159. package/src/dto/update-collaborator-type.dto.ts +3 -0
  160. package/src/dto/update-collaborator.dto.ts +3 -0
  161. package/src/dto/update-task.dto.ts +36 -0
  162. package/src/operations.controller.ts +1 -278
  163. package/src/operations.module.ts +23 -2
  164. package/src/operations.service.spec.ts +450 -0
  165. package/src/operations.service.ts +4641 -2163
  166. package/src/services/shared/operations-access.service.ts +52 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { EmptyState, Page, SearchBar } from '@/components/entity-list';
4
4
  import { Button } from '@/components/ui/button';
5
+ import { FormActions } from '@/components/ui/form-actions';
5
6
  import { Input } from '@/components/ui/input';
6
7
  import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
7
8
  import {
@@ -29,19 +30,19 @@ import {
29
30
  import { Textarea } from '@/components/ui/textarea';
30
31
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
31
32
  import { Palmtree, Plus } from 'lucide-react';
32
- import { useMemo, useState } from 'react';
33
33
  import { useTranslations } from 'next-intl';
34
+ import { useMemo, useState } from 'react';
34
35
  import { OperationsHeader } from '../_components/operations-header';
35
36
  import { StatusBadge } from '../_components/status-badge';
36
37
  import { fetchOperations, mutateOperations } from '../_lib/api';
37
38
  import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
38
39
  import type { OperationsTimeOffRequest } from '../_lib/types';
39
- import { parseNumberInput, trimToNull } from '../_lib/utils/forms';
40
40
  import {
41
41
  formatDateRange,
42
42
  formatEnumLabel,
43
43
  getStatusBadgeClass,
44
44
  } from '../_lib/utils/format';
45
+ import { parseNumberInput, trimToNull } from '../_lib/utils/forms';
45
46
 
46
47
  type TimeOffFormState = {
47
48
  requestType: string;
@@ -59,50 +60,71 @@ const emptyForm: TimeOffFormState = {
59
60
  reason: '',
60
61
  };
61
62
 
62
- export default function OperationsTimeOffPage() {
63
- const t = useTranslations('operations.TimeOffPage');
64
- const commonT = useTranslations('operations.Common');
65
- const { request, showToastHandler, currentLocaleCode } = useApp();
66
- const access = useOperationsAccess();
67
- const [search, setSearch] = useState('');
68
- const [statusFilter, setStatusFilter] = useState('all');
69
- const [isSheetOpen, setIsSheetOpen] = useState(false);
70
- const [form, setForm] = useState<TimeOffFormState>(emptyForm);
71
-
72
- const getRequestTypeLabel = (value?: string | null) => {
73
- if (!value) {
74
- return '-';
75
- }
76
-
77
- const key = `options.requestTypes.${value}`;
78
- return t.has(key) ? t(key) : formatEnumLabel(value);
79
- };
80
-
81
- const getStatusLabel = (value?: string | null) => {
82
- if (!value) {
83
- return '-';
84
- }
85
-
86
- const key = `options.statuses.${value}`;
87
- return t.has(key) ? t(key) : formatEnumLabel(value);
88
- };
63
+ export default function OperationsTimeOffPage() {
64
+ const t = useTranslations('operations.TimeOffPage');
65
+ const commonT = useTranslations('operations.Common');
66
+ const { request, showToastHandler, currentLocaleCode } = useApp();
67
+ const access = useOperationsAccess();
68
+ const [search, setSearch] = useState('');
69
+ const [statusFilter, setStatusFilter] = useState('all');
70
+ const [isSheetOpen, setIsSheetOpen] = useState(false);
71
+ const [form, setForm] = useState<TimeOffFormState>(emptyForm);
89
72
 
90
- const { data: requests = [], refetch } = useQuery<OperationsTimeOffRequest[]>({
91
- queryKey: ['operations-time-off', currentLocaleCode],
92
- enabled: access.isCollaborator,
93
- queryFn: () =>
94
- fetchOperations<OperationsTimeOffRequest[]>(request, '/operations/time-off'),
95
- });
73
+ const getRequestTypeLabel = (value?: string | null) => {
74
+ if (!value) {
75
+ return '-';
76
+ }
77
+
78
+ const key = `options.requestTypes.${value}`;
79
+ return t.has(key) ? t(key) : formatEnumLabel(value);
80
+ };
81
+
82
+ const getStatusLabel = (value?: string | null) => {
83
+ if (!value) {
84
+ return '-';
85
+ }
86
+
87
+ const key = `options.statuses.${value}`;
88
+ return t.has(key) ? t(key) : formatEnumLabel(value);
89
+ };
90
+
91
+ const getStatusDescription = (value?: string | null) => {
92
+ if (!value) {
93
+ return null;
94
+ }
95
+
96
+ const key = `statusHelp.${value}`;
97
+ return t.has(key) ? t(key) : null;
98
+ };
99
+
100
+ const { data: requests = [], refetch } = useQuery<OperationsTimeOffRequest[]>(
101
+ {
102
+ queryKey: ['operations-time-off', currentLocaleCode],
103
+ enabled: access.isCollaborator,
104
+ queryFn: () =>
105
+ fetchOperations<OperationsTimeOffRequest[]>(
106
+ request,
107
+ '/operations/time-off'
108
+ ),
109
+ }
110
+ );
96
111
 
97
112
  const filteredRows = useMemo(
98
113
  () =>
99
114
  requests.filter((item) => {
100
115
  const matchesSearch = !search.trim()
101
116
  ? true
102
- : [item.collaboratorName, item.approverName, item.reason, item.requestType]
117
+ : [
118
+ item.collaboratorName,
119
+ item.approverName,
120
+ item.reason,
121
+ item.requestType,
122
+ ]
103
123
  .filter(Boolean)
104
124
  .some((value) =>
105
- String(value).toLowerCase().includes(search.trim().toLowerCase())
125
+ String(value)
126
+ .toLowerCase()
127
+ .includes(search.trim().toLowerCase())
106
128
  );
107
129
  const matchesStatus =
108
130
  statusFilter === 'all' ? true : item.status === statusFilter;
@@ -115,16 +137,19 @@ export default function OperationsTimeOffPage() {
115
137
  {
116
138
  key: 'submitted',
117
139
  title: t('cards.submitted'),
140
+ description: t('cards.submittedDescription'),
118
141
  value: requests.filter((item) => item.status === 'submitted').length,
119
142
  },
120
143
  {
121
144
  key: 'approved',
122
145
  title: t('cards.approved'),
146
+ description: t('cards.approvedDescription'),
123
147
  value: requests.filter((item) => item.status === 'approved').length,
124
148
  },
125
149
  {
126
150
  key: 'days',
127
151
  title: t('cards.days'),
152
+ description: t('cards.daysDescription'),
128
153
  value: requests.reduce(
129
154
  (total, item) => total + Number(item.totalDays ?? 0),
130
155
  0
@@ -172,6 +197,8 @@ export default function OperationsTimeOffPage() {
172
197
  }
173
198
  />
174
199
 
200
+ <KpiCardsGrid items={cards} columns={3} />
201
+
175
202
  <SearchBar
176
203
  searchQuery={search}
177
204
  onSearchChange={setSearch}
@@ -184,18 +211,16 @@ export default function OperationsTimeOffPage() {
184
211
  value: statusFilter,
185
212
  onChange: setStatusFilter,
186
213
  placeholder: commonT('labels.status'),
187
- options: [
188
- { value: 'all', label: commonT('filters.allStatuses') },
189
- { value: 'submitted', label: getStatusLabel('submitted') },
190
- { value: 'approved', label: getStatusLabel('approved') },
191
- { value: 'rejected', label: getStatusLabel('rejected') },
192
- ],
214
+ options: [
215
+ { value: 'all', label: commonT('filters.allStatuses') },
216
+ { value: 'submitted', label: getStatusLabel('submitted') },
217
+ { value: 'approved', label: getStatusLabel('approved') },
218
+ { value: 'rejected', label: getStatusLabel('rejected') },
219
+ ],
193
220
  },
194
221
  ]}
195
222
  />
196
223
 
197
- <KpiCardsGrid items={cards} columns={3} />
198
-
199
224
  {filteredRows.length > 0 ? (
200
225
  <div className="overflow-x-auto rounded-md border">
201
226
  <Table>
@@ -212,12 +237,17 @@ export default function OperationsTimeOffPage() {
212
237
  </TableHeader>
213
238
  <TableBody>
214
239
  {filteredRows.map((requestItem) => (
215
- <TableRow key={requestItem.id}>
240
+ <TableRow key={requestItem.id} className="hover:bg-muted/30">
216
241
  <TableCell>{requestItem.collaboratorName}</TableCell>
217
- <TableCell>{getRequestTypeLabel(requestItem.requestType)}</TableCell>
242
+ <TableCell>
243
+ {getRequestTypeLabel(requestItem.requestType)}
244
+ </TableCell>
218
245
  <TableCell>
219
246
  <div>
220
- {formatDateRange(requestItem.startDate, requestItem.endDate)}
247
+ {formatDateRange(
248
+ requestItem.startDate,
249
+ requestItem.endDate
250
+ )}
221
251
  </div>
222
252
  <div className="text-xs text-muted-foreground">
223
253
  {requestItem.totalDays ?? 0} {commonT('labels.days')}
@@ -227,10 +257,17 @@ export default function OperationsTimeOffPage() {
227
257
  {requestItem.approverName || commonT('labels.notAssigned')}
228
258
  </TableCell>
229
259
  <TableCell>
230
- <StatusBadge
231
- label={getStatusLabel(requestItem.status)}
232
- className={getStatusBadgeClass(requestItem.status)}
233
- />
260
+ <div className="space-y-1">
261
+ <StatusBadge
262
+ label={getStatusLabel(requestItem.status)}
263
+ className={getStatusBadgeClass(requestItem.status)}
264
+ />
265
+ {getStatusDescription(requestItem.status) ? (
266
+ <p className="max-w-40 text-xs text-muted-foreground">
267
+ {getStatusDescription(requestItem.status)}
268
+ </p>
269
+ ) : null}
270
+ </div>
234
271
  </TableCell>
235
272
  <TableCell>
236
273
  {requestItem.reason || commonT('labels.noNotes')}
@@ -248,8 +285,16 @@ export default function OperationsTimeOffPage() {
248
285
  icon={<Palmtree className="size-12" />}
249
286
  title={commonT('states.emptyTitle')}
250
287
  description={t('emptyDescription')}
251
- actionLabel={access.isCollaborator ? commonT('actions.create') : commonT('actions.refresh')}
252
- onAction={access.isCollaborator ? () => setIsSheetOpen(true) : () => void refetch()}
288
+ actionLabel={
289
+ access.isCollaborator
290
+ ? commonT('actions.create')
291
+ : commonT('actions.refresh')
292
+ }
293
+ onAction={
294
+ access.isCollaborator
295
+ ? () => setIsSheetOpen(true)
296
+ : () => void refetch()
297
+ }
253
298
  />
254
299
  )}
255
300
 
@@ -260,39 +305,54 @@ export default function OperationsTimeOffPage() {
260
305
  <SheetDescription>{t('sheet.description')}</SheetDescription>
261
306
  </SheetHeader>
262
307
 
263
- <div className="mt-6 grid gap-4">
308
+ <div className="mt-6 grid gap-4 px-4 pb-20">
309
+ <div className="rounded-lg border bg-muted/30 p-3 text-sm">
310
+ <div className="font-medium text-foreground">
311
+ {t('sheet.helperTitle')}
312
+ </div>
313
+ <p className="mt-1 text-muted-foreground">
314
+ {t('sheet.helperDescription')}
315
+ </p>
316
+ </div>
317
+
264
318
  <div className="space-y-2">
265
- <label className="text-sm font-medium">{commonT('labels.requestType')}</label>
319
+ <label className="text-sm font-medium">
320
+ {commonT('labels.requestType')}
321
+ </label>
266
322
  <Select
267
323
  value={form.requestType}
268
324
  onValueChange={(value) =>
269
325
  setForm((current) => ({ ...current, requestType: value }))
270
326
  }
271
327
  >
272
- <SelectTrigger>
273
- <SelectValue />
274
- </SelectTrigger>
275
- <SelectContent>
276
- <SelectItem value="vacation">
277
- {getRequestTypeLabel('vacation')}
278
- </SelectItem>
279
- <SelectItem value="personal_time">
280
- {getRequestTypeLabel('personal_time')}
281
- </SelectItem>
282
- <SelectItem value="sick_leave">
283
- {getRequestTypeLabel('sick_leave')}
284
- </SelectItem>
285
- <SelectItem value="unpaid_leave">
286
- {getRequestTypeLabel('unpaid_leave')}
287
- </SelectItem>
288
- <SelectItem value="other">{getRequestTypeLabel('other')}</SelectItem>
289
- </SelectContent>
290
- </Select>
328
+ <SelectTrigger className="w-full">
329
+ <SelectValue />
330
+ </SelectTrigger>
331
+ <SelectContent>
332
+ <SelectItem value="vacation">
333
+ {getRequestTypeLabel('vacation')}
334
+ </SelectItem>
335
+ <SelectItem value="personal_time">
336
+ {getRequestTypeLabel('personal_time')}
337
+ </SelectItem>
338
+ <SelectItem value="sick_leave">
339
+ {getRequestTypeLabel('sick_leave')}
340
+ </SelectItem>
341
+ <SelectItem value="unpaid_leave">
342
+ {getRequestTypeLabel('unpaid_leave')}
343
+ </SelectItem>
344
+ <SelectItem value="other">
345
+ {getRequestTypeLabel('other')}
346
+ </SelectItem>
347
+ </SelectContent>
348
+ </Select>
291
349
  </div>
292
350
 
293
351
  <div className="grid gap-4 md:grid-cols-2">
294
352
  <div className="space-y-2">
295
- <label className="text-sm font-medium">{commonT('labels.startDate')}</label>
353
+ <label className="text-sm font-medium">
354
+ {commonT('labels.startDate')}
355
+ </label>
296
356
  <Input
297
357
  type="date"
298
358
  value={form.startDate}
@@ -305,7 +365,9 @@ export default function OperationsTimeOffPage() {
305
365
  />
306
366
  </div>
307
367
  <div className="space-y-2">
308
- <label className="text-sm font-medium">{commonT('labels.endDate')}</label>
368
+ <label className="text-sm font-medium">
369
+ {commonT('labels.endDate')}
370
+ </label>
309
371
  <Input
310
372
  type="date"
311
373
  value={form.endDate}
@@ -320,7 +382,9 @@ export default function OperationsTimeOffPage() {
320
382
  </div>
321
383
 
322
384
  <div className="space-y-2">
323
- <label className="text-sm font-medium">{commonT('labels.days')}</label>
385
+ <label className="text-sm font-medium">
386
+ {commonT('labels.days')}
387
+ </label>
324
388
  <Input
325
389
  type="number"
326
390
  step="0.5"
@@ -335,18 +399,30 @@ export default function OperationsTimeOffPage() {
335
399
  </div>
336
400
 
337
401
  <div className="space-y-2">
338
- <label className="text-sm font-medium">{commonT('labels.reason')}</label>
402
+ <label className="text-sm font-medium">
403
+ {commonT('labels.reason')}
404
+ </label>
339
405
  <Textarea
340
406
  rows={4}
407
+ placeholder={t('sheet.reasonPlaceholder')}
341
408
  value={form.reason}
342
409
  onChange={(event) =>
343
- setForm((current) => ({ ...current, reason: event.target.value }))
410
+ setForm((current) => ({
411
+ ...current,
412
+ reason: event.target.value,
413
+ }))
344
414
  }
345
415
  />
346
416
  </div>
347
-
348
- <Button onClick={() => void onSubmit()}>{commonT('actions.save')}</Button>
349
417
  </div>
418
+
419
+ <FormActions
420
+ sheet
421
+ cancelLabel={commonT('actions.cancel')}
422
+ onCancel={() => setIsSheetOpen(false)}
423
+ onSubmit={() => void onSubmit()}
424
+ submitLabel={commonT('actions.save')}
425
+ />
350
426
  </SheetContent>
351
427
  </Sheet>
352
428
  </Page>