@hed-hog/finance 0.0.250 → 0.0.252

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.
@@ -21,14 +21,6 @@ import {
21
21
  CardHeader,
22
22
  CardTitle,
23
23
  } from '@/components/ui/card';
24
- import {
25
- Dialog,
26
- DialogContent,
27
- DialogDescription,
28
- DialogFooter,
29
- DialogHeader,
30
- DialogTitle,
31
- } from '@/components/ui/dialog';
32
24
  import {
33
25
  DropdownMenu,
34
26
  DropdownMenuContent,
@@ -54,6 +46,13 @@ import {
54
46
  SelectTrigger,
55
47
  SelectValue,
56
48
  } from '@/components/ui/select';
49
+ import {
50
+ Sheet,
51
+ SheetContent,
52
+ SheetDescription,
53
+ SheetHeader,
54
+ SheetTitle,
55
+ } from '@/components/ui/sheet';
57
56
  import { StatusBadge } from '@/components/ui/status-badge';
58
57
  import {
59
58
  Table,
@@ -78,25 +77,33 @@ import {
78
77
  } from 'lucide-react';
79
78
  import { useTranslations } from 'next-intl';
80
79
  import Link from 'next/link';
81
- import { useParams, useRouter } from 'next/navigation';
82
- import { useEffect, useState } from 'react';
80
+ import { useParams, useRouter, useSearchParams } from 'next/navigation';
81
+ import { useEffect, useMemo, useState } from 'react';
83
82
  import { useForm } from 'react-hook-form';
84
83
  import { z } from 'zod';
85
84
  import { formatarData } from '../../../_lib/formatters';
86
85
  import { useFinanceData } from '../../../_lib/use-finance-data';
87
86
 
88
- const settleSchema = z.object({
89
- installmentId: z.string().min(1, 'Parcela obrigatória'),
90
- amount: z.number().min(0.01, 'Valor deve ser maior que zero'),
91
- description: z.string().optional(),
92
- });
93
-
94
- type SettleFormValues = z.infer<typeof settleSchema>;
87
+ type SettleFormValues = {
88
+ installmentId: string;
89
+ amount: number;
90
+ description?: string;
91
+ };
95
92
 
96
93
  export default function TituloDetalhePage() {
97
94
  const t = useTranslations('finance.PayableInstallmentDetailPage');
95
+ const settleSchema = useMemo(
96
+ () =>
97
+ z.object({
98
+ installmentId: z.string().min(1, t('validation.installmentRequired')),
99
+ amount: z.number().min(0.01, t('validation.amountGreaterThanZero')),
100
+ description: z.string().optional(),
101
+ }),
102
+ [t]
103
+ );
98
104
  const { request, showToastHandler } = useApp();
99
105
  const router = useRouter();
106
+ const searchParams = useSearchParams();
100
107
  const params = useParams<{ id: string }>();
101
108
  const id = params?.id;
102
109
  const { data, refetch } = useFinanceData();
@@ -111,6 +118,9 @@ export default function TituloDetalhePage() {
111
118
  } = data;
112
119
 
113
120
  const titulo = titulosPagar.find((t) => t.id === id);
121
+ const canSettle = ['aberto', 'parcial', 'vencido'].includes(
122
+ titulo?.status || ''
123
+ );
114
124
 
115
125
  const settleCandidates = (titulo?.parcelas || []).filter(
116
126
  (parcela: any) =>
@@ -136,6 +146,9 @@ export default function TituloDetalhePage() {
136
146
  const [isCanceling, setIsCanceling] = useState(false);
137
147
  const [isSettleDialogOpen, setIsSettleDialogOpen] = useState(false);
138
148
  const [isCancelDialogOpen, setIsCancelDialogOpen] = useState(false);
149
+ const [isReverseDialogOpen, setIsReverseDialogOpen] = useState(false);
150
+ const [selectedSettlementIdToReverse, setSelectedSettlementIdToReverse] =
151
+ useState<string | null>(null);
139
152
  const [reversingSettlementId, setReversingSettlementId] = useState<
140
153
  string | null
141
154
  >(null);
@@ -173,6 +186,18 @@ export default function TituloDetalhePage() {
173
186
  });
174
187
  }, [settleForm, settleCandidates]);
175
188
 
189
+ useEffect(() => {
190
+ const action = searchParams.get('action');
191
+
192
+ if (action !== 'settle') {
193
+ return;
194
+ }
195
+
196
+ if (canSettle && settleCandidates.length > 0) {
197
+ setIsSettleDialogOpen(true);
198
+ }
199
+ }, [canSettle, searchParams, settleCandidates.length]);
200
+
176
201
  if (!titulo) {
177
202
  return (
178
203
  <div className="space-y-6">
@@ -362,8 +387,17 @@ export default function TituloDetalhePage() {
362
387
 
363
388
  const canApprove = titulo.status === 'rascunho';
364
389
  const canEdit = titulo.status === 'rascunho';
365
- const canSettle = ['aberto', 'parcial', 'vencido'].includes(titulo.status);
366
390
  const canCancel = !['cancelado', 'liquidado'].includes(titulo.status);
391
+ const reversibleSettlements = titulo.parcelas
392
+ .flatMap((parcela: any) => parcela.liquidacoes || [])
393
+ .filter((liquidacao: any) => {
394
+ return (
395
+ !!liquidacao?.settlementId &&
396
+ liquidacao?.status !== 'reversed' &&
397
+ liquidacao?.status !== 'estornado'
398
+ );
399
+ });
400
+ const canReverse = reversibleSettlements.length > 0;
367
401
 
368
402
  const getErrorMessage = (error: any, fallback: string) => {
369
403
  const message = error?.response?.data?.message;
@@ -392,11 +426,11 @@ export default function TituloDetalhePage() {
392
426
  });
393
427
 
394
428
  await refetch();
395
- showToastHandler?.('success', 'Título aprovado com sucesso');
429
+ showToastHandler?.('success', t('messages.approveSuccess'));
396
430
  } catch (error) {
397
431
  showToastHandler?.(
398
432
  'error',
399
- getErrorMessage(error, 'Não foi possível aprovar o título')
433
+ getErrorMessage(error, t('messages.approveError'))
400
434
  );
401
435
  } finally {
402
436
  setIsApproving(false);
@@ -422,11 +456,11 @@ export default function TituloDetalhePage() {
422
456
 
423
457
  await refetch();
424
458
  setIsSettleDialogOpen(false);
425
- showToastHandler?.('success', 'Baixa registrada com sucesso');
459
+ showToastHandler?.('success', t('messages.settleSuccess'));
426
460
  } catch (error) {
427
461
  showToastHandler?.(
428
462
  'error',
429
- getErrorMessage(error, 'Não foi possível registrar a baixa')
463
+ getErrorMessage(error, t('messages.settleError'))
430
464
  );
431
465
  } finally {
432
466
  setIsSettling(false);
@@ -447,11 +481,11 @@ export default function TituloDetalhePage() {
447
481
  });
448
482
 
449
483
  await refetch();
450
- showToastHandler?.('success', 'Estorno realizado com sucesso');
484
+ showToastHandler?.('success', t('messages.reverseSuccess'));
451
485
  } catch (error) {
452
486
  showToastHandler?.(
453
487
  'error',
454
- getErrorMessage(error, 'Não foi possível estornar a liquidação')
488
+ getErrorMessage(error, t('messages.reverseError'))
455
489
  );
456
490
  } finally {
457
491
  setReversingSettlementId(null);
@@ -473,11 +507,11 @@ export default function TituloDetalhePage() {
473
507
 
474
508
  await refetch();
475
509
  setIsCancelDialogOpen(false);
476
- showToastHandler?.('success', 'Título cancelado com sucesso');
510
+ showToastHandler?.('success', t('messages.cancelSuccess'));
477
511
  } catch (error) {
478
512
  showToastHandler?.(
479
513
  'error',
480
- getErrorMessage(error, 'Não foi possível cancelar o título')
514
+ getErrorMessage(error, t('messages.cancelError'))
481
515
  );
482
516
  } finally {
483
517
  setIsCanceling(false);
@@ -533,7 +567,20 @@ export default function TituloDetalhePage() {
533
567
  <Download className="mr-2 h-4 w-4" />
534
568
  {t('actions.settle')}
535
569
  </DropdownMenuItem>
536
- <DropdownMenuItem disabled>
570
+ <DropdownMenuItem
571
+ disabled={!canReverse || !!reversingSettlementId}
572
+ onClick={() => {
573
+ const latestSettlement = reversibleSettlements.at(-1);
574
+ if (!latestSettlement?.settlementId) {
575
+ return;
576
+ }
577
+
578
+ setSelectedSettlementIdToReverse(
579
+ String(latestSettlement.settlementId)
580
+ );
581
+ setIsReverseDialogOpen(true);
582
+ }}
583
+ >
537
584
  <Undo className="mr-2 h-4 w-4" />
538
585
  {t('actions.reverse')}
539
586
  </DropdownMenuItem>
@@ -555,42 +602,88 @@ export default function TituloDetalhePage() {
555
602
  >
556
603
  <AlertDialogContent>
557
604
  <AlertDialogHeader>
558
- <AlertDialogTitle>Confirmar cancelamento</AlertDialogTitle>
605
+ <AlertDialogTitle>
606
+ {t('dialogs.cancel.title')}
607
+ </AlertDialogTitle>
559
608
  <AlertDialogDescription>
560
- Essa ação altera o título para cancelado e não remove os
561
- registros de auditoria.
609
+ {t('dialogs.cancel.description')}
562
610
  </AlertDialogDescription>
563
611
  </AlertDialogHeader>
564
612
  <AlertDialogFooter>
565
613
  <AlertDialogCancel disabled={isCanceling}>
566
- Cancelar
614
+ {t('dialogs.cancel.cancel')}
567
615
  </AlertDialogCancel>
568
616
  <AlertDialogAction
569
617
  disabled={isCanceling}
570
618
  onClick={() => void handleCancel()}
571
619
  >
572
- Confirmar cancelamento
620
+ {t('dialogs.cancel.confirm')}
621
+ </AlertDialogAction>
622
+ </AlertDialogFooter>
623
+ </AlertDialogContent>
624
+ </AlertDialog>
625
+
626
+ <AlertDialog
627
+ open={isReverseDialogOpen}
628
+ onOpenChange={(open) => {
629
+ setIsReverseDialogOpen(open);
630
+
631
+ if (!open) {
632
+ setSelectedSettlementIdToReverse(null);
633
+ }
634
+ }}
635
+ >
636
+ <AlertDialogContent>
637
+ <AlertDialogHeader>
638
+ <AlertDialogTitle>
639
+ {t('dialogs.reverse.title')}
640
+ </AlertDialogTitle>
641
+ <AlertDialogDescription>
642
+ {t('dialogs.reverse.description')}
643
+ </AlertDialogDescription>
644
+ </AlertDialogHeader>
645
+ <AlertDialogFooter>
646
+ <AlertDialogCancel disabled={!!reversingSettlementId}>
647
+ {t('dialogs.reverse.cancel')}
648
+ </AlertDialogCancel>
649
+ <AlertDialogAction
650
+ disabled={
651
+ !!reversingSettlementId || !selectedSettlementIdToReverse
652
+ }
653
+ onClick={() => {
654
+ if (!selectedSettlementIdToReverse) {
655
+ return;
656
+ }
657
+
658
+ void handleReverseSettlement(
659
+ selectedSettlementIdToReverse
660
+ ).finally(() => {
661
+ setIsReverseDialogOpen(false);
662
+ setSelectedSettlementIdToReverse(null);
663
+ });
664
+ }}
665
+ >
666
+ {t('dialogs.reverse.confirm')}
573
667
  </AlertDialogAction>
574
668
  </AlertDialogFooter>
575
669
  </AlertDialogContent>
576
670
  </AlertDialog>
577
671
 
578
- <Dialog
672
+ <Sheet
579
673
  open={isSettleDialogOpen}
580
674
  onOpenChange={setIsSettleDialogOpen}
581
675
  >
582
- <DialogContent>
583
- <DialogHeader>
584
- <DialogTitle>Registrar baixa</DialogTitle>
585
- <DialogDescription>
586
- Informe a parcela e o valor da baixa. O backend valida os
587
- estados e limites de valor automaticamente.
588
- </DialogDescription>
589
- </DialogHeader>
676
+ <SheetContent className="w-full sm:max-w-lg overflow-y-auto">
677
+ <SheetHeader>
678
+ <SheetTitle>{t('settleSheet.title')}</SheetTitle>
679
+ <SheetDescription>
680
+ {t('settleSheet.description')}
681
+ </SheetDescription>
682
+ </SheetHeader>
590
683
 
591
684
  <Form {...settleForm}>
592
685
  <form
593
- className="space-y-4"
686
+ className="space-y-4 px-4"
594
687
  onSubmit={settleForm.handleSubmit(handleSettle)}
595
688
  >
596
689
  <FormField
@@ -598,7 +691,9 @@ export default function TituloDetalhePage() {
598
691
  name="installmentId"
599
692
  render={({ field }) => (
600
693
  <FormItem>
601
- <FormLabel>Parcela</FormLabel>
694
+ <FormLabel>
695
+ {t('settleSheet.installmentLabel')}
696
+ </FormLabel>
602
697
  <Select
603
698
  value={field.value}
604
699
  onValueChange={(value) => {
@@ -618,18 +713,24 @@ export default function TituloDetalhePage() {
618
713
  }}
619
714
  >
620
715
  <FormControl>
621
- <SelectTrigger>
622
- <SelectValue placeholder="Selecione" />
716
+ <SelectTrigger className="w-full">
717
+ <SelectValue
718
+ placeholder={t(
719
+ 'settleSheet.installmentPlaceholder'
720
+ )}
721
+ />
623
722
  </SelectTrigger>
624
723
  </FormControl>
625
724
  <SelectContent>
626
725
  {settleCandidates.map((parcela: any) => (
627
726
  <SelectItem key={parcela.id} value={parcela.id}>
628
- Parcela {parcela.numero} - em aberto:{' '}
629
- {new Intl.NumberFormat('pt-BR', {
630
- style: 'currency',
631
- currency: 'BRL',
632
- }).format(Number(parcela.valorAberto || 0))}
727
+ {t('settleSheet.installmentOption', {
728
+ number: parcela.numero,
729
+ amount: new Intl.NumberFormat('pt-BR', {
730
+ style: 'currency',
731
+ currency: 'BRL',
732
+ }).format(Number(parcela.valorAberto || 0)),
733
+ })}
633
734
  </SelectItem>
634
735
  ))}
635
736
  </SelectContent>
@@ -644,7 +745,7 @@ export default function TituloDetalhePage() {
644
745
  name="amount"
645
746
  render={({ field }) => (
646
747
  <FormItem>
647
- <FormLabel>Valor</FormLabel>
748
+ <FormLabel>{t('settleSheet.amountLabel')}</FormLabel>
648
749
  <FormControl>
649
750
  <InputMoney
650
751
  value={Number(field.value || 0)}
@@ -663,7 +764,9 @@ export default function TituloDetalhePage() {
663
764
  name="description"
664
765
  render={({ field }) => (
665
766
  <FormItem>
666
- <FormLabel>Descrição (opcional)</FormLabel>
767
+ <FormLabel>
768
+ {t('settleSheet.descriptionLabel')}
769
+ </FormLabel>
667
770
  <FormControl>
668
771
  <Input {...field} value={field.value || ''} />
669
772
  </FormControl>
@@ -672,23 +775,19 @@ export default function TituloDetalhePage() {
672
775
  )}
673
776
  />
674
777
 
675
- <DialogFooter>
778
+ <div className="flex flex-col gap-4">
676
779
  <Button
677
- type="button"
678
- variant="outline"
780
+ className="w-full"
781
+ type="submit"
679
782
  disabled={isSettling}
680
- onClick={() => setIsSettleDialogOpen(false)}
681
783
  >
682
- Cancelar
683
- </Button>
684
- <Button type="submit" disabled={isSettling}>
685
- Confirmar baixa
784
+ {t('settleSheet.confirm')}
686
785
  </Button>
687
- </DialogFooter>
786
+ </div>
688
787
  </form>
689
788
  </Form>
690
- </DialogContent>
691
- </Dialog>
789
+ </SheetContent>
790
+ </Sheet>
692
791
  </div>
693
792
  }
694
793
  />
@@ -905,7 +1004,9 @@ export default function TituloDetalhePage() {
905
1004
  </TableHead>
906
1005
  <TableHead>{t('settlementsTable.account')}</TableHead>
907
1006
  <TableHead>{t('settlementsTable.method')}</TableHead>
908
- <TableHead className="text-right">Ações</TableHead>
1007
+ <TableHead className="text-right">
1008
+ {t('settlementsTable.actions')}
1009
+ </TableHead>
909
1010
  </TableRow>
910
1011
  </TableHeader>
911
1012
  <TableBody>
@@ -944,22 +1045,25 @@ export default function TituloDetalhePage() {
944
1045
  }
945
1046
  >
946
1047
  <Undo className="mr-2 h-4 w-4" />
947
- Estornar
1048
+ {t('settlementsTable.reverseButton')}
948
1049
  </Button>
949
1050
  </AlertDialogTrigger>
950
1051
  <AlertDialogContent>
951
1052
  <AlertDialogHeader>
952
1053
  <AlertDialogTitle>
953
- Confirmar estorno
1054
+ {t('settlementsTable.reverseDialogTitle')}
954
1055
  </AlertDialogTitle>
955
1056
  <AlertDialogDescription>
956
- Esta ação cria o estorno da liquidação e
957
- recalcula saldos e status.
1057
+ {t(
1058
+ 'settlementsTable.reverseDialogDescription'
1059
+ )}
958
1060
  </AlertDialogDescription>
959
1061
  </AlertDialogHeader>
960
1062
  <AlertDialogFooter>
961
1063
  <AlertDialogCancel>
962
- Cancelar
1064
+ {t(
1065
+ 'settlementsTable.reverseDialogCancel'
1066
+ )}
963
1067
  </AlertDialogCancel>
964
1068
  <AlertDialogAction
965
1069
  onClick={() =>
@@ -968,7 +1072,9 @@ export default function TituloDetalhePage() {
968
1072
  )
969
1073
  }
970
1074
  >
971
- Confirmar estorno
1075
+ {t(
1076
+ 'settlementsTable.reverseDialogConfirm'
1077
+ )}
972
1078
  </AlertDialogAction>
973
1079
  </AlertDialogFooter>
974
1080
  </AlertDialogContent>
@@ -1729,6 +1729,8 @@ export default function TitulosPagarPage() {
1729
1729
  const [search, setSearch] = useState('');
1730
1730
  const [statusFilter, setStatusFilter] = useState<string>('');
1731
1731
  const [editingTitleId, setEditingTitleId] = useState<string | null>(null);
1732
+ const [approvingTitleId, setApprovingTitleId] = useState<string | null>(null);
1733
+ const [reversingTitleId, setReversingTitleId] = useState<string | null>(null);
1732
1734
  const [cancelingTitleId, setCancelingTitleId] = useState<string | null>(null);
1733
1735
 
1734
1736
  const editingTitle = useMemo(
@@ -1792,13 +1794,97 @@ export default function TitulosPagarPage() {
1792
1794
 
1793
1795
  await refetch();
1794
1796
  showToastHandler?.('success', 'Título cancelado com sucesso');
1795
- } catch {
1796
- showToastHandler?.('error', 'Não foi possível cancelar o título');
1797
+ } catch (error: any) {
1798
+ const message = error?.response?.data?.message;
1799
+ showToastHandler?.(
1800
+ 'error',
1801
+ typeof message === 'string' && message.trim()
1802
+ ? message
1803
+ : 'Não foi possível cancelar o título'
1804
+ );
1797
1805
  } finally {
1798
1806
  setCancelingTitleId(null);
1799
1807
  }
1800
1808
  };
1801
1809
 
1810
+ const handleApproveTitle = async (titleId: string) => {
1811
+ if (!titleId || approvingTitleId) {
1812
+ return;
1813
+ }
1814
+
1815
+ setApprovingTitleId(titleId);
1816
+ try {
1817
+ await request({
1818
+ url: `/finance/accounts-payable/installments/${titleId}/approve`,
1819
+ method: 'PATCH',
1820
+ });
1821
+
1822
+ await refetch();
1823
+ showToastHandler?.('success', 'Título aprovado com sucesso');
1824
+ } catch (error: any) {
1825
+ const message = error?.response?.data?.message;
1826
+ showToastHandler?.(
1827
+ 'error',
1828
+ typeof message === 'string' && message.trim()
1829
+ ? message
1830
+ : 'Não foi possível aprovar o título'
1831
+ );
1832
+ } finally {
1833
+ setApprovingTitleId(null);
1834
+ }
1835
+ };
1836
+
1837
+ const handleSettleTitle = (titleId: string) => {
1838
+ router.push(
1839
+ `/finance/accounts-payable/installments/${titleId}?action=settle`
1840
+ );
1841
+ };
1842
+
1843
+ const handleReverseTitle = async (title: any) => {
1844
+ const titleId = title?.id;
1845
+
1846
+ if (!titleId || reversingTitleId) {
1847
+ return;
1848
+ }
1849
+
1850
+ const settlementToReverse = (title?.parcelas || [])
1851
+ .flatMap((parcela: any) => parcela?.liquidacoes || [])
1852
+ .find((liquidacao: any) => {
1853
+ return (
1854
+ !!liquidacao?.settlementId &&
1855
+ liquidacao?.status !== 'reversed' &&
1856
+ liquidacao?.status !== 'estornado'
1857
+ );
1858
+ });
1859
+
1860
+ if (!settlementToReverse?.settlementId) {
1861
+ showToastHandler?.('error', 'Nenhuma liquidação ativa para estornar');
1862
+ return;
1863
+ }
1864
+
1865
+ setReversingTitleId(titleId);
1866
+ try {
1867
+ await request({
1868
+ url: `/finance/accounts-payable/installments/${titleId}/settlements/${settlementToReverse.settlementId}/reverse`,
1869
+ method: 'PATCH',
1870
+ data: {},
1871
+ });
1872
+
1873
+ await refetch();
1874
+ showToastHandler?.('success', 'Estorno realizado com sucesso');
1875
+ } catch (error: any) {
1876
+ const message = error?.response?.data?.message;
1877
+ showToastHandler?.(
1878
+ 'error',
1879
+ typeof message === 'string' && message.trim()
1880
+ ? message
1881
+ : 'Não foi possível estornar a liquidação'
1882
+ );
1883
+ } finally {
1884
+ setReversingTitleId(null);
1885
+ }
1886
+ };
1887
+
1802
1888
  const filteredTitulos = titulosPagar.filter((titulo) => {
1803
1889
  const matchesSearch =
1804
1890
  titulo.documento.toLowerCase().includes(search.toLowerCase()) ||
@@ -1985,23 +2071,32 @@ export default function TitulosPagarPage() {
1985
2071
  </DropdownMenuItem>
1986
2072
  <DropdownMenuSeparator />
1987
2073
  <DropdownMenuItem
1988
- disabled={titulo.status !== 'rascunho'}
2074
+ disabled={
2075
+ titulo.status !== 'rascunho' ||
2076
+ approvingTitleId === titulo.id
2077
+ }
2078
+ onClick={() => void handleApproveTitle(titulo.id)}
1989
2079
  >
1990
2080
  <CheckCircle className="mr-2 h-4 w-4" />
1991
2081
  {t('table.actions.approve')}
1992
2082
  </DropdownMenuItem>
1993
2083
  <DropdownMenuItem
1994
2084
  disabled={
1995
- !['aberto', 'parcial'].includes(titulo.status)
2085
+ !['aberto', 'parcial', 'vencido'].includes(
2086
+ titulo.status
2087
+ )
1996
2088
  }
2089
+ onClick={() => handleSettleTitle(titulo.id)}
1997
2090
  >
1998
2091
  <Download className="mr-2 h-4 w-4" />
1999
2092
  {t('table.actions.settle')}
2000
2093
  </DropdownMenuItem>
2001
2094
  <DropdownMenuItem
2002
2095
  disabled={
2003
- !['parcial', 'liquidado'].includes(titulo.status)
2096
+ !['parcial', 'liquidado'].includes(titulo.status) ||
2097
+ reversingTitleId === titulo.id
2004
2098
  }
2099
+ onClick={() => void handleReverseTitle(titulo)}
2005
2100
  >
2006
2101
  <Undo className="mr-2 h-4 w-4" />
2007
2102
  {t('table.actions.reverse')}
@@ -22,14 +22,6 @@ import {
22
22
  CardHeader,
23
23
  CardTitle,
24
24
  } from '@/components/ui/card';
25
- import {
26
- Dialog,
27
- DialogContent,
28
- DialogDescription,
29
- DialogFooter,
30
- DialogHeader,
31
- DialogTitle,
32
- } from '@/components/ui/dialog';
33
25
  import {
34
26
  DropdownMenu,
35
27
  DropdownMenuContent,
@@ -54,6 +46,13 @@ import {
54
46
  SelectTrigger,
55
47
  SelectValue,
56
48
  } from '@/components/ui/select';
49
+ import {
50
+ Sheet,
51
+ SheetContent,
52
+ SheetDescription,
53
+ SheetHeader,
54
+ SheetTitle,
55
+ } from '@/components/ui/sheet';
57
56
  import { StatusBadge } from '@/components/ui/status-badge';
58
57
  import {
59
58
  Table,
@@ -522,21 +521,21 @@ export default function TituloReceberDetalhePage() {
522
521
  </DropdownMenuContent>
523
522
  </DropdownMenu>
524
523
 
525
- <Dialog
524
+ <Sheet
526
525
  open={isSettleDialogOpen}
527
526
  onOpenChange={setIsSettleDialogOpen}
528
527
  >
529
- <DialogContent>
530
- <DialogHeader>
531
- <DialogTitle>Registrar recebimento</DialogTitle>
532
- <DialogDescription>
528
+ <SheetContent className="w-full sm:max-w-lg overflow-y-auto">
529
+ <SheetHeader>
530
+ <SheetTitle>Registrar recebimento</SheetTitle>
531
+ <SheetDescription>
533
532
  Informe a parcela e o valor para baixa parcial ou total.
534
- </DialogDescription>
535
- </DialogHeader>
533
+ </SheetDescription>
534
+ </SheetHeader>
536
535
 
537
536
  <Form {...settleForm}>
538
537
  <form
539
- className="space-y-4"
538
+ className="space-y-4 px-1"
540
539
  onSubmit={settleForm.handleSubmit(handleSettle)}
541
540
  >
542
541
  <FormField
@@ -618,7 +617,7 @@ export default function TituloReceberDetalhePage() {
618
617
  )}
619
618
  />
620
619
 
621
- <DialogFooter>
620
+ <div className="flex justify-end gap-2 pt-2">
622
621
  <Button
623
622
  type="button"
624
623
  variant="outline"
@@ -630,11 +629,11 @@ export default function TituloReceberDetalhePage() {
630
629
  <Button type="submit" disabled={isSettling}>
631
630
  Confirmar recebimento
632
631
  </Button>
633
- </DialogFooter>
632
+ </div>
634
633
  </form>
635
634
  </Form>
636
- </DialogContent>
637
- </Dialog>
635
+ </SheetContent>
636
+ </Sheet>
638
637
  </div>
639
638
  }
640
639
  />