@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
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { EmptyState, Page, PageHeader } from '@/components/entity-list';
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { EmptyState, Page, PageHeader } from '@/components/entity-list';
|
|
4
4
|
import {
|
|
5
5
|
AlertDialog,
|
|
6
6
|
AlertDialogAction,
|
|
@@ -13,14 +13,13 @@ import {
|
|
|
13
13
|
} from '@/components/ui/alert-dialog';
|
|
14
14
|
import { Badge } from '@/components/ui/badge';
|
|
15
15
|
import { Button } from '@/components/ui/button';
|
|
16
|
-
import {
|
|
17
|
-
Card,
|
|
18
|
-
CardContent,
|
|
19
|
-
CardDescription,
|
|
20
|
-
CardHeader,
|
|
21
|
-
CardTitle,
|
|
22
|
-
} from '@/components/ui/card';
|
|
23
|
-
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
16
|
+
import {
|
|
17
|
+
Card,
|
|
18
|
+
CardContent,
|
|
19
|
+
CardDescription,
|
|
20
|
+
CardHeader,
|
|
21
|
+
CardTitle,
|
|
22
|
+
} from '@/components/ui/card';
|
|
24
23
|
import {
|
|
25
24
|
Form,
|
|
26
25
|
FormControl,
|
|
@@ -31,6 +30,7 @@ import {
|
|
|
31
30
|
} from '@/components/ui/form';
|
|
32
31
|
import { Input } from '@/components/ui/input';
|
|
33
32
|
import { InputMoney } from '@/components/ui/input-money';
|
|
33
|
+
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
34
34
|
import { Money } from '@/components/ui/money';
|
|
35
35
|
import {
|
|
36
36
|
Select,
|
|
@@ -39,18 +39,13 @@ import {
|
|
|
39
39
|
SelectTrigger,
|
|
40
40
|
SelectValue,
|
|
41
41
|
} from '@/components/ui/select';
|
|
42
|
-
import {
|
|
43
|
-
Sheet,
|
|
44
|
-
SheetContent,
|
|
45
|
-
SheetDescription,
|
|
46
|
-
SheetHeader,
|
|
47
|
-
SheetTitle,
|
|
48
|
-
} from '@/components/ui/sheet';
|
|
49
|
-
import {
|
|
50
|
-
FinancePageSection,
|
|
51
|
-
FinanceSheetBody,
|
|
52
|
-
FinanceSheetSection,
|
|
53
|
-
} from '../../_components/finance-layout';
|
|
42
|
+
import {
|
|
43
|
+
Sheet,
|
|
44
|
+
SheetContent,
|
|
45
|
+
SheetDescription,
|
|
46
|
+
SheetHeader,
|
|
47
|
+
SheetTitle,
|
|
48
|
+
} from '@/components/ui/sheet';
|
|
54
49
|
import { useApp, useQuery } from '@hed-hog/next-app-provider';
|
|
55
50
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
56
51
|
import {
|
|
@@ -72,6 +67,11 @@ import Link from 'next/link';
|
|
|
72
67
|
import { useEffect, useRef, useState, type ChangeEvent } from 'react';
|
|
73
68
|
import { useForm } from 'react-hook-form';
|
|
74
69
|
import { z } from 'zod';
|
|
70
|
+
import {
|
|
71
|
+
FinancePageSection,
|
|
72
|
+
FinanceSheetBody,
|
|
73
|
+
FinanceSheetSection,
|
|
74
|
+
} from '../../_components/finance-layout';
|
|
75
75
|
|
|
76
76
|
const bankAccountFormSchema = z.object({
|
|
77
77
|
banco: z.string().trim().min(1, 'Banco é obrigatório'),
|
|
@@ -475,52 +475,33 @@ function NovaContaSheet({
|
|
|
475
475
|
}
|
|
476
476
|
}}
|
|
477
477
|
>
|
|
478
|
-
<SheetContent className="flex h-full w-full flex-col sm:max-w-2xl">
|
|
478
|
+
<SheetContent className="flex h-full w-full flex-col sm:max-w-2xl">
|
|
479
479
|
<SheetHeader>
|
|
480
480
|
<SheetTitle>
|
|
481
481
|
{editingAccount ? t('common.edit') : t('newAccount.title')}
|
|
482
482
|
</SheetTitle>
|
|
483
483
|
<SheetDescription>{t('newAccount.description')}</SheetDescription>
|
|
484
484
|
</SheetHeader>
|
|
485
|
-
<Form {...form}>
|
|
486
|
-
<form
|
|
487
|
-
className="flex h-full flex-col"
|
|
488
|
-
onSubmit={form.handleSubmit(handleSubmit)}
|
|
489
|
-
>
|
|
490
|
-
<FinanceSheetBody>
|
|
491
|
-
<FinanceSheetSection
|
|
492
|
-
title={t('sections.accountData.title')}
|
|
493
|
-
description={t('sections.accountData.description')}
|
|
494
|
-
>
|
|
495
|
-
<FormField
|
|
496
|
-
control={form.control}
|
|
497
|
-
name="banco"
|
|
498
|
-
render={({ field }) => (
|
|
499
|
-
<FormItem>
|
|
500
|
-
<FormLabel>{t('fields.bank')}</FormLabel>
|
|
501
|
-
<FormControl>
|
|
502
|
-
<Input
|
|
503
|
-
placeholder={t('fields.bankPlaceholder')}
|
|
504
|
-
{...field}
|
|
505
|
-
/>
|
|
506
|
-
</FormControl>
|
|
507
|
-
<FormMessage />
|
|
508
|
-
</FormItem>
|
|
509
|
-
)}
|
|
510
|
-
/>
|
|
511
|
-
|
|
512
|
-
<div className="grid gap-4 md:grid-cols-2">
|
|
485
|
+
<Form {...form}>
|
|
486
|
+
<form
|
|
487
|
+
className="flex h-full flex-col"
|
|
488
|
+
onSubmit={form.handleSubmit(handleSubmit)}
|
|
489
|
+
>
|
|
490
|
+
<FinanceSheetBody>
|
|
491
|
+
<FinanceSheetSection
|
|
492
|
+
title={t('sections.accountData.title')}
|
|
493
|
+
description={t('sections.accountData.description')}
|
|
494
|
+
>
|
|
513
495
|
<FormField
|
|
514
496
|
control={form.control}
|
|
515
|
-
name="
|
|
497
|
+
name="banco"
|
|
516
498
|
render={({ field }) => (
|
|
517
499
|
<FormItem>
|
|
518
|
-
<FormLabel>{t('fields.
|
|
500
|
+
<FormLabel>{t('fields.bank')}</FormLabel>
|
|
519
501
|
<FormControl>
|
|
520
502
|
<Input
|
|
521
|
-
placeholder=
|
|
503
|
+
placeholder={t('fields.bankPlaceholder')}
|
|
522
504
|
{...field}
|
|
523
|
-
value={field.value || ''}
|
|
524
505
|
/>
|
|
525
506
|
</FormControl>
|
|
526
507
|
<FormMessage />
|
|
@@ -528,15 +509,114 @@ function NovaContaSheet({
|
|
|
528
509
|
)}
|
|
529
510
|
/>
|
|
530
511
|
|
|
512
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
513
|
+
<FormField
|
|
514
|
+
control={form.control}
|
|
515
|
+
name="agencia"
|
|
516
|
+
render={({ field }) => (
|
|
517
|
+
<FormItem>
|
|
518
|
+
<FormLabel>{t('fields.branch')}</FormLabel>
|
|
519
|
+
<FormControl>
|
|
520
|
+
<Input
|
|
521
|
+
placeholder="0000"
|
|
522
|
+
{...field}
|
|
523
|
+
value={field.value || ''}
|
|
524
|
+
/>
|
|
525
|
+
</FormControl>
|
|
526
|
+
<FormMessage />
|
|
527
|
+
</FormItem>
|
|
528
|
+
)}
|
|
529
|
+
/>
|
|
530
|
+
|
|
531
|
+
<FormField
|
|
532
|
+
control={form.control}
|
|
533
|
+
name="conta"
|
|
534
|
+
render={({ field }) => (
|
|
535
|
+
<FormItem>
|
|
536
|
+
<FormLabel>{t('fields.account')}</FormLabel>
|
|
537
|
+
<FormControl>
|
|
538
|
+
<Input
|
|
539
|
+
placeholder="00000-0"
|
|
540
|
+
{...field}
|
|
541
|
+
value={field.value || ''}
|
|
542
|
+
/>
|
|
543
|
+
</FormControl>
|
|
544
|
+
<FormMessage />
|
|
545
|
+
</FormItem>
|
|
546
|
+
)}
|
|
547
|
+
/>
|
|
548
|
+
</div>
|
|
549
|
+
|
|
550
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
551
|
+
<FormField
|
|
552
|
+
control={form.control}
|
|
553
|
+
name="tipo"
|
|
554
|
+
render={({ field }) => (
|
|
555
|
+
<FormItem>
|
|
556
|
+
<FormLabel>{t('fields.type')}</FormLabel>
|
|
557
|
+
<Select
|
|
558
|
+
value={field.value}
|
|
559
|
+
onValueChange={field.onChange}
|
|
560
|
+
>
|
|
561
|
+
<FormControl>
|
|
562
|
+
<SelectTrigger className="w-full">
|
|
563
|
+
<SelectValue placeholder={t('common.select')} />
|
|
564
|
+
</SelectTrigger>
|
|
565
|
+
</FormControl>
|
|
566
|
+
<SelectContent>
|
|
567
|
+
<SelectItem value="corrente">
|
|
568
|
+
{t('types.corrente')}
|
|
569
|
+
</SelectItem>
|
|
570
|
+
<SelectItem value="poupanca">
|
|
571
|
+
{t('types.poupanca')}
|
|
572
|
+
</SelectItem>
|
|
573
|
+
<SelectItem value="investimento">
|
|
574
|
+
{t('types.investimento')}
|
|
575
|
+
</SelectItem>
|
|
576
|
+
<SelectItem value="caixa">
|
|
577
|
+
{t('types.caixa')}
|
|
578
|
+
</SelectItem>
|
|
579
|
+
</SelectContent>
|
|
580
|
+
</Select>
|
|
581
|
+
<FormMessage />
|
|
582
|
+
</FormItem>
|
|
583
|
+
)}
|
|
584
|
+
/>
|
|
585
|
+
|
|
586
|
+
<FormField
|
|
587
|
+
control={form.control}
|
|
588
|
+
name="saldoInicial"
|
|
589
|
+
render={({ field }) => (
|
|
590
|
+
<FormItem>
|
|
591
|
+
<FormLabel>{t('fields.initialBalance')}</FormLabel>
|
|
592
|
+
<FormControl>
|
|
593
|
+
<InputMoney
|
|
594
|
+
ref={field.ref}
|
|
595
|
+
name={field.name}
|
|
596
|
+
value={field.value}
|
|
597
|
+
onBlur={field.onBlur}
|
|
598
|
+
onValueChange={(value) =>
|
|
599
|
+
field.onChange(value ?? 0)
|
|
600
|
+
}
|
|
601
|
+
placeholder="0,00"
|
|
602
|
+
disabled={!!editingAccount}
|
|
603
|
+
/>
|
|
604
|
+
</FormControl>
|
|
605
|
+
<FormMessage />
|
|
606
|
+
</FormItem>
|
|
607
|
+
)}
|
|
608
|
+
/>
|
|
609
|
+
</div>
|
|
610
|
+
|
|
531
611
|
<FormField
|
|
532
612
|
control={form.control}
|
|
533
|
-
name="
|
|
613
|
+
name="descricao"
|
|
534
614
|
render={({ field }) => (
|
|
535
615
|
<FormItem>
|
|
536
|
-
<FormLabel>{t('fields.
|
|
616
|
+
<FormLabel>{t('fields.description')}</FormLabel>
|
|
537
617
|
<FormControl>
|
|
538
618
|
<Input
|
|
539
|
-
placeholder=
|
|
619
|
+
placeholder={t('fields.descriptionPlaceholder')}
|
|
540
620
|
{...field}
|
|
541
621
|
value={field.value || ''}
|
|
542
622
|
/>
|
|
@@ -545,156 +625,78 @@ function NovaContaSheet({
|
|
|
545
625
|
</FormItem>
|
|
546
626
|
)}
|
|
547
627
|
/>
|
|
548
|
-
</
|
|
628
|
+
</FinanceSheetSection>
|
|
549
629
|
|
|
550
|
-
<
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
<SelectValue placeholder={t('common.select')} />
|
|
564
|
-
</SelectTrigger>
|
|
565
|
-
</FormControl>
|
|
566
|
-
<SelectContent>
|
|
567
|
-
<SelectItem value="corrente">
|
|
568
|
-
{t('types.corrente')}
|
|
569
|
-
</SelectItem>
|
|
570
|
-
<SelectItem value="poupanca">
|
|
571
|
-
{t('types.poupanca')}
|
|
572
|
-
</SelectItem>
|
|
573
|
-
<SelectItem value="investimento">
|
|
574
|
-
{t('types.investimento')}
|
|
575
|
-
</SelectItem>
|
|
576
|
-
<SelectItem value="caixa">
|
|
577
|
-
{t('types.caixa')}
|
|
578
|
-
</SelectItem>
|
|
579
|
-
</SelectContent>
|
|
580
|
-
</Select>
|
|
581
|
-
<FormMessage />
|
|
582
|
-
</FormItem>
|
|
583
|
-
)}
|
|
584
|
-
/>
|
|
585
|
-
|
|
586
|
-
<FormField
|
|
587
|
-
control={form.control}
|
|
588
|
-
name="saldoInicial"
|
|
589
|
-
render={({ field }) => (
|
|
590
|
-
<FormItem>
|
|
591
|
-
<FormLabel>{t('fields.initialBalance')}</FormLabel>
|
|
592
|
-
<FormControl>
|
|
593
|
-
<InputMoney
|
|
594
|
-
ref={field.ref}
|
|
595
|
-
name={field.name}
|
|
596
|
-
value={field.value}
|
|
597
|
-
onBlur={field.onBlur}
|
|
598
|
-
onValueChange={(value) => field.onChange(value ?? 0)}
|
|
599
|
-
placeholder="0,00"
|
|
600
|
-
disabled={!!editingAccount}
|
|
630
|
+
<FinanceSheetSection
|
|
631
|
+
title={t('sections.logo.title')}
|
|
632
|
+
description={t('sections.logo.description')}
|
|
633
|
+
>
|
|
634
|
+
<FormItem>
|
|
635
|
+
<FormLabel>{t('fields.logo')}</FormLabel>
|
|
636
|
+
<div className="flex flex-col gap-4 rounded-xl border border-dashed border-border/70 bg-muted/20 p-4 sm:flex-row sm:items-start">
|
|
637
|
+
<div className="flex h-20 w-20 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted">
|
|
638
|
+
{logoPreviewUrl ? (
|
|
639
|
+
<img
|
|
640
|
+
src={logoPreviewUrl}
|
|
641
|
+
alt={t('fields.logo')}
|
|
642
|
+
className="h-full w-full object-cover"
|
|
601
643
|
/>
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
<FormLabel>{t('fields.description')}</FormLabel>
|
|
615
|
-
<FormControl>
|
|
616
|
-
<Input
|
|
617
|
-
placeholder={t('fields.descriptionPlaceholder')}
|
|
618
|
-
{...field}
|
|
619
|
-
value={field.value || ''}
|
|
644
|
+
) : (
|
|
645
|
+
<Landmark className="h-8 w-8 text-muted-foreground" />
|
|
646
|
+
)}
|
|
647
|
+
</div>
|
|
648
|
+
|
|
649
|
+
<div className="flex-1 space-y-2">
|
|
650
|
+
<input
|
|
651
|
+
ref={logoInputRef}
|
|
652
|
+
type="file"
|
|
653
|
+
accept="image/*"
|
|
654
|
+
className="hidden"
|
|
655
|
+
onChange={handleLogoUpload}
|
|
620
656
|
/>
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
{t('fields.logoAction')}
|
|
665
|
-
</Button>
|
|
666
|
-
|
|
667
|
-
{logoFileId ? (
|
|
668
|
-
<Button
|
|
669
|
-
type="button"
|
|
670
|
-
variant="ghost"
|
|
671
|
-
onClick={() => void handleRemoveLogo()}
|
|
672
|
-
disabled={isUploadingLogo}
|
|
673
|
-
>
|
|
674
|
-
<Trash2 className="mr-2 h-4 w-4" />
|
|
675
|
-
{t('fields.logoRemove')}
|
|
676
|
-
</Button>
|
|
677
|
-
) : null}
|
|
678
|
-
</div>
|
|
679
|
-
|
|
680
|
-
<p className="text-xs text-muted-foreground">
|
|
681
|
-
{t('fields.logoHint')}
|
|
682
|
-
</p>
|
|
683
|
-
|
|
684
|
-
{isUploadingLogo ? (
|
|
685
|
-
<p className="text-xs text-muted-foreground">
|
|
686
|
-
{t('fields.logoUploading', {
|
|
687
|
-
progress: logoUploadProgress,
|
|
688
|
-
})}
|
|
689
|
-
</p>
|
|
690
|
-
) : null}
|
|
691
|
-
</div>
|
|
692
|
-
</div>
|
|
693
|
-
</FormItem>
|
|
694
|
-
</FinanceSheetSection>
|
|
695
|
-
</FinanceSheetBody>
|
|
696
|
-
|
|
697
|
-
<div className="flex justify-end gap-2 border-t px-4 py-4 sm:px-6">
|
|
657
|
+
|
|
658
|
+
<div className="flex flex-wrap gap-2">
|
|
659
|
+
<Button
|
|
660
|
+
type="button"
|
|
661
|
+
variant="outline"
|
|
662
|
+
onClick={handleSelectLogo}
|
|
663
|
+
disabled={isUploadingLogo}
|
|
664
|
+
>
|
|
665
|
+
<Upload className="mr-2 h-4 w-4" />
|
|
666
|
+
{t('fields.logoAction')}
|
|
667
|
+
</Button>
|
|
668
|
+
|
|
669
|
+
{logoFileId ? (
|
|
670
|
+
<Button
|
|
671
|
+
type="button"
|
|
672
|
+
variant="ghost"
|
|
673
|
+
onClick={() => void handleRemoveLogo()}
|
|
674
|
+
disabled={isUploadingLogo}
|
|
675
|
+
>
|
|
676
|
+
<Trash2 className="mr-2 h-4 w-4" />
|
|
677
|
+
{t('fields.logoRemove')}
|
|
678
|
+
</Button>
|
|
679
|
+
) : null}
|
|
680
|
+
</div>
|
|
681
|
+
|
|
682
|
+
<p className="text-xs text-muted-foreground">
|
|
683
|
+
{t('fields.logoHint')}
|
|
684
|
+
</p>
|
|
685
|
+
|
|
686
|
+
{isUploadingLogo ? (
|
|
687
|
+
<p className="text-xs text-muted-foreground">
|
|
688
|
+
{t('fields.logoUploading', {
|
|
689
|
+
progress: logoUploadProgress,
|
|
690
|
+
})}
|
|
691
|
+
</p>
|
|
692
|
+
) : null}
|
|
693
|
+
</div>
|
|
694
|
+
</div>
|
|
695
|
+
</FormItem>
|
|
696
|
+
</FinanceSheetSection>
|
|
697
|
+
</FinanceSheetBody>
|
|
698
|
+
|
|
699
|
+
<div className="flex justify-end gap-2 border-t px-4 py-4 sm:px-6">
|
|
698
700
|
<Button type="button" variant="outline" onClick={handleCancel}>
|
|
699
701
|
{t('common.cancel')}
|
|
700
702
|
</Button>
|
|
@@ -764,47 +766,47 @@ export default function ContasBancariasPage() {
|
|
|
764
766
|
.filter((c) => c.ativo)
|
|
765
767
|
.reduce((acc, c) => acc + c.saldoAtual, 0);
|
|
766
768
|
|
|
767
|
-
const saldoConciliadoTotal = accounts
|
|
768
|
-
.filter((c) => c.ativo)
|
|
769
|
-
.reduce((acc, c) => acc + c.saldoConciliado, 0);
|
|
770
|
-
const activeAccountsCount = accounts.filter((c) => c.ativo).length;
|
|
771
|
-
const inactiveAccountsCount = accounts.length - activeAccountsCount;
|
|
772
|
-
const summaryCards = [
|
|
773
|
-
{
|
|
774
|
-
key: 'balance',
|
|
775
|
-
title: t('cards.totalBalance'),
|
|
776
|
-
value: <Money value={saldoTotal} />,
|
|
777
|
-
description: t('cards.activeAccounts', {
|
|
778
|
-
count: activeAccountsCount,
|
|
779
|
-
}),
|
|
780
|
-
icon: Landmark,
|
|
781
|
-
layout: 'compact' as const,
|
|
782
|
-
},
|
|
783
|
-
{
|
|
784
|
-
key: 'reconciled',
|
|
785
|
-
title: t('cards.reconciledBalance'),
|
|
786
|
-
value: <Money value={saldoConciliadoTotal} />,
|
|
787
|
-
description: `${t('cards.difference')}: ${new Intl.NumberFormat(
|
|
788
|
-
currentLocaleCode === 'pt' ? 'pt-BR' : 'en-US',
|
|
789
|
-
{
|
|
790
|
-
style: 'currency',
|
|
791
|
-
currency: 'BRL',
|
|
792
|
-
}
|
|
793
|
-
).format(saldoTotal - saldoConciliadoTotal)}`,
|
|
794
|
-
icon: RefreshCw,
|
|
795
|
-
layout: 'compact' as const,
|
|
796
|
-
},
|
|
797
|
-
{
|
|
798
|
-
key: 'accounts',
|
|
799
|
-
title: t('cards.accountsOverview'),
|
|
800
|
-
value: activeAccountsCount,
|
|
801
|
-
description: t('cards.inactiveAccounts', {
|
|
802
|
-
count: inactiveAccountsCount,
|
|
803
|
-
}),
|
|
804
|
-
icon: Building2,
|
|
805
|
-
layout: 'compact' as const,
|
|
806
|
-
},
|
|
807
|
-
];
|
|
769
|
+
const saldoConciliadoTotal = accounts
|
|
770
|
+
.filter((c) => c.ativo)
|
|
771
|
+
.reduce((acc, c) => acc + c.saldoConciliado, 0);
|
|
772
|
+
const activeAccountsCount = accounts.filter((c) => c.ativo).length;
|
|
773
|
+
const inactiveAccountsCount = accounts.length - activeAccountsCount;
|
|
774
|
+
const summaryCards = [
|
|
775
|
+
{
|
|
776
|
+
key: 'balance',
|
|
777
|
+
title: t('cards.totalBalance'),
|
|
778
|
+
value: <Money value={saldoTotal} />,
|
|
779
|
+
description: t('cards.activeAccounts', {
|
|
780
|
+
count: activeAccountsCount,
|
|
781
|
+
}),
|
|
782
|
+
icon: Landmark,
|
|
783
|
+
layout: 'compact' as const,
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
key: 'reconciled',
|
|
787
|
+
title: t('cards.reconciledBalance'),
|
|
788
|
+
value: <Money value={saldoConciliadoTotal} />,
|
|
789
|
+
description: `${t('cards.difference')}: ${new Intl.NumberFormat(
|
|
790
|
+
currentLocaleCode === 'pt' ? 'pt-BR' : 'en-US',
|
|
791
|
+
{
|
|
792
|
+
style: 'currency',
|
|
793
|
+
currency: 'BRL',
|
|
794
|
+
}
|
|
795
|
+
).format(saldoTotal - saldoConciliadoTotal)}`,
|
|
796
|
+
icon: RefreshCw,
|
|
797
|
+
layout: 'compact' as const,
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
key: 'accounts',
|
|
801
|
+
title: t('cards.accountsOverview'),
|
|
802
|
+
value: activeAccountsCount,
|
|
803
|
+
description: t('cards.inactiveAccounts', {
|
|
804
|
+
count: inactiveAccountsCount,
|
|
805
|
+
}),
|
|
806
|
+
icon: Building2,
|
|
807
|
+
layout: 'compact' as const,
|
|
808
|
+
},
|
|
809
|
+
];
|
|
808
810
|
|
|
809
811
|
const handleCreate = () => {
|
|
810
812
|
setEditingAccount(null);
|
|
@@ -886,163 +888,157 @@ export default function ContasBancariasPage() {
|
|
|
886
888
|
</AlertDialogContent>
|
|
887
889
|
</AlertDialog>
|
|
888
890
|
|
|
889
|
-
<KpiCardsGrid items={summaryCards} columns={3} />
|
|
890
|
-
|
|
891
|
-
<FinancePageSection
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
const
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
actionLabel={t('newAccount.action')}
|
|
1041
|
-
onAction={handleCreate}
|
|
1042
|
-
/>
|
|
1043
|
-
</div>
|
|
1044
|
-
)}
|
|
1045
|
-
</FinancePageSection>
|
|
891
|
+
<KpiCardsGrid items={summaryCards} columns={3} />
|
|
892
|
+
|
|
893
|
+
<FinancePageSection variant="flat" contentClassName="p-0">
|
|
894
|
+
{accounts.length > 0 ? (
|
|
895
|
+
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
|
896
|
+
{accounts.map((conta) => {
|
|
897
|
+
const tipo =
|
|
898
|
+
tipoConfig[conta.tipo as keyof typeof tipoConfig] ||
|
|
899
|
+
tipoConfig.corrente;
|
|
900
|
+
const TipoIcon = tipo.icon;
|
|
901
|
+
const diferenca = conta.saldoAtual - conta.saldoConciliado;
|
|
902
|
+
|
|
903
|
+
return (
|
|
904
|
+
<Card
|
|
905
|
+
key={conta.id}
|
|
906
|
+
className={!conta.ativo ? 'border-border/60 opacity-70' : ''}
|
|
907
|
+
>
|
|
908
|
+
<CardHeader className="space-y-4">
|
|
909
|
+
<div className="flex items-start justify-between gap-3">
|
|
910
|
+
<div className="flex min-w-0 items-center gap-3">
|
|
911
|
+
<BankAccountLogo account={conta} icon={TipoIcon} />
|
|
912
|
+
<div className="min-w-0">
|
|
913
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
914
|
+
<CardTitle className="text-base">
|
|
915
|
+
{conta.banco}
|
|
916
|
+
</CardTitle>
|
|
917
|
+
{conta.descricao ? (
|
|
918
|
+
<span className="text-xs text-muted-foreground">
|
|
919
|
+
{conta.descricao}
|
|
920
|
+
</span>
|
|
921
|
+
) : null}
|
|
922
|
+
</div>
|
|
923
|
+
<CardDescription className="space-y-0.5">
|
|
924
|
+
{conta.agencia !== '-' ? (
|
|
925
|
+
<span className="block">
|
|
926
|
+
{t('accountCard.bankAccount', {
|
|
927
|
+
agency: conta.agencia,
|
|
928
|
+
account: conta.conta,
|
|
929
|
+
})}
|
|
930
|
+
</span>
|
|
931
|
+
) : null}
|
|
932
|
+
<span className="block">{tipo.label}</span>
|
|
933
|
+
</CardDescription>
|
|
934
|
+
</div>
|
|
935
|
+
</div>
|
|
936
|
+
{!conta.ativo ? (
|
|
937
|
+
<Badge
|
|
938
|
+
variant="outline"
|
|
939
|
+
className="text-muted-foreground"
|
|
940
|
+
>
|
|
941
|
+
{t('status.inactive')}
|
|
942
|
+
</Badge>
|
|
943
|
+
) : null}
|
|
944
|
+
</div>
|
|
945
|
+
</CardHeader>
|
|
946
|
+
<CardContent>
|
|
947
|
+
<div className="space-y-3">
|
|
948
|
+
<div>
|
|
949
|
+
<p className="text-sm text-muted-foreground">
|
|
950
|
+
{t('accountCard.currentBalance')}
|
|
951
|
+
</p>
|
|
952
|
+
<p className="text-2xl font-bold">
|
|
953
|
+
<Money value={conta.saldoAtual} />
|
|
954
|
+
</p>
|
|
955
|
+
</div>
|
|
956
|
+
<div className="flex items-center justify-between text-sm">
|
|
957
|
+
<span className="text-muted-foreground">
|
|
958
|
+
{t('accountCard.reconciledBalance')}
|
|
959
|
+
</span>
|
|
960
|
+
<Money value={conta.saldoConciliado} />
|
|
961
|
+
</div>
|
|
962
|
+
{diferenca !== 0 ? (
|
|
963
|
+
<div className="flex items-center justify-between text-sm">
|
|
964
|
+
<span className="text-muted-foreground">
|
|
965
|
+
{t('accountCard.difference')}
|
|
966
|
+
</span>
|
|
967
|
+
<span
|
|
968
|
+
className={
|
|
969
|
+
diferenca > 0 ? 'text-green-600' : 'text-red-600'
|
|
970
|
+
}
|
|
971
|
+
>
|
|
972
|
+
<Money value={diferenca} showSign />
|
|
973
|
+
</span>
|
|
974
|
+
</div>
|
|
975
|
+
) : null}
|
|
976
|
+
<div className="flex flex-wrap gap-2 pt-2">
|
|
977
|
+
<Button
|
|
978
|
+
variant="outline"
|
|
979
|
+
size="sm"
|
|
980
|
+
className="min-w-0 flex-1 basis-[calc(50%-0.25rem)] bg-transparent"
|
|
981
|
+
asChild
|
|
982
|
+
>
|
|
983
|
+
<Link
|
|
984
|
+
href={`/finance/cash-and-banks/statements?bank_account_id=${conta.id}`}
|
|
985
|
+
>
|
|
986
|
+
<Eye className="mr-2 h-4 w-4" />
|
|
987
|
+
{t('accountCard.statement')}
|
|
988
|
+
</Link>
|
|
989
|
+
</Button>
|
|
990
|
+
<Button
|
|
991
|
+
variant="outline"
|
|
992
|
+
size="sm"
|
|
993
|
+
className="min-w-0 flex-1 basis-[calc(50%-0.25rem)] bg-transparent"
|
|
994
|
+
asChild
|
|
995
|
+
>
|
|
996
|
+
<Link
|
|
997
|
+
href={`/finance/cash-and-banks/bank-reconciliation?bank_account_id=${conta.id}`}
|
|
998
|
+
>
|
|
999
|
+
<RefreshCw className="mr-2 h-4 w-4" />
|
|
1000
|
+
{t('accountCard.reconcile')}
|
|
1001
|
+
</Link>
|
|
1002
|
+
</Button>
|
|
1003
|
+
<div className="ml-auto inline-flex shrink-0 overflow-hidden rounded-md border bg-background shadow-sm">
|
|
1004
|
+
<Button
|
|
1005
|
+
variant="ghost"
|
|
1006
|
+
size="sm"
|
|
1007
|
+
className="rounded-none border-0 px-3 hover:bg-muted"
|
|
1008
|
+
onClick={() => handleEdit(conta)}
|
|
1009
|
+
aria-label={t('common.edit')}
|
|
1010
|
+
title={t('common.edit')}
|
|
1011
|
+
>
|
|
1012
|
+
<Pencil className="h-4 w-4" />
|
|
1013
|
+
</Button>
|
|
1014
|
+
<Button
|
|
1015
|
+
variant="ghost"
|
|
1016
|
+
size="sm"
|
|
1017
|
+
className="rounded-none border-0 border-l px-3 text-destructive hover:bg-destructive/10 hover:text-destructive"
|
|
1018
|
+
onClick={() => setAccountIdToDelete(conta.id)}
|
|
1019
|
+
aria-label={deleteDialogTitle}
|
|
1020
|
+
title={deleteDialogTitle}
|
|
1021
|
+
>
|
|
1022
|
+
<Trash2 className="h-4 w-4" />
|
|
1023
|
+
</Button>
|
|
1024
|
+
</div>
|
|
1025
|
+
</div>
|
|
1026
|
+
</div>
|
|
1027
|
+
</CardContent>
|
|
1028
|
+
</Card>
|
|
1029
|
+
);
|
|
1030
|
+
})}
|
|
1031
|
+
</div>
|
|
1032
|
+
) : (
|
|
1033
|
+
<EmptyState
|
|
1034
|
+
icon={<Landmark className="h-12 w-12" />}
|
|
1035
|
+
title={t('empty.title')}
|
|
1036
|
+
description={t('empty.description')}
|
|
1037
|
+
actionLabel={t('newAccount.action')}
|
|
1038
|
+
onAction={handleCreate}
|
|
1039
|
+
/>
|
|
1040
|
+
)}
|
|
1041
|
+
</FinancePageSection>
|
|
1046
1042
|
</Page>
|
|
1047
1043
|
);
|
|
1048
1044
|
}
|