@hed-hog/finance 0.0.328 → 0.0.330

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 (27) hide show
  1. package/hedhog/frontend/app/_components/category-picker-field.tsx.ejs +3 -0
  2. package/hedhog/frontend/app/_components/cost-center-picker-field.tsx.ejs +3 -0
  3. package/hedhog/frontend/app/_components/finance-entity-field-with-create.tsx.ejs +9 -4
  4. package/hedhog/frontend/app/_components/finance-picker.tsx.ejs +242 -0
  5. package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +18 -15
  6. package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +13 -19
  7. package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +4 -8
  8. package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +13 -19
  9. package/hedhog/frontend/app/administration/audit-logs/page.tsx.ejs +10 -4
  10. package/hedhog/frontend/app/administration/categories/page.tsx.ejs +3 -5
  11. package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +3 -5
  12. package/hedhog/frontend/app/administration/currencies/page.tsx.ejs +3 -5
  13. package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +3 -5
  14. package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +48 -72
  15. package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +7 -9
  16. package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +91 -73
  17. package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +33 -25
  18. package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +2 -2
  19. package/hedhog/frontend/app/reports/actual-vs-forecast/page.tsx.ejs +3 -1
  20. package/hedhog/frontend/app/reports/aging-default/page.tsx.ejs +3 -1
  21. package/hedhog/frontend/app/reports/cash-position/page.tsx.ejs +3 -1
  22. package/hedhog/frontend/app/reports/overview-results/page.tsx.ejs +15 -8
  23. package/hedhog/frontend/app/reports/top-customers/page.tsx.ejs +9 -5
  24. package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +9 -5
  25. package/hedhog/frontend/messages/en.json +110 -2
  26. package/hedhog/frontend/messages/pt.json +92 -2
  27. package/package.json +4 -4
@@ -141,10 +141,8 @@ function CurrencySheet({
141
141
  getSettingValue,
142
142
  currentLocaleCode
143
143
  );
144
- return currentLocaleCode.startsWith('pt')
145
- ? `Rascunho salvo ${relativeLabel} Último salvamento: ${absoluteLabel}`
146
- : `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
147
- }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
144
+ return t('draftStatus', { relativeLabel, absoluteLabel });
145
+ }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, t]);
148
146
 
149
147
  useEffect(() => {
150
148
  if (!open) return;
@@ -225,7 +223,7 @@ function CurrencySheet({
225
223
 
226
224
  return (
227
225
  <Sheet open={open} onOpenChange={handleOpenChange}>
228
- <SheetContent className="w-full overflow-y-auto sm:max-w-lg">
226
+ <SheetContent className="w-full overflow-y-auto sm:max-w-lg">
229
227
  <SheetHeader>
230
228
  <SheetTitle>
231
229
  {editingCurrency ? t('sheet.editTitle') : t('sheet.newTitle')}
@@ -160,10 +160,8 @@ function ClosePeriodSheet({
160
160
  currentLocaleCode
161
161
  );
162
162
 
163
- return currentLocaleCode.startsWith('pt')
164
- ? `Rascunho salvo ${relativeLabel} Último salvamento: ${absoluteLabel}`
165
- : `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
166
- }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
163
+ return t('draftStatus', { relativeLabel, absoluteLabel });
164
+ }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, t]);
167
165
 
168
166
  useEffect(() => {
169
167
  if (!open) {
@@ -212,7 +210,7 @@ function ClosePeriodSheet({
212
210
  onOpenChange(nextOpen);
213
211
  }}
214
212
  >
215
- <SheetContent className="w-full overflow-y-auto sm:max-w-lg">
213
+ <SheetContent className="w-full overflow-y-auto sm:max-w-lg">
216
214
  <SheetHeader>
217
215
  <SheetTitle>{t('sheet.title')}</SheetTitle>
218
216
  <SheetDescription>{t('sheet.description')}</SheetDescription>
@@ -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
  ]}
@@ -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,46 @@ 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<ReturnType<typeof createStatementEntrySchema>>;
130
+
131
+ const createBankAccountFormSchema = (
132
+ tBank: ReturnType<typeof useTranslations>
133
+ ) =>
134
+ z.object({
135
+ banco: z.string().trim().min(1, tBank('validation.bankRequired')),
136
+ agencia: z.string().optional(),
137
+ conta: z.string().optional(),
138
+ tipo: z.string().min(1, tBank('validation.typeRequired')),
139
+ descricao: z.string().optional(),
140
+ dataInicial: z.string().optional(),
141
+ saldoInicial: z.number().min(0, tBank('validation.initialBalanceInvalid')),
142
+ });
143
+
144
+ const createImportStatementSchema = (t: ReturnType<typeof useTranslations>) =>
145
+ z.object({
146
+ bankAccountId: z
147
+ .string()
148
+ .trim()
149
+ .min(1, t('formErrors.bankAccountRequired')),
150
+ file: z.instanceof(File, { message: t('formErrors.fileRequired') }).refine(
151
+ (value) => {
152
+ const fileName = value.name.toLowerCase();
153
+ return fileName.endsWith('.csv') || fileName.endsWith('.ofx');
154
+ },
155
+ { message: t('formErrors.fileTypeNotAllowed') }
156
+ ),
157
+ });
158
+
159
+ type ImportStatementFormValues = z.infer<ReturnType<typeof createImportStatementSchema>>;
160
+ type BankAccountFormValues = z.infer<ReturnType<typeof createBankAccountFormSchema>>;
153
161
 
154
162
  type StatementBankAccountDraftPayload = {
155
163
  values: BankAccountFormValues;
@@ -180,7 +188,7 @@ function NovaContaBancariaSheet({
180
188
  useApp();
181
189
 
182
190
  const form = useForm<BankAccountFormValues>({
183
- resolver: zodResolver(bankAccountFormSchema),
191
+ resolver: zodResolver(createBankAccountFormSchema(tBank)),
184
192
  defaultValues: {
185
193
  banco: '',
186
194
  agencia: '',
@@ -250,10 +258,11 @@ function NovaContaBancariaSheet({
250
258
  currentLocaleCode
251
259
  );
252
260
 
253
- return currentLocaleCode.startsWith('pt')
254
- ? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
255
- : `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
256
- }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
261
+ return tBank('draftStatus', {
262
+ relativeLabel,
263
+ absoluteLabel,
264
+ });
265
+ }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, tBank]);
257
266
 
258
267
  useEffect(() => {
259
268
  if (!open) {
@@ -534,7 +543,7 @@ function ImportarExtratoSheet({
534
543
  };
535
544
 
536
545
  const form = useForm<ImportStatementFormValues>({
537
- resolver: zodResolver(importStatementSchema),
546
+ resolver: zodResolver(createImportStatementSchema(t)),
538
547
  defaultValues: {
539
548
  bankAccountId: defaultBankAccountId || '',
540
549
  },
@@ -585,10 +594,11 @@ function ImportarExtratoSheet({
585
594
  currentLocaleCode
586
595
  );
587
596
 
588
- return currentLocaleCode.startsWith('pt')
589
- ? `Rascunho salvo ${relativeLabel} • Último salvamento: ${absoluteLabel}`
590
- : `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
591
- }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
597
+ return t('draftStatus', {
598
+ relativeLabel,
599
+ absoluteLabel,
600
+ });
601
+ }, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, t]);
592
602
 
593
603
  useEffect(() => {
594
604
  if (!isOpen) {
@@ -618,10 +628,10 @@ function ImportarExtratoSheet({
618
628
 
619
629
  clearDraft();
620
630
  await onImported();
621
- showToastHandler?.('success', 'Extrato importado com sucesso');
631
+ showToastHandler?.('success', t('messages.importSuccess'));
622
632
  handleOpenChange(false);
623
633
  } catch {
624
- showToastHandler?.('error', 'Não foi possível importar o extrato');
634
+ showToastHandler?.('error', t('messages.importError'));
625
635
  }
626
636
  };
627
637
 
@@ -676,7 +686,7 @@ function ImportarExtratoSheet({
676
686
  variant="outline"
677
687
  size="icon"
678
688
  onClick={() => setOpenNovaContaSheet(true)}
679
- aria-label="Nova conta bancária"
689
+ aria-label={t('aria.newBankAccount')}
680
690
  >
681
691
  <Plus className="h-4 w-4" />
682
692
  </Button>
@@ -751,11 +761,13 @@ function ImportarExtratoSheet({
751
761
  }
752
762
 
753
763
  function NovoLancamentoSheet({
764
+ t,
754
765
  open,
755
766
  onOpenChange,
756
767
  bankAccountId,
757
768
  onCreated,
758
769
  }: {
770
+ t: ReturnType<typeof useTranslations>;
759
771
  open: boolean;
760
772
  onOpenChange: (open: boolean) => void;
761
773
  bankAccountId?: string;
@@ -763,7 +775,7 @@ function NovoLancamentoSheet({
763
775
  }) {
764
776
  const { request, showToastHandler } = useApp();
765
777
  const form = useForm<StatementEntryFormValues>({
766
- resolver: zodResolver(statementEntrySchema),
778
+ resolver: zodResolver(createStatementEntrySchema(t)),
767
779
  defaultValues: {
768
780
  date: new Date().toISOString().slice(0, 10),
769
781
  description: '',
@@ -785,7 +797,7 @@ function NovoLancamentoSheet({
785
797
 
786
798
  const handleSubmit = async (values: StatementEntryFormValues) => {
787
799
  if (!bankAccountId) {
788
- showToastHandler?.('error', 'Selecione uma conta bancária');
800
+ showToastHandler?.('error', t('messages.selectBankAccount'));
789
801
  return;
790
802
  }
791
803
 
@@ -804,14 +816,14 @@ function NovoLancamentoSheet({
804
816
 
805
817
  await onCreated();
806
818
  onOpenChange(false);
807
- showToastHandler?.('success', 'Movimentação adicionada com sucesso');
819
+ showToastHandler?.('success', t('messages.createEntrySuccess'));
808
820
  } catch (error: any) {
809
821
  const message = error?.response?.data?.message;
810
822
  showToastHandler?.(
811
823
  'error',
812
824
  typeof message === 'string' && message.trim()
813
825
  ? message
814
- : 'Não foi possível adicionar a movimentação'
826
+ : t('messages.createEntryError')
815
827
  );
816
828
  }
817
829
  };
@@ -820,10 +832,8 @@ function NovoLancamentoSheet({
820
832
  <Sheet open={open} onOpenChange={onOpenChange}>
821
833
  <SheetContent className="w-full sm:max-w-lg overflow-y-auto">
822
834
  <SheetHeader>
823
- <SheetTitle>Novo lançamento</SheetTitle>
824
- <SheetDescription>
825
- Adicione uma movimentação manual ao extrato da conta bancária.
826
- </SheetDescription>
835
+ <SheetTitle>{t('newEntry.title')}</SheetTitle>
836
+ <SheetDescription>{t('newEntry.description')}</SheetDescription>
827
837
  </SheetHeader>
828
838
 
829
839
  <Form {...form}>
@@ -837,7 +847,7 @@ function NovoLancamentoSheet({
837
847
  name="date"
838
848
  render={({ field }) => (
839
849
  <FormItem>
840
- <FormLabel>Data</FormLabel>
850
+ <FormLabel>{t('newEntry.fields.date')}</FormLabel>
841
851
  <FormControl>
842
852
  <Input type="date" {...field} />
843
853
  </FormControl>
@@ -851,7 +861,7 @@ function NovoLancamentoSheet({
851
861
  name="type"
852
862
  render={({ field }) => (
853
863
  <FormItem>
854
- <FormLabel>Tipo</FormLabel>
864
+ <FormLabel>{t('newEntry.fields.type')}</FormLabel>
855
865
  <Select value={field.value} onValueChange={field.onChange}>
856
866
  <FormControl>
857
867
  <SelectTrigger className="w-full">
@@ -859,8 +869,12 @@ function NovoLancamentoSheet({
859
869
  </SelectTrigger>
860
870
  </FormControl>
861
871
  <SelectContent>
862
- <SelectItem value="entrada">Entrada</SelectItem>
863
- <SelectItem value="saida">Saída</SelectItem>
872
+ <SelectItem value="entrada">
873
+ {t('types.inflow')}
874
+ </SelectItem>
875
+ <SelectItem value="saida">
876
+ {t('types.outflow')}
877
+ </SelectItem>
864
878
  </SelectContent>
865
879
  </Select>
866
880
  <FormMessage />
@@ -874,7 +888,7 @@ function NovoLancamentoSheet({
874
888
  name="description"
875
889
  render={({ field }) => (
876
890
  <FormItem>
877
- <FormLabel>Descrição</FormLabel>
891
+ <FormLabel>{t('newEntry.fields.description')}</FormLabel>
878
892
  <FormControl>
879
893
  <Input {...field} />
880
894
  </FormControl>
@@ -888,7 +902,7 @@ function NovoLancamentoSheet({
888
902
  name="amount"
889
903
  render={({ field }) => (
890
904
  <FormItem>
891
- <FormLabel>Valor</FormLabel>
905
+ <FormLabel>{t('newEntry.fields.amount')}</FormLabel>
892
906
  <FormControl>
893
907
  <InputMoney
894
908
  ref={field.ref}
@@ -910,10 +924,10 @@ function NovoLancamentoSheet({
910
924
  variant="outline"
911
925
  onClick={() => onOpenChange(false)}
912
926
  >
913
- Cancelar
927
+ {t('common.cancel')}
914
928
  </Button>
915
929
  <Button type="submit" disabled={form.formState.isSubmitting}>
916
- Salvar
930
+ {t('newEntry.submit')}
917
931
  </Button>
918
932
  </div>
919
933
  </form>
@@ -1166,7 +1180,7 @@ export default function ExtratosPage() {
1166
1180
  ];
1167
1181
  const handleExport = async () => {
1168
1182
  if (!activeContaFilter) {
1169
- showToastHandler?.('error', 'Selecione uma conta bancária para exportar');
1183
+ showToastHandler?.('error', t('messages.selectBankAccountToExport'));
1170
1184
  return;
1171
1185
  }
1172
1186
 
@@ -1217,7 +1231,7 @@ export default function ExtratosPage() {
1217
1231
  link.remove();
1218
1232
  window.URL.revokeObjectURL(url);
1219
1233
  } catch {
1220
- showToastHandler?.('error', 'Não foi possível exportar o extrato');
1234
+ showToastHandler?.('error', t('messages.exportError'));
1221
1235
  }
1222
1236
  };
1223
1237
 
@@ -1298,7 +1312,7 @@ export default function ExtratosPage() {
1298
1312
  'error',
1299
1313
  typeof message === 'string' && message.trim()
1300
1314
  ? message
1301
- : 'Não foi possível salvar as alterações'
1315
+ : t('messages.saveChangesError')
1302
1316
  );
1303
1317
  } finally {
1304
1318
  setIsSavingEdits(false);
@@ -1310,7 +1324,7 @@ export default function ExtratosPage() {
1310
1324
  setEditingDescId(null);
1311
1325
  setEditingDateId(null);
1312
1326
  await Promise.all([refetchExtratos(), refetchContasBancarias()]);
1313
- showToastHandler?.('success', 'Alterações salvas com sucesso');
1327
+ showToastHandler?.('success', t('messages.saveChangesSuccess'));
1314
1328
  }
1315
1329
  };
1316
1330
 
@@ -1337,14 +1351,14 @@ export default function ExtratosPage() {
1337
1351
 
1338
1352
  setStatementToDelete(null);
1339
1353
  await Promise.all([refetchExtratos(), refetchContasBancarias()]);
1340
- showToastHandler?.('success', 'Movimentação excluída com sucesso');
1354
+ showToastHandler?.('success', t('messages.deleteEntrySuccess'));
1341
1355
  } catch (error: any) {
1342
1356
  const message = error?.response?.data?.message;
1343
1357
  showToastHandler?.(
1344
1358
  'error',
1345
1359
  typeof message === 'string' && message.trim()
1346
1360
  ? message
1347
- : 'Não foi possível excluir a movimentação'
1361
+ : t('messages.deleteEntryError')
1348
1362
  );
1349
1363
  } finally {
1350
1364
  setIsDeletingStatement(false);
@@ -1369,7 +1383,7 @@ export default function ExtratosPage() {
1369
1383
  </Button>
1370
1384
  <Button onClick={() => setIsNewEntrySheetOpen(true)}>
1371
1385
  <Plus className="mr-2 h-4 w-4" />
1372
- Novo lançamento
1386
+ {t('newEntry.action')}
1373
1387
  </Button>
1374
1388
  <ImportarExtratoSheet
1375
1389
  contasBancarias={contasBancarias}
@@ -1385,6 +1399,7 @@ export default function ExtratosPage() {
1385
1399
  />
1386
1400
 
1387
1401
  <NovoLancamentoSheet
1402
+ t={t}
1388
1403
  open={isNewEntrySheetOpen}
1389
1404
  onOpenChange={setIsNewEntrySheetOpen}
1390
1405
  bankAccountId={activeContaFilter}
@@ -1773,7 +1788,9 @@ export default function ExtratosPage() {
1773
1788
  onClick={() => void handleSaveAllPendingEdits()}
1774
1789
  disabled={isSavingEdits}
1775
1790
  >
1776
- {isSavingEdits ? 'Salvando...' : 'Salvar alterações'}
1791
+ {isSavingEdits
1792
+ ? t('actions.savingChanges')
1793
+ : t('actions.saveChanges')}
1777
1794
  </Button>
1778
1795
  </div>
1779
1796
  ) : null}
@@ -1786,10 +1803,9 @@ export default function ExtratosPage() {
1786
1803
  >
1787
1804
  <DialogContent className="sm:max-w-sm">
1788
1805
  <DialogHeader>
1789
- <DialogTitle>Excluir movimentação</DialogTitle>
1806
+ <DialogTitle>{t('deleteDialog.title')}</DialogTitle>
1790
1807
  <DialogDescription>
1791
- Esta ação não pode ser desfeita. A movimentação será removida
1792
- permanentemente do extrato.
1808
+ {t('deleteDialog.description')}
1793
1809
  </DialogDescription>
1794
1810
  </DialogHeader>
1795
1811
  <DialogFooter className="mt-2">
@@ -1798,14 +1814,16 @@ export default function ExtratosPage() {
1798
1814
  onClick={() => setStatementToDelete(null)}
1799
1815
  disabled={isDeletingStatement}
1800
1816
  >
1801
- Cancelar
1817
+ {t('common.cancel')}
1802
1818
  </Button>
1803
1819
  <Button
1804
1820
  variant="destructive"
1805
1821
  onClick={() => void confirmDeleteStatement()}
1806
1822
  disabled={isDeletingStatement}
1807
1823
  >
1808
- {isDeletingStatement ? 'Excluindo...' : 'Excluir'}
1824
+ {isDeletingStatement
1825
+ ? t('deleteDialog.deleting')
1826
+ : t('deleteDialog.confirm')}
1809
1827
  </Button>
1810
1828
  </DialogFooter>
1811
1829
  </DialogContent>