@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,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { FinancePageSection } from '@/app/(app)/(libraries)/finance/_components/finance-layout';
3
4
  import { Page, PageHeader } from '@/components/entity-list';
4
5
  import {
5
6
  AlertDialog,
@@ -13,13 +14,8 @@ import {
13
14
  } from '@/components/ui/alert-dialog';
14
15
  import { Badge } from '@/components/ui/badge';
15
16
  import { Button } from '@/components/ui/button';
16
- import {
17
- Card,
18
- CardContent,
19
- CardDescription,
20
- CardHeader,
21
- CardTitle,
22
- } from '@/components/ui/card';
17
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
18
+ import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
23
19
  import { Checkbox } from '@/components/ui/checkbox';
24
20
  import {
25
21
  Form,
@@ -32,7 +28,6 @@ import {
32
28
  import { Input } from '@/components/ui/input';
33
29
  import { InputMoney } from '@/components/ui/input-money';
34
30
  import { Money } from '@/components/ui/money';
35
- import { Progress } from '@/components/ui/progress';
36
31
  import {
37
32
  Select,
38
33
  SelectContent,
@@ -508,6 +503,49 @@ export default function ConciliacaoPage() {
508
503
  totalExtratoConta > 0
509
504
  ? Math.round((totalConciliado / totalExtratoConta) * 100)
510
505
  : 0;
506
+ const summaryCards = [
507
+ {
508
+ key: 'reconciled',
509
+ title: t('cards.reconciled'),
510
+ value: `${percentualConciliado}%`,
511
+ description: `${totalConciliado}/${totalExtratoConta}`,
512
+ icon: Check,
513
+ layout: 'compact' as const,
514
+ iconContainerClassName: 'bg-green-500/10 text-green-700',
515
+ accentClassName: 'from-green-500/20 via-emerald-500/10 to-transparent',
516
+ },
517
+ {
518
+ key: 'pending',
519
+ title: t('cards.pending'),
520
+ value: extratosFiltered.length,
521
+ description: t('cards.transactions'),
522
+ icon: RefreshCw,
523
+ layout: 'compact' as const,
524
+ },
525
+ {
526
+ key: 'discrepancies',
527
+ title: t('cards.discrepancies'),
528
+ value: reconciliationSummary?.discrepancies || 0,
529
+ description: t('cards.toResolve'),
530
+ icon: DollarSign,
531
+ layout: 'compact' as const,
532
+ valueClassName: 'text-orange-600',
533
+ iconContainerClassName: 'bg-orange-500/10 text-orange-700',
534
+ accentClassName: 'from-orange-500/20 via-amber-500/10 to-transparent',
535
+ },
536
+ {
537
+ key: 'difference',
538
+ title: t('cards.difference'),
539
+ value: <Money value={reconciliationSummary?.difference || 0} />,
540
+ description: t('cards.notReconciled'),
541
+ icon:
542
+ reconciliationSummary?.difference &&
543
+ reconciliationSummary.difference < 0
544
+ ? Trash2
545
+ : Link2,
546
+ layout: 'compact' as const,
547
+ },
548
+ ];
511
549
 
512
550
  const handleRefreshData = async () => {
513
551
  await Promise.all([
@@ -591,7 +629,7 @@ export default function ConciliacaoPage() {
591
629
  router.replace(`${pathname}?${params.toString()}`);
592
630
  }}
593
631
  >
594
- <SelectTrigger className="w-full sm:w-[280px]">
632
+ <SelectTrigger className="w-full sm:w-70">
595
633
  <SelectValue placeholder={t('filters.selectAccount')} />
596
634
  </SelectTrigger>
597
635
  <SelectContent>
@@ -619,100 +657,95 @@ export default function ConciliacaoPage() {
619
657
  </div>
620
658
  </div>
621
659
 
622
- <div className="grid gap-4 md:grid-cols-4">
623
- <Card>
624
- <CardHeader className="pb-2">
625
- <CardTitle className="text-sm font-medium">
626
- {t('cards.reconciled')}
627
- </CardTitle>
628
- </CardHeader>
629
- <CardContent>
630
- <div className="text-2xl font-bold">{percentualConciliado}%</div>
631
- <Progress value={percentualConciliado} className="mt-2" />
632
- </CardContent>
633
- </Card>
634
-
635
- <Card>
636
- <CardHeader className="pb-2">
637
- <CardTitle className="text-sm font-medium">
638
- {t('cards.pending')}
639
- </CardTitle>
640
- </CardHeader>
641
- <CardContent>
642
- <div className="text-2xl font-bold">{extratosFiltered.length}</div>
643
- <p className="text-xs text-muted-foreground">
644
- {t('cards.transactions')}
645
- </p>
646
- </CardContent>
647
- </Card>
648
-
649
- <Card>
650
- <CardHeader className="pb-2">
651
- <CardTitle className="text-sm font-medium">
652
- {t('cards.discrepancies')}
653
- </CardTitle>
654
- </CardHeader>
655
- <CardContent>
656
- <div className="text-2xl font-bold text-orange-600">
657
- {reconciliationSummary?.discrepancies || 0}
658
- </div>
659
- <p className="text-xs text-muted-foreground">
660
- {t('cards.toResolve')}
661
- </p>
662
- </CardContent>
663
- </Card>
664
-
665
- <Card>
666
- <CardHeader className="pb-2">
667
- <CardTitle className="text-sm font-medium">
668
- {t('cards.difference')}
669
- </CardTitle>
670
- </CardHeader>
671
- <CardContent>
672
- <div className="text-2xl font-bold">
673
- <Money value={reconciliationSummary?.difference || 0} />
674
- </div>
675
- <p className="text-xs text-muted-foreground">
676
- {t('cards.notReconciled')}
677
- </p>
678
- </CardContent>
679
- </Card>
680
- </div>
660
+ <KpiCardsGrid items={summaryCards} columns={4} />
681
661
 
682
662
  <div className="grid gap-6 lg:grid-cols-2">
683
- <Card>
684
- <CardHeader>
685
- <CardTitle className="flex items-center gap-2">
663
+ <FinancePageSection
664
+ title={
665
+ <span className="flex items-center gap-2">
686
666
  <RefreshCw className="h-4 w-4" />
687
667
  {t('statement.title')}
688
- </CardTitle>
689
- <CardDescription>{t('statement.description')}</CardDescription>
690
- </CardHeader>
691
- <CardContent>
692
- <div className="space-y-2">
693
- {extratosFiltered.length > 0 ? (
694
- extratosFiltered.map((extrato) => (
695
- <div
696
- key={extrato.id}
697
- className={`flex cursor-pointer items-center gap-3 rounded-lg border p-3 transition-colors ${
698
- selectedExtrato === extrato.id
699
- ? 'border-primary bg-primary/5'
700
- : 'hover:bg-muted/50'
701
- }`}
702
- onClick={() =>
668
+ </span>
669
+ }
670
+ description={t('statement.description')}
671
+ contentClassName="p-4 sm:p-5"
672
+ >
673
+ <div className="space-y-2">
674
+ {extratosFiltered.length > 0 ? (
675
+ extratosFiltered.map((extrato) => (
676
+ <div
677
+ key={extrato.id}
678
+ className={`flex cursor-pointer items-center gap-3 rounded-lg border p-3 transition-colors ${
679
+ selectedExtrato === extrato.id
680
+ ? 'border-primary bg-primary/5'
681
+ : 'hover:bg-muted/50'
682
+ }`}
683
+ onClick={() =>
684
+ setSelectedExtrato(
685
+ selectedExtrato === extrato.id ? null : extrato.id
686
+ )
687
+ }
688
+ >
689
+ <Checkbox
690
+ checked={selectedExtrato === extrato.id}
691
+ onCheckedChange={() =>
703
692
  setSelectedExtrato(
704
693
  selectedExtrato === extrato.id ? null : extrato.id
705
694
  )
706
695
  }
696
+ />
697
+ <div className="flex-1">
698
+ <div className="flex items-center justify-between">
699
+ <span className="text-sm font-medium">
700
+ {extrato.descricao}
701
+ </span>
702
+ <span
703
+ className={`font-semibold ${
704
+ extrato.tipo === 'entrada'
705
+ ? 'text-green-600'
706
+ : 'text-red-600'
707
+ }`}
708
+ >
709
+ {extrato.tipo === 'saida' && '-'}
710
+ {formatarMoeda(Math.abs(extrato.valor))}
711
+ </span>
712
+ </div>
713
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
714
+ <span>{formatarData(extrato.data)}</span>
715
+ <StatusBadge
716
+ status={extrato.statusConciliacao}
717
+ type="conciliacao"
718
+ />
719
+ </div>
720
+ </div>
721
+ </div>
722
+ ))
723
+ ) : (
724
+ <div className="flex flex-col items-center justify-center py-8 text-center">
725
+ <Check className="h-12 w-12 text-green-500" />
726
+ <p className="mt-2 font-medium">{t('statement.emptyTitle')}</p>
727
+ <p className="text-sm text-muted-foreground">
728
+ {t('statement.emptyDescription')}
729
+ </p>
730
+ </div>
731
+ )}
732
+
733
+ {extratosConciliadosFiltered.length > 0 ? (
734
+ <>
735
+ <div className="mt-6 border-t pt-4">
736
+ <p className="text-sm font-medium">
737
+ {t('statement.reconciledTitle')}
738
+ </p>
739
+ <p className="text-xs text-muted-foreground">
740
+ {t('statement.reconciledDescription')}
741
+ </p>
742
+ </div>
743
+
744
+ {extratosConciliadosFiltered.map((extrato) => (
745
+ <div
746
+ key={`reconciled-${extrato.id}`}
747
+ className="flex items-center gap-3 rounded-lg border p-3"
707
748
  >
708
- <Checkbox
709
- checked={selectedExtrato === extrato.id}
710
- onCheckedChange={() =>
711
- setSelectedExtrato(
712
- selectedExtrato === extrato.id ? null : extrato.id
713
- )
714
- }
715
- />
716
749
  <div className="flex-1">
717
750
  <div className="flex items-center justify-between">
718
751
  <span className="text-sm font-medium">
@@ -737,149 +770,94 @@ export default function ConciliacaoPage() {
737
770
  />
738
771
  </div>
739
772
  </div>
740
- </div>
741
- ))
742
- ) : (
743
- <div className="flex flex-col items-center justify-center py-8 text-center">
744
- <Check className="h-12 w-12 text-green-500" />
745
- <p className="mt-2 font-medium">
746
- {t('statement.emptyTitle')}
747
- </p>
748
- <p className="text-sm text-muted-foreground">
749
- {t('statement.emptyDescription')}
750
- </p>
751
- </div>
752
- )}
753
-
754
- {extratosConciliadosFiltered.length > 0 ? (
755
- <>
756
- <div className="mt-6 border-t pt-4">
757
- <p className="text-sm font-medium">
758
- {t('statement.reconciledTitle')}
759
- </p>
760
- <p className="text-xs text-muted-foreground">
761
- {t('statement.reconciledDescription')}
762
- </p>
763
- </div>
764
-
765
- {extratosConciliadosFiltered.map((extrato) => (
766
- <div
767
- key={`reconciled-${extrato.id}`}
768
- className="flex items-center gap-3 rounded-lg border p-3"
773
+ <Button
774
+ type="button"
775
+ variant="outline"
776
+ size="sm"
777
+ onClick={() =>
778
+ setReconciliationToUndo(
779
+ extrato.reconciliationId || null
780
+ )
781
+ }
782
+ disabled={!extrato.reconciliationId}
769
783
  >
770
- <div className="flex-1">
771
- <div className="flex items-center justify-between">
772
- <span className="text-sm font-medium">
773
- {extrato.descricao}
774
- </span>
775
- <span
776
- className={`font-semibold ${
777
- extrato.tipo === 'entrada'
778
- ? 'text-green-600'
779
- : 'text-red-600'
780
- }`}
781
- >
782
- {extrato.tipo === 'saida' && '-'}
783
- {formatarMoeda(Math.abs(extrato.valor))}
784
- </span>
785
- </div>
786
- <div className="flex items-center gap-2 text-xs text-muted-foreground">
787
- <span>{formatarData(extrato.data)}</span>
788
- <StatusBadge
789
- status={extrato.statusConciliacao}
790
- type="conciliacao"
791
- />
792
- </div>
793
- </div>
794
- <Button
795
- type="button"
796
- variant="outline"
797
- size="sm"
798
- onClick={() =>
799
- setReconciliationToUndo(
800
- extrato.reconciliationId || null
801
- )
802
- }
803
- disabled={!extrato.reconciliationId}
804
- >
805
- <Trash2 className="mr-2 h-4 w-4" />
806
- {t('unreconcile.action')}
807
- </Button>
808
- </div>
809
- ))}
810
- </>
811
- ) : null}
812
- </div>
813
- </CardContent>
814
- </Card>
815
-
816
- <Card>
817
- <CardHeader>
818
- <CardTitle className="flex items-center gap-2">
784
+ <Trash2 className="mr-2 h-4 w-4" />
785
+ {t('unreconcile.action')}
786
+ </Button>
787
+ </div>
788
+ ))}
789
+ </>
790
+ ) : null}
791
+ </div>
792
+ </FinancePageSection>
793
+
794
+ <FinancePageSection
795
+ title={
796
+ <span className="flex items-center gap-2">
819
797
  <DollarSign className="h-4 w-4" />
820
798
  {t('openTitles.title')}
821
- </CardTitle>
822
- <CardDescription>{t('openTitles.description')}</CardDescription>
823
- </CardHeader>
824
- <CardContent>
825
- <div className="max-h-[400px] space-y-2 overflow-y-auto">
826
- {titulosCompativeis.map((titulo) => (
827
- <div
828
- key={titulo.id}
829
- className={`flex cursor-pointer items-center gap-3 rounded-lg border p-3 transition-colors ${
830
- selectedTitulo === titulo.id
831
- ? 'border-primary bg-primary/5'
832
- : 'hover:bg-muted/50'
833
- }`}
834
- onClick={() =>
799
+ </span>
800
+ }
801
+ description={t('openTitles.description')}
802
+ contentClassName="p-4 sm:p-5"
803
+ >
804
+ <div className="max-h-100 space-y-2 overflow-y-auto">
805
+ {titulosCompativeis.map((titulo) => (
806
+ <div
807
+ key={titulo.id}
808
+ className={`flex cursor-pointer items-center gap-3 rounded-lg border p-3 transition-colors ${
809
+ selectedTitulo === titulo.id
810
+ ? 'border-primary bg-primary/5'
811
+ : 'hover:bg-muted/50'
812
+ }`}
813
+ onClick={() =>
814
+ setSelectedTitulo(
815
+ selectedTitulo === titulo.id ? null : titulo.id
816
+ )
817
+ }
818
+ >
819
+ <Checkbox
820
+ checked={selectedTitulo === titulo.id}
821
+ onCheckedChange={() =>
835
822
  setSelectedTitulo(
836
823
  selectedTitulo === titulo.id ? null : titulo.id
837
824
  )
838
825
  }
839
- >
840
- <Checkbox
841
- checked={selectedTitulo === titulo.id}
842
- onCheckedChange={() =>
843
- setSelectedTitulo(
844
- selectedTitulo === titulo.id ? null : titulo.id
845
- )
846
- }
847
- />
848
- <div className="flex-1">
849
- <div className="flex items-center justify-between">
850
- <div className="flex items-center gap-2">
851
- <span className="text-sm font-medium">
852
- {titulo.documento}
853
- </span>
854
- <Badge
855
- variant="outline"
856
- className={
857
- titulo.tipo === 'pagar'
858
- ? 'border-red-200 text-red-700'
859
- : 'border-green-200 text-green-700'
860
- }
861
- >
862
- {titulo.tipo === 'pagar'
863
- ? t('openTitles.pay')
864
- : t('openTitles.receive')}
865
- </Badge>
866
- </div>
867
- <span className="font-semibold">
868
- {formatarMoeda(titulo.valor)}
826
+ />
827
+ <div className="flex-1">
828
+ <div className="flex items-center justify-between">
829
+ <div className="flex items-center gap-2">
830
+ <span className="text-sm font-medium">
831
+ {titulo.documento}
869
832
  </span>
833
+ <Badge
834
+ variant="outline"
835
+ className={
836
+ titulo.tipo === 'pagar'
837
+ ? 'border-red-200 text-red-700'
838
+ : 'border-green-200 text-green-700'
839
+ }
840
+ >
841
+ {titulo.tipo === 'pagar'
842
+ ? t('openTitles.pay')
843
+ : t('openTitles.receive')}
844
+ </Badge>
870
845
  </div>
871
- <div className="text-xs text-muted-foreground">
872
- {t('openTitles.personDue', {
873
- person: titulo.pessoa,
874
- dueDate: formatarData(titulo.vencimento),
875
- })}
876
- </div>
846
+ <span className="font-semibold">
847
+ {formatarMoeda(titulo.valor)}
848
+ </span>
849
+ </div>
850
+ <div className="text-xs text-muted-foreground">
851
+ {t('openTitles.personDue', {
852
+ person: titulo.pessoa,
853
+ dueDate: formatarData(titulo.vencimento),
854
+ })}
877
855
  </div>
878
856
  </div>
879
- ))}
880
- </div>
881
- </CardContent>
882
- </Card>
857
+ </div>
858
+ ))}
859
+ </div>
860
+ </FinancePageSection>
883
861
  </div>
884
862
 
885
863
  {selectedExtratoItem && selectedTituloItem && (