From 3bbacdfca887f318783bd73a4904a2b177be4829 Mon Sep 17 00:00:00 2001 From: Cleyton Jesus Date: Sun, 16 Nov 2025 13:15:32 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20envio=20de=20formul=C3=A1rio=20e=20valid?= =?UTF-8?q?a=C3=A7=C3=B5es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/forms/ExpenseForm/index.jsx | 31 ++++++------ frontend/src/components/forms/GroupForm.jsx | 16 ++++-- frontend/src/schemas/createGroup/form.js | 21 +++++++- frontend/src/schemas/createNewExpense/form.js | 50 +++++++++++++++++-- 4 files changed, 95 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/forms/ExpenseForm/index.jsx b/frontend/src/components/forms/ExpenseForm/index.jsx index 040ad8d9..2c9c9a0a 100644 --- a/frontend/src/components/forms/ExpenseForm/index.jsx +++ b/frontend/src/components/forms/ExpenseForm/index.jsx @@ -11,6 +11,7 @@ import { FiUserPlus, FiX } from 'react-icons/fi'; import { useCreateExpenseMutation } from '../../../hooks/ReactQuery/Mutations/useCreateExpenseMutation'; import { useMembersQuery } from '../../../hooks/ReactQuery/Queries/useMembersQuery'; +import { validatorCreateNewExpense } from '../../../schemas/createNewExpense/form'; export default function ExpenseForm({ groupData: group }) { const [search, setSearch] = useState(''); // termo final para API @@ -50,8 +51,6 @@ export default function ExpenseForm({ groupData: group }) { ); } - setIsSubmitting(true); - try { const formData = new FormData(e.target); const formValues = Object.fromEntries(formData); @@ -70,18 +69,22 @@ export default function ExpenseForm({ groupData: group }) { ), }; - await useExpenseMutation.mutateAsync(expenseData, { - onSuccess: () => - customToast( - 'Nova despesa', - 'Despesa adicionada com sucesso!', - 'success' - ), - onError: ({ response: { data: error } }) => { - customToast(error.title, error.message, 'error'); - }, - onSettled: () => setIsSubmitting(false), - }); + if (validatorCreateNewExpense(expenseData)) { + setIsSubmitting(true); + + await useExpenseMutation.mutateAsync(expenseData, { + onSuccess: () => + customToast( + 'Nova despesa', + 'Despesa adicionada com sucesso!', + 'success' + ), + onError: ({ response: { data: error } }) => { + customToast(error.title, error.message, 'error'); + }, + onSettled: () => setIsSubmitting(false), + }); + } } catch (err) { console.error(err); customToast( diff --git a/frontend/src/components/forms/GroupForm.jsx b/frontend/src/components/forms/GroupForm.jsx index b2df0724..59976a86 100644 --- a/frontend/src/components/forms/GroupForm.jsx +++ b/frontend/src/components/forms/GroupForm.jsx @@ -8,6 +8,11 @@ import { createGroup } from '../../services/groups'; import { Form } from 'react-router'; import { REACTQUERY_KEYS } from '../../libs/ReactQuery/keys'; import { customToast } from '../CustomToast'; +import { + createGroupFormSchema, + validatorGroupForm, +} from '../../schemas/createGroup/form'; +import { ZodError } from 'zod'; export default function GroupForm({ page }) { const queryClient = useQueryClient(); @@ -50,13 +55,18 @@ export default function GroupForm({ page }) { async e => { e.preventDefault(); if (isPending || isDisabled) return; - mutateAsync({ + + const formData = { name: groupName, description, icon: selectedIcon, - }); + }; + + if (validatorGroupForm(formData)) { + await mutateAsync(formData); + } }, - [isPending, isDisabled] + [isPending, isDisabled, groupName, selectedIcon, mutateAsync] ); return ( diff --git a/frontend/src/schemas/createGroup/form.js b/frontend/src/schemas/createGroup/form.js index 28edc6d4..e256f766 100644 --- a/frontend/src/schemas/createGroup/form.js +++ b/frontend/src/schemas/createGroup/form.js @@ -1,4 +1,5 @@ -import { z } from 'zod'; +import { z, ZodError } from 'zod'; +import { customToast } from '../../components/CustomToast'; export const createGroupFormSchema = z.object({ name: z @@ -8,3 +9,21 @@ export const createGroupFormSchema = z.object({ icon: z.string(), description: z.string().optional(), }); + +export function validatorGroupForm(formData) { + const { success, error } = createGroupFormSchema.safeParse(formData); + if (success) return success; + + const errors = {}; + if (error instanceof ZodError) { + error.issues.forEach(issue => { + const fieldName = issue.path[0]; + const title = + createGroupFormSchema.shape[fieldName]?.description || fieldName; + customToast(title, issue.message, 'error'); + }); + } else { + errors.global = 'Ocorreu um erro inesperado.'; + } + return success; +} diff --git a/frontend/src/schemas/createNewExpense/form.js b/frontend/src/schemas/createNewExpense/form.js index f9bda953..cd2aa2cc 100644 --- a/frontend/src/schemas/createNewExpense/form.js +++ b/frontend/src/schemas/createNewExpense/form.js @@ -1,8 +1,48 @@ -import { z } from 'zod'; +import { z, ZodError } from 'zod'; +import { customToast } from '../../components/CustomToast'; export const createNewExpenseFormSchema = z.object({ - title:z.string().min(3), - ExpenseValue:, - category:, - WhoPaid: + title: z.string().min(3, 'Título deve ter ao menos 3 caracteres'), + expenseValue: z + .number({ + required_error: 'O valor da despesa é obrigatório', + invalid_type_error: 'Valor inválido', + }) + .positive('A despesa deve ter um valor maior que zero'), + + categoryId: z + .number({ + required_error: 'Categoria é obrigatória', + invalid_type_error: 'Categoria inválida', + }) + .int() + .positive(), + description: z.string().optional(), + deadlineDate: z.string(), // normalmente vem como string ISO de input date + + expenseDivision: z + .array( + z.object({ + id: z.number().int().positive(), + value: z.number().nonnegative(), + }) + ) + .min(1, 'A divisão da despesa é obrigatória'), }); + +export function validatorCreateNewExpense(formData) { + const { success, error } = createNewExpenseFormSchema.safeParse(formData); + if (success) return success; + const errors = {}; + if (error instanceof ZodError) { + error.issues.forEach(issue => { + const fieldName = issue.path[0]; + const title = + createNewExpenseFormSchema.shape[fieldName]?.description || fieldName; + customToast(title, issue.message, 'error'); + }); + } else { + errors.global = 'Ocorreu um erro inesperado.'; + } + return success; +}