@hed-hog/finance 0.0.300 → 0.0.301

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 (39) hide show
  1. package/dist/finance.contract-activated.subscriber.d.ts +24 -0
  2. package/dist/finance.contract-activated.subscriber.d.ts.map +1 -0
  3. package/dist/finance.contract-activated.subscriber.js +519 -0
  4. package/dist/finance.contract-activated.subscriber.js.map +1 -0
  5. package/dist/finance.contract-activated.subscriber.spec.d.ts +2 -0
  6. package/dist/finance.contract-activated.subscriber.spec.d.ts.map +1 -0
  7. package/dist/finance.contract-activated.subscriber.spec.js +302 -0
  8. package/dist/finance.contract-activated.subscriber.spec.js.map +1 -0
  9. package/dist/finance.module.d.ts.map +1 -1
  10. package/dist/finance.module.js +6 -1
  11. package/dist/finance.module.js.map +1 -1
  12. package/hedhog/frontend/app/_components/finance-layout.tsx.ejs +108 -0
  13. package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +91 -106
  14. package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +1306 -1145
  15. package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +288 -268
  16. package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +491 -351
  17. package/hedhog/frontend/app/administration/audit-logs/page.tsx.ejs +157 -173
  18. package/hedhog/frontend/app/administration/categories/page.tsx.ejs +44 -62
  19. package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +62 -80
  20. package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +151 -170
  21. package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +332 -286
  22. package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +204 -226
  23. package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +122 -140
  24. package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +32 -49
  25. package/hedhog/frontend/app/planning/cash-flow-forecast/page.tsx.ejs +84 -108
  26. package/hedhog/frontend/app/planning/receivables-calendar/page.tsx.ejs +53 -70
  27. package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +98 -95
  28. package/hedhog/frontend/app/reports/actual-vs-forecast/page.tsx.ejs +100 -125
  29. package/hedhog/frontend/app/reports/aging-default/page.tsx.ejs +77 -105
  30. package/hedhog/frontend/app/reports/cash-position/page.tsx.ejs +99 -134
  31. package/hedhog/frontend/app/reports/overview-results/page.tsx.ejs +147 -182
  32. package/hedhog/frontend/app/reports/top-customers/page.tsx.ejs +49 -61
  33. package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +49 -67
  34. package/hedhog/frontend/messages/en.json +176 -68
  35. package/hedhog/frontend/messages/pt.json +176 -68
  36. package/package.json +7 -6
  37. package/src/finance.contract-activated.subscriber.spec.ts +392 -0
  38. package/src/finance.contract-activated.subscriber.ts +780 -0
  39. package/src/finance.module.ts +6 -1
@@ -1,6 +1,6 @@
1
- 'use client';
2
-
3
- import { 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,13 +13,14 @@ 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';
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';
23
24
  import {
24
25
  Form,
25
26
  FormControl,
@@ -38,13 +39,18 @@ import {
38
39
  SelectTrigger,
39
40
  SelectValue,
40
41
  } from '@/components/ui/select';
41
- import {
42
- Sheet,
43
- SheetContent,
44
- SheetDescription,
45
- SheetHeader,
46
- SheetTitle,
47
- } from '@/components/ui/sheet';
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';
48
54
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
49
55
  import { zodResolver } from '@hookform/resolvers/zod';
50
56
  import {
@@ -469,34 +475,41 @@ function NovaContaSheet({
469
475
  }
470
476
  }}
471
477
  >
472
- <SheetContent className="w-full sm:max-w-lg">
478
+ <SheetContent className="flex h-full w-full flex-col sm:max-w-2xl">
473
479
  <SheetHeader>
474
480
  <SheetTitle>
475
481
  {editingAccount ? t('common.edit') : t('newAccount.title')}
476
482
  </SheetTitle>
477
483
  <SheetDescription>{t('newAccount.description')}</SheetDescription>
478
484
  </SheetHeader>
479
- <Form {...form}>
480
- <form className="p-4" onSubmit={form.handleSubmit(handleSubmit)}>
481
- <div className="grid gap-4">
482
- <FormField
483
- control={form.control}
484
- name="banco"
485
- render={({ field }) => (
486
- <FormItem>
487
- <FormLabel>{t('fields.bank')}</FormLabel>
488
- <FormControl>
489
- <Input
490
- placeholder={t('fields.bankPlaceholder')}
491
- {...field}
492
- />
493
- </FormControl>
494
- <FormMessage />
495
- </FormItem>
496
- )}
497
- />
498
-
499
- <div className="grid grid-cols-2 gap-4">
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">
500
513
  <FormField
501
514
  control={form.control}
502
515
  name="agencia"
@@ -534,7 +547,7 @@ function NovaContaSheet({
534
547
  />
535
548
  </div>
536
549
 
537
- <div className="grid grid-cols-2 gap-4">
550
+ <div className="grid gap-4 md:grid-cols-2">
538
551
  <FormField
539
552
  control={form.control}
540
553
  name="tipo"
@@ -593,11 +606,11 @@ function NovaContaSheet({
593
606
  />
594
607
  </div>
595
608
 
596
- <FormField
597
- control={form.control}
598
- name="descricao"
599
- render={({ field }) => (
600
- <FormItem>
609
+ <FormField
610
+ control={form.control}
611
+ name="descricao"
612
+ render={({ field }) => (
613
+ <FormItem>
601
614
  <FormLabel>{t('fields.description')}</FormLabel>
602
615
  <FormControl>
603
616
  <Input
@@ -606,76 +619,82 @@ function NovaContaSheet({
606
619
  value={field.value || ''}
607
620
  />
608
621
  </FormControl>
609
- <FormMessage />
610
- </FormItem>
611
- )}
612
- />
613
-
614
- <FormItem>
615
- <FormLabel>{t('fields.logo')}</FormLabel>
616
- <div className="flex items-start gap-4 rounded-lg border p-3">
617
- <div className="flex h-20 w-20 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted">
618
- {logoPreviewUrl ? (
619
- <img
620
- src={logoPreviewUrl}
621
- alt={t('fields.logo')}
622
- className="h-full w-full object-cover"
623
- />
624
- ) : (
625
- <Landmark className="h-8 w-8 text-muted-foreground" />
626
- )}
627
- </div>
628
-
629
- <div className="flex-1 space-y-2">
630
- <input
631
- ref={logoInputRef}
632
- type="file"
633
- accept="image/*"
634
- className="hidden"
635
- onChange={handleLogoUpload}
636
- />
637
-
638
- <div className="flex flex-wrap gap-2">
639
- <Button
640
- type="button"
641
- variant="outline"
642
- onClick={handleSelectLogo}
643
- disabled={isUploadingLogo}
644
- >
645
- <Upload className="mr-2 h-4 w-4" />
646
- {t('fields.logoAction')}
647
- </Button>
648
-
649
- {logoFileId ? (
650
- <Button
651
- type="button"
652
- variant="ghost"
653
- onClick={() => void handleRemoveLogo()}
654
- disabled={isUploadingLogo}
655
- >
656
- <Trash2 className="mr-2 h-4 w-4" />
657
- {t('fields.logoRemove')}
658
- </Button>
659
- ) : null}
660
- </div>
661
-
662
- <p className="text-xs text-muted-foreground">
663
- {t('fields.logoHint')}
664
- </p>
665
-
666
- {isUploadingLogo ? (
667
- <p className="text-xs text-muted-foreground">
668
- {t('fields.logoUploading', {
669
- progress: logoUploadProgress,
670
- })}
671
- </p>
672
- ) : null}
673
- </div>
674
- </div>
675
- </FormItem>
676
- </div>
677
-
678
- <div className="flex justify-end gap-2 pt-4">
622
+ <FormMessage />
623
+ </FormItem>
624
+ )}
625
+ />
626
+ </FinanceSheetSection>
627
+
628
+ <FinanceSheetSection
629
+ title={t('sections.logo.title')}
630
+ description={t('sections.logo.description')}
631
+ >
632
+ <FormItem>
633
+ <FormLabel>{t('fields.logo')}</FormLabel>
634
+ <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">
635
+ <div className="flex h-20 w-20 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted">
636
+ {logoPreviewUrl ? (
637
+ <img
638
+ src={logoPreviewUrl}
639
+ alt={t('fields.logo')}
640
+ className="h-full w-full object-cover"
641
+ />
642
+ ) : (
643
+ <Landmark className="h-8 w-8 text-muted-foreground" />
644
+ )}
645
+ </div>
646
+
647
+ <div className="flex-1 space-y-2">
648
+ <input
649
+ ref={logoInputRef}
650
+ type="file"
651
+ accept="image/*"
652
+ className="hidden"
653
+ onChange={handleLogoUpload}
654
+ />
655
+
656
+ <div className="flex flex-wrap gap-2">
657
+ <Button
658
+ type="button"
659
+ variant="outline"
660
+ onClick={handleSelectLogo}
661
+ disabled={isUploadingLogo}
662
+ >
663
+ <Upload className="mr-2 h-4 w-4" />
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">
679
698
  <Button type="button" variant="outline" onClick={handleCancel}>
680
699
  {t('common.cancel')}
681
700
  </Button>
@@ -745,9 +764,47 @@ export default function ContasBancariasPage() {
745
764
  .filter((c) => c.ativo)
746
765
  .reduce((acc, c) => acc + c.saldoAtual, 0);
747
766
 
748
- const saldoConciliadoTotal = accounts
749
- .filter((c) => c.ativo)
750
- .reduce((acc, c) => acc + c.saldoConciliado, 0);
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
+ ];
751
808
 
752
809
  const handleCreate = () => {
753
810
  setEditingAccount(null);
@@ -829,174 +886,163 @@ export default function ContasBancariasPage() {
829
886
  </AlertDialogContent>
830
887
  </AlertDialog>
831
888
 
832
- <div className="grid gap-4 md:grid-cols-2">
833
- <Card>
834
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
835
- <CardTitle className="text-sm font-medium">
836
- {t('cards.totalBalance')}
837
- </CardTitle>
838
- <Landmark className="h-4 w-4 text-muted-foreground" />
839
- </CardHeader>
840
- <CardContent>
841
- <div className="text-2xl font-bold">
842
- <Money value={saldoTotal} />
843
- </div>
844
- <p className="text-xs text-muted-foreground">
845
- {t('cards.activeAccounts', {
846
- count: accounts.filter((c) => c.ativo).length,
847
- })}
848
- </p>
849
- </CardContent>
850
- </Card>
851
- <Card>
852
- <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
853
- <CardTitle className="text-sm font-medium">
854
- {t('cards.reconciledBalance')}
855
- </CardTitle>
856
- <RefreshCw className="h-4 w-4 text-muted-foreground" />
857
- </CardHeader>
858
- <CardContent>
859
- <div className="text-2xl font-bold">
860
- <Money value={saldoConciliadoTotal} />
861
- </div>
862
- <p className="text-xs text-muted-foreground">
863
- {t('cards.difference')}:{' '}
864
- <Money value={saldoTotal - saldoConciliadoTotal} />
865
- </p>
866
- </CardContent>
867
- </Card>
868
- </div>
869
-
870
- <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
871
- {accounts.map((conta) => {
872
- const tipo =
873
- tipoConfig[conta.tipo as keyof typeof tipoConfig] ||
874
- tipoConfig.corrente;
875
- const TipoIcon = tipo.icon;
876
- const diferenca = conta.saldoAtual - conta.saldoConciliado;
877
-
878
- return (
879
- <Card key={conta.id} className={!conta.ativo ? 'opacity-60' : ''}>
880
- <CardHeader>
881
- <div className="flex items-center justify-between">
882
- <div className="flex items-center gap-2">
883
- <BankAccountLogo account={conta} icon={TipoIcon} />
884
- <div>
885
- <div className="flex gap-4 items-center">
886
- <CardTitle className="text-base">
887
- {conta.banco}
888
- </CardTitle>
889
- {conta.descricao && (
890
- <span className="block text-muted-foreground text-xs">
891
- {conta.descricao}
892
- </span>
893
- )}
894
- </div>
895
- <CardDescription className="space-y-0.5">
896
- {conta.agencia !== '-' && (
897
- <span className="block">
898
- {t('accountCard.bankAccount', {
899
- agency: conta.agencia,
900
- account: conta.conta,
901
- })}
902
- </span>
903
- )}
904
- </CardDescription>
905
- </div>
906
- </div>
907
- {!conta.ativo && (
908
- <Badge variant="outline" className="text-muted-foreground">
909
- {t('status.inactive')}
910
- </Badge>
911
- )}
912
- </div>
913
- </CardHeader>
914
- <CardContent>
915
- <div className="space-y-3">
916
- <div>
917
- <p className="text-sm text-muted-foreground">
918
- {t('accountCard.currentBalance')}
919
- </p>
920
- <p className="text-2xl font-bold">
921
- <Money value={conta.saldoAtual} />
922
- </p>
923
- </div>
924
- <div className="flex items-center justify-between text-sm">
925
- <span className="text-muted-foreground">
926
- {t('accountCard.reconciledBalance')}
927
- </span>
928
- <Money value={conta.saldoConciliado} />
929
- </div>
930
- {diferenca !== 0 && (
931
- <div className="flex items-center justify-between text-sm">
932
- <span className="text-muted-foreground">
933
- {t('accountCard.difference')}
934
- </span>
935
- <span
936
- className={
937
- diferenca > 0 ? 'text-green-600' : 'text-red-600'
938
- }
939
- >
940
- <Money value={diferenca} showSign />
941
- </span>
942
- </div>
943
- )}
944
- <div className="flex flex-wrap gap-2 pt-2">
945
- <Button
946
- variant="outline"
947
- size="sm"
948
- className="min-w-0 flex-1 basis-[calc(50%-0.25rem)] bg-transparent"
949
- asChild
950
- >
951
- <Link
952
- href={`/finance/cash-and-banks/statements?bank_account_id=${conta.id}`}
953
- >
954
- <Eye className="mr-2 h-4 w-4" />
955
- {t('accountCard.statement')}
956
- </Link>
957
- </Button>
958
- <Button
959
- variant="outline"
960
- size="sm"
961
- className="min-w-0 flex-1 basis-[calc(50%-0.25rem)] bg-transparent"
962
- asChild
963
- >
964
- <Link
965
- href={`/finance/cash-and-banks/bank-reconciliation?bank_account_id=${conta.id}`}
966
- >
967
- <RefreshCw className="mr-2 h-4 w-4" />
968
- {t('accountCard.reconcile')}
969
- </Link>
970
- </Button>
971
- <div className="ml-auto inline-flex shrink-0 overflow-hidden rounded-md border bg-background shadow-sm">
972
- <Button
973
- variant="ghost"
974
- size="sm"
975
- className="rounded-none border-0 px-3 hover:bg-muted"
976
- onClick={() => handleEdit(conta)}
977
- aria-label={t('common.edit')}
978
- title={t('common.edit')}
979
- >
980
- <Pencil className="h-4 w-4" />
981
- </Button>
982
- <Button
983
- variant="ghost"
984
- size="sm"
985
- className="rounded-none border-0 border-l px-3 text-destructive hover:bg-destructive/10 hover:text-destructive"
986
- onClick={() => setAccountIdToDelete(conta.id)}
987
- aria-label={deleteDialogTitle}
988
- title={deleteDialogTitle}
989
- >
990
- <Trash2 className="h-4 w-4" />
991
- </Button>
992
- </div>
993
- </div>
994
- </div>
995
- </CardContent>
996
- </Card>
997
- );
998
- })}
999
- </div>
889
+ <KpiCardsGrid items={summaryCards} columns={3} />
890
+
891
+ <FinancePageSection
892
+ title={t('list.title')}
893
+ description={t('list.description')}
894
+ contentClassName="p-4 sm:p-5"
895
+ >
896
+ {accounts.length > 0 ? (
897
+ <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
898
+ {accounts.map((conta) => {
899
+ const tipo =
900
+ tipoConfig[conta.tipo as keyof typeof tipoConfig] ||
901
+ tipoConfig.corrente;
902
+ const TipoIcon = tipo.icon;
903
+ const diferenca = conta.saldoAtual - conta.saldoConciliado;
904
+
905
+ return (
906
+ <Card
907
+ key={conta.id}
908
+ className={!conta.ativo ? 'border-border/60 opacity-70' : ''}
909
+ >
910
+ <CardHeader className="space-y-4">
911
+ <div className="flex items-start justify-between gap-3">
912
+ <div className="flex min-w-0 items-center gap-3">
913
+ <BankAccountLogo account={conta} icon={TipoIcon} />
914
+ <div className="min-w-0">
915
+ <div className="flex flex-wrap items-center gap-2">
916
+ <CardTitle className="text-base">
917
+ {conta.banco}
918
+ </CardTitle>
919
+ {conta.descricao ? (
920
+ <span className="text-xs text-muted-foreground">
921
+ {conta.descricao}
922
+ </span>
923
+ ) : null}
924
+ </div>
925
+ <CardDescription className="space-y-0.5">
926
+ {conta.agencia !== '-' ? (
927
+ <span className="block">
928
+ {t('accountCard.bankAccount', {
929
+ agency: conta.agencia,
930
+ account: conta.conta,
931
+ })}
932
+ </span>
933
+ ) : null}
934
+ <span className="block">{tipo.label}</span>
935
+ </CardDescription>
936
+ </div>
937
+ </div>
938
+ {!conta.ativo ? (
939
+ <Badge
940
+ variant="outline"
941
+ className="text-muted-foreground"
942
+ >
943
+ {t('status.inactive')}
944
+ </Badge>
945
+ ) : null}
946
+ </div>
947
+ </CardHeader>
948
+ <CardContent>
949
+ <div className="space-y-3">
950
+ <div>
951
+ <p className="text-sm text-muted-foreground">
952
+ {t('accountCard.currentBalance')}
953
+ </p>
954
+ <p className="text-2xl font-bold">
955
+ <Money value={conta.saldoAtual} />
956
+ </p>
957
+ </div>
958
+ <div className="flex items-center justify-between text-sm">
959
+ <span className="text-muted-foreground">
960
+ {t('accountCard.reconciledBalance')}
961
+ </span>
962
+ <Money value={conta.saldoConciliado} />
963
+ </div>
964
+ {diferenca !== 0 ? (
965
+ <div className="flex items-center justify-between text-sm">
966
+ <span className="text-muted-foreground">
967
+ {t('accountCard.difference')}
968
+ </span>
969
+ <span
970
+ className={
971
+ diferenca > 0 ? 'text-green-600' : 'text-red-600'
972
+ }
973
+ >
974
+ <Money value={diferenca} showSign />
975
+ </span>
976
+ </div>
977
+ ) : null}
978
+ <div className="flex flex-wrap gap-2 pt-2">
979
+ <Button
980
+ variant="outline"
981
+ size="sm"
982
+ className="min-w-0 flex-1 basis-[calc(50%-0.25rem)] bg-transparent"
983
+ asChild
984
+ >
985
+ <Link
986
+ href={`/finance/cash-and-banks/statements?bank_account_id=${conta.id}`}
987
+ >
988
+ <Eye className="mr-2 h-4 w-4" />
989
+ {t('accountCard.statement')}
990
+ </Link>
991
+ </Button>
992
+ <Button
993
+ variant="outline"
994
+ size="sm"
995
+ className="min-w-0 flex-1 basis-[calc(50%-0.25rem)] bg-transparent"
996
+ asChild
997
+ >
998
+ <Link
999
+ href={`/finance/cash-and-banks/bank-reconciliation?bank_account_id=${conta.id}`}
1000
+ >
1001
+ <RefreshCw className="mr-2 h-4 w-4" />
1002
+ {t('accountCard.reconcile')}
1003
+ </Link>
1004
+ </Button>
1005
+ <div className="ml-auto inline-flex shrink-0 overflow-hidden rounded-md border bg-background shadow-sm">
1006
+ <Button
1007
+ variant="ghost"
1008
+ size="sm"
1009
+ className="rounded-none border-0 px-3 hover:bg-muted"
1010
+ onClick={() => handleEdit(conta)}
1011
+ aria-label={t('common.edit')}
1012
+ title={t('common.edit')}
1013
+ >
1014
+ <Pencil className="h-4 w-4" />
1015
+ </Button>
1016
+ <Button
1017
+ variant="ghost"
1018
+ size="sm"
1019
+ className="rounded-none border-0 border-l px-3 text-destructive hover:bg-destructive/10 hover:text-destructive"
1020
+ onClick={() => setAccountIdToDelete(conta.id)}
1021
+ aria-label={deleteDialogTitle}
1022
+ title={deleteDialogTitle}
1023
+ >
1024
+ <Trash2 className="h-4 w-4" />
1025
+ </Button>
1026
+ </div>
1027
+ </div>
1028
+ </div>
1029
+ </CardContent>
1030
+ </Card>
1031
+ );
1032
+ })}
1033
+ </div>
1034
+ ) : (
1035
+ <div className="p-6 sm:p-8">
1036
+ <EmptyState
1037
+ icon={<Landmark className="h-12 w-12" />}
1038
+ title={t('empty.title')}
1039
+ description={t('empty.description')}
1040
+ actionLabel={t('newAccount.action')}
1041
+ onAction={handleCreate}
1042
+ />
1043
+ </div>
1044
+ )}
1045
+ </FinancePageSection>
1000
1046
  </Page>
1001
1047
  );
1002
1048
  }