@hed-hog/operations 0.0.306 → 0.0.310

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 (123) hide show
  1. package/dist/controllers/operations-approvals.controller.d.ts +114 -1
  2. package/dist/controllers/operations-approvals.controller.d.ts.map +1 -1
  3. package/dist/controllers/operations-approvals.controller.js +16 -3
  4. package/dist/controllers/operations-approvals.controller.js.map +1 -1
  5. package/dist/controllers/operations-collaborators.controller.d.ts +16 -1
  6. package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
  7. package/dist/controllers/operations-collaborators.controller.js +16 -3
  8. package/dist/controllers/operations-collaborators.controller.js.map +1 -1
  9. package/dist/controllers/operations-contracts.controller.d.ts +14 -453
  10. package/dist/controllers/operations-contracts.controller.d.ts.map +1 -1
  11. package/dist/controllers/operations-contracts.controller.js +11 -112
  12. package/dist/controllers/operations-contracts.controller.js.map +1 -1
  13. package/dist/controllers/operations-org-structure.controller.d.ts +65 -2
  14. package/dist/controllers/operations-org-structure.controller.d.ts.map +1 -1
  15. package/dist/controllers/operations-org-structure.controller.js +18 -5
  16. package/dist/controllers/operations-org-structure.controller.js.map +1 -1
  17. package/dist/controllers/operations-projects.controller.d.ts +28 -4
  18. package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
  19. package/dist/controllers/operations-projects.controller.js +17 -5
  20. package/dist/controllers/operations-projects.controller.js.map +1 -1
  21. package/dist/controllers/operations-timesheets.controller.d.ts +31 -4
  22. package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
  23. package/dist/controllers/operations-timesheets.controller.js +16 -11
  24. package/dist/controllers/operations-timesheets.controller.js.map +1 -1
  25. package/dist/dto/list-approvals.dto.d.ts +6 -0
  26. package/dist/dto/list-approvals.dto.d.ts.map +1 -0
  27. package/dist/dto/list-approvals.dto.js +28 -0
  28. package/dist/dto/list-approvals.dto.js.map +1 -0
  29. package/dist/dto/list-collaborator-types.dto.d.ts +3 -1
  30. package/dist/dto/list-collaborator-types.dto.d.ts.map +1 -1
  31. package/dist/dto/list-collaborator-types.dto.js +7 -1
  32. package/dist/dto/list-collaborator-types.dto.js.map +1 -1
  33. package/dist/dto/list-collaborators.dto.d.ts +1 -0
  34. package/dist/dto/list-collaborators.dto.d.ts.map +1 -1
  35. package/dist/dto/list-collaborators.dto.js +5 -0
  36. package/dist/dto/list-collaborators.dto.js.map +1 -1
  37. package/dist/dto/list-contracts.dto.d.ts +8 -0
  38. package/dist/dto/list-contracts.dto.d.ts.map +1 -0
  39. package/dist/dto/list-contracts.dto.js +38 -0
  40. package/dist/dto/list-contracts.dto.js.map +1 -0
  41. package/dist/dto/list-departments.dto.d.ts +5 -0
  42. package/dist/dto/list-departments.dto.d.ts.map +1 -0
  43. package/dist/dto/list-departments.dto.js +23 -0
  44. package/dist/dto/list-departments.dto.js.map +1 -0
  45. package/dist/dto/list-projects.dto.d.ts +5 -0
  46. package/dist/dto/list-projects.dto.d.ts.map +1 -0
  47. package/dist/dto/list-projects.dto.js +23 -0
  48. package/dist/dto/list-projects.dto.js.map +1 -0
  49. package/dist/dto/list-schedule-adjustments.dto.d.ts +5 -0
  50. package/dist/dto/list-schedule-adjustments.dto.d.ts.map +1 -0
  51. package/dist/dto/list-schedule-adjustments.dto.js +23 -0
  52. package/dist/dto/list-schedule-adjustments.dto.js.map +1 -0
  53. package/dist/dto/list-time-off-requests.dto.d.ts +5 -0
  54. package/dist/dto/list-time-off-requests.dto.d.ts.map +1 -0
  55. package/dist/dto/list-time-off-requests.dto.js +23 -0
  56. package/dist/dto/list-time-off-requests.dto.js.map +1 -0
  57. package/dist/dto/list-timesheets.dto.d.ts +5 -0
  58. package/dist/dto/list-timesheets.dto.d.ts.map +1 -0
  59. package/dist/dto/list-timesheets.dto.js +23 -0
  60. package/dist/dto/list-timesheets.dto.js.map +1 -0
  61. package/dist/dto/reorder-collaborator-types.dto.d.ts +4 -0
  62. package/dist/dto/reorder-collaborator-types.dto.d.ts.map +1 -0
  63. package/dist/dto/reorder-collaborator-types.dto.js +25 -0
  64. package/dist/dto/reorder-collaborator-types.dto.js.map +1 -0
  65. package/dist/operations.service.d.ts +340 -271
  66. package/dist/operations.service.d.ts.map +1 -1
  67. package/dist/operations.service.js +1007 -1043
  68. package/dist/operations.service.js.map +1 -1
  69. package/dist/operations.service.spec.js +0 -22
  70. package/dist/operations.service.spec.js.map +1 -1
  71. package/hedhog/data/menu.yaml +0 -36
  72. package/hedhog/data/route.yaml +42 -73
  73. package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +8 -1
  74. package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +15 -10
  75. package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +108 -213
  76. package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +251 -2039
  77. package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +167 -60
  78. package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +70 -301
  79. package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +102 -51
  80. package/hedhog/frontend/app/_lib/types.ts.ejs +19 -24
  81. package/hedhog/frontend/app/_lib/utils/format.ts.ejs +14 -9
  82. package/hedhog/frontend/app/approvals/page.tsx.ejs +842 -150
  83. package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +445 -153
  84. package/hedhog/frontend/app/collaborators/page.tsx.ejs +118 -49
  85. package/hedhog/frontend/app/contracts/[id]/page.tsx.ejs +2 -2
  86. package/hedhog/frontend/app/contracts/page.tsx.ejs +215 -617
  87. package/hedhog/frontend/app/departments/page.tsx.ejs +257 -113
  88. package/hedhog/frontend/app/projects/page.tsx.ejs +90 -51
  89. package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +412 -147
  90. package/hedhog/frontend/app/time-off/page.tsx.ejs +400 -123
  91. package/hedhog/frontend/app/timesheets/page.tsx.ejs +460 -365
  92. package/hedhog/frontend/messages/en.json +143 -14
  93. package/hedhog/frontend/messages/pt.json +192 -54
  94. package/hedhog/table/operations_contract.yaml +0 -9
  95. package/package.json +4 -4
  96. package/src/controllers/operations-approvals.controller.ts +9 -3
  97. package/src/controllers/operations-collaborators.controller.ts +15 -2
  98. package/src/controllers/operations-contracts.controller.ts +8 -92
  99. package/src/controllers/operations-org-structure.controller.ts +17 -4
  100. package/src/controllers/operations-projects.controller.ts +10 -4
  101. package/src/controllers/operations-timesheets.controller.ts +17 -8
  102. package/src/dto/list-approvals.dto.ts +12 -0
  103. package/src/dto/list-collaborator-types.dto.ts +7 -2
  104. package/src/dto/list-collaborators.dto.ts +4 -0
  105. package/src/dto/list-contracts.dto.ts +20 -0
  106. package/src/dto/list-departments.dto.ts +8 -0
  107. package/src/dto/list-projects.dto.ts +8 -0
  108. package/src/dto/list-schedule-adjustments.dto.ts +8 -0
  109. package/src/dto/list-time-off-requests.dto.ts +8 -0
  110. package/src/dto/list-timesheets.dto.ts +8 -0
  111. package/src/dto/reorder-collaborator-types.dto.ts +10 -0
  112. package/src/operations.service.spec.ts +0 -30
  113. package/src/operations.service.ts +1557 -1806
  114. package/hedhog/frontend/app/_components/contract-creation-wizard.tsx.ejs +0 -631
  115. package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +0 -526
  116. package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +0 -247
  117. package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +0 -3520
  118. package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +0 -380
  119. package/hedhog/frontend/app/team/page.tsx.ejs +0 -352
  120. package/hedhog/table/operations_contract_financial_term.yaml +0 -40
  121. package/hedhog/table/operations_contract_revision.yaml +0 -38
  122. package/hedhog/table/operations_contract_signature.yaml +0 -38
  123. package/hedhog/table/operations_contract_template.yaml +0 -58
@@ -1,7 +1,13 @@
1
1
  'use client';
2
2
 
3
- import { EmptyState, Page, SearchBar } from '@/components/entity-list';
3
+ import {
4
+ EmptyState,
5
+ Page,
6
+ PaginationFooter,
7
+ SearchBar,
8
+ } from '@/components/entity-list';
4
9
  import { Button } from '@/components/ui/button';
10
+ import { Card, CardContent } from '@/components/ui/card';
5
11
  import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
6
12
  import {
7
13
  Sheet,
@@ -18,8 +24,9 @@ import {
18
24
  TableHeader,
19
25
  TableRow,
20
26
  } from '@/components/ui/table';
27
+ import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
21
28
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
22
- import { Building2, Pencil, Users } from 'lucide-react';
29
+ import { Building2, LayoutGrid, List, Pencil, Users } from 'lucide-react';
23
30
  import { useTranslations } from 'next-intl';
24
31
  import Link from 'next/link';
25
32
  import { useMemo, useState } from 'react';
@@ -27,7 +34,7 @@ import { OperationsHeader } from '../_components/operations-header';
27
34
  import { StatusBadge } from '../_components/status-badge';
28
35
  import { fetchOperations, mutateOperations } from '../_lib/api';
29
36
  import { useOperationsAccess } from '../_lib/hooks/use-operations-access';
30
- import type { OperationsDepartment } from '../_lib/types';
37
+ import type { OperationsDepartment, PaginatedResponse } from '../_lib/types';
31
38
  import { formatEnumLabel, getStatusBadgeClass } from '../_lib/utils/format';
32
39
 
33
40
  type DepartmentFormState = {
@@ -47,12 +54,28 @@ const EMPTY_FORM_STATE: DepartmentFormState = {
47
54
  export default function OperationsDepartmentsPage() {
48
55
  const t = useTranslations('operations.DepartmentsPage');
49
56
  const commonT = useTranslations('operations.Common');
57
+ const collaboratorFormT = useTranslations('operations.CollaboratorFormPage');
50
58
  const { request, showToastHandler, currentLocaleCode } = useApp();
59
+ const getStatusLabel = (value?: string | null) =>
60
+ value
61
+ ? collaboratorFormT.has(`options.statuses.${value}`)
62
+ ? collaboratorFormT(`options.statuses.${value}`)
63
+ : formatEnumLabel(value)
64
+ : '-';
51
65
  const access = useOperationsAccess();
52
66
  const [search, setSearch] = useState('');
53
67
  const [statusFilter, setStatusFilter] = useState<
54
68
  'all' | 'active' | 'inactive'
55
69
  >('all');
70
+ const [page, setPage] = useState(1);
71
+ const [pageSize, setPageSize] = useState(12);
72
+ const [viewMode, setViewMode] = useState<'table' | 'cards'>(() => {
73
+ if (typeof window === 'undefined') return 'table';
74
+ const saved = window.localStorage.getItem(
75
+ 'operations-departments-view-mode'
76
+ );
77
+ return saved === 'cards' ? 'cards' : 'table';
78
+ });
56
79
  const [isSheetOpen, setIsSheetOpen] = useState(false);
57
80
  const [isSaving, setIsSaving] = useState(false);
58
81
  const [editingDepartment, setEditingDepartment] =
@@ -60,39 +83,35 @@ export default function OperationsDepartmentsPage() {
60
83
  const [form, setForm] = useState<DepartmentFormState>(EMPTY_FORM_STATE);
61
84
 
62
85
  const {
63
- data: departments = [],
86
+ data: departmentsResponse,
64
87
  isLoading,
65
88
  refetch,
66
- } = useQuery<OperationsDepartment[]>({
67
- queryKey: ['operations-departments-list', currentLocaleCode],
89
+ } = useQuery<PaginatedResponse<OperationsDepartment>>({
90
+ queryKey: [
91
+ 'operations-departments-list',
92
+ currentLocaleCode,
93
+ search,
94
+ statusFilter,
95
+ page,
96
+ pageSize,
97
+ ],
68
98
  enabled: access.isDirector,
69
- queryFn: () =>
70
- fetchOperations<OperationsDepartment[]>(
99
+ queryFn: () => {
100
+ const params = new URLSearchParams({
101
+ page: String(page),
102
+ pageSize: String(pageSize),
103
+ });
104
+ if (search.trim()) params.set('search', search.trim());
105
+ if (statusFilter !== 'all') params.set('status', statusFilter);
106
+ return fetchOperations<PaginatedResponse<OperationsDepartment>>(
71
107
  request,
72
- '/operations/departments'
73
- ),
108
+ `/operations/departments?${params.toString()}`
109
+ );
110
+ },
111
+ placeholderData: (previous) => previous,
74
112
  });
75
113
 
76
- const filteredDepartments = useMemo(
77
- () =>
78
- departments.filter((department) => {
79
- const matchesSearch = !search.trim()
80
- ? true
81
- : [department.name, department.code, department.description]
82
- .filter(Boolean)
83
- .some((value) =>
84
- String(value)
85
- .toLowerCase()
86
- .includes(search.trim().toLowerCase())
87
- );
88
-
89
- const matchesStatus =
90
- statusFilter === 'all' ? true : department.status === statusFilter;
91
-
92
- return matchesSearch && matchesStatus;
93
- }),
94
- [departments, search, statusFilter]
95
- );
114
+ const departments = departmentsResponse?.data ?? [];
96
115
 
97
116
  const statsCards = useMemo(
98
117
  () => [
@@ -127,6 +146,14 @@ export default function OperationsDepartmentsPage() {
127
146
  [departments, t]
128
147
  );
129
148
 
149
+ const handleViewModeChange = (value: string) => {
150
+ if (value !== 'table' && value !== 'cards') return;
151
+ setViewMode(value as 'table' | 'cards');
152
+ if (typeof window !== 'undefined') {
153
+ window.localStorage.setItem('operations-departments-view-mode', value);
154
+ }
155
+ };
156
+
130
157
  const updateForm = <K extends keyof DepartmentFormState>(
131
158
  field: K,
132
159
  value: DepartmentFormState[K]
@@ -257,97 +284,202 @@ export default function OperationsDepartmentsPage() {
257
284
 
258
285
  <KpiCardsGrid items={statsCards} columns={4} />
259
286
 
260
- <div className="flex flex-col gap-4">
261
- <SearchBar
262
- searchQuery={search}
263
- onSearchChange={setSearch}
264
- onSearch={() => undefined}
265
- placeholder={t('searchPlaceholder')}
266
- controls={[
267
- {
268
- id: 'status',
269
- type: 'select',
270
- value: statusFilter,
271
- onChange: (value) =>
272
- setStatusFilter(
273
- (value as 'all' | 'active' | 'inactive') ?? 'all'
274
- ),
275
- placeholder: commonT('labels.status'),
276
- options: [
277
- { value: 'all', label: commonT('filters.allStatuses') },
278
- { value: 'active', label: formatEnumLabel('active') },
279
- { value: 'inactive', label: formatEnumLabel('inactive') },
280
- ],
281
- },
282
- ]}
283
- />
287
+ <div className="flex min-w-0 flex-col gap-4 xl:flex-row xl:items-center">
288
+ <div className="flex-1">
289
+ <SearchBar
290
+ searchQuery={search}
291
+ onSearchChange={(value) => {
292
+ setSearch(value);
293
+ setPage(1);
294
+ }}
295
+ showSearchButton={false}
296
+ debounceMs={500}
297
+ placeholder={t('searchPlaceholder')}
298
+ controls={[
299
+ {
300
+ id: 'status',
301
+ type: 'select',
302
+ value: statusFilter,
303
+ onChange: (value) => {
304
+ setStatusFilter(
305
+ (value as 'all' | 'active' | 'inactive') ?? 'all'
306
+ );
307
+ setPage(1);
308
+ },
309
+ placeholder: commonT('labels.status'),
310
+ options: [
311
+ { value: 'all', label: commonT('filters.allStatuses') },
312
+ { value: 'active', label: getStatusLabel('active') },
313
+ { value: 'inactive', label: getStatusLabel('inactive') },
314
+ ],
315
+ },
316
+ ]}
317
+ />
318
+ </div>
319
+
320
+ <div className="flex items-center justify-between gap-3 xl:justify-end">
321
+ <span className="text-xs font-medium text-muted-foreground">
322
+ {t('viewMode')}
323
+ </span>
324
+ <ToggleGroup
325
+ type="single"
326
+ value={viewMode}
327
+ onValueChange={handleViewModeChange}
328
+ variant="outline"
329
+ size="sm"
330
+ aria-label={t('viewMode')}
331
+ >
332
+ <ToggleGroupItem
333
+ value="table"
334
+ className="gap-1.5 px-2.5"
335
+ aria-label={t('viewModeTable')}
336
+ >
337
+ <List className="h-4 w-4" />
338
+ <span className="hidden sm:inline">{t('viewModeTable')}</span>
339
+ </ToggleGroupItem>
340
+ <ToggleGroupItem
341
+ value="cards"
342
+ className="gap-1.5 px-2.5"
343
+ aria-label={t('viewModeCards')}
344
+ >
345
+ <LayoutGrid className="h-4 w-4" />
346
+ <span className="hidden sm:inline">{t('viewModeCards')}</span>
347
+ </ToggleGroupItem>
348
+ </ToggleGroup>
349
+ </div>
284
350
  </div>
285
351
 
286
352
  {isLoading ? (
287
353
  <div className="rounded-md border px-4 py-6 text-sm text-muted-foreground">
288
354
  {commonT('actions.refresh')}...
289
355
  </div>
290
- ) : filteredDepartments.length > 0 ? (
291
- <div className="overflow-x-auto rounded-md border">
292
- <Table>
293
- <TableHeader>
294
- <TableRow>
295
- <TableHead>{t('columns.name')}</TableHead>
296
- <TableHead>{t('columns.code')}</TableHead>
297
- <TableHead>{t('columns.description')}</TableHead>
298
- <TableHead>{t('columns.collaborators')}</TableHead>
299
- <TableHead>{commonT('labels.status')}</TableHead>
300
- <TableHead className="text-right">
301
- {commonT('labels.actions')}
302
- </TableHead>
303
- </TableRow>
304
- </TableHeader>
305
- <TableBody>
306
- {filteredDepartments.map((department) => (
307
- <TableRow key={department.id}>
308
- <TableCell className="font-medium">
309
- {department.name}
310
- </TableCell>
311
- <TableCell>{department.code || '—'}</TableCell>
312
- <TableCell className="max-w-md text-sm text-muted-foreground">
313
- {department.description || commonT('labels.notAvailable')}
314
- </TableCell>
315
- <TableCell>
316
- {Number(department.collaboratorCount ?? 0)}
317
- </TableCell>
318
- <TableCell>
356
+ ) : departments.length > 0 ? (
357
+ viewMode === 'cards' ? (
358
+ <div className="grid gap-4 md:grid-cols-2 2xl:grid-cols-3">
359
+ {departments.map((department) => (
360
+ <Card
361
+ key={department.id}
362
+ className="cursor-pointer overflow-hidden border-border/60 py-0 shadow-sm transition-all hover:-translate-y-0.5 hover:shadow-md"
363
+ onClick={() => openEditSheet(department)}
364
+ >
365
+ <CardContent className="space-y-3 p-4">
366
+ <div className="flex items-start justify-between gap-3">
367
+ <div className="min-w-0">
368
+ <div className="truncate font-semibold">
369
+ {department.name}
370
+ </div>
371
+ {department.code ? (
372
+ <div className="truncate text-xs text-muted-foreground">
373
+ {department.code}
374
+ </div>
375
+ ) : null}
376
+ </div>
319
377
  <StatusBadge
320
- label={formatEnumLabel(department.status)}
378
+ label={getStatusLabel(department.status)}
321
379
  className={getStatusBadgeClass(department.status)}
322
380
  />
323
- </TableCell>
324
- <TableCell>
325
- <div className="flex justify-end gap-2">
326
- <Button
327
- variant="outline"
328
- size="icon"
329
- className="cursor-pointer"
330
- onClick={() => openEditSheet(department)}
331
- >
332
- <Pencil className="size-4" />
333
- </Button>
334
- <Button
335
- variant="outline"
336
- size="sm"
337
- className="cursor-pointer"
338
- onClick={() => void toggleStatus(department)}
339
- >
340
- {department.status === 'inactive'
341
- ? commonT('actions.activate')
342
- : commonT('actions.deactivate')}
343
- </Button>
344
- </div>
345
- </TableCell>
381
+ </div>
382
+ <div className="flex flex-wrap gap-2">
383
+ <span className="inline-flex items-center rounded-full bg-muted px-2.5 py-1 text-xs font-medium text-muted-foreground">
384
+ {Number(department.collaboratorCount ?? 0)}{' '}
385
+ {commonT('labels.collaborators')}
386
+ </span>
387
+ </div>
388
+ {department.description ? (
389
+ <p className="line-clamp-2 text-sm text-muted-foreground">
390
+ {department.description}
391
+ </p>
392
+ ) : null}
393
+ <div className="flex justify-end gap-2 border-t border-border/60 pt-3">
394
+ <Button
395
+ variant="outline"
396
+ size="icon"
397
+ onClick={(e) => {
398
+ e.stopPropagation();
399
+ openEditSheet(department);
400
+ }}
401
+ >
402
+ <Pencil className="size-4" />
403
+ </Button>
404
+ <Button
405
+ variant="outline"
406
+ size="sm"
407
+ onClick={(e) => {
408
+ e.stopPropagation();
409
+ void toggleStatus(department);
410
+ }}
411
+ >
412
+ {department.status === 'inactive'
413
+ ? commonT('actions.activate')
414
+ : commonT('actions.deactivate')}
415
+ </Button>
416
+ </div>
417
+ </CardContent>
418
+ </Card>
419
+ ))}
420
+ </div>
421
+ ) : (
422
+ <div className="overflow-x-auto rounded-md border">
423
+ <Table>
424
+ <TableHeader>
425
+ <TableRow>
426
+ <TableHead>{t('columns.name')}</TableHead>
427
+ <TableHead>{t('columns.code')}</TableHead>
428
+ <TableHead>{t('columns.description')}</TableHead>
429
+ <TableHead>{t('columns.collaborators')}</TableHead>
430
+ <TableHead>{commonT('labels.status')}</TableHead>
431
+ <TableHead className="text-right">
432
+ {commonT('labels.actions')}
433
+ </TableHead>
346
434
  </TableRow>
347
- ))}
348
- </TableBody>
349
- </Table>
350
- </div>
435
+ </TableHeader>
436
+ <TableBody>
437
+ {departments.map((department) => (
438
+ <TableRow key={department.id}>
439
+ <TableCell className="font-medium">
440
+ {department.name}
441
+ </TableCell>
442
+ <TableCell>{department.code || '—'}</TableCell>
443
+ <TableCell className="max-w-md text-sm text-muted-foreground">
444
+ {department.description || commonT('labels.notAvailable')}
445
+ </TableCell>
446
+ <TableCell>
447
+ {Number(department.collaboratorCount ?? 0)}
448
+ </TableCell>
449
+ <TableCell>
450
+ <StatusBadge
451
+ label={getStatusLabel(department.status)}
452
+ className={getStatusBadgeClass(department.status)}
453
+ />
454
+ </TableCell>
455
+ <TableCell>
456
+ <div className="flex justify-end gap-2">
457
+ <Button
458
+ variant="outline"
459
+ size="icon"
460
+ className="cursor-pointer"
461
+ onClick={() => openEditSheet(department)}
462
+ >
463
+ <Pencil className="size-4" />
464
+ </Button>
465
+ <Button
466
+ variant="outline"
467
+ size="sm"
468
+ className="cursor-pointer"
469
+ onClick={() => void toggleStatus(department)}
470
+ >
471
+ {department.status === 'inactive'
472
+ ? commonT('actions.activate')
473
+ : commonT('actions.deactivate')}
474
+ </Button>
475
+ </div>
476
+ </TableCell>
477
+ </TableRow>
478
+ ))}
479
+ </TableBody>
480
+ </Table>
481
+ </div>
482
+ )
351
483
  ) : (
352
484
  <EmptyState
353
485
  icon={<Building2 className="size-12" />}
@@ -358,6 +490,18 @@ export default function OperationsDepartmentsPage() {
358
490
  />
359
491
  )}
360
492
 
493
+ <PaginationFooter
494
+ currentPage={page}
495
+ pageSize={pageSize}
496
+ totalItems={departmentsResponse?.total ?? 0}
497
+ pageSizeOptions={[12, 24, 48]}
498
+ onPageChange={setPage}
499
+ onPageSizeChange={(size) => {
500
+ setPageSize(size);
501
+ setPage(1);
502
+ }}
503
+ />
504
+
361
505
  <Sheet open={isSheetOpen} onOpenChange={setIsSheetOpen}>
362
506
  <SheetContent className="w-full overflow-y-auto sm:max-w-xl">
363
507
  <SheetHeader>
@@ -438,8 +582,8 @@ export default function OperationsDepartmentsPage() {
438
582
  }
439
583
  className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
440
584
  >
441
- <option value="active">{formatEnumLabel('active')}</option>
442
- <option value="inactive">{formatEnumLabel('inactive')}</option>
585
+ <option value="active">{getStatusLabel('active')}</option>
586
+ <option value="inactive">{getStatusLabel('inactive')}</option>
443
587
  </select>
444
588
  </div>
445
589