From 18408a6bf63d2a65f4872a88528e78df4cb1feb5 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 29 Dec 2025 18:28:30 +0000 Subject: [PATCH] finish form extraction --- client/src/hooks/forms/UseInitProfileForm.tsx | 2 +- .../src/hooks/forms/UseInitRecoveryForm.tsx | 7 +++-- client/src/hooks/forms/UseInitResetForm.tsx | 2 +- client/src/pages/auth/Invite.tsx | 2 +- client/src/pages/profile/Profile.tsx | 4 +-- client/src/pages/profile/ProfileForm.tsx | 12 ++++----- client/src/pages/recovery/Recovery.tsx | 9 +++---- client/src/pages/recovery/RecoveryForm.tsx | 12 ++++----- client/src/pages/recovery/Reset.tsx | 7 ++--- client/src/pages/recovery/ResetForm.tsx | 10 +++---- client/src/validation/index.ts | 5 +++- client/src/validation/invite.ts | 9 +++++++ client/src/validation/{zod.ts => profile.ts} | 26 +------------------ client/src/validation/recovery.ts | 6 +++++ client/src/validation/reset.ts | 16 ++++++++++++ 15 files changed, 65 insertions(+), 64 deletions(-) create mode 100644 client/src/validation/invite.ts rename client/src/validation/{zod.ts => profile.ts} (57%) create mode 100644 client/src/validation/recovery.ts create mode 100644 client/src/validation/reset.ts diff --git a/client/src/hooks/forms/UseInitProfileForm.tsx b/client/src/hooks/forms/UseInitProfileForm.tsx index 5be1c3d3b..4fafc2e1e 100644 --- a/client/src/hooks/forms/UseInitProfileForm.tsx +++ b/client/src/hooks/forms/UseInitProfileForm.tsx @@ -1,4 +1,4 @@ -import { profileSchema } from "@/validation/zod"; +import { profileSchema } from "@/validation"; import { useMemo } from "react"; import { z } from "zod"; diff --git a/client/src/hooks/forms/UseInitRecoveryForm.tsx b/client/src/hooks/forms/UseInitRecoveryForm.tsx index 4429d4b5a..7b7fe1577 100644 --- a/client/src/hooks/forms/UseInitRecoveryForm.tsx +++ b/client/src/hooks/forms/UseInitRecoveryForm.tsx @@ -1,14 +1,13 @@ -import { recoverySchema } from "@/validation/zod"; import { useMemo } from "react"; +import type { FormValues } from "@/pages/recovery/RecoveryForm"; -import { z } from "zod"; export const useInitForm = ({ initialData, }: { - initialData: Partial> | undefined; + initialData: FormValues | undefined; }) => { return useMemo(() => { - const defaults: z.infer = { + const defaults: FormValues = { email: initialData?.email || "", }; return { defaults }; diff --git a/client/src/hooks/forms/UseInitResetForm.tsx b/client/src/hooks/forms/UseInitResetForm.tsx index f44f15e4c..9bb2a902c 100644 --- a/client/src/hooks/forms/UseInitResetForm.tsx +++ b/client/src/hooks/forms/UseInitResetForm.tsx @@ -1,4 +1,4 @@ -import { resetSchema } from "@/validation/zod"; +import { resetSchema } from "@/validation"; import { useMemo } from "react"; import { z } from "zod"; diff --git a/client/src/pages/auth/Invite.tsx b/client/src/pages/auth/Invite.tsx index 58f1cee20..2f3ff1aec 100644 --- a/client/src/pages/auth/Invite.tsx +++ b/client/src/pages/auth/Invite.tsx @@ -8,7 +8,7 @@ import { BasePage, ConfigBox } from "@/components/design-elements"; import { useEffect, useState, useMemo } from "react"; import { useTheme } from "@mui/material/styles"; import { useTranslation } from "react-i18next"; -import { inviteSchema } from "@/validation/zod"; +import { inviteSchema } from "@/validation"; import { z } from "zod"; import { useForm, Controller } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; diff --git a/client/src/pages/profile/Profile.tsx b/client/src/pages/profile/Profile.tsx index 331ed5bc4..c540d125a 100644 --- a/client/src/pages/profile/Profile.tsx +++ b/client/src/pages/profile/Profile.tsx @@ -5,12 +5,10 @@ import { useState, useEffect } from "react"; import type { IUser } from "@/types/user"; import type { ApiResponse } from "@/types/api"; import { useGet, usePatch, useGetOnDemand } from "@/hooks/UseApi"; -import { z } from "zod"; -import { profileSchema } from "@/validation/zod"; import { useTranslation } from "react-i18next"; import { useAppDispatch } from "@/hooks/AppHooks"; import { setUser as setUserGlobal } from "@/features/authSlice"; -type FormValues = z.infer; +import type { FormValues } from "@/pages/profile/ProfileForm"; const Profile = () => { const { t } = useTranslation(); diff --git a/client/src/pages/profile/ProfileForm.tsx b/client/src/pages/profile/ProfileForm.tsx index 621a4c9e2..9ba36dc4d 100644 --- a/client/src/pages/profile/ProfileForm.tsx +++ b/client/src/pages/profile/ProfileForm.tsx @@ -12,8 +12,8 @@ import { useTheme } from "@mui/material/styles"; import { useInitForm } from "@/hooks/forms/UseInitProfileForm"; import type { IUser } from "@/types/user"; -import { profileSchema } from "@/validation/zod"; -type FormData = z.infer; +import { profileSchema } from "@/validation"; +export type FormValues = z.infer; export const ProfileForm = ({ user, @@ -23,21 +23,21 @@ export const ProfileForm = ({ }: { user: Partial | null; hasUser?: boolean; - initialData?: Partial; - onSubmit: SubmitHandler; + initialData?: Partial; + onSubmit: SubmitHandler; loading: boolean; }) => { const { t } = useTranslation(); const theme = useTheme(); const { defaults } = useInitForm({ initialData: initialData }); - const resolver = zodResolver(profileSchema) as unknown as Resolver; + const resolver = zodResolver(profileSchema) as Resolver; const { handleSubmit, control, reset, formState: { errors }, - } = useForm({ + } = useForm({ resolver, defaultValues: { firstName: "", diff --git a/client/src/pages/recovery/Recovery.tsx b/client/src/pages/recovery/Recovery.tsx index 61b061cb6..8f681155d 100644 --- a/client/src/pages/recovery/Recovery.tsx +++ b/client/src/pages/recovery/Recovery.tsx @@ -2,19 +2,16 @@ import { AuthBasePage } from "@/components/auth"; import { useTranslation } from "react-i18next"; import { RecoveryForm } from "./RecoveryForm"; -import { recoverySchema } from "@/validation/zod"; -import { z } from "zod"; import { usePost } from "@/hooks/UseApi"; import { useToast } from "@/hooks/UseToast"; - -type FormData = z.infer; +import type { FormValues } from "@/pages/recovery/RecoveryForm"; const Recovery = () => { const { t } = useTranslation(); - const { post, loading } = usePost(); + const { post, loading } = usePost(); const { showToast } = useToast(); - const onSubmit = async (data: FormData) => { + const onSubmit = async (data: FormValues) => { await post("/recovery", data); showToast(t("common.toasts.recoveryEmailSent")); }; diff --git a/client/src/pages/recovery/RecoveryForm.tsx b/client/src/pages/recovery/RecoveryForm.tsx index 85cca8de2..e12bf7b2b 100644 --- a/client/src/pages/recovery/RecoveryForm.tsx +++ b/client/src/pages/recovery/RecoveryForm.tsx @@ -9,18 +9,18 @@ import { useEffect } from "react"; import { z } from "zod"; import { useTranslation } from "react-i18next"; import { useTheme } from "@mui/material/styles"; -import { useInitForm } from "@/hooks/forms/UseInitRegisterForm"; +import { useInitForm } from "@/hooks/forms/UseInitRecoveryForm"; -import { recoverySchema } from "@/validation/zod"; -type FormData = z.infer; +import { recoverySchema } from "@/validation"; +export type FormValues = z.infer; export const RecoveryForm = ({ initialData, onSubmit, loading, }: { - initialData?: Partial; - onSubmit: SubmitHandler; + initialData?: FormValues; + onSubmit: SubmitHandler; loading: boolean; }) => { const { t } = useTranslation(); @@ -32,7 +32,7 @@ export const RecoveryForm = ({ control, reset, formState: { errors }, - } = useForm({ + } = useForm({ resolver: zodResolver(recoverySchema), defaultValues: { email: "", diff --git a/client/src/pages/recovery/Reset.tsx b/client/src/pages/recovery/Reset.tsx index 9f7adfc9a..8acf3f3ef 100644 --- a/client/src/pages/recovery/Reset.tsx +++ b/client/src/pages/recovery/Reset.tsx @@ -3,8 +3,6 @@ import { AuthBasePage } from "@/components/auth"; import { useTranslation } from "react-i18next"; import { useParams } from "react-router"; import { ResetForm } from "@/pages/recovery/ResetForm"; -import { resetSchema } from "@/validation/zod"; -import { z } from "zod"; import { usePost } from "@/hooks/UseApi"; import { useToast } from "@/hooks/UseToast"; import { useNavigate } from "react-router"; @@ -15,8 +13,7 @@ import { setSelectedTeamId, logout, } from "@/features/authSlice"; - -type FormData = z.infer; +import type { FormValues } from "@/pages/recovery/ResetForm"; const Reset = () => { const { t } = useTranslation(); @@ -26,7 +23,7 @@ const Reset = () => { const { toastSuccess } = useToast(); const { id: token } = useParams<{ id: string }>(); - const onSubmit = async (data: FormData) => { + const onSubmit = async (data: FormValues) => { const result = await post(`/recovery/reset`, { password: data.password, token: token || "", diff --git a/client/src/pages/recovery/ResetForm.tsx b/client/src/pages/recovery/ResetForm.tsx index c7ec3b99c..339cce0b5 100644 --- a/client/src/pages/recovery/ResetForm.tsx +++ b/client/src/pages/recovery/ResetForm.tsx @@ -11,8 +11,8 @@ import { useTranslation } from "react-i18next"; import { useTheme } from "@mui/material/styles"; import { useInitForm } from "@/hooks/forms/UseInitResetForm"; -import { resetSchema } from "@/validation/zod"; -type FormData = z.infer; +import { resetSchema } from "@/validation"; +export type FormValues = z.infer; export const ResetForm = ({ initialData, @@ -20,8 +20,8 @@ export const ResetForm = ({ loading, }: { hasUser?: boolean; - initialData?: Partial; - onSubmit: SubmitHandler; + initialData?: Partial; + onSubmit: SubmitHandler; loading: boolean; }) => { const { t } = useTranslation(); @@ -33,7 +33,7 @@ export const ResetForm = ({ control, reset, formState: { errors }, - } = useForm({ + } = useForm({ resolver: zodResolver(resetSchema), defaultValues: { password: "", diff --git a/client/src/validation/index.ts b/client/src/validation/index.ts index 2116edbe2..49dde829e 100644 --- a/client/src/validation/index.ts +++ b/client/src/validation/index.ts @@ -1,4 +1,3 @@ -export * from "./zod"; export * from "./login"; export * from "./register"; export * from "./monitor"; @@ -10,3 +9,7 @@ export * from "./maintenance"; export * from "./team"; export * from "./system-settings"; export * from "./status-page"; +export * from "./invite"; +export * from "./recovery"; +export * from "./reset"; +export * from "./profile"; diff --git a/client/src/validation/invite.ts b/client/src/validation/invite.ts new file mode 100644 index 000000000..2af76084a --- /dev/null +++ b/client/src/validation/invite.ts @@ -0,0 +1,9 @@ +import { z } from "zod"; + +export const inviteSchema = z.object({ + email: z.email("Invalid email address"), + teamId: z.string().min(1, "Team is required"), + teamRoleId: z.string().min(1, "Role is required"), + orgRoleId: z.string().optional(), +}); + diff --git a/client/src/validation/zod.ts b/client/src/validation/profile.ts similarity index 57% rename from client/src/validation/zod.ts rename to client/src/validation/profile.ts index 6246fc94c..8113223a7 100644 --- a/client/src/validation/zod.ts +++ b/client/src/validation/profile.ts @@ -1,30 +1,5 @@ import { z } from "zod"; -export const inviteSchema = z.object({ - email: z.email("Invalid email address"), - teamId: z.string().min(1, "Team is required"), - teamRoleId: z.string().min(1, "Role is required"), - orgRoleId: z.string().optional(), -}); - -export const recoverySchema = z.object({ - email: z.email({ message: "Invalid email address" }).trim().toLowerCase(), -}); - -export const resetSchema = z - .object({ - password: z - .string() - .min(6, { message: "Password must be at least 6 characters" }), - confirmPassword: z - .string() - .min(6, { message: "Confirm Password must be at least 6 characters" }), - }) - .refine((data) => data.password === data.confirmPassword, { - path: ["confirmPassword"], - message: "Passwords must match", - }); - export const profileSchema = z .object({ firstName: z @@ -63,3 +38,4 @@ export const profileSchema = z path: ["confirmPassword"], } ); + diff --git a/client/src/validation/recovery.ts b/client/src/validation/recovery.ts new file mode 100644 index 000000000..f6bd0cfab --- /dev/null +++ b/client/src/validation/recovery.ts @@ -0,0 +1,6 @@ +import { z } from "zod"; + +export const recoverySchema = z.object({ + email: z.email({ message: "Invalid email address" }).trim().toLowerCase(), +}); + diff --git a/client/src/validation/reset.ts b/client/src/validation/reset.ts new file mode 100644 index 000000000..6e4b63ba0 --- /dev/null +++ b/client/src/validation/reset.ts @@ -0,0 +1,16 @@ +import { z } from "zod"; + +export const resetSchema = z + .object({ + password: z + .string() + .min(6, { message: "Password must be at least 6 characters" }), + confirmPassword: z + .string() + .min(6, { message: "Confirm Password must be at least 6 characters" }), + }) + .refine((data) => data.password === data.confirmPassword, { + path: ["confirmPassword"], + message: "Passwords must match", + }); +