@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.
- package/dist/controllers/operations-approvals.controller.d.ts +114 -1
- package/dist/controllers/operations-approvals.controller.d.ts.map +1 -1
- package/dist/controllers/operations-approvals.controller.js +16 -3
- package/dist/controllers/operations-approvals.controller.js.map +1 -1
- package/dist/controllers/operations-collaborators.controller.d.ts +16 -1
- package/dist/controllers/operations-collaborators.controller.d.ts.map +1 -1
- package/dist/controllers/operations-collaborators.controller.js +16 -3
- package/dist/controllers/operations-collaborators.controller.js.map +1 -1
- package/dist/controllers/operations-contracts.controller.d.ts +14 -453
- package/dist/controllers/operations-contracts.controller.d.ts.map +1 -1
- package/dist/controllers/operations-contracts.controller.js +11 -112
- package/dist/controllers/operations-contracts.controller.js.map +1 -1
- package/dist/controllers/operations-org-structure.controller.d.ts +65 -2
- package/dist/controllers/operations-org-structure.controller.d.ts.map +1 -1
- package/dist/controllers/operations-org-structure.controller.js +18 -5
- package/dist/controllers/operations-org-structure.controller.js.map +1 -1
- package/dist/controllers/operations-projects.controller.d.ts +28 -4
- package/dist/controllers/operations-projects.controller.d.ts.map +1 -1
- package/dist/controllers/operations-projects.controller.js +17 -5
- package/dist/controllers/operations-projects.controller.js.map +1 -1
- package/dist/controllers/operations-timesheets.controller.d.ts +31 -4
- package/dist/controllers/operations-timesheets.controller.d.ts.map +1 -1
- package/dist/controllers/operations-timesheets.controller.js +16 -11
- package/dist/controllers/operations-timesheets.controller.js.map +1 -1
- package/dist/dto/list-approvals.dto.d.ts +6 -0
- package/dist/dto/list-approvals.dto.d.ts.map +1 -0
- package/dist/dto/list-approvals.dto.js +28 -0
- package/dist/dto/list-approvals.dto.js.map +1 -0
- package/dist/dto/list-collaborator-types.dto.d.ts +3 -1
- package/dist/dto/list-collaborator-types.dto.d.ts.map +1 -1
- package/dist/dto/list-collaborator-types.dto.js +7 -1
- package/dist/dto/list-collaborator-types.dto.js.map +1 -1
- package/dist/dto/list-collaborators.dto.d.ts +1 -0
- package/dist/dto/list-collaborators.dto.d.ts.map +1 -1
- package/dist/dto/list-collaborators.dto.js +5 -0
- package/dist/dto/list-collaborators.dto.js.map +1 -1
- package/dist/dto/list-contracts.dto.d.ts +8 -0
- package/dist/dto/list-contracts.dto.d.ts.map +1 -0
- package/dist/dto/list-contracts.dto.js +38 -0
- package/dist/dto/list-contracts.dto.js.map +1 -0
- package/dist/dto/list-departments.dto.d.ts +5 -0
- package/dist/dto/list-departments.dto.d.ts.map +1 -0
- package/dist/dto/list-departments.dto.js +23 -0
- package/dist/dto/list-departments.dto.js.map +1 -0
- package/dist/dto/list-projects.dto.d.ts +5 -0
- package/dist/dto/list-projects.dto.d.ts.map +1 -0
- package/dist/dto/list-projects.dto.js +23 -0
- package/dist/dto/list-projects.dto.js.map +1 -0
- package/dist/dto/list-schedule-adjustments.dto.d.ts +5 -0
- package/dist/dto/list-schedule-adjustments.dto.d.ts.map +1 -0
- package/dist/dto/list-schedule-adjustments.dto.js +23 -0
- package/dist/dto/list-schedule-adjustments.dto.js.map +1 -0
- package/dist/dto/list-time-off-requests.dto.d.ts +5 -0
- package/dist/dto/list-time-off-requests.dto.d.ts.map +1 -0
- package/dist/dto/list-time-off-requests.dto.js +23 -0
- package/dist/dto/list-time-off-requests.dto.js.map +1 -0
- package/dist/dto/list-timesheets.dto.d.ts +5 -0
- package/dist/dto/list-timesheets.dto.d.ts.map +1 -0
- package/dist/dto/list-timesheets.dto.js +23 -0
- package/dist/dto/list-timesheets.dto.js.map +1 -0
- package/dist/dto/reorder-collaborator-types.dto.d.ts +4 -0
- package/dist/dto/reorder-collaborator-types.dto.d.ts.map +1 -0
- package/dist/dto/reorder-collaborator-types.dto.js +25 -0
- package/dist/dto/reorder-collaborator-types.dto.js.map +1 -0
- package/dist/operations.service.d.ts +340 -271
- package/dist/operations.service.d.ts.map +1 -1
- package/dist/operations.service.js +1007 -1043
- package/dist/operations.service.js.map +1 -1
- package/dist/operations.service.spec.js +0 -22
- package/dist/operations.service.spec.js.map +1 -1
- package/hedhog/data/menu.yaml +0 -36
- package/hedhog/data/route.yaml +42 -73
- package/hedhog/frontend/app/_components/collaborator-form-screen.tsx.ejs +8 -1
- package/hedhog/frontend/app/_components/collaborator-select-with-create.tsx.ejs +15 -10
- package/hedhog/frontend/app/_components/contract-details-screen.tsx.ejs +108 -213
- package/hedhog/frontend/app/_components/contract-form-screen.tsx.ejs +251 -2039
- package/hedhog/frontend/app/_components/project-details-screen.tsx.ejs +167 -60
- package/hedhog/frontend/app/_components/project-form-screen.tsx.ejs +70 -301
- package/hedhog/frontend/app/_components/system-user-select-with-create.tsx.ejs +102 -51
- package/hedhog/frontend/app/_lib/types.ts.ejs +19 -24
- package/hedhog/frontend/app/_lib/utils/format.ts.ejs +14 -9
- package/hedhog/frontend/app/approvals/page.tsx.ejs +842 -150
- package/hedhog/frontend/app/collaborator-types/page.tsx.ejs +445 -153
- package/hedhog/frontend/app/collaborators/page.tsx.ejs +118 -49
- package/hedhog/frontend/app/contracts/[id]/page.tsx.ejs +2 -2
- package/hedhog/frontend/app/contracts/page.tsx.ejs +215 -617
- package/hedhog/frontend/app/departments/page.tsx.ejs +257 -113
- package/hedhog/frontend/app/projects/page.tsx.ejs +90 -51
- package/hedhog/frontend/app/schedule-adjustments/page.tsx.ejs +412 -147
- package/hedhog/frontend/app/time-off/page.tsx.ejs +400 -123
- package/hedhog/frontend/app/timesheets/page.tsx.ejs +460 -365
- package/hedhog/frontend/messages/en.json +143 -14
- package/hedhog/frontend/messages/pt.json +192 -54
- package/hedhog/table/operations_contract.yaml +0 -9
- package/package.json +4 -4
- package/src/controllers/operations-approvals.controller.ts +9 -3
- package/src/controllers/operations-collaborators.controller.ts +15 -2
- package/src/controllers/operations-contracts.controller.ts +8 -92
- package/src/controllers/operations-org-structure.controller.ts +17 -4
- package/src/controllers/operations-projects.controller.ts +10 -4
- package/src/controllers/operations-timesheets.controller.ts +17 -8
- package/src/dto/list-approvals.dto.ts +12 -0
- package/src/dto/list-collaborator-types.dto.ts +7 -2
- package/src/dto/list-collaborators.dto.ts +4 -0
- package/src/dto/list-contracts.dto.ts +20 -0
- package/src/dto/list-departments.dto.ts +8 -0
- package/src/dto/list-projects.dto.ts +8 -0
- package/src/dto/list-schedule-adjustments.dto.ts +8 -0
- package/src/dto/list-time-off-requests.dto.ts +8 -0
- package/src/dto/list-timesheets.dto.ts +8 -0
- package/src/dto/reorder-collaborator-types.dto.ts +10 -0
- package/src/operations.service.spec.ts +0 -30
- package/src/operations.service.ts +1557 -1806
- package/hedhog/frontend/app/_components/contract-creation-wizard.tsx.ejs +0 -631
- package/hedhog/frontend/app/_components/contract-template-form-screen.tsx.ejs +0 -526
- package/hedhog/frontend/app/_components/contract-template-select-with-create.tsx.ejs +0 -247
- package/hedhog/frontend/app/_components/contract-wizard-sheet.tsx.ejs +0 -3520
- package/hedhog/frontend/app/contracts/templates/page.tsx.ejs +0 -380
- package/hedhog/frontend/app/team/page.tsx.ejs +0 -352
- package/hedhog/table/operations_contract_financial_term.yaml +0 -40
- package/hedhog/table/operations_contract_revision.yaml +0 -38
- package/hedhog/table/operations_contract_signature.yaml +0 -38
- package/hedhog/table/operations_contract_template.yaml +0 -58
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
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:
|
|
86
|
+
data: departmentsResponse,
|
|
64
87
|
isLoading,
|
|
65
88
|
refetch,
|
|
66
|
-
} = useQuery<OperationsDepartment
|
|
67
|
-
queryKey: [
|
|
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
|
-
|
|
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
|
-
|
|
73
|
-
)
|
|
108
|
+
`/operations/departments?${params.toString()}`
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
placeholderData: (previous) => previous,
|
|
74
112
|
});
|
|
75
113
|
|
|
76
|
-
const
|
|
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
|
-
<
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
) :
|
|
291
|
-
|
|
292
|
-
<
|
|
293
|
-
|
|
294
|
-
<
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
<
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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={
|
|
378
|
+
label={getStatusLabel(department.status)}
|
|
321
379
|
className={getStatusBadgeClass(department.status)}
|
|
322
380
|
/>
|
|
323
|
-
</
|
|
324
|
-
<
|
|
325
|
-
<
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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">{
|
|
442
|
-
<option value="inactive">{
|
|
585
|
+
<option value="active">{getStatusLabel('active')}</option>
|
|
586
|
+
<option value="inactive">{getStatusLabel('inactive')}</option>
|
|
443
587
|
</select>
|
|
444
588
|
</div>
|
|
445
589
|
|