@hed-hog/finance 0.0.253 → 0.0.256

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.
@@ -1,16 +1,7 @@
1
1
  'use client';
2
2
 
3
+ import { FinanceTitleActionsMenu } from '@/app/(app)/(libraries)/finance/_components/finance-title-actions-menu';
3
4
  import { Page, PageHeader } from '@/components/entity-list';
4
- import {
5
- AlertDialog,
6
- AlertDialogAction,
7
- AlertDialogCancel,
8
- AlertDialogContent,
9
- AlertDialogDescription,
10
- AlertDialogFooter,
11
- AlertDialogHeader,
12
- AlertDialogTitle,
13
- } from '@/components/ui/alert-dialog';
14
5
  import { AuditTimeline } from '@/components/ui/audit-timeline';
15
6
  import { Button } from '@/components/ui/button';
16
7
  import {
@@ -20,13 +11,6 @@ import {
20
11
  CardHeader,
21
12
  CardTitle,
22
13
  } from '@/components/ui/card';
23
- import {
24
- DropdownMenu,
25
- DropdownMenuContent,
26
- DropdownMenuItem,
27
- DropdownMenuSeparator,
28
- DropdownMenuTrigger,
29
- } from '@/components/ui/dropdown-menu';
30
14
  import {
31
15
  Form,
32
16
  FormControl,
@@ -37,6 +21,7 @@ import {
37
21
  } from '@/components/ui/form';
38
22
  import { Input } from '@/components/ui/input';
39
23
  import { InputMoney } from '@/components/ui/input-money';
24
+ import { Label } from '@/components/ui/label';
40
25
  import { Money } from '@/components/ui/money';
41
26
  import {
42
27
  Select,
@@ -65,18 +50,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
65
50
  import { TagSelectorSheet } from '@/components/ui/tag-selector-sheet';
66
51
  import { useApp, useQuery } from '@hed-hog/next-app-provider';
67
52
  import { zodResolver } from '@hookform/resolvers/zod';
68
- import {
69
- CheckCircle,
70
- ChevronDown,
71
- ChevronUp,
72
- Download,
73
- Edit,
74
- FileText,
75
- Loader2,
76
- MoreHorizontal,
77
- Undo,
78
- XCircle,
79
- } from 'lucide-react';
53
+ import { ChevronDown, ChevronUp, FileText, Loader2, Undo } from 'lucide-react';
80
54
  import { useTranslations } from 'next-intl';
81
55
  import Link from 'next/link';
82
56
  import { useParams, useRouter, useSearchParams } from 'next/navigation';
@@ -84,6 +58,13 @@ import { Fragment, useEffect, useMemo, useState } from 'react';
84
58
  import { useForm } from 'react-hook-form';
85
59
  import { z } from 'zod';
86
60
  import { formatarData } from '../../../_lib/formatters';
61
+ import {
62
+ canApproveTitle,
63
+ canCancelTitle,
64
+ canEditTitle,
65
+ canReverseTitle,
66
+ canSettleTitle,
67
+ } from '../../../_lib/title-action-rules';
87
68
  import { useFinanceData } from '../../../_lib/use-finance-data';
88
69
 
89
70
  type SettleFormValues = {
@@ -172,9 +153,7 @@ export default function TituloDetalhePage() {
172
153
  placeholderData: (old) => old,
173
154
  });
174
155
 
175
- const canSettle = ['aberto', 'parcial', 'vencido'].includes(
176
- titulo?.status || ''
177
- );
156
+ const canSettle = canSettleTitle(titulo?.status);
178
157
 
179
158
  const settleCandidates = (titulo?.parcelas || []).filter(
180
159
  (parcela: any) =>
@@ -199,7 +178,6 @@ export default function TituloDetalhePage() {
199
178
  const [isSettling, setIsSettling] = useState(false);
200
179
  const [isCanceling, setIsCanceling] = useState(false);
201
180
  const [isSettleDialogOpen, setIsSettleDialogOpen] = useState(false);
202
- const [isCancelDialogOpen, setIsCancelDialogOpen] = useState(false);
203
181
  const [isReverseDialogOpen, setIsReverseDialogOpen] = useState(false);
204
182
  const [selectedSettlementIdToReverse, setSelectedSettlementIdToReverse] =
205
183
  useState<string | null>(null);
@@ -339,11 +317,6 @@ export default function TituloDetalhePage() {
339
317
  window.open(url, '_blank', 'noopener,noreferrer');
340
318
  };
341
319
 
342
- const tTagSelector = (key: string, fallback: string) => {
343
- const fullKey = `tagSelector.${key}`;
344
- return t.has(fullKey) ? t(fullKey) : fallback;
345
- };
346
-
347
320
  const toTagSlug = (value: string) => {
348
321
  return value
349
322
  .normalize('NFD')
@@ -391,10 +364,7 @@ export default function TituloDetalhePage() {
391
364
  return [...current, newTag];
392
365
  });
393
366
 
394
- showToastHandler?.(
395
- 'success',
396
- tTagSelector('messages.createSuccess', 'Tag criada com sucesso')
397
- );
367
+ showToastHandler?.('success', t('tagSelector.messages.createSuccess'));
398
368
 
399
369
  return {
400
370
  id: newTag.id,
@@ -402,10 +372,7 @@ export default function TituloDetalhePage() {
402
372
  color: newTag.cor,
403
373
  };
404
374
  } catch {
405
- showToastHandler?.(
406
- 'error',
407
- tTagSelector('messages.createError', 'Não foi possível criar a tag')
408
- );
375
+ showToastHandler?.('error', t('tagSelector.messages.createError'));
409
376
  return null;
410
377
  } finally {
411
378
  setIsCreatingTag(false);
@@ -432,26 +399,21 @@ export default function TituloDetalhePage() {
432
399
  setSelectedTagIds(nextTagIds);
433
400
  }
434
401
  } catch {
435
- showToastHandler?.(
436
- 'error',
437
- tTagSelector(
438
- 'messages.updateError',
439
- 'Não foi possível atualizar as tags'
440
- )
441
- );
402
+ showToastHandler?.('error', t('tagSelector.messages.updateError'));
442
403
  }
443
404
  };
444
405
 
445
- const canApprove = titulo.status === 'rascunho';
446
- const canEdit = titulo.status === 'rascunho';
447
- const canCancel = !['cancelado', 'liquidado'].includes(titulo.status);
406
+ const canApprove = canApproveTitle(titulo.status);
407
+ const canEdit = canEditTitle(titulo.status);
408
+ const canCancel = canCancelTitle(titulo.status);
448
409
  const reversibleSettlements = settlementsHistory.filter(
449
410
  (group) =>
450
411
  group.normal?.id &&
451
412
  group.statusLabel === 'ATIVO' &&
452
413
  !group.normal?.reconciled
453
414
  );
454
- const canReverse = reversibleSettlements.length > 0;
415
+ const canReverse =
416
+ canReverseTitle(titulo.status) && reversibleSettlements.length > 0;
455
417
 
456
418
  const getErrorMessage = (error: any, fallback: string) => {
457
419
  const message = error?.response?.data?.message;
@@ -508,7 +470,7 @@ export default function TituloDetalhePage() {
508
470
  },
509
471
  });
510
472
 
511
- await refetch();
473
+ await Promise.all([refetch(), refetchSettlementsHistory()]);
512
474
  setIsSettleDialogOpen(false);
513
475
  showToastHandler?.('success', t('messages.settleSuccess'));
514
476
  } catch (error) {
@@ -521,7 +483,10 @@ export default function TituloDetalhePage() {
521
483
  }
522
484
  };
523
485
 
524
- const handleReverseSettlement = async (settlementId: string) => {
486
+ const handleReverseSettlement = async (
487
+ settlementId: string,
488
+ reasonOverride?: string
489
+ ) => {
525
490
  if (!settlementId || reversingSettlementId) {
526
491
  return;
527
492
  }
@@ -532,8 +497,8 @@ export default function TituloDetalhePage() {
532
497
  url: `/finance/settlements/${settlementId}/reverse`,
533
498
  method: 'POST',
534
499
  data: {
535
- reason: reverseReason?.trim() || undefined,
536
- memo: reverseReason?.trim() || undefined,
500
+ reason: reasonOverride?.trim() || reverseReason?.trim() || undefined,
501
+ memo: reasonOverride?.trim() || reverseReason?.trim() || undefined,
537
502
  },
538
503
  });
539
504
 
@@ -563,11 +528,11 @@ export default function TituloDetalhePage() {
563
528
  });
564
529
 
565
530
  await Promise.all([refetch(), refetchSettlementsHistory()]);
566
- showToastHandler?.('success', 'Conciliação removida com sucesso');
531
+ showToastHandler?.('success', t('messages.unreconcileSuccess'));
567
532
  } catch (error) {
568
533
  showToastHandler?.(
569
534
  'error',
570
- getErrorMessage(error, 'Não foi possível desconciliar a liquidação')
535
+ getErrorMessage(error, t('messages.unreconcileError'))
571
536
  );
572
537
  } finally {
573
538
  setUnreconcilingId(null);
@@ -595,7 +560,6 @@ export default function TituloDetalhePage() {
595
560
  });
596
561
 
597
562
  await refetch();
598
- setIsCancelDialogOpen(false);
599
563
  showToastHandler?.('success', t('messages.cancelSuccess'));
600
564
  } catch (error) {
601
565
  showToastHandler?.(
@@ -623,97 +587,62 @@ export default function TituloDetalhePage() {
623
587
  ]}
624
588
  actions={
625
589
  <div className="flex items-center gap-2">
626
- <DropdownMenu>
627
- <DropdownMenuTrigger asChild>
628
- <Button variant="outline">
629
- <MoreHorizontal className="mr-2 h-4 w-4" />
630
- {t('actions.title')}
631
- </Button>
632
- </DropdownMenuTrigger>
633
- <DropdownMenuContent align="end">
634
- <DropdownMenuItem
635
- disabled={!canEdit}
636
- onClick={() =>
637
- router.push(
638
- `/finance/accounts-payable/installments?editId=${titulo.id}`
639
- )
640
- }
641
- >
642
- <Edit className="mr-2 h-4 w-4" />
643
- {t('actions.edit')}
644
- </DropdownMenuItem>
645
- <DropdownMenuItem
646
- disabled={!canApprove || isApproving}
647
- onClick={() => void handleApprove()}
648
- >
649
- <CheckCircle className="mr-2 h-4 w-4" />
650
- {t('actions.approve')}
651
- </DropdownMenuItem>
652
- <DropdownMenuItem
653
- disabled={!canSettle || settleCandidates.length === 0}
654
- onClick={() => setIsSettleDialogOpen(true)}
655
- >
656
- <Download className="mr-2 h-4 w-4" />
657
- {t('actions.settle')}
658
- </DropdownMenuItem>
659
- <DropdownMenuItem
660
- disabled={!canReverse || !!reversingSettlementId}
661
- onClick={() => {
662
- const latestSettlement = reversibleSettlements[0];
663
- if (!latestSettlement?.normal?.id) {
664
- return;
665
- }
666
-
667
- setSelectedSettlementIdToReverse(
668
- String(latestSettlement.normal.id)
669
- );
670
- setReverseReason('');
671
- setIsReverseDialogOpen(true);
672
- }}
673
- >
674
- <Undo className="mr-2 h-4 w-4" />
675
- {t('actions.reverse')}
676
- </DropdownMenuItem>
677
- <DropdownMenuSeparator />
678
- <DropdownMenuItem
679
- className="text-destructive"
680
- disabled={!canCancel || isCanceling}
681
- onClick={() => setIsCancelDialogOpen(true)}
682
- >
683
- <XCircle className="mr-2 h-4 w-4" />
684
- {t('actions.cancel')}
685
- </DropdownMenuItem>
686
- </DropdownMenuContent>
687
- </DropdownMenu>
688
-
689
- <AlertDialog
690
- open={isCancelDialogOpen}
691
- onOpenChange={setIsCancelDialogOpen}
692
- >
693
- <AlertDialogContent>
694
- <AlertDialogHeader>
695
- <AlertDialogTitle>
696
- {t('dialogs.cancel.title')}
697
- </AlertDialogTitle>
698
- <AlertDialogDescription>
699
- {t('dialogs.cancel.description')}
700
- </AlertDialogDescription>
701
- </AlertDialogHeader>
702
- <AlertDialogFooter>
703
- <AlertDialogCancel disabled={isCanceling}>
704
- {t('dialogs.cancel.cancel')}
705
- </AlertDialogCancel>
706
- <AlertDialogAction
707
- disabled={isCanceling}
708
- onClick={() => void handleCancel()}
709
- >
710
- {t('dialogs.cancel.confirm')}
711
- </AlertDialogAction>
712
- </AlertDialogFooter>
713
- </AlertDialogContent>
714
- </AlertDialog>
590
+ <FinanceTitleActionsMenu
591
+ triggerVariant="outline"
592
+ canEdit={canEdit}
593
+ canApprove={canApprove}
594
+ canSettle={canSettle && settleCandidates.length > 0}
595
+ canReverse={canReverse}
596
+ canCancel={canCancel}
597
+ isApproving={isApproving}
598
+ isReversing={!!reversingSettlementId}
599
+ isCanceling={isCanceling}
600
+ labels={{
601
+ menu: t('actions.title'),
602
+ srActions: t('actions.title'),
603
+ edit: t('actions.edit'),
604
+ approve: t('actions.approve'),
605
+ settle: t('actions.settle'),
606
+ reverse: t('actions.reverse'),
607
+ cancel: t('actions.cancel'),
608
+ }}
609
+ dialogs={{
610
+ cancelTitle: t('dialogs.cancel.title'),
611
+ cancelDescription: t('dialogs.cancel.description'),
612
+ cancelButton: t('dialogs.cancel.cancel'),
613
+ confirmCancelButton: t('dialogs.cancel.confirm'),
614
+ reverseTitle: t('dialogs.reverse.title'),
615
+ reverseDescription: t('dialogs.reverse.description'),
616
+ reverseReasonLabel: t('dialogs.reverse.reasonLabel'),
617
+ reverseReasonPlaceholder: t(
618
+ 'dialogs.reverse.reasonPlaceholder'
619
+ ),
620
+ reverseButton: t('dialogs.reverse.cancel'),
621
+ confirmReverseButton: t('dialogs.reverse.confirm'),
622
+ }}
623
+ onEdit={() =>
624
+ router.push(
625
+ `/finance/accounts-payable/installments?editId=${titulo.id}`
626
+ )
627
+ }
628
+ onApprove={() => void handleApprove()}
629
+ onSettle={() => setIsSettleDialogOpen(true)}
630
+ onReverse={(reason) => {
631
+ const latestSettlementId = reversibleSettlements[0]?.normal?.id;
632
+
633
+ if (!latestSettlementId) {
634
+ return;
635
+ }
636
+
637
+ return handleReverseSettlement(
638
+ String(latestSettlementId),
639
+ reason
640
+ );
641
+ }}
642
+ onCancel={() => handleCancel()}
643
+ />
715
644
 
716
- <AlertDialog
645
+ <Sheet
717
646
  open={isReverseDialogOpen}
718
647
  onOpenChange={(open) => {
719
648
  setIsReverseDialogOpen(open);
@@ -724,52 +653,63 @@ export default function TituloDetalhePage() {
724
653
  }
725
654
  }}
726
655
  >
727
- <AlertDialogContent>
728
- <AlertDialogHeader>
729
- <AlertDialogTitle>
730
- {t('dialogs.reverse.title')}
731
- </AlertDialogTitle>
732
- <AlertDialogDescription>
656
+ <SheetContent className="w-full sm:max-w-lg overflow-y-auto">
657
+ <SheetHeader>
658
+ <SheetTitle>{t('dialogs.reverse.title')}</SheetTitle>
659
+ <SheetDescription>
733
660
  {t('dialogs.reverse.description')}
734
- </AlertDialogDescription>
735
- </AlertDialogHeader>
736
- <div className="space-y-2">
737
- <FormLabel>Motivo do estorno</FormLabel>
738
- <Input
739
- value={reverseReason}
740
- onChange={(event) => setReverseReason(event.target.value)}
741
- placeholder="Ex.: baixa duplicada"
742
- maxLength={255}
743
- disabled={!!reversingSettlementId}
744
- />
745
- </div>
746
- <AlertDialogFooter>
747
- <AlertDialogCancel disabled={!!reversingSettlementId}>
748
- {t('dialogs.reverse.cancel')}
749
- </AlertDialogCancel>
750
- <AlertDialogAction
751
- disabled={
752
- !!reversingSettlementId || !selectedSettlementIdToReverse
753
- }
754
- onClick={() => {
755
- if (!selectedSettlementIdToReverse) {
756
- return;
757
- }
661
+ </SheetDescription>
662
+ </SheetHeader>
758
663
 
759
- void handleReverseSettlement(
760
- selectedSettlementIdToReverse
761
- ).finally(() => {
664
+ <div className="space-y-4 px-4">
665
+ <div className="space-y-2">
666
+ <Label>{t('dialogs.reverse.reasonLabel')}</Label>
667
+ <Input
668
+ value={reverseReason}
669
+ onChange={(event) => setReverseReason(event.target.value)}
670
+ placeholder={t('dialogs.reverse.reasonPlaceholder')}
671
+ maxLength={255}
672
+ disabled={!!reversingSettlementId}
673
+ />
674
+ </div>
675
+
676
+ <div className="flex flex-col gap-2">
677
+ <Button
678
+ disabled={
679
+ !!reversingSettlementId ||
680
+ !selectedSettlementIdToReverse
681
+ }
682
+ onClick={() => {
683
+ if (!selectedSettlementIdToReverse) {
684
+ return;
685
+ }
686
+
687
+ void handleReverseSettlement(
688
+ selectedSettlementIdToReverse
689
+ ).finally(() => {
690
+ setIsReverseDialogOpen(false);
691
+ setSelectedSettlementIdToReverse(null);
692
+ setReverseReason('');
693
+ });
694
+ }}
695
+ >
696
+ {t('dialogs.reverse.confirm')}
697
+ </Button>
698
+ <Button
699
+ variant="outline"
700
+ disabled={!!reversingSettlementId}
701
+ onClick={() => {
762
702
  setIsReverseDialogOpen(false);
763
703
  setSelectedSettlementIdToReverse(null);
764
704
  setReverseReason('');
765
- });
766
- }}
767
- >
768
- {t('dialogs.reverse.confirm')}
769
- </AlertDialogAction>
770
- </AlertDialogFooter>
771
- </AlertDialogContent>
772
- </AlertDialog>
705
+ }}
706
+ >
707
+ {t('dialogs.reverse.cancel')}
708
+ </Button>
709
+ </div>
710
+ </div>
711
+ </SheetContent>
712
+ </Sheet>
773
713
 
774
714
  <Sheet
775
715
  open={isSettleDialogOpen}
@@ -971,35 +911,21 @@ export default function TituloDetalhePage() {
971
911
  onChange={handleChangeTags}
972
912
  onCreateTag={handleCreateTag}
973
913
  disabled={isCreatingTag}
974
- emptyText={tTagSelector('noTags', 'Sem tags')}
914
+ emptyText={t('tagSelector.noTags')}
975
915
  labels={{
976
- addTag: tTagSelector('addTag', 'Adicionar tag'),
977
- sheetTitle: tTagSelector('sheetTitle', 'Gerenciar tags'),
978
- sheetDescription: tTagSelector(
979
- 'sheetDescription',
980
- 'Selecione tags existentes ou crie uma nova.'
981
- ),
982
- createLabel: tTagSelector('createLabel', 'Nova tag'),
983
- createPlaceholder: tTagSelector(
984
- 'createPlaceholder',
985
- 'Digite o nome da tag'
986
- ),
987
- createAction: tTagSelector('createAction', 'Criar tag'),
988
- popularTitle: tTagSelector(
989
- 'popularTitle',
990
- 'Tags mais usadas'
991
- ),
992
- selectedTitle: tTagSelector(
993
- 'selectedTitle',
994
- 'Tags selecionadas'
995
- ),
996
- noTags: tTagSelector('noTags', 'Sem tags'),
997
- cancel: tTagSelector('cancel', 'Cancelar'),
998
- apply: tTagSelector('apply', 'Aplicar'),
916
+ addTag: t('tagSelector.addTag'),
917
+ sheetTitle: t('tagSelector.sheetTitle'),
918
+ sheetDescription: t('tagSelector.sheetDescription'),
919
+ createLabel: t('tagSelector.createLabel'),
920
+ createPlaceholder: t('tagSelector.createPlaceholder'),
921
+ createAction: t('tagSelector.createAction'),
922
+ popularTitle: t('tagSelector.popularTitle'),
923
+ selectedTitle: t('tagSelector.selectedTitle'),
924
+ noTags: t('tagSelector.noTags'),
925
+ cancel: t('tagSelector.cancel'),
926
+ apply: t('tagSelector.apply'),
999
927
  removeTagAria: (tagName: string) =>
1000
- t.has('tagSelector.removeTagAria')
1001
- ? t('tagSelector.removeTagAria', { tag: tagName })
1002
- : `Remover tag ${tagName}`,
928
+ t('tagSelector.removeTagAria', { tag: tagName }),
1003
929
  }}
1004
930
  />
1005
931
  </dd>
@@ -1090,20 +1016,36 @@ export default function TituloDetalhePage() {
1090
1016
  {isFetchingSettlementsHistory ? (
1091
1017
  <div className="flex items-center justify-center py-8 text-muted-foreground">
1092
1018
  <Loader2 className="mr-2 h-4 w-4 animate-spin" />
1093
- Carregando histórico de liquidações...
1019
+ {t('settlementsHistory.loading')}
1094
1020
  </div>
1095
1021
  ) : settlementsHistory.length > 0 ? (
1096
1022
  <Table>
1097
1023
  <TableHeader>
1098
1024
  <TableRow>
1099
- <TableHead>Date</TableHead>
1100
- <TableHead>Type</TableHead>
1101
- <TableHead>Status</TableHead>
1102
- <TableHead className="text-right">Net Amount</TableHead>
1103
- <TableHead>Method</TableHead>
1104
- <TableHead>Account</TableHead>
1105
- <TableHead>Reconciled</TableHead>
1106
- <TableHead className="text-right">Actions</TableHead>
1025
+ <TableHead>
1026
+ {t('settlementsHistory.headers.date')}
1027
+ </TableHead>
1028
+ <TableHead>
1029
+ {t('settlementsHistory.headers.type')}
1030
+ </TableHead>
1031
+ <TableHead>
1032
+ {t('settlementsHistory.headers.status')}
1033
+ </TableHead>
1034
+ <TableHead className="text-right">
1035
+ {t('settlementsHistory.headers.netAmount')}
1036
+ </TableHead>
1037
+ <TableHead>
1038
+ {t('settlementsHistory.headers.method')}
1039
+ </TableHead>
1040
+ <TableHead>
1041
+ {t('settlementsHistory.headers.account')}
1042
+ </TableHead>
1043
+ <TableHead>
1044
+ {t('settlementsHistory.headers.reconciled')}
1045
+ </TableHead>
1046
+ <TableHead className="text-right">
1047
+ {t('settlementsHistory.headers.actions')}
1048
+ </TableHead>
1107
1049
  </TableRow>
1108
1050
  </TableHeader>
1109
1051
  <TableBody>
@@ -1123,7 +1065,7 @@ export default function TituloDetalhePage() {
1123
1065
  </TableCell>
1124
1066
  <TableCell>
1125
1067
  <span className="rounded-md border px-2 py-1 text-xs font-medium">
1126
- NORMAL
1068
+ {t('settlementsHistory.normalType')}
1127
1069
  </span>
1128
1070
  </TableCell>
1129
1071
  <TableCell>
@@ -1158,8 +1100,8 @@ export default function TituloDetalhePage() {
1158
1100
  }`}
1159
1101
  >
1160
1102
  {group.normal.reconciled
1161
- ? 'Conciliado'
1162
- : 'Pendente'}
1103
+ ? t('settlementsHistory.reconciled.yes')
1104
+ : t('settlementsHistory.reconciled.no')}
1163
1105
  </span>
1164
1106
  </TableCell>
1165
1107
  <TableCell className="text-right">
@@ -1181,8 +1123,10 @@ export default function TituloDetalhePage() {
1181
1123
  >
1182
1124
  {unreconcilingId ===
1183
1125
  group.normal.reconciliationId
1184
- ? 'Desconciliando...'
1185
- : 'Desconciliar'}
1126
+ ? t(
1127
+ 'settlementsHistory.unreconcileLoading'
1128
+ )
1129
+ : t('settlementsHistory.unreconcile')}
1186
1130
  </Button>
1187
1131
  ) : null}
1188
1132
 
@@ -1191,7 +1135,7 @@ export default function TituloDetalhePage() {
1191
1135
  size="sm"
1192
1136
  title={
1193
1137
  group.normal.reconciled
1194
- ? 'Desconciliar primeiro'
1138
+ ? t('settlementsHistory.unreconcileFirst')
1195
1139
  : undefined
1196
1140
  }
1197
1141
  disabled={!canReverseNormal}
@@ -1202,7 +1146,7 @@ export default function TituloDetalhePage() {
1202
1146
  }}
1203
1147
  >
1204
1148
  <Undo className="mr-2 h-4 w-4" />
1205
- Estornar
1149
+ {t('settlementsHistory.reverse')}
1206
1150
  </Button>
1207
1151
 
1208
1152
  <Button
@@ -1229,12 +1173,12 @@ export default function TituloDetalhePage() {
1229
1173
  </TableCell>
1230
1174
  <TableCell>
1231
1175
  <span className="rounded-md border px-2 py-1 text-xs font-medium">
1232
- REVERSAL
1176
+ {t('settlementsHistory.reversalType')}
1233
1177
  </span>
1234
1178
  </TableCell>
1235
1179
  <TableCell>
1236
1180
  <span className="rounded-md bg-muted px-2 py-1 text-xs font-medium text-muted-foreground">
1237
- ESTORNO
1181
+ {t('settlementsHistory.reversedStatus')}
1238
1182
  </span>
1239
1183
  </TableCell>
1240
1184
  <TableCell className="text-right font-medium text-destructive">
@@ -1260,7 +1204,7 @@ export default function TituloDetalhePage() {
1260
1204
  <TableCell colSpan={8}>
1261
1205
  <div className="rounded-md border bg-muted/20 p-3">
1262
1206
  <p className="mb-2 text-xs font-medium text-muted-foreground">
1263
- Allocations
1207
+ {t('settlementsHistory.allocationsTitle')}
1264
1208
  </p>
1265
1209
  <div className="space-y-1">
1266
1210
  {group.allocations.map((allocation) => (
@@ -1269,7 +1213,10 @@ export default function TituloDetalhePage() {
1269
1213
  className="flex items-center justify-between text-sm"
1270
1214
  >
1271
1215
  <span>
1272
- Parcela #{allocation.installmentSeq}
1216
+ {t(
1217
+ 'settlementsHistory.allocationInstallment'
1218
+ )}{' '}
1219
+ #{allocation.installmentSeq}
1273
1220
  </span>
1274
1221
  <span className="font-medium">
1275
1222
  +