@hed-hog/finance 0.0.329 → 0.0.331

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 (24) hide show
  1. package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +18 -15
  2. package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +10 -18
  3. package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +4 -8
  4. package/hedhog/frontend/app/accounts-receivable/installments/[id]/page.tsx.ejs +2 -2
  5. package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +16 -24
  6. package/hedhog/frontend/app/administration/audit-logs/page.tsx.ejs +22 -11
  7. package/hedhog/frontend/app/administration/categories/page.tsx.ejs +3 -5
  8. package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +3 -5
  9. package/hedhog/frontend/app/administration/currencies/page.tsx.ejs +3 -5
  10. package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +12 -9
  11. package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +49 -73
  12. package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +7 -9
  13. package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +98 -74
  14. package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +33 -25
  15. package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +2 -2
  16. package/hedhog/frontend/app/reports/actual-vs-forecast/page.tsx.ejs +3 -1
  17. package/hedhog/frontend/app/reports/aging-default/page.tsx.ejs +3 -1
  18. package/hedhog/frontend/app/reports/cash-position/page.tsx.ejs +3 -1
  19. package/hedhog/frontend/app/reports/overview-results/page.tsx.ejs +15 -8
  20. package/hedhog/frontend/app/reports/top-customers/page.tsx.ejs +9 -5
  21. package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +9 -5
  22. package/hedhog/frontend/messages/en.json +114 -3
  23. package/hedhog/frontend/messages/pt.json +99 -3
  24. package/package.json +5 -5
@@ -82,19 +82,22 @@ import {
82
82
  FinanceSheetSection,
83
83
  } from '../../_components/finance-layout';
84
84
 
85
- const bankAccountFormSchema = z.object({
86
- banco: z.string().trim().min(1, 'Banco é obrigatório'),
87
- agencia: z.string().optional(),
88
- conta: z.string().optional(),
89
- tipo: z.string().min(1, 'Tipo é obrigatório'),
90
- descricao: z.string().optional(),
91
- logoFileId: z.number().int().nullable().optional(),
92
- currencyId: z.number().int().nullable().optional(),
93
- dataInicial: z.string().optional(),
94
- saldoInicial: z.number().min(0, 'Saldo inicial inválido'),
95
- });
96
-
97
- type BankAccountFormValues = z.infer<typeof bankAccountFormSchema>;
85
+ const createBankAccountFormSchema = (t: ReturnType<typeof useTranslations>) =>
86
+ z.object({
87
+ banco: z.string().trim().min(1, t('validation.bankRequired')),
88
+ agencia: z.string().optional(),
89
+ conta: z.string().optional(),
90
+ tipo: z.string().min(1, t('validation.typeRequired')),
91
+ descricao: z.string().optional(),
92
+ logoFileId: z.number().int().nullable().optional(),
93
+ currencyId: z.number().int().nullable().optional(),
94
+ dataInicial: z.string().optional(),
95
+ saldoInicial: z.number().min(0, t('validation.initialBalanceInvalid')),
96
+ });
97
+
98
+ type BankAccountFormValues = z.infer<
99
+ ReturnType<typeof createBankAccountFormSchema>
100
+ >;
98
101
 
99
102
  type Currency = {
100
103
  id: string;
@@ -226,36 +229,18 @@ function NovaContaSheet({
226
229
  });
227
230
  const currencies = currenciesData || [];
228
231
 
229
- const createSuccessMessage = t.has('messages.createSuccess')
230
- ? t('messages.createSuccess')
231
- : 'Conta bancária cadastrada com sucesso';
232
- const createErrorMessage = t.has('messages.createError')
233
- ? t('messages.createError')
234
- : 'Erro ao cadastrar conta bancária';
235
- const updateSuccessMessage = t.has('messages.updateSuccess')
236
- ? t('messages.updateSuccess')
237
- : 'Conta bancária atualizada com sucesso';
238
- const updateErrorMessage = t.has('messages.updateError')
239
- ? t('messages.updateError')
240
- : 'Erro ao atualizar conta bancária';
241
- const logoUploadSuccessMessage = t.has('messages.logoUploadSuccess')
242
- ? t('messages.logoUploadSuccess')
243
- : 'Logo enviado com sucesso';
244
- const logoUploadErrorMessage = t.has('messages.logoUploadError')
245
- ? t('messages.logoUploadError')
246
- : 'Erro ao enviar logo';
247
- const logoRemoveSuccessMessage = t.has('messages.logoRemoveSuccess')
248
- ? t('messages.logoRemoveSuccess')
249
- : 'Logo removido com sucesso';
250
- const logoInvalidTypeMessage = t.has('messages.logoInvalidType')
251
- ? t('messages.logoInvalidType')
252
- : 'Selecione um arquivo de imagem válido';
253
- const logoTooLargeMessage = t.has('messages.logoTooLarge')
254
- ? t('messages.logoTooLarge')
255
- : 'O logo deve ter no máximo 5 MB';
232
+ const createSuccessMessage = t('messages.createSuccess');
233
+ const createErrorMessage = t('messages.createError');
234
+ const updateSuccessMessage = t('messages.updateSuccess');
235
+ const updateErrorMessage = t('messages.updateError');
236
+ const logoUploadSuccessMessage = t('messages.logoUploadSuccess');
237
+ const logoUploadErrorMessage = t('messages.logoUploadError');
238
+ const logoRemoveSuccessMessage = t('messages.logoRemoveSuccess');
239
+ const logoInvalidTypeMessage = t('messages.logoInvalidType');
240
+ const logoTooLargeMessage = t('messages.logoTooLarge');
256
241
 
257
242
  const form = useForm<BankAccountFormValues>({
258
- resolver: zodResolver(bankAccountFormSchema),
243
+ resolver: zodResolver(createBankAccountFormSchema(t)),
259
244
  defaultValues: {
260
245
  banco: '',
261
246
  agencia: '',
@@ -354,10 +339,11 @@ function NovaContaSheet({
354
339
  currentLocaleCode
355
340
  );
356
341
 
357
- return currentLocaleCode.startsWith('pt')
358
- ? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
359
- : `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
360
- }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
342
+ return t('draftStatus', {
343
+ relativeLabel,
344
+ absoluteLabel,
345
+ });
346
+ }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, t]);
361
347
 
362
348
  const getLogoUrl = (fileId?: number | null) => {
363
349
  if (typeof fileId !== 'number' || fileId <= 0) {
@@ -839,35 +825,35 @@ function NovaContaSheet({
839
825
  <EntityPicker<Currency>
840
826
  form={form as never}
841
827
  name="currencyId"
842
- label="Moeda"
843
- placeholder="Selecione uma moeda"
844
- searchPlaceholder="Buscar moeda..."
845
- entityLabel="moeda"
828
+ label={t('fields.currency')}
829
+ placeholder={t('fields.currencyPlaceholder')}
830
+ searchPlaceholder={t('fields.currencySearchPlaceholder')}
831
+ entityLabel={t('fields.currencyEntityLabel')}
846
832
  valueType="number"
847
833
  clearable
848
834
  options={currencies}
849
835
  getOptionValue={(c) => c.id}
850
836
  getOptionLabel={(c) => `${c.symbol} ${c.code} — ${c.name}`}
851
- createTitle="Nova moeda"
852
- createDescription="Cadastre uma nova moeda para usar nas contas bancárias."
837
+ createTitle={t('fields.currencyCreateTitle')}
838
+ createDescription={t('fields.currencyCreateDescription')}
853
839
  mapSearchToCreateValues={(s) => ({ code: s.toUpperCase() })}
854
840
  createFields={[
855
841
  {
856
842
  name: 'code',
857
- label: 'Código ISO',
858
- placeholder: 'Ex.: BRL',
843
+ label: t('fields.currencyCode'),
844
+ placeholder: t('fields.currencyCodePlaceholder'),
859
845
  required: true,
860
846
  },
861
847
  {
862
848
  name: 'name',
863
- label: 'Nome',
864
- placeholder: 'Ex.: Real Brasileiro',
849
+ label: t('fields.currencyName'),
850
+ placeholder: t('fields.currencyNamePlaceholder'),
865
851
  required: true,
866
852
  },
867
853
  {
868
854
  name: 'symbol',
869
- label: 'Símbolo',
870
- placeholder: 'Ex.: R$',
855
+ label: t('fields.currencySymbol'),
856
+ placeholder: t('fields.currencySymbolPlaceholder'),
871
857
  required: true,
872
858
  },
873
859
  ]}
@@ -888,7 +874,7 @@ function NovaContaSheet({
888
874
 
889
875
  <FormField
890
876
  control={form.control}
891
- name="descricao"
877
+ name="descricao"
892
878
  render={({ field }) => (
893
879
  <FormItem>
894
880
  <FormLabel>{t('fields.description')}</FormLabel>
@@ -1004,21 +990,11 @@ export default function ContasBancariasPage() {
1004
990
  const t = useTranslations('finance.BankAccountsPage');
1005
991
  const { request, showToastHandler, currentLocaleCode } = useApp();
1006
992
 
1007
- const deleteSuccessMessage = t.has('messages.deleteSuccess')
1008
- ? t('messages.deleteSuccess')
1009
- : 'Conta bancária inativada com sucesso';
1010
- const deleteErrorMessage = t.has('messages.deleteError')
1011
- ? t('messages.deleteError')
1012
- : 'Erro ao inativar conta bancária';
1013
- const deleteDialogTitle = t.has('deleteDialog.title')
1014
- ? t('deleteDialog.title')
1015
- : 'Inativar conta bancária';
1016
- const deleteDialogDescription = t.has('deleteDialog.description')
1017
- ? t('deleteDialog.description')
1018
- : 'Deseja realmente inativar esta conta bancária?';
1019
- const deleteDialogConfirm = t.has('deleteDialog.confirm')
1020
- ? t('deleteDialog.confirm')
1021
- : 'Inativar';
993
+ const deleteSuccessMessage = t('messages.deleteSuccess');
994
+ const deleteErrorMessage = t('messages.deleteError');
995
+ const deleteDialogTitle = t('deleteDialog.title');
996
+ const deleteDialogDescription = t('deleteDialog.description');
997
+ const deleteDialogConfirm = t('deleteDialog.confirm');
1022
998
 
1023
999
  const [sheetOpen, setSheetOpen] = useState(false);
1024
1000
  const [editingAccount, setEditingAccount] = useState<BankAccount | null>(
@@ -201,10 +201,8 @@ function CriarAjusteSheet({
201
201
  currentLocaleCode
202
202
  );
203
203
 
204
- return currentLocaleCode.startsWith('pt')
205
- ? `Rascunho salvo ${relativeLabel} Último salvamento: ${absoluteLabel}`
206
- : `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
207
- }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
204
+ return t('draftStatus', { relativeLabel, absoluteLabel });
205
+ }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, t]);
208
206
 
209
207
  useEffect(() => {
210
208
  if (!open) {
@@ -226,7 +224,7 @@ function CriarAjusteSheet({
226
224
 
227
225
  const handleSubmit = async (values: AjusteFormValues) => {
228
226
  if (!contaFilter) {
229
- showToastHandler?.('error', 'Selecione uma conta bancária');
227
+ showToastHandler?.('error', t('messages.selectBankAccount'));
230
228
  return;
231
229
  }
232
230
 
@@ -244,10 +242,10 @@ function CriarAjusteSheet({
244
242
 
245
243
  clearDraft();
246
244
  await onCreated();
247
- showToastHandler?.('success', 'Ajuste criado com sucesso');
245
+ showToastHandler?.('success', t('messages.createAdjustmentSuccess'));
248
246
  setOpen(false);
249
247
  } catch {
250
- showToastHandler?.('error', 'Não foi possível criar o ajuste');
248
+ showToastHandler?.('error', t('messages.createAdjustmentError'));
251
249
  }
252
250
  };
253
251
 
@@ -677,9 +675,9 @@ export default function ConciliacaoPage() {
677
675
  setSelectedExtrato(null);
678
676
  setSelectedTitulo(null);
679
677
  await handleRefreshData();
680
- showToastHandler?.('success', 'Conciliação realizada com sucesso');
678
+ showToastHandler?.('success', t('messages.reconcileSuccess'));
681
679
  } catch {
682
- showToastHandler?.('error', 'Não foi possível conciliar os registros');
680
+ showToastHandler?.('error', t('messages.reconcileError'));
683
681
  }
684
682
  };
685
683
 
@@ -118,38 +118,52 @@ type PaginatedStatementsResponse = {
118
118
  };
119
119
  };
120
120
 
121
- const statementEntrySchema = z.object({
122
- date: z.string().trim().min(1, 'Data é obrigatória'),
123
- description: z.string().trim().min(1, 'Descrição é obrigatória'),
124
- type: z.enum(['entrada', 'saida']),
125
- amount: z.number().min(0.01, 'Valor deve ser maior que zero'),
126
- });
127
-
128
- type StatementEntryFormValues = z.infer<typeof statementEntrySchema>;
129
-
130
- const bankAccountFormSchema = z.object({
131
- banco: z.string().trim().min(1, 'Banco é obrigatório'),
132
- agencia: z.string().optional(),
133
- conta: z.string().optional(),
134
- tipo: z.string().min(1, 'Tipo é obrigatório'),
135
- descricao: z.string().optional(),
136
- dataInicial: z.string().optional(),
137
- saldoInicial: z.number().min(0, 'Saldo inicial inválido'),
138
- });
139
-
140
- const importStatementSchema = z.object({
141
- bankAccountId: z.string().trim().min(1, 'Conta bancária é obrigatória'),
142
- file: z.instanceof(File, { message: 'Arquivo é obrigatório' }).refine(
143
- (value) => {
144
- const fileName = value.name.toLowerCase();
145
- return fileName.endsWith('.csv') || fileName.endsWith('.ofx');
146
- },
147
- { message: 'Apenas arquivos CSV ou OFX são permitidos' }
148
- ),
149
- });
121
+ const createStatementEntrySchema = (t: ReturnType<typeof useTranslations>) =>
122
+ z.object({
123
+ date: z.string().trim().min(1, t('formErrors.dateRequired')),
124
+ description: z.string().trim().min(1, t('formErrors.descriptionRequired')),
125
+ type: z.enum(['entrada', 'saida']),
126
+ amount: z.number().min(0.01, t('formErrors.amountMustBePositive')),
127
+ });
150
128
 
151
- type ImportStatementFormValues = z.infer<typeof importStatementSchema>;
152
- type BankAccountFormValues = z.infer<typeof bankAccountFormSchema>;
129
+ type StatementEntryFormValues = z.infer<
130
+ ReturnType<typeof createStatementEntrySchema>
131
+ >;
132
+
133
+ const createBankAccountFormSchema = (
134
+ tBank: ReturnType<typeof useTranslations>
135
+ ) =>
136
+ z.object({
137
+ banco: z.string().trim().min(1, tBank('validation.bankRequired')),
138
+ agencia: z.string().optional(),
139
+ conta: z.string().optional(),
140
+ tipo: z.string().min(1, tBank('validation.typeRequired')),
141
+ descricao: z.string().optional(),
142
+ dataInicial: z.string().optional(),
143
+ saldoInicial: z.number().min(0, tBank('validation.initialBalanceInvalid')),
144
+ });
145
+
146
+ const createImportStatementSchema = (t: ReturnType<typeof useTranslations>) =>
147
+ z.object({
148
+ bankAccountId: z
149
+ .string()
150
+ .trim()
151
+ .min(1, t('formErrors.bankAccountRequired')),
152
+ file: z.instanceof(File, { message: t('formErrors.fileRequired') }).refine(
153
+ (value) => {
154
+ const fileName = value.name.toLowerCase();
155
+ return fileName.endsWith('.csv') || fileName.endsWith('.ofx');
156
+ },
157
+ { message: t('formErrors.fileTypeNotAllowed') }
158
+ ),
159
+ });
160
+
161
+ type ImportStatementFormValues = z.infer<
162
+ ReturnType<typeof createImportStatementSchema>
163
+ >;
164
+ type BankAccountFormValues = z.infer<
165
+ ReturnType<typeof createBankAccountFormSchema>
166
+ >;
153
167
 
154
168
  type StatementBankAccountDraftPayload = {
155
169
  values: BankAccountFormValues;
@@ -180,7 +194,7 @@ function NovaContaBancariaSheet({
180
194
  useApp();
181
195
 
182
196
  const form = useForm<BankAccountFormValues>({
183
- resolver: zodResolver(bankAccountFormSchema),
197
+ resolver: zodResolver(createBankAccountFormSchema(tBank)),
184
198
  defaultValues: {
185
199
  banco: '',
186
200
  agencia: '',
@@ -250,10 +264,11 @@ function NovaContaBancariaSheet({
250
264
  currentLocaleCode
251
265
  );
252
266
 
253
- return currentLocaleCode.startsWith('pt')
254
- ? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
255
- : `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
256
- }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
267
+ return tBank('draftStatus', {
268
+ relativeLabel,
269
+ absoluteLabel,
270
+ });
271
+ }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, tBank]);
257
272
 
258
273
  useEffect(() => {
259
274
  if (!open) {
@@ -462,7 +477,7 @@ function NovaContaBancariaSheet({
462
477
 
463
478
  <FormField
464
479
  control={form.control}
465
- name="descricao"
480
+ name="descricao"
466
481
  render={({ field }) => (
467
482
  <FormItem>
468
483
  <FormLabel>{tBank('fields.description')}</FormLabel>
@@ -534,7 +549,7 @@ function ImportarExtratoSheet({
534
549
  };
535
550
 
536
551
  const form = useForm<ImportStatementFormValues>({
537
- resolver: zodResolver(importStatementSchema),
552
+ resolver: zodResolver(createImportStatementSchema(t)),
538
553
  defaultValues: {
539
554
  bankAccountId: defaultBankAccountId || '',
540
555
  },
@@ -585,10 +600,11 @@ function ImportarExtratoSheet({
585
600
  currentLocaleCode
586
601
  );
587
602
 
588
- return currentLocaleCode.startsWith('pt')
589
- ? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
590
- : `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
591
- }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
603
+ return t('draftStatus', {
604
+ relativeLabel,
605
+ absoluteLabel,
606
+ });
607
+ }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, t]);
592
608
 
593
609
  useEffect(() => {
594
610
  if (!isOpen) {
@@ -618,10 +634,10 @@ function ImportarExtratoSheet({
618
634
 
619
635
  clearDraft();
620
636
  await onImported();
621
- showToastHandler?.('success', 'Extrato importado com sucesso');
637
+ showToastHandler?.('success', t('messages.importSuccess'));
622
638
  handleOpenChange(false);
623
639
  } catch {
624
- showToastHandler?.('error', 'Não foi possível importar o extrato');
640
+ showToastHandler?.('error', t('messages.importError'));
625
641
  }
626
642
  };
627
643
 
@@ -676,7 +692,7 @@ function ImportarExtratoSheet({
676
692
  variant="outline"
677
693
  size="icon"
678
694
  onClick={() => setOpenNovaContaSheet(true)}
679
- aria-label="Nova conta bancária"
695
+ aria-label={t('aria.newBankAccount')}
680
696
  >
681
697
  <Plus className="h-4 w-4" />
682
698
  </Button>
@@ -751,11 +767,13 @@ function ImportarExtratoSheet({
751
767
  }
752
768
 
753
769
  function NovoLancamentoSheet({
770
+ t,
754
771
  open,
755
772
  onOpenChange,
756
773
  bankAccountId,
757
774
  onCreated,
758
775
  }: {
776
+ t: ReturnType<typeof useTranslations>;
759
777
  open: boolean;
760
778
  onOpenChange: (open: boolean) => void;
761
779
  bankAccountId?: string;
@@ -763,7 +781,7 @@ function NovoLancamentoSheet({
763
781
  }) {
764
782
  const { request, showToastHandler } = useApp();
765
783
  const form = useForm<StatementEntryFormValues>({
766
- resolver: zodResolver(statementEntrySchema),
784
+ resolver: zodResolver(createStatementEntrySchema(t)),
767
785
  defaultValues: {
768
786
  date: new Date().toISOString().slice(0, 10),
769
787
  description: '',
@@ -785,7 +803,7 @@ function NovoLancamentoSheet({
785
803
 
786
804
  const handleSubmit = async (values: StatementEntryFormValues) => {
787
805
  if (!bankAccountId) {
788
- showToastHandler?.('error', 'Selecione uma conta bancária');
806
+ showToastHandler?.('error', t('messages.selectBankAccount'));
789
807
  return;
790
808
  }
791
809
 
@@ -804,14 +822,14 @@ function NovoLancamentoSheet({
804
822
 
805
823
  await onCreated();
806
824
  onOpenChange(false);
807
- showToastHandler?.('success', 'Movimentação adicionada com sucesso');
825
+ showToastHandler?.('success', t('messages.createEntrySuccess'));
808
826
  } catch (error: any) {
809
827
  const message = error?.response?.data?.message;
810
828
  showToastHandler?.(
811
829
  'error',
812
830
  typeof message === 'string' && message.trim()
813
831
  ? message
814
- : 'Não foi possível adicionar a movimentação'
832
+ : t('messages.createEntryError')
815
833
  );
816
834
  }
817
835
  };
@@ -820,10 +838,8 @@ function NovoLancamentoSheet({
820
838
  <Sheet open={open} onOpenChange={onOpenChange}>
821
839
  <SheetContent className="w-full sm:max-w-lg overflow-y-auto">
822
840
  <SheetHeader>
823
- <SheetTitle>Novo lançamento</SheetTitle>
824
- <SheetDescription>
825
- Adicione uma movimentação manual ao extrato da conta bancária.
826
- </SheetDescription>
841
+ <SheetTitle>{t('newEntry.title')}</SheetTitle>
842
+ <SheetDescription>{t('newEntry.description')}</SheetDescription>
827
843
  </SheetHeader>
828
844
 
829
845
  <Form {...form}>
@@ -837,7 +853,7 @@ function NovoLancamentoSheet({
837
853
  name="date"
838
854
  render={({ field }) => (
839
855
  <FormItem>
840
- <FormLabel>Data</FormLabel>
856
+ <FormLabel>{t('newEntry.fields.date')}</FormLabel>
841
857
  <FormControl>
842
858
  <Input type="date" {...field} />
843
859
  </FormControl>
@@ -851,7 +867,7 @@ function NovoLancamentoSheet({
851
867
  name="type"
852
868
  render={({ field }) => (
853
869
  <FormItem>
854
- <FormLabel>Tipo</FormLabel>
870
+ <FormLabel>{t('newEntry.fields.type')}</FormLabel>
855
871
  <Select value={field.value} onValueChange={field.onChange}>
856
872
  <FormControl>
857
873
  <SelectTrigger className="w-full">
@@ -859,8 +875,12 @@ function NovoLancamentoSheet({
859
875
  </SelectTrigger>
860
876
  </FormControl>
861
877
  <SelectContent>
862
- <SelectItem value="entrada">Entrada</SelectItem>
863
- <SelectItem value="saida">Saída</SelectItem>
878
+ <SelectItem value="entrada">
879
+ {t('types.inflow')}
880
+ </SelectItem>
881
+ <SelectItem value="saida">
882
+ {t('types.outflow')}
883
+ </SelectItem>
864
884
  </SelectContent>
865
885
  </Select>
866
886
  <FormMessage />
@@ -874,7 +894,7 @@ function NovoLancamentoSheet({
874
894
  name="description"
875
895
  render={({ field }) => (
876
896
  <FormItem>
877
- <FormLabel>Descrição</FormLabel>
897
+ <FormLabel>{t('newEntry.fields.description')}</FormLabel>
878
898
  <FormControl>
879
899
  <Input {...field} />
880
900
  </FormControl>
@@ -888,7 +908,7 @@ function NovoLancamentoSheet({
888
908
  name="amount"
889
909
  render={({ field }) => (
890
910
  <FormItem>
891
- <FormLabel>Valor</FormLabel>
911
+ <FormLabel>{t('newEntry.fields.amount')}</FormLabel>
892
912
  <FormControl>
893
913
  <InputMoney
894
914
  ref={field.ref}
@@ -910,10 +930,10 @@ function NovoLancamentoSheet({
910
930
  variant="outline"
911
931
  onClick={() => onOpenChange(false)}
912
932
  >
913
- Cancelar
933
+ {t('common.cancel')}
914
934
  </Button>
915
935
  <Button type="submit" disabled={form.formState.isSubmitting}>
916
- Salvar
936
+ {t('newEntry.submit')}
917
937
  </Button>
918
938
  </div>
919
939
  </form>
@@ -1166,7 +1186,7 @@ export default function ExtratosPage() {
1166
1186
  ];
1167
1187
  const handleExport = async () => {
1168
1188
  if (!activeContaFilter) {
1169
- showToastHandler?.('error', 'Selecione uma conta bancária para exportar');
1189
+ showToastHandler?.('error', t('messages.selectBankAccountToExport'));
1170
1190
  return;
1171
1191
  }
1172
1192
 
@@ -1217,7 +1237,7 @@ export default function ExtratosPage() {
1217
1237
  link.remove();
1218
1238
  window.URL.revokeObjectURL(url);
1219
1239
  } catch {
1220
- showToastHandler?.('error', 'Não foi possível exportar o extrato');
1240
+ showToastHandler?.('error', t('messages.exportError'));
1221
1241
  }
1222
1242
  };
1223
1243
 
@@ -1298,7 +1318,7 @@ export default function ExtratosPage() {
1298
1318
  'error',
1299
1319
  typeof message === 'string' && message.trim()
1300
1320
  ? message
1301
- : 'Não foi possível salvar as alterações'
1321
+ : t('messages.saveChangesError')
1302
1322
  );
1303
1323
  } finally {
1304
1324
  setIsSavingEdits(false);
@@ -1310,7 +1330,7 @@ export default function ExtratosPage() {
1310
1330
  setEditingDescId(null);
1311
1331
  setEditingDateId(null);
1312
1332
  await Promise.all([refetchExtratos(), refetchContasBancarias()]);
1313
- showToastHandler?.('success', 'Alterações salvas com sucesso');
1333
+ showToastHandler?.('success', t('messages.saveChangesSuccess'));
1314
1334
  }
1315
1335
  };
1316
1336
 
@@ -1337,14 +1357,14 @@ export default function ExtratosPage() {
1337
1357
 
1338
1358
  setStatementToDelete(null);
1339
1359
  await Promise.all([refetchExtratos(), refetchContasBancarias()]);
1340
- showToastHandler?.('success', 'Movimentação excluída com sucesso');
1360
+ showToastHandler?.('success', t('messages.deleteEntrySuccess'));
1341
1361
  } catch (error: any) {
1342
1362
  const message = error?.response?.data?.message;
1343
1363
  showToastHandler?.(
1344
1364
  'error',
1345
1365
  typeof message === 'string' && message.trim()
1346
1366
  ? message
1347
- : 'Não foi possível excluir a movimentação'
1367
+ : t('messages.deleteEntryError')
1348
1368
  );
1349
1369
  } finally {
1350
1370
  setIsDeletingStatement(false);
@@ -1369,7 +1389,7 @@ export default function ExtratosPage() {
1369
1389
  </Button>
1370
1390
  <Button onClick={() => setIsNewEntrySheetOpen(true)}>
1371
1391
  <Plus className="mr-2 h-4 w-4" />
1372
- Novo lançamento
1392
+ {t('newEntry.action')}
1373
1393
  </Button>
1374
1394
  <ImportarExtratoSheet
1375
1395
  contasBancarias={contasBancarias}
@@ -1385,6 +1405,7 @@ export default function ExtratosPage() {
1385
1405
  />
1386
1406
 
1387
1407
  <NovoLancamentoSheet
1408
+ t={t}
1388
1409
  open={isNewEntrySheetOpen}
1389
1410
  onOpenChange={setIsNewEntrySheetOpen}
1390
1411
  bankAccountId={activeContaFilter}
@@ -1773,7 +1794,9 @@ export default function ExtratosPage() {
1773
1794
  onClick={() => void handleSaveAllPendingEdits()}
1774
1795
  disabled={isSavingEdits}
1775
1796
  >
1776
- {isSavingEdits ? 'Salvando...' : 'Salvar alterações'}
1797
+ {isSavingEdits
1798
+ ? t('actions.savingChanges')
1799
+ : t('actions.saveChanges')}
1777
1800
  </Button>
1778
1801
  </div>
1779
1802
  ) : null}
@@ -1786,10 +1809,9 @@ export default function ExtratosPage() {
1786
1809
  >
1787
1810
  <DialogContent className="sm:max-w-sm">
1788
1811
  <DialogHeader>
1789
- <DialogTitle>Excluir movimentação</DialogTitle>
1812
+ <DialogTitle>{t('deleteDialog.title')}</DialogTitle>
1790
1813
  <DialogDescription>
1791
- Esta ação não pode ser desfeita. A movimentação será removida
1792
- permanentemente do extrato.
1814
+ {t('deleteDialog.description')}
1793
1815
  </DialogDescription>
1794
1816
  </DialogHeader>
1795
1817
  <DialogFooter className="mt-2">
@@ -1798,14 +1820,16 @@ export default function ExtratosPage() {
1798
1820
  onClick={() => setStatementToDelete(null)}
1799
1821
  disabled={isDeletingStatement}
1800
1822
  >
1801
- Cancelar
1823
+ {t('common.cancel')}
1802
1824
  </Button>
1803
1825
  <Button
1804
1826
  variant="destructive"
1805
1827
  onClick={() => void confirmDeleteStatement()}
1806
1828
  disabled={isDeletingStatement}
1807
1829
  >
1808
- {isDeletingStatement ? 'Excluindo...' : 'Excluir'}
1830
+ {isDeletingStatement
1831
+ ? t('deleteDialog.deleting')
1832
+ : t('deleteDialog.confirm')}
1809
1833
  </Button>
1810
1834
  </DialogFooter>
1811
1835
  </DialogContent>