@hed-hog/finance 0.0.301 → 0.0.303
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/hedhog/data/menu.yaml +0 -17
- package/hedhog/frontend/app/_components/finance-layout.tsx.ejs +23 -0
- package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +86 -91
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +1457 -1446
- package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +323 -324
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +1252 -1235
- package/hedhog/frontend/app/administration/audit-logs/page.tsx.ejs +12 -7
- package/hedhog/frontend/app/administration/categories/page.tsx.ejs +32 -14
- package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +13 -5
- package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +148 -147
- package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +400 -404
- package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +9 -10
- package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +2 -6
- package/hedhog/frontend/app/page.tsx.ejs +3 -370
- package/hedhog/frontend/app/planning/cash-flow-forecast/page.tsx.ejs +66 -74
- package/hedhog/frontend/app/planning/receivables-calendar/page.tsx.ejs +1 -5
- package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +1 -1
- package/package.json +5 -5
|
@@ -45,6 +45,8 @@ type PaginatedAuditLogs = {
|
|
|
45
45
|
data: AuditLog[];
|
|
46
46
|
};
|
|
47
47
|
|
|
48
|
+
const EMPTY_AUDIT_LOGS: AuditLog[] = [];
|
|
49
|
+
|
|
48
50
|
export default function AuditLogsPage() {
|
|
49
51
|
const t = useTranslations('finance.AdminAuditLogsPage');
|
|
50
52
|
const { request } = useApp();
|
|
@@ -100,7 +102,7 @@ export default function AuditLogsPage() {
|
|
|
100
102
|
placeholderData: (old) => old,
|
|
101
103
|
});
|
|
102
104
|
|
|
103
|
-
const rows = data?.data
|
|
105
|
+
const rows = data?.data ?? EMPTY_AUDIT_LOGS;
|
|
104
106
|
|
|
105
107
|
const entityOptions = useMemo(() => {
|
|
106
108
|
const unique = new Set(rows.map((row) => row.entityTable).filter(Boolean));
|
|
@@ -122,8 +124,8 @@ export default function AuditLogsPage() {
|
|
|
122
124
|
]}
|
|
123
125
|
/>
|
|
124
126
|
|
|
125
|
-
<div className="
|
|
126
|
-
<div className="relative
|
|
127
|
+
<div className="flex flex-col gap-4 xl:flex-row xl:flex-wrap xl:items-center">
|
|
128
|
+
<div className="relative min-w-[260px] flex-1">
|
|
127
129
|
<Search className="pointer-events-none absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
|
128
130
|
<Input
|
|
129
131
|
placeholder={t('filters.searchPlaceholder')}
|
|
@@ -143,7 +145,7 @@ export default function AuditLogsPage() {
|
|
|
143
145
|
setPage(1);
|
|
144
146
|
}}
|
|
145
147
|
>
|
|
146
|
-
<SelectTrigger className="w-full">
|
|
148
|
+
<SelectTrigger className="w-full sm:w-[180px]">
|
|
147
149
|
<SelectValue placeholder={t('filters.action')} />
|
|
148
150
|
</SelectTrigger>
|
|
149
151
|
<SelectContent>
|
|
@@ -161,6 +163,7 @@ export default function AuditLogsPage() {
|
|
|
161
163
|
setActorUserId(event.target.value);
|
|
162
164
|
setPage(1);
|
|
163
165
|
}}
|
|
166
|
+
className="w-full sm:w-[220px]"
|
|
164
167
|
/>
|
|
165
168
|
|
|
166
169
|
<Select
|
|
@@ -170,7 +173,7 @@ export default function AuditLogsPage() {
|
|
|
170
173
|
setPage(1);
|
|
171
174
|
}}
|
|
172
175
|
>
|
|
173
|
-
<SelectTrigger className="w-full">
|
|
176
|
+
<SelectTrigger className="w-full sm:w-[180px]">
|
|
174
177
|
<SelectValue placeholder={t('filters.entity')} />
|
|
175
178
|
</SelectTrigger>
|
|
176
179
|
<SelectContent>
|
|
@@ -189,6 +192,7 @@ export default function AuditLogsPage() {
|
|
|
189
192
|
setFrom(event.target.value);
|
|
190
193
|
setPage(1);
|
|
191
194
|
}}
|
|
195
|
+
className="w-full sm:w-[220px]"
|
|
192
196
|
/>
|
|
193
197
|
|
|
194
198
|
<Input
|
|
@@ -198,11 +202,12 @@ export default function AuditLogsPage() {
|
|
|
198
202
|
setTo(event.target.value);
|
|
199
203
|
setPage(1);
|
|
200
204
|
}}
|
|
205
|
+
className="w-full sm:w-[220px]"
|
|
201
206
|
/>
|
|
202
207
|
|
|
203
208
|
<Button
|
|
204
209
|
variant="outline"
|
|
205
|
-
className="gap-2"
|
|
210
|
+
className="gap-2 xl:ml-auto w-full sm:w-auto"
|
|
206
211
|
onClick={() => {
|
|
207
212
|
setSearch('');
|
|
208
213
|
setAction('all');
|
|
@@ -266,7 +271,7 @@ export default function AuditLogsPage() {
|
|
|
266
271
|
</TableCell>
|
|
267
272
|
<TableCell>{row.entityTable}</TableCell>
|
|
268
273
|
<TableCell>{row.entityId}</TableCell>
|
|
269
|
-
<TableCell className="max-w-
|
|
274
|
+
<TableCell className="max-w-90 truncate">
|
|
270
275
|
{row.summary || '-'}
|
|
271
276
|
</TableCell>
|
|
272
277
|
<TableCell>{row.ipAddress || '-'}</TableCell>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { Page, PageHeader } from '@/components/entity-list';
|
|
3
|
+
import { EmptyState, Page, PageHeader } from '@/components/entity-list';
|
|
4
4
|
import {
|
|
5
5
|
AlertDialog,
|
|
6
6
|
AlertDialogAction,
|
|
@@ -55,6 +55,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
55
55
|
import {
|
|
56
56
|
ChevronDown,
|
|
57
57
|
ChevronRight,
|
|
58
|
+
FolderTree,
|
|
58
59
|
GripVertical,
|
|
59
60
|
Pencil,
|
|
60
61
|
Plus,
|
|
@@ -81,6 +82,8 @@ type FinanceCategory = {
|
|
|
81
82
|
ativo: boolean;
|
|
82
83
|
};
|
|
83
84
|
|
|
85
|
+
const EMPTY_CATEGORIES: FinanceCategory[] = [];
|
|
86
|
+
|
|
84
87
|
type CategoryNode = FinanceCategory & { children: CategoryNode[] };
|
|
85
88
|
|
|
86
89
|
function buildTree(categories: FinanceCategory[]): CategoryNode[] {
|
|
@@ -126,7 +129,7 @@ function CategoriaSheet({
|
|
|
126
129
|
}: {
|
|
127
130
|
open: boolean;
|
|
128
131
|
onOpenChange: (open: boolean) => void;
|
|
129
|
-
onSaved: () => Promise<
|
|
132
|
+
onSaved: () => Promise<unknown> | void;
|
|
130
133
|
editing: FinanceCategory | null;
|
|
131
134
|
setEditing: (item: FinanceCategory | null) => void;
|
|
132
135
|
categories: FinanceCategory[];
|
|
@@ -351,12 +354,19 @@ function CategoryRow({
|
|
|
351
354
|
dropHint?: 'inside' | 'before' | 'after';
|
|
352
355
|
t: ReturnType<typeof useTranslations>;
|
|
353
356
|
}) {
|
|
354
|
-
const
|
|
357
|
+
const {
|
|
358
|
+
attributes,
|
|
359
|
+
listeners,
|
|
360
|
+
setActivatorNodeRef,
|
|
361
|
+
setNodeRef,
|
|
362
|
+
transform,
|
|
363
|
+
transition,
|
|
364
|
+
} = useSortable({ id: item.id });
|
|
355
365
|
const style = {
|
|
356
|
-
transform:
|
|
357
|
-
? `translate3d(${
|
|
366
|
+
transform: transform
|
|
367
|
+
? `translate3d(${transform.x}px, ${transform.y}px, 0)`
|
|
358
368
|
: undefined,
|
|
359
|
-
transition
|
|
369
|
+
transition,
|
|
360
370
|
};
|
|
361
371
|
|
|
362
372
|
const hasChildren = item.children.length > 0;
|
|
@@ -364,7 +374,7 @@ function CategoryRow({
|
|
|
364
374
|
|
|
365
375
|
return (
|
|
366
376
|
<div
|
|
367
|
-
ref={
|
|
377
|
+
ref={setNodeRef}
|
|
368
378
|
style={style}
|
|
369
379
|
className={`rounded-md border bg-background ${
|
|
370
380
|
dropHint === 'inside' ? 'ring-1 ring-primary/50 bg-muted/40' : ''
|
|
@@ -402,9 +412,9 @@ function CategoryRow({
|
|
|
402
412
|
|
|
403
413
|
<button
|
|
404
414
|
type="button"
|
|
405
|
-
ref={
|
|
406
|
-
{...
|
|
407
|
-
{...
|
|
415
|
+
ref={setActivatorNodeRef}
|
|
416
|
+
{...listeners}
|
|
417
|
+
{...attributes}
|
|
408
418
|
className="inline-flex h-7 w-7 items-center justify-center rounded border"
|
|
409
419
|
>
|
|
410
420
|
<GripVertical className="h-4 w-4" />
|
|
@@ -467,7 +477,7 @@ export default function CategoriesPage() {
|
|
|
467
477
|
placeholderData: [],
|
|
468
478
|
});
|
|
469
479
|
|
|
470
|
-
const categories = data
|
|
480
|
+
const categories = data ?? EMPTY_CATEGORIES;
|
|
471
481
|
const tree = useMemo(() => buildTree(categories), [categories]);
|
|
472
482
|
const flat = useMemo(() => flattenTree(tree), [tree]);
|
|
473
483
|
|
|
@@ -628,9 +638,17 @@ export default function CategoriesPage() {
|
|
|
628
638
|
|
|
629
639
|
<div className="space-y-4">
|
|
630
640
|
{flat.length === 0 ? (
|
|
631
|
-
<
|
|
632
|
-
{
|
|
633
|
-
|
|
641
|
+
<EmptyState
|
|
642
|
+
icon={<FolderTree className="h-12 w-12" />}
|
|
643
|
+
title={t('table.empty')}
|
|
644
|
+
description={t('header.description')}
|
|
645
|
+
actionLabel={t('actions.newCategory')}
|
|
646
|
+
actionIcon={<Plus className="mr-2 h-4 w-4" />}
|
|
647
|
+
onAction={() => {
|
|
648
|
+
setEditing(null);
|
|
649
|
+
setSheetOpen(true);
|
|
650
|
+
}}
|
|
651
|
+
/>
|
|
634
652
|
) : (
|
|
635
653
|
<DndContext
|
|
636
654
|
sensors={sensors}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { Page, PageHeader } from '@/components/entity-list';
|
|
3
|
+
import { EmptyState, Page, PageHeader } from '@/components/entity-list';
|
|
4
4
|
import {
|
|
5
5
|
AlertDialog,
|
|
6
6
|
AlertDialogAction,
|
|
@@ -59,7 +59,7 @@ function CentroCustoSheet({
|
|
|
59
59
|
}: {
|
|
60
60
|
open: boolean;
|
|
61
61
|
onOpenChange: (open: boolean) => void;
|
|
62
|
-
onSaved: () => Promise<
|
|
62
|
+
onSaved: () => Promise<unknown> | void;
|
|
63
63
|
editingCostCenter: CostCenter | null;
|
|
64
64
|
onEditingChange: (costCenter: CostCenter | null) => void;
|
|
65
65
|
t: ReturnType<typeof useTranslations>;
|
|
@@ -270,9 +270,17 @@ export default function CostCentersPage() {
|
|
|
270
270
|
|
|
271
271
|
<div className="space-y-4">
|
|
272
272
|
{centers.length === 0 ? (
|
|
273
|
-
<
|
|
274
|
-
{
|
|
275
|
-
|
|
273
|
+
<EmptyState
|
|
274
|
+
icon={<Building2 className="h-12 w-12" />}
|
|
275
|
+
title={t('table.empty')}
|
|
276
|
+
description={t('header.description')}
|
|
277
|
+
actionLabel={t('actions.newCostCenter')}
|
|
278
|
+
actionIcon={<Plus className="mr-2 h-4 w-4" />}
|
|
279
|
+
onAction={() => {
|
|
280
|
+
setEditingCostCenter(null);
|
|
281
|
+
setSheetOpen(true);
|
|
282
|
+
}}
|
|
283
|
+
/>
|
|
276
284
|
) : (
|
|
277
285
|
<div className="grid gap-3">
|
|
278
286
|
{centers.map((costCenter) => (
|
|
@@ -78,7 +78,7 @@ function ClosePeriodSheet({
|
|
|
78
78
|
}: {
|
|
79
79
|
open: boolean;
|
|
80
80
|
onOpenChange: (open: boolean) => void;
|
|
81
|
-
onCreated: () => Promise<
|
|
81
|
+
onCreated: () => Promise<unknown> | void;
|
|
82
82
|
t: ReturnType<typeof useTranslations>;
|
|
83
83
|
}) {
|
|
84
84
|
const { request, showToastHandler } = useApp();
|
|
@@ -310,168 +310,169 @@ export default function PeriodClosePage() {
|
|
|
310
310
|
}
|
|
311
311
|
/>
|
|
312
312
|
|
|
313
|
-
<div className="
|
|
314
|
-
<div className="
|
|
315
|
-
<
|
|
316
|
-
<Search className="pointer-events-none absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
|
317
|
-
<Input
|
|
318
|
-
placeholder={t('filters.searchPlaceholder')}
|
|
319
|
-
value={search}
|
|
320
|
-
onChange={(event) => {
|
|
321
|
-
setSearch(event.target.value);
|
|
322
|
-
setPage(1);
|
|
323
|
-
}}
|
|
324
|
-
className="pl-8"
|
|
325
|
-
/>
|
|
326
|
-
</div>
|
|
327
|
-
|
|
328
|
-
<Select
|
|
329
|
-
value={status}
|
|
330
|
-
onValueChange={(value) => {
|
|
331
|
-
setStatus(value);
|
|
332
|
-
setPage(1);
|
|
333
|
-
}}
|
|
334
|
-
>
|
|
335
|
-
<SelectTrigger className="w-full">
|
|
336
|
-
<SelectValue placeholder={t('filters.status')} />
|
|
337
|
-
</SelectTrigger>
|
|
338
|
-
<SelectContent>
|
|
339
|
-
<SelectItem value="all">{t('filters.allStatus')}</SelectItem>
|
|
340
|
-
<SelectItem value="closed">{t('status.closed')}</SelectItem>
|
|
341
|
-
<SelectItem value="open">{t('status.open')}</SelectItem>
|
|
342
|
-
</SelectContent>
|
|
343
|
-
</Select>
|
|
344
|
-
|
|
313
|
+
<div className="flex flex-col gap-4 xl:flex-row xl:flex-wrap xl:items-center">
|
|
314
|
+
<div className="relative min-w-[260px] flex-1">
|
|
315
|
+
<Search className="pointer-events-none absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
|
345
316
|
<Input
|
|
346
|
-
placeholder={t('filters.
|
|
347
|
-
value={
|
|
317
|
+
placeholder={t('filters.searchPlaceholder')}
|
|
318
|
+
value={search}
|
|
348
319
|
onChange={(event) => {
|
|
349
|
-
|
|
320
|
+
setSearch(event.target.value);
|
|
350
321
|
setPage(1);
|
|
351
322
|
}}
|
|
323
|
+
className="pl-8"
|
|
352
324
|
/>
|
|
325
|
+
</div>
|
|
353
326
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
327
|
+
<Select
|
|
328
|
+
value={status}
|
|
329
|
+
onValueChange={(value) => {
|
|
330
|
+
setStatus(value);
|
|
331
|
+
setPage(1);
|
|
332
|
+
}}
|
|
333
|
+
>
|
|
334
|
+
<SelectTrigger className="w-full sm:w-[180px]">
|
|
335
|
+
<SelectValue placeholder={t('filters.status')} />
|
|
336
|
+
</SelectTrigger>
|
|
337
|
+
<SelectContent>
|
|
338
|
+
<SelectItem value="all">{t('filters.allStatus')}</SelectItem>
|
|
339
|
+
<SelectItem value="closed">{t('status.closed')}</SelectItem>
|
|
340
|
+
<SelectItem value="open">{t('status.open')}</SelectItem>
|
|
341
|
+
</SelectContent>
|
|
342
|
+
</Select>
|
|
343
|
+
|
|
344
|
+
<Input
|
|
345
|
+
placeholder={t('filters.userPlaceholder')}
|
|
346
|
+
value={user}
|
|
347
|
+
onChange={(event) => {
|
|
348
|
+
setUser(event.target.value);
|
|
349
|
+
setPage(1);
|
|
350
|
+
}}
|
|
351
|
+
className="w-full sm:w-[220px]"
|
|
352
|
+
/>
|
|
362
353
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
354
|
+
<Input
|
|
355
|
+
type="date"
|
|
356
|
+
value={from}
|
|
357
|
+
onChange={(event) => {
|
|
358
|
+
setFrom(event.target.value);
|
|
359
|
+
setPage(1);
|
|
360
|
+
}}
|
|
361
|
+
className="w-full sm:w-[220px]"
|
|
362
|
+
/>
|
|
371
363
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
setPage(1);
|
|
382
|
-
refetch();
|
|
383
|
-
}}
|
|
384
|
-
>
|
|
385
|
-
<X className="h-4 w-4" />
|
|
386
|
-
{t('filters.clear')}
|
|
387
|
-
</Button>
|
|
388
|
-
</div>
|
|
364
|
+
<Input
|
|
365
|
+
type="date"
|
|
366
|
+
value={to}
|
|
367
|
+
onChange={(event) => {
|
|
368
|
+
setTo(event.target.value);
|
|
369
|
+
setPage(1);
|
|
370
|
+
}}
|
|
371
|
+
className="w-full sm:w-[220px]"
|
|
372
|
+
/>
|
|
389
373
|
|
|
390
|
-
<
|
|
391
|
-
|
|
392
|
-
|
|
374
|
+
<Button
|
|
375
|
+
variant="outline"
|
|
376
|
+
className="gap-2 xl:ml-auto w-full sm:w-auto"
|
|
377
|
+
onClick={() => {
|
|
378
|
+
setSearch('');
|
|
379
|
+
setStatus('all');
|
|
380
|
+
setUser('');
|
|
381
|
+
setFrom('');
|
|
382
|
+
setTo('');
|
|
383
|
+
setPage(1);
|
|
384
|
+
refetch();
|
|
385
|
+
}}
|
|
386
|
+
>
|
|
387
|
+
<X className="h-4 w-4" />
|
|
388
|
+
{t('filters.clear')}
|
|
389
|
+
</Button>
|
|
390
|
+
</div>
|
|
391
|
+
|
|
392
|
+
<div className="rounded-md border">
|
|
393
|
+
<Table>
|
|
394
|
+
<TableHeader>
|
|
395
|
+
<TableRow>
|
|
396
|
+
<TableHead>{t('table.headers.start')}</TableHead>
|
|
397
|
+
<TableHead>{t('table.headers.end')}</TableHead>
|
|
398
|
+
<TableHead>{t('table.headers.status')}</TableHead>
|
|
399
|
+
<TableHead>{t('table.headers.closedBy')}</TableHead>
|
|
400
|
+
<TableHead>{t('table.headers.closedAt')}</TableHead>
|
|
401
|
+
<TableHead>{t('table.headers.notes')}</TableHead>
|
|
402
|
+
</TableRow>
|
|
403
|
+
</TableHeader>
|
|
404
|
+
<TableBody>
|
|
405
|
+
{isLoading ? (
|
|
406
|
+
<TableRow>
|
|
407
|
+
<TableCell colSpan={6} className="h-24 text-center">
|
|
408
|
+
{t('table.loading')}
|
|
409
|
+
</TableCell>
|
|
410
|
+
</TableRow>
|
|
411
|
+
) : rows.length === 0 ? (
|
|
393
412
|
<TableRow>
|
|
394
|
-
<
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
<TableHead>{t('table.headers.closedBy')}</TableHead>
|
|
398
|
-
<TableHead>{t('table.headers.closedAt')}</TableHead>
|
|
399
|
-
<TableHead>{t('table.headers.notes')}</TableHead>
|
|
413
|
+
<TableCell colSpan={6} className="h-24 text-center">
|
|
414
|
+
{t('table.empty')}
|
|
415
|
+
</TableCell>
|
|
400
416
|
</TableRow>
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
417
|
+
) : (
|
|
418
|
+
rows.map((row) => (
|
|
419
|
+
<TableRow key={row.id}>
|
|
420
|
+
<TableCell>
|
|
421
|
+
{row.periodStart
|
|
422
|
+
? new Date(row.periodStart).toLocaleDateString('pt-BR')
|
|
423
|
+
: '-'}
|
|
407
424
|
</TableCell>
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
425
|
+
<TableCell>
|
|
426
|
+
{row.periodEnd
|
|
427
|
+
? new Date(row.periodEnd).toLocaleDateString('pt-BR')
|
|
428
|
+
: '-'}
|
|
429
|
+
</TableCell>
|
|
430
|
+
<TableCell>
|
|
431
|
+
<Badge
|
|
432
|
+
variant={
|
|
433
|
+
row.status === 'closed' ? 'default' : 'secondary'
|
|
434
|
+
}
|
|
435
|
+
>
|
|
436
|
+
{row.status === 'closed'
|
|
437
|
+
? t('status.closed')
|
|
438
|
+
: t('status.open')}
|
|
439
|
+
</Badge>
|
|
440
|
+
</TableCell>
|
|
441
|
+
<TableCell>
|
|
442
|
+
<div className="flex flex-col">
|
|
443
|
+
<span>{row.closedByName || '-'}</span>
|
|
444
|
+
<span className="text-xs text-muted-foreground">
|
|
445
|
+
{row.closedByEmail || row.closedByUserId || '-'}
|
|
446
|
+
</span>
|
|
447
|
+
</div>
|
|
448
|
+
</TableCell>
|
|
449
|
+
<TableCell>
|
|
450
|
+
{row.closedAt
|
|
451
|
+
? new Date(row.closedAt).toLocaleString('pt-BR')
|
|
452
|
+
: '-'}
|
|
453
|
+
</TableCell>
|
|
454
|
+
<TableCell className="max-w-80 truncate">
|
|
455
|
+
{row.notes || '-'}
|
|
413
456
|
</TableCell>
|
|
414
457
|
</TableRow>
|
|
415
|
-
)
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
{row.periodStart
|
|
420
|
-
? new Date(row.periodStart).toLocaleDateString('pt-BR')
|
|
421
|
-
: '-'}
|
|
422
|
-
</TableCell>
|
|
423
|
-
<TableCell>
|
|
424
|
-
{row.periodEnd
|
|
425
|
-
? new Date(row.periodEnd).toLocaleDateString('pt-BR')
|
|
426
|
-
: '-'}
|
|
427
|
-
</TableCell>
|
|
428
|
-
<TableCell>
|
|
429
|
-
<Badge
|
|
430
|
-
variant={
|
|
431
|
-
row.status === 'closed' ? 'default' : 'secondary'
|
|
432
|
-
}
|
|
433
|
-
>
|
|
434
|
-
{row.status === 'closed'
|
|
435
|
-
? t('status.closed')
|
|
436
|
-
: t('status.open')}
|
|
437
|
-
</Badge>
|
|
438
|
-
</TableCell>
|
|
439
|
-
<TableCell>
|
|
440
|
-
<div className="flex flex-col">
|
|
441
|
-
<span>{row.closedByName || '-'}</span>
|
|
442
|
-
<span className="text-xs text-muted-foreground">
|
|
443
|
-
{row.closedByEmail || row.closedByUserId || '-'}
|
|
444
|
-
</span>
|
|
445
|
-
</div>
|
|
446
|
-
</TableCell>
|
|
447
|
-
<TableCell>
|
|
448
|
-
{row.closedAt
|
|
449
|
-
? new Date(row.closedAt).toLocaleString('pt-BR')
|
|
450
|
-
: '-'}
|
|
451
|
-
</TableCell>
|
|
452
|
-
<TableCell className="max-w-80 truncate">
|
|
453
|
-
{row.notes || '-'}
|
|
454
|
-
</TableCell>
|
|
455
|
-
</TableRow>
|
|
456
|
-
))
|
|
457
|
-
)}
|
|
458
|
-
</TableBody>
|
|
459
|
-
</Table>
|
|
460
|
-
</div>
|
|
461
|
-
|
|
462
|
-
<PaginationFooter
|
|
463
|
-
currentPage={data?.page || page}
|
|
464
|
-
pageSize={data?.pageSize || pageSize}
|
|
465
|
-
totalItems={data?.total || 0}
|
|
466
|
-
onPageChange={(newPage) => setPage(newPage)}
|
|
467
|
-
onPageSizeChange={(newPageSize) => {
|
|
468
|
-
setPageSize(newPageSize);
|
|
469
|
-
setPage(1);
|
|
470
|
-
}}
|
|
471
|
-
pageSizeOptions={[10, 20, 30, 40, 50]}
|
|
472
|
-
/>
|
|
458
|
+
))
|
|
459
|
+
)}
|
|
460
|
+
</TableBody>
|
|
461
|
+
</Table>
|
|
473
462
|
</div>
|
|
474
463
|
|
|
464
|
+
<PaginationFooter
|
|
465
|
+
currentPage={data?.page || page}
|
|
466
|
+
pageSize={data?.pageSize || pageSize}
|
|
467
|
+
totalItems={data?.total || 0}
|
|
468
|
+
onPageChange={(newPage) => setPage(newPage)}
|
|
469
|
+
onPageSizeChange={(newPageSize) => {
|
|
470
|
+
setPageSize(newPageSize);
|
|
471
|
+
setPage(1);
|
|
472
|
+
}}
|
|
473
|
+
pageSizeOptions={[10, 20, 30, 40, 50]}
|
|
474
|
+
/>
|
|
475
|
+
|
|
475
476
|
<ClosePeriodSheet
|
|
476
477
|
open={sheetOpen}
|
|
477
478
|
onOpenChange={setSheetOpen}
|