@hed-hog/finance 0.0.233 → 0.0.236
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/dto/create-cost-center.dto.d.ts +4 -0
- package/dist/dto/create-cost-center.dto.d.ts.map +1 -0
- package/dist/dto/create-cost-center.dto.js +24 -0
- package/dist/dto/create-cost-center.dto.js.map +1 -0
- package/dist/dto/create-finance-category.dto.d.ts +6 -0
- package/dist/dto/create-finance-category.dto.d.ts.map +1 -0
- package/dist/dto/create-finance-category.dto.js +37 -0
- package/dist/dto/create-finance-category.dto.js.map +1 -0
- package/dist/dto/create-period-close.dto.d.ts +7 -0
- package/dist/dto/create-period-close.dto.d.ts.map +1 -0
- package/dist/dto/create-period-close.dto.js +44 -0
- package/dist/dto/create-period-close.dto.js.map +1 -0
- package/dist/dto/move-finance-category.dto.d.ts +5 -0
- package/dist/dto/move-finance-category.dto.d.ts.map +1 -0
- package/dist/dto/move-finance-category.dto.js +32 -0
- package/dist/dto/move-finance-category.dto.js.map +1 -0
- package/dist/dto/update-cost-center.dto.d.ts +5 -0
- package/dist/dto/update-cost-center.dto.d.ts.map +1 -0
- package/dist/dto/update-cost-center.dto.js +32 -0
- package/dist/dto/update-cost-center.dto.js.map +1 -0
- package/dist/dto/update-finance-category.dto.d.ts +7 -0
- package/dist/dto/update-finance-category.dto.d.ts.map +1 -0
- package/dist/dto/update-finance-category.dto.js +46 -0
- package/dist/dto/update-finance-category.dto.js.map +1 -0
- package/dist/finance-audit-logs.controller.d.ts +13 -0
- package/dist/finance-audit-logs.controller.d.ts.map +1 -0
- package/dist/finance-audit-logs.controller.js +54 -0
- package/dist/finance-audit-logs.controller.js.map +1 -0
- package/dist/finance-categories.controller.d.ts +42 -0
- package/dist/finance-categories.controller.d.ts.map +1 -0
- package/dist/finance-categories.controller.js +84 -0
- package/dist/finance-categories.controller.js.map +1 -0
- package/dist/finance-cost-centers.controller.d.ts +32 -0
- package/dist/finance-cost-centers.controller.d.ts.map +1 -0
- package/dist/finance-cost-centers.controller.js +72 -0
- package/dist/finance-cost-centers.controller.js.map +1 -0
- package/dist/finance-installments.controller.d.ts +4 -0
- package/dist/finance-installments.controller.d.ts.map +1 -1
- package/dist/finance-period-close.controller.d.ts +27 -0
- package/dist/finance-period-close.controller.d.ts.map +1 -0
- package/dist/finance-period-close.controller.js +64 -0
- package/dist/finance-period-close.controller.js.map +1 -0
- package/dist/finance.module.d.ts.map +1 -1
- package/dist/finance.module.js +8 -0
- package/dist/finance.module.js.map +1 -1
- package/dist/finance.service.d.ts +119 -0
- package/dist/finance.service.d.ts.map +1 -1
- package/dist/finance.service.js +733 -36
- package/dist/finance.service.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/hedhog/data/route.yaml +108 -0
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +76 -6
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +76 -6
- package/hedhog/frontend/app/administration/audit-logs/page.tsx.ejs +309 -0
- package/hedhog/frontend/app/administration/categories/page.tsx.ejs +642 -0
- package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +371 -0
- package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +502 -0
- package/hedhog/frontend/messages/en.json +225 -0
- package/hedhog/frontend/messages/pt.json +225 -0
- package/package.json +5 -5
- package/src/dto/create-cost-center.dto.ts +9 -0
- package/src/dto/create-finance-category.dto.ts +21 -0
- package/src/dto/create-period-close.dto.ts +34 -0
- package/src/dto/move-finance-category.dto.ts +18 -0
- package/src/dto/update-cost-center.dto.ts +17 -0
- package/src/dto/update-finance-category.dto.ts +30 -0
- package/src/finance-audit-logs.controller.ts +30 -0
- package/src/finance-categories.controller.ts +52 -0
- package/src/finance-cost-centers.controller.ts +43 -0
- package/src/finance-period-close.controller.ts +34 -0
- package/src/finance.module.ts +8 -0
- package/src/finance.service.ts +1020 -29
- package/src/index.ts +4 -0
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
Download,
|
|
55
55
|
Edit,
|
|
56
56
|
Eye,
|
|
57
|
+
Loader2,
|
|
57
58
|
MoreHorizontal,
|
|
58
59
|
Paperclip,
|
|
59
60
|
Plus,
|
|
@@ -100,6 +101,10 @@ function NovoTituloSheet({
|
|
|
100
101
|
const [uploadedFileName, setUploadedFileName] = useState('');
|
|
101
102
|
const [isUploadingFile, setIsUploadingFile] = useState(false);
|
|
102
103
|
const [isExtractingFileData, setIsExtractingFileData] = useState(false);
|
|
104
|
+
const [extractionConfidence, setExtractionConfidence] = useState<
|
|
105
|
+
number | null
|
|
106
|
+
>(null);
|
|
107
|
+
const [extractionWarnings, setExtractionWarnings] = useState<string[]>([]);
|
|
103
108
|
const [uploadProgress, setUploadProgress] = useState(0);
|
|
104
109
|
|
|
105
110
|
const normalizeFilenameForDisplay = (filename: string) => {
|
|
@@ -164,6 +169,8 @@ function NovoTituloSheet({
|
|
|
164
169
|
form.reset();
|
|
165
170
|
setUploadedFileId(null);
|
|
166
171
|
setUploadedFileName('');
|
|
172
|
+
setExtractionConfidence(null);
|
|
173
|
+
setExtractionWarnings([]);
|
|
167
174
|
setOpen(false);
|
|
168
175
|
showToastHandler?.('success', 'Título criado com sucesso');
|
|
169
176
|
} catch {
|
|
@@ -175,6 +182,8 @@ function NovoTituloSheet({
|
|
|
175
182
|
form.reset();
|
|
176
183
|
setUploadedFileId(null);
|
|
177
184
|
setUploadedFileName('');
|
|
185
|
+
setExtractionConfidence(null);
|
|
186
|
+
setExtractionWarnings([]);
|
|
178
187
|
setUploadProgress(0);
|
|
179
188
|
setOpen(false);
|
|
180
189
|
};
|
|
@@ -228,6 +237,9 @@ function NovoTituloSheet({
|
|
|
228
237
|
centroCustoId?: string;
|
|
229
238
|
canal?: string;
|
|
230
239
|
descricao?: string | null;
|
|
240
|
+
confidence?: number | null;
|
|
241
|
+
confidenceLevel?: 'low' | 'high' | null;
|
|
242
|
+
warnings?: string[];
|
|
231
243
|
}>({
|
|
232
244
|
url: '/finance/accounts-receivable/installments/extract-from-file',
|
|
233
245
|
method: 'POST',
|
|
@@ -237,6 +249,14 @@ function NovoTituloSheet({
|
|
|
237
249
|
});
|
|
238
250
|
|
|
239
251
|
const extracted = extraction.data || {};
|
|
252
|
+
setExtractionConfidence(
|
|
253
|
+
typeof extracted.confidence === 'number' ? extracted.confidence : null
|
|
254
|
+
);
|
|
255
|
+
setExtractionWarnings(
|
|
256
|
+
Array.isArray(extracted.warnings)
|
|
257
|
+
? extracted.warnings.filter(Boolean)
|
|
258
|
+
: []
|
|
259
|
+
);
|
|
240
260
|
|
|
241
261
|
if (extracted.documento) {
|
|
242
262
|
form.setValue('documento', extracted.documento, {
|
|
@@ -297,6 +317,8 @@ function NovoTituloSheet({
|
|
|
297
317
|
'Dados da fatura extraídos e preenchidos automaticamente'
|
|
298
318
|
);
|
|
299
319
|
} catch {
|
|
320
|
+
setExtractionConfidence(null);
|
|
321
|
+
setExtractionWarnings([]);
|
|
300
322
|
showToastHandler?.(
|
|
301
323
|
'error',
|
|
302
324
|
'Não foi possível extrair os dados automaticamente'
|
|
@@ -307,6 +329,8 @@ function NovoTituloSheet({
|
|
|
307
329
|
} catch {
|
|
308
330
|
setUploadedFileId(null);
|
|
309
331
|
setUploadedFileName('');
|
|
332
|
+
setExtractionConfidence(null);
|
|
333
|
+
setExtractionWarnings([]);
|
|
310
334
|
setUploadProgress(0);
|
|
311
335
|
showToastHandler?.('error', 'Não foi possível enviar o arquivo');
|
|
312
336
|
} finally {
|
|
@@ -344,10 +368,16 @@ function NovoTituloSheet({
|
|
|
344
368
|
|
|
345
369
|
setUploadedFileId(null);
|
|
346
370
|
setUploadedFileName('');
|
|
371
|
+
setExtractionConfidence(null);
|
|
372
|
+
setExtractionWarnings([]);
|
|
347
373
|
setUploadProgress(0);
|
|
348
374
|
void uploadRelatedFile(file);
|
|
349
375
|
}}
|
|
350
|
-
disabled={
|
|
376
|
+
disabled={
|
|
377
|
+
isUploadingFile ||
|
|
378
|
+
isExtractingFileData ||
|
|
379
|
+
form.formState.isSubmitting
|
|
380
|
+
}
|
|
351
381
|
/>
|
|
352
382
|
</div>
|
|
353
383
|
{isUploadingFile && (
|
|
@@ -364,10 +394,36 @@ function NovoTituloSheet({
|
|
|
364
394
|
</p>
|
|
365
395
|
)}
|
|
366
396
|
{isExtractingFileData && (
|
|
367
|
-
<
|
|
368
|
-
|
|
369
|
-
|
|
397
|
+
<div className="rounded-md border border-primary/30 bg-primary/5 p-3">
|
|
398
|
+
<div className="flex items-center gap-2 text-sm font-medium text-primary">
|
|
399
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
400
|
+
Analisando documento com IA
|
|
401
|
+
</div>
|
|
402
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
403
|
+
Os campos serão preenchidos automaticamente em instantes.
|
|
404
|
+
Revise os dados antes de salvar.
|
|
405
|
+
</p>
|
|
406
|
+
</div>
|
|
370
407
|
)}
|
|
408
|
+
{!isExtractingFileData &&
|
|
409
|
+
extractionConfidence !== null &&
|
|
410
|
+
extractionConfidence < 70 && (
|
|
411
|
+
<div className="rounded-md border border-destructive/30 bg-destructive/5 p-3">
|
|
412
|
+
<div className="text-sm font-medium text-destructive">
|
|
413
|
+
Confiança da extração:{' '}
|
|
414
|
+
{Math.round(extractionConfidence)}%
|
|
415
|
+
</div>
|
|
416
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
417
|
+
Revise principalmente valor e vencimento antes de
|
|
418
|
+
salvar.
|
|
419
|
+
</p>
|
|
420
|
+
{extractionWarnings.length > 0 && (
|
|
421
|
+
<p className="mt-1 text-xs text-muted-foreground">
|
|
422
|
+
{extractionWarnings[0]}
|
|
423
|
+
</p>
|
|
424
|
+
)}
|
|
425
|
+
</div>
|
|
426
|
+
)}
|
|
371
427
|
</div>
|
|
372
428
|
|
|
373
429
|
<FormField
|
|
@@ -577,8 +633,22 @@ function NovoTituloSheet({
|
|
|
577
633
|
<Button type="button" variant="outline" onClick={handleCancel}>
|
|
578
634
|
{t('common.cancel')}
|
|
579
635
|
</Button>
|
|
580
|
-
<Button
|
|
581
|
-
|
|
636
|
+
<Button
|
|
637
|
+
type="submit"
|
|
638
|
+
disabled={
|
|
639
|
+
form.formState.isSubmitting ||
|
|
640
|
+
isUploadingFile ||
|
|
641
|
+
isExtractingFileData
|
|
642
|
+
}
|
|
643
|
+
>
|
|
644
|
+
{(isUploadingFile || isExtractingFileData) && (
|
|
645
|
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
646
|
+
)}
|
|
647
|
+
{isExtractingFileData
|
|
648
|
+
? 'Preenchendo com IA...'
|
|
649
|
+
: isUploadingFile
|
|
650
|
+
? 'Enviando arquivo...'
|
|
651
|
+
: t('common.save')}
|
|
582
652
|
</Button>
|
|
583
653
|
</div>
|
|
584
654
|
</form>
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Page, PageHeader, PaginationFooter } from '@/components/entity-list';
|
|
4
|
+
import { Badge } from '@/components/ui/badge';
|
|
5
|
+
import { Button } from '@/components/ui/button';
|
|
6
|
+
import {
|
|
7
|
+
Card,
|
|
8
|
+
CardContent,
|
|
9
|
+
CardDescription,
|
|
10
|
+
CardHeader,
|
|
11
|
+
CardTitle,
|
|
12
|
+
} from '@/components/ui/card';
|
|
13
|
+
import { Input } from '@/components/ui/input';
|
|
14
|
+
import {
|
|
15
|
+
Select,
|
|
16
|
+
SelectContent,
|
|
17
|
+
SelectItem,
|
|
18
|
+
SelectTrigger,
|
|
19
|
+
SelectValue,
|
|
20
|
+
} from '@/components/ui/select';
|
|
21
|
+
import {
|
|
22
|
+
Table,
|
|
23
|
+
TableBody,
|
|
24
|
+
TableCell,
|
|
25
|
+
TableHead,
|
|
26
|
+
TableHeader,
|
|
27
|
+
TableRow,
|
|
28
|
+
} from '@/components/ui/table';
|
|
29
|
+
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
30
|
+
import { Search, X } from 'lucide-react';
|
|
31
|
+
import { useTranslations } from 'next-intl';
|
|
32
|
+
import { useMemo, useState } from 'react';
|
|
33
|
+
|
|
34
|
+
type AuditLog = {
|
|
35
|
+
id: string;
|
|
36
|
+
actorUserId: string | null;
|
|
37
|
+
actorName: string;
|
|
38
|
+
actorEmail: string;
|
|
39
|
+
action: string;
|
|
40
|
+
entityTable: string;
|
|
41
|
+
entityId: string;
|
|
42
|
+
summary: string;
|
|
43
|
+
ipAddress: string;
|
|
44
|
+
createdAt: string | null;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type PaginatedAuditLogs = {
|
|
48
|
+
total: number;
|
|
49
|
+
lastPage: number;
|
|
50
|
+
page: number;
|
|
51
|
+
pageSize: number;
|
|
52
|
+
data: AuditLog[];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default function AuditLogsPage() {
|
|
56
|
+
const t = useTranslations('finance.AdminAuditLogsPage');
|
|
57
|
+
const { request } = useApp();
|
|
58
|
+
|
|
59
|
+
const [page, setPage] = useState(1);
|
|
60
|
+
const [pageSize, setPageSize] = useState(10);
|
|
61
|
+
const [search, setSearch] = useState('');
|
|
62
|
+
const [action, setAction] = useState('all');
|
|
63
|
+
const [entityTable, setEntityTable] = useState('all');
|
|
64
|
+
const [actorUserId, setActorUserId] = useState('');
|
|
65
|
+
const [from, setFrom] = useState('');
|
|
66
|
+
const [to, setTo] = useState('');
|
|
67
|
+
|
|
68
|
+
const { data, isLoading, refetch } = useQuery<PaginatedAuditLogs>({
|
|
69
|
+
queryKey: [
|
|
70
|
+
'finance-audit-logs',
|
|
71
|
+
page,
|
|
72
|
+
pageSize,
|
|
73
|
+
search,
|
|
74
|
+
action,
|
|
75
|
+
entityTable,
|
|
76
|
+
actorUserId,
|
|
77
|
+
from,
|
|
78
|
+
to,
|
|
79
|
+
],
|
|
80
|
+
queryFn: async () => {
|
|
81
|
+
const params = new URLSearchParams();
|
|
82
|
+
params.set('page', String(page));
|
|
83
|
+
params.set('pageSize', String(pageSize));
|
|
84
|
+
params.set('sortField', 'created_at');
|
|
85
|
+
params.set('sortOrder', 'desc');
|
|
86
|
+
|
|
87
|
+
if (search.trim()) params.set('search', search.trim());
|
|
88
|
+
if (action !== 'all') params.set('action', action);
|
|
89
|
+
if (entityTable !== 'all') params.set('entity_table', entityTable);
|
|
90
|
+
if (actorUserId.trim()) params.set('actor_user_id', actorUserId.trim());
|
|
91
|
+
if (from) params.set('from', from);
|
|
92
|
+
if (to) params.set('to', to);
|
|
93
|
+
|
|
94
|
+
const response = await request({
|
|
95
|
+
url: `/finance/audit-logs?${params.toString()}`,
|
|
96
|
+
method: 'GET',
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return (response.data || {
|
|
100
|
+
total: 0,
|
|
101
|
+
lastPage: 1,
|
|
102
|
+
page: 1,
|
|
103
|
+
pageSize,
|
|
104
|
+
data: [],
|
|
105
|
+
}) as PaginatedAuditLogs;
|
|
106
|
+
},
|
|
107
|
+
placeholderData: (old) => old,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const rows = data?.data || [];
|
|
111
|
+
|
|
112
|
+
const entityOptions = useMemo(() => {
|
|
113
|
+
const unique = new Set(rows.map((row) => row.entityTable).filter(Boolean));
|
|
114
|
+
return ['all', ...Array.from(unique).sort()];
|
|
115
|
+
}, [rows]);
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<Page>
|
|
119
|
+
<PageHeader
|
|
120
|
+
title={t('header.title')}
|
|
121
|
+
description={t('header.description')}
|
|
122
|
+
breadcrumbs={[
|
|
123
|
+
{ label: t('breadcrumbs.finance'), href: '/finance' },
|
|
124
|
+
{
|
|
125
|
+
label: t('breadcrumbs.administration'),
|
|
126
|
+
href: '/finance/administration',
|
|
127
|
+
},
|
|
128
|
+
{ label: t('breadcrumbs.current') },
|
|
129
|
+
]}
|
|
130
|
+
/>
|
|
131
|
+
|
|
132
|
+
<Card>
|
|
133
|
+
<CardHeader>
|
|
134
|
+
<CardTitle>{t('card.title')}</CardTitle>
|
|
135
|
+
<CardDescription>{t('card.description')}</CardDescription>
|
|
136
|
+
</CardHeader>
|
|
137
|
+
|
|
138
|
+
<CardContent className="space-y-4">
|
|
139
|
+
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-4">
|
|
140
|
+
<div className="relative lg:col-span-2">
|
|
141
|
+
<Search className="pointer-events-none absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
|
142
|
+
<Input
|
|
143
|
+
placeholder={t('filters.searchPlaceholder')}
|
|
144
|
+
value={search}
|
|
145
|
+
onChange={(event) => {
|
|
146
|
+
setSearch(event.target.value);
|
|
147
|
+
setPage(1);
|
|
148
|
+
}}
|
|
149
|
+
className="pl-8"
|
|
150
|
+
/>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<Select
|
|
154
|
+
value={action}
|
|
155
|
+
onValueChange={(value) => {
|
|
156
|
+
setAction(value);
|
|
157
|
+
setPage(1);
|
|
158
|
+
}}
|
|
159
|
+
>
|
|
160
|
+
<SelectTrigger>
|
|
161
|
+
<SelectValue placeholder={t('filters.action')} />
|
|
162
|
+
</SelectTrigger>
|
|
163
|
+
<SelectContent>
|
|
164
|
+
<SelectItem value="all">{t('filters.allActions')}</SelectItem>
|
|
165
|
+
<SelectItem value="create">create</SelectItem>
|
|
166
|
+
<SelectItem value="update">update</SelectItem>
|
|
167
|
+
<SelectItem value="delete">delete</SelectItem>
|
|
168
|
+
</SelectContent>
|
|
169
|
+
</Select>
|
|
170
|
+
|
|
171
|
+
<Input
|
|
172
|
+
placeholder={t('filters.userPlaceholder')}
|
|
173
|
+
value={actorUserId}
|
|
174
|
+
onChange={(event) => {
|
|
175
|
+
setActorUserId(event.target.value);
|
|
176
|
+
setPage(1);
|
|
177
|
+
}}
|
|
178
|
+
/>
|
|
179
|
+
|
|
180
|
+
<Select
|
|
181
|
+
value={entityTable}
|
|
182
|
+
onValueChange={(value) => {
|
|
183
|
+
setEntityTable(value);
|
|
184
|
+
setPage(1);
|
|
185
|
+
}}
|
|
186
|
+
>
|
|
187
|
+
<SelectTrigger>
|
|
188
|
+
<SelectValue placeholder={t('filters.entity')} />
|
|
189
|
+
</SelectTrigger>
|
|
190
|
+
<SelectContent>
|
|
191
|
+
{entityOptions.map((option) => (
|
|
192
|
+
<SelectItem key={option} value={option}>
|
|
193
|
+
{option === 'all' ? t('filters.allEntities') : option}
|
|
194
|
+
</SelectItem>
|
|
195
|
+
))}
|
|
196
|
+
</SelectContent>
|
|
197
|
+
</Select>
|
|
198
|
+
|
|
199
|
+
<Input
|
|
200
|
+
type="date"
|
|
201
|
+
value={from}
|
|
202
|
+
onChange={(event) => {
|
|
203
|
+
setFrom(event.target.value);
|
|
204
|
+
setPage(1);
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
|
|
208
|
+
<Input
|
|
209
|
+
type="date"
|
|
210
|
+
value={to}
|
|
211
|
+
onChange={(event) => {
|
|
212
|
+
setTo(event.target.value);
|
|
213
|
+
setPage(1);
|
|
214
|
+
}}
|
|
215
|
+
/>
|
|
216
|
+
|
|
217
|
+
<Button
|
|
218
|
+
variant="outline"
|
|
219
|
+
className="gap-2"
|
|
220
|
+
onClick={() => {
|
|
221
|
+
setSearch('');
|
|
222
|
+
setAction('all');
|
|
223
|
+
setEntityTable('all');
|
|
224
|
+
setActorUserId('');
|
|
225
|
+
setFrom('');
|
|
226
|
+
setTo('');
|
|
227
|
+
setPage(1);
|
|
228
|
+
refetch();
|
|
229
|
+
}}
|
|
230
|
+
>
|
|
231
|
+
<X className="h-4 w-4" />
|
|
232
|
+
{t('filters.clear')}
|
|
233
|
+
</Button>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
<div className="rounded-md border">
|
|
237
|
+
<Table>
|
|
238
|
+
<TableHeader>
|
|
239
|
+
<TableRow>
|
|
240
|
+
<TableHead>{t('table.headers.date')}</TableHead>
|
|
241
|
+
<TableHead>{t('table.headers.author')}</TableHead>
|
|
242
|
+
<TableHead>{t('table.headers.action')}</TableHead>
|
|
243
|
+
<TableHead>{t('table.headers.entity')}</TableHead>
|
|
244
|
+
<TableHead>{t('table.headers.record')}</TableHead>
|
|
245
|
+
<TableHead>{t('table.headers.summary')}</TableHead>
|
|
246
|
+
<TableHead>IP</TableHead>
|
|
247
|
+
</TableRow>
|
|
248
|
+
</TableHeader>
|
|
249
|
+
<TableBody>
|
|
250
|
+
{isLoading ? (
|
|
251
|
+
<TableRow>
|
|
252
|
+
<TableCell colSpan={7} className="h-24 text-center">
|
|
253
|
+
{t('table.loading')}
|
|
254
|
+
</TableCell>
|
|
255
|
+
</TableRow>
|
|
256
|
+
) : rows.length === 0 ? (
|
|
257
|
+
<TableRow>
|
|
258
|
+
<TableCell colSpan={7} className="h-24 text-center">
|
|
259
|
+
{t('table.empty')}
|
|
260
|
+
</TableCell>
|
|
261
|
+
</TableRow>
|
|
262
|
+
) : (
|
|
263
|
+
rows.map((row) => (
|
|
264
|
+
<TableRow key={row.id}>
|
|
265
|
+
<TableCell>
|
|
266
|
+
{row.createdAt
|
|
267
|
+
? new Date(row.createdAt).toLocaleString('pt-BR')
|
|
268
|
+
: '-'}
|
|
269
|
+
</TableCell>
|
|
270
|
+
<TableCell>
|
|
271
|
+
<div className="flex flex-col">
|
|
272
|
+
<span>{row.actorName || '-'}</span>
|
|
273
|
+
<span className="text-xs text-muted-foreground">
|
|
274
|
+
{row.actorEmail || row.actorUserId || '-'}
|
|
275
|
+
</span>
|
|
276
|
+
</div>
|
|
277
|
+
</TableCell>
|
|
278
|
+
<TableCell>
|
|
279
|
+
<Badge variant="outline">{row.action}</Badge>
|
|
280
|
+
</TableCell>
|
|
281
|
+
<TableCell>{row.entityTable}</TableCell>
|
|
282
|
+
<TableCell>{row.entityId}</TableCell>
|
|
283
|
+
<TableCell className="max-w-[360px] truncate">
|
|
284
|
+
{row.summary || '-'}
|
|
285
|
+
</TableCell>
|
|
286
|
+
<TableCell>{row.ipAddress || '-'}</TableCell>
|
|
287
|
+
</TableRow>
|
|
288
|
+
))
|
|
289
|
+
)}
|
|
290
|
+
</TableBody>
|
|
291
|
+
</Table>
|
|
292
|
+
</div>
|
|
293
|
+
|
|
294
|
+
<PaginationFooter
|
|
295
|
+
currentPage={data?.page || page}
|
|
296
|
+
pageSize={data?.pageSize || pageSize}
|
|
297
|
+
totalItems={data?.total || 0}
|
|
298
|
+
onPageChange={(newPage) => setPage(newPage)}
|
|
299
|
+
onPageSizeChange={(newPageSize) => {
|
|
300
|
+
setPageSize(newPageSize);
|
|
301
|
+
setPage(1);
|
|
302
|
+
}}
|
|
303
|
+
pageSizeOptions={[10, 20, 30, 40, 50]}
|
|
304
|
+
/>
|
|
305
|
+
</CardContent>
|
|
306
|
+
</Card>
|
|
307
|
+
</Page>
|
|
308
|
+
);
|
|
309
|
+
}
|