@hed-hog/finance 0.0.329 → 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.
- package/hedhog/frontend/app/accounts-payable/approvals/page.tsx.ejs +18 -15
- package/hedhog/frontend/app/accounts-payable/installments/page.tsx.ejs +6 -14
- package/hedhog/frontend/app/accounts-receivable/collections-default/page.tsx.ejs +4 -8
- package/hedhog/frontend/app/accounts-receivable/installments/page.tsx.ejs +6 -14
- package/hedhog/frontend/app/administration/audit-logs/page.tsx.ejs +10 -4
- package/hedhog/frontend/app/administration/categories/page.tsx.ejs +3 -5
- package/hedhog/frontend/app/administration/cost-centers/page.tsx.ejs +3 -5
- package/hedhog/frontend/app/administration/currencies/page.tsx.ejs +3 -5
- package/hedhog/frontend/app/administration/period-close/page.tsx.ejs +3 -5
- package/hedhog/frontend/app/cash-and-banks/bank-accounts/page.tsx.ejs +48 -72
- package/hedhog/frontend/app/cash-and-banks/bank-reconciliation/page.tsx.ejs +7 -9
- package/hedhog/frontend/app/cash-and-banks/statements/page.tsx.ejs +91 -73
- package/hedhog/frontend/app/cash-and-banks/transfers/page.tsx.ejs +33 -25
- package/hedhog/frontend/app/planning/scenarios/page.tsx.ejs +2 -2
- package/hedhog/frontend/app/reports/actual-vs-forecast/page.tsx.ejs +3 -1
- package/hedhog/frontend/app/reports/aging-default/page.tsx.ejs +3 -1
- package/hedhog/frontend/app/reports/cash-position/page.tsx.ejs +3 -1
- package/hedhog/frontend/app/reports/overview-results/page.tsx.ejs +15 -8
- package/hedhog/frontend/app/reports/top-customers/page.tsx.ejs +9 -5
- package/hedhog/frontend/app/reports/top-operational-expenses/page.tsx.ejs +9 -5
- package/hedhog/frontend/messages/en.json +110 -2
- package/hedhog/frontend/messages/pt.json +92 -2
- package/package.json +6 -6
|
@@ -118,38 +118,46 @@ type PaginatedStatementsResponse = {
|
|
|
118
118
|
};
|
|
119
119
|
};
|
|
120
120
|
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
|
152
|
-
|
|
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(
|
|
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
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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(
|
|
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
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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', '
|
|
631
|
+
showToastHandler?.('success', t('messages.importSuccess'));
|
|
622
632
|
handleOpenChange(false);
|
|
623
633
|
} catch {
|
|
624
|
-
showToastHandler?.('error', '
|
|
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=
|
|
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(
|
|
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', '
|
|
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', '
|
|
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
|
-
: '
|
|
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>
|
|
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>
|
|
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>
|
|
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">
|
|
863
|
-
|
|
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>
|
|
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>
|
|
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
|
-
|
|
927
|
+
{t('common.cancel')}
|
|
914
928
|
</Button>
|
|
915
929
|
<Button type="submit" disabled={form.formState.isSubmitting}>
|
|
916
|
-
|
|
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', '
|
|
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', '
|
|
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
|
-
: '
|
|
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', '
|
|
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', '
|
|
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
|
-
: '
|
|
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
|
-
|
|
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
|
|
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>
|
|
1806
|
+
<DialogTitle>{t('deleteDialog.title')}</DialogTitle>
|
|
1790
1807
|
<DialogDescription>
|
|
1791
|
-
|
|
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
|
-
|
|
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
|
|
1824
|
+
{isDeletingStatement
|
|
1825
|
+
? t('deleteDialog.deleting')
|
|
1826
|
+
: t('deleteDialog.confirm')}
|
|
1809
1827
|
</Button>
|
|
1810
1828
|
</DialogFooter>
|
|
1811
1829
|
</DialogContent>
|
|
@@ -88,23 +88,30 @@ type TransferDraftPayload = {
|
|
|
88
88
|
|
|
89
89
|
const TRANSFER_FORM_DRAFT_STORAGE_KEY = 'finance-transfer-form-draft';
|
|
90
90
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
91
|
+
const createTransferFormSchema = (t: ReturnType<typeof useTranslations>) =>
|
|
92
|
+
z
|
|
93
|
+
.object({
|
|
94
|
+
sourceAccountId: z
|
|
95
|
+
.string()
|
|
96
|
+
.trim()
|
|
97
|
+
.min(1, t('formErrors.sourceAccountRequired')),
|
|
98
|
+
destinationAccountId: z
|
|
99
|
+
.string()
|
|
100
|
+
.trim()
|
|
101
|
+
.min(1, t('formErrors.destinationAccountRequired')),
|
|
102
|
+
date: z.string().trim().min(1, t('formErrors.dateRequired')),
|
|
103
|
+
amount: z.number().min(0.01, t('formErrors.amountMustBePositive')),
|
|
104
|
+
description: z.string().optional(),
|
|
105
|
+
})
|
|
106
|
+
.refine(
|
|
107
|
+
(values) => values.sourceAccountId !== values.destinationAccountId,
|
|
108
|
+
{
|
|
109
|
+
message: t('formErrors.differentAccounts'),
|
|
110
|
+
path: ['destinationAccountId'],
|
|
111
|
+
}
|
|
112
|
+
);
|
|
106
113
|
|
|
107
|
-
type TransferFormValues = z.infer<typeof
|
|
114
|
+
type TransferFormValues = z.infer<ReturnType<typeof createTransferFormSchema>>;
|
|
108
115
|
|
|
109
116
|
function NovaTransferenciaSheet({
|
|
110
117
|
contasBancarias,
|
|
@@ -118,6 +125,7 @@ function NovaTransferenciaSheet({
|
|
|
118
125
|
const { request, showToastHandler, currentLocaleCode, getSettingValue } =
|
|
119
126
|
useApp();
|
|
120
127
|
const [open, setOpen] = useState(false);
|
|
128
|
+
const transferFormSchema = useMemo(() => createTransferFormSchema(t), [t]);
|
|
121
129
|
|
|
122
130
|
const form = useForm<TransferFormValues>({
|
|
123
131
|
resolver: zodResolver(transferFormSchema),
|
|
@@ -187,10 +195,8 @@ function NovaTransferenciaSheet({
|
|
|
187
195
|
currentLocaleCode
|
|
188
196
|
);
|
|
189
197
|
|
|
190
|
-
return
|
|
191
|
-
|
|
192
|
-
: `Draft saved ${relativeLabel} • Last saved: ${absoluteLabel}`;
|
|
193
|
-
}, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft]);
|
|
198
|
+
return t('draftStatus', { relativeLabel, absoluteLabel });
|
|
199
|
+
}, [draftSavedAt, currentLocaleCode, getSettingValue, hasDraft, t]);
|
|
194
200
|
|
|
195
201
|
useEffect(() => {
|
|
196
202
|
if (!open) {
|
|
@@ -227,9 +233,9 @@ function NovaTransferenciaSheet({
|
|
|
227
233
|
clearDraft();
|
|
228
234
|
await onCreated();
|
|
229
235
|
setOpen(false);
|
|
230
|
-
showToastHandler?.('success', '
|
|
236
|
+
showToastHandler?.('success', t('transferCreatedSuccess'));
|
|
231
237
|
} catch {
|
|
232
|
-
showToastHandler?.('error', '
|
|
238
|
+
showToastHandler?.('error', t('transferCreateError'));
|
|
233
239
|
}
|
|
234
240
|
};
|
|
235
241
|
|
|
@@ -540,7 +546,7 @@ export default function TransferenciasPage() {
|
|
|
540
546
|
<SelectValue placeholder={t('common.select')} />
|
|
541
547
|
</SelectTrigger>
|
|
542
548
|
<SelectContent>
|
|
543
|
-
<SelectItem value="all">
|
|
549
|
+
<SelectItem value="all">{t('allAccounts')}</SelectItem>
|
|
544
550
|
{contasBancarias.map((conta) => (
|
|
545
551
|
<SelectItem key={conta.id} value={conta.id}>
|
|
546
552
|
{conta.banco} - {conta.descricao}
|
|
@@ -551,7 +557,7 @@ export default function TransferenciasPage() {
|
|
|
551
557
|
|
|
552
558
|
<div className="flex-1">
|
|
553
559
|
<FilterBar
|
|
554
|
-
searchPlaceholder=
|
|
560
|
+
searchPlaceholder={t('searchPlaceholder')}
|
|
555
561
|
searchValue={search}
|
|
556
562
|
onSearchChange={setSearch}
|
|
557
563
|
/>
|
|
@@ -575,7 +581,9 @@ export default function TransferenciasPage() {
|
|
|
575
581
|
<TableRow>
|
|
576
582
|
<TableHead>{t('table.headers.date')}</TableHead>
|
|
577
583
|
<TableHead>{t('table.headers.source')}</TableHead>
|
|
578
|
-
<TableHead className="text-center"
|
|
584
|
+
<TableHead className="text-center">
|
|
585
|
+
{t('table.headers.arrow')}
|
|
586
|
+
</TableHead>
|
|
579
587
|
<TableHead>{t('table.headers.destination')}</TableHead>
|
|
580
588
|
<TableHead className="text-right">
|
|
581
589
|
{t('table.headers.value')}
|
|
@@ -316,9 +316,9 @@ export default function CenariosPage() {
|
|
|
316
316
|
});
|
|
317
317
|
|
|
318
318
|
await refetch();
|
|
319
|
-
showToastHandler?.('success', '
|
|
319
|
+
showToastHandler?.('success', t('messages.saveSuccess'));
|
|
320
320
|
} catch {
|
|
321
|
-
showToastHandler?.('error', '
|
|
321
|
+
showToastHandler?.('error', t('messages.saveError'));
|
|
322
322
|
} finally {
|
|
323
323
|
setIsSaving(false);
|
|
324
324
|
}
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
TableHeader,
|
|
22
22
|
TableRow,
|
|
23
23
|
} from '@/components/ui/table';
|
|
24
|
+
import { useApp } from '@hed-hog/next-app-provider';
|
|
24
25
|
import {
|
|
25
26
|
Download,
|
|
26
27
|
Loader2,
|
|
@@ -47,6 +48,7 @@ type Scenario = 'base' | 'pessimista' | 'otimista';
|
|
|
47
48
|
|
|
48
49
|
export default function ActualVsForecastPage() {
|
|
49
50
|
const t = useTranslations('finance.ActualVsForecastPage');
|
|
51
|
+
const { currentLocaleCode } = useApp();
|
|
50
52
|
|
|
51
53
|
const [horizonte, setHorizonte] = useState<string | null>(null);
|
|
52
54
|
const [cenario, setCenario] = useState<Scenario | null>(null);
|
|
@@ -219,7 +221,7 @@ export default function ActualVsForecastPage() {
|
|
|
219
221
|
/>
|
|
220
222
|
<Tooltip
|
|
221
223
|
formatter={(value: number) =>
|
|
222
|
-
new Intl.NumberFormat(
|
|
224
|
+
new Intl.NumberFormat(currentLocaleCode || undefined, {
|
|
223
225
|
style: 'currency',
|
|
224
226
|
currency: 'BRL',
|
|
225
227
|
}).format(value)
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
TableHeader,
|
|
21
21
|
TableRow,
|
|
22
22
|
} from '@/components/ui/table';
|
|
23
|
+
import { useApp } from '@hed-hog/next-app-provider';
|
|
23
24
|
import { AlertTriangle, Loader2 } from 'lucide-react';
|
|
24
25
|
import { useTranslations } from 'next-intl';
|
|
25
26
|
import { useMemo, useState } from 'react';
|
|
@@ -46,6 +47,7 @@ function toPercent(value: number) {
|
|
|
46
47
|
|
|
47
48
|
export default function AgingDefaultReportPage() {
|
|
48
49
|
const t = useTranslations('finance.AgingDefaultReportPage');
|
|
50
|
+
const { currentLocaleCode } = useApp();
|
|
49
51
|
|
|
50
52
|
const [horizonte, setHorizonte] = useState<string | null>(null);
|
|
51
53
|
const [cenario, setCenario] = useState<Scenario | null>(null);
|
|
@@ -213,7 +215,7 @@ export default function AgingDefaultReportPage() {
|
|
|
213
215
|
/>
|
|
214
216
|
<Tooltip
|
|
215
217
|
formatter={(value: number) =>
|
|
216
|
-
new Intl.NumberFormat(
|
|
218
|
+
new Intl.NumberFormat(currentLocaleCode || undefined, {
|
|
217
219
|
style: 'currency',
|
|
218
220
|
currency: 'BRL',
|
|
219
221
|
}).format(value)
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
TableHeader,
|
|
22
22
|
TableRow,
|
|
23
23
|
} from '@/components/ui/table';
|
|
24
|
+
import { useApp } from '@hed-hog/next-app-provider';
|
|
24
25
|
import {
|
|
25
26
|
Download,
|
|
26
27
|
Loader2,
|
|
@@ -46,6 +47,7 @@ type Scenario = 'base' | 'pessimista' | 'otimista';
|
|
|
46
47
|
|
|
47
48
|
export default function CashPositionReportPage() {
|
|
48
49
|
const t = useTranslations('finance.CashPositionReportPage');
|
|
50
|
+
const { currentLocaleCode } = useApp();
|
|
49
51
|
|
|
50
52
|
const [horizonte, setHorizonte] = useState<string | null>(null);
|
|
51
53
|
const [cenario, setCenario] = useState<Scenario | null>(null);
|
|
@@ -309,7 +311,7 @@ export default function CashPositionReportPage() {
|
|
|
309
311
|
/>
|
|
310
312
|
<Tooltip
|
|
311
313
|
formatter={(value: number) =>
|
|
312
|
-
new Intl.NumberFormat(
|
|
314
|
+
new Intl.NumberFormat(currentLocaleCode || undefined, {
|
|
313
315
|
style: 'currency',
|
|
314
316
|
currency: 'BRL',
|
|
315
317
|
}).format(value)
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import { FinancePageSection } from '@/app/(app)/(libraries)/finance/_components/finance-layout';
|
|
4
4
|
import { Page, PageHeader } from '@/components/entity-list';
|
|
5
5
|
import { Badge } from '@/components/ui/badge';
|
|
6
|
-
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
7
6
|
import { Input } from '@/components/ui/input';
|
|
7
|
+
import { KpiCardsGrid } from '@/components/ui/kpi-cards-grid';
|
|
8
8
|
import { Label } from '@/components/ui/label';
|
|
9
9
|
import { Money } from '@/components/ui/money';
|
|
10
10
|
import {
|
|
@@ -49,16 +49,12 @@ const LINE_COLORS = {
|
|
|
49
49
|
diferenca: '#16a34a',
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
const currencyFormatter = new Intl.NumberFormat('pt-BR', {
|
|
53
|
-
style: 'currency',
|
|
54
|
-
currency: 'BRL',
|
|
55
|
-
});
|
|
56
|
-
|
|
57
52
|
function renderSolidTooltip({
|
|
58
53
|
active,
|
|
59
54
|
payload,
|
|
60
55
|
label,
|
|
61
|
-
|
|
56
|
+
currencyFormatter,
|
|
57
|
+
}: TooltipProps<number, string> & { currencyFormatter: Intl.NumberFormat }) {
|
|
62
58
|
if (!active || !payload?.length) return null;
|
|
63
59
|
return (
|
|
64
60
|
<div className="rounded-lg border bg-popover px-3 py-2 shadow-xl text-popover-foreground text-sm">
|
|
@@ -84,6 +80,10 @@ function renderSolidTooltip({
|
|
|
84
80
|
export default function OverviewResultsReportPage() {
|
|
85
81
|
const t = useTranslations('finance.OverviewResultsReportPage');
|
|
86
82
|
const locale = useLocale();
|
|
83
|
+
const currencyFormatter = new Intl.NumberFormat(locale || undefined, {
|
|
84
|
+
style: 'currency',
|
|
85
|
+
currency: 'BRL',
|
|
86
|
+
});
|
|
87
87
|
const defaults = getDefaultDateRange();
|
|
88
88
|
const [from, setFrom] = useState(defaults.from);
|
|
89
89
|
const [to, setTo] = useState(defaults.to);
|
|
@@ -225,7 +225,14 @@ export default function OverviewResultsReportPage() {
|
|
|
225
225
|
tick={{ fontSize: 12 }}
|
|
226
226
|
tickFormatter={(value) => `${(value / 1000).toFixed(0)}k`}
|
|
227
227
|
/>
|
|
228
|
-
<Tooltip
|
|
228
|
+
<Tooltip
|
|
229
|
+
content={(props) =>
|
|
230
|
+
renderSolidTooltip({
|
|
231
|
+
...(props as TooltipProps<number, string>),
|
|
232
|
+
currencyFormatter,
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
/>
|
|
229
236
|
<Legend />
|
|
230
237
|
<Line
|
|
231
238
|
type="monotone"
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
TableHeader,
|
|
29
29
|
TableRow,
|
|
30
30
|
} from '@/components/ui/table';
|
|
31
|
+
import { useApp } from '@hed-hog/next-app-provider';
|
|
31
32
|
import { Crown, PieChartIcon, Users } from 'lucide-react';
|
|
32
33
|
import { useTranslations } from 'next-intl';
|
|
33
34
|
import { type FormEvent, useState } from 'react';
|
|
@@ -64,13 +65,9 @@ const rankingColors = [
|
|
|
64
65
|
'#B45309',
|
|
65
66
|
];
|
|
66
67
|
|
|
67
|
-
const currencyFormatter = new Intl.NumberFormat('pt-BR', {
|
|
68
|
-
style: 'currency',
|
|
69
|
-
currency: 'BRL',
|
|
70
|
-
});
|
|
71
|
-
|
|
72
68
|
export default function TopCustomersReportPage() {
|
|
73
69
|
const t = useTranslations('finance.TopCustomersReportPage');
|
|
70
|
+
const { currentLocaleCode } = useApp();
|
|
74
71
|
const defaults = getDefaultDateRange();
|
|
75
72
|
const [filters, setFilters] = useState({
|
|
76
73
|
from: defaults.from,
|
|
@@ -79,6 +76,13 @@ export default function TopCustomersReportPage() {
|
|
|
79
76
|
search: '',
|
|
80
77
|
});
|
|
81
78
|
const [appliedFilters, setAppliedFilters] = useState(filters);
|
|
79
|
+
const currencyFormatter = new Intl.NumberFormat(
|
|
80
|
+
currentLocaleCode || undefined,
|
|
81
|
+
{
|
|
82
|
+
style: 'currency',
|
|
83
|
+
currency: 'BRL',
|
|
84
|
+
}
|
|
85
|
+
);
|
|
82
86
|
const { data: viewData, isFetching } = useTopCustomersReport({
|
|
83
87
|
from: appliedFilters.from,
|
|
84
88
|
to: appliedFilters.to,
|