Refactor form submission logic in BusinessForm component

This commit is contained in:
THIS ONE IS A LITTLE BIT TRICKY KRUB 2024-10-17 09:39:07 +07:00
parent 3a9f3faa48
commit dca9af5e3b
2 changed files with 147 additions and 124 deletions

View File

@ -28,8 +28,7 @@ type businessSchema = z.infer<typeof businessFormSchema>;
interface BusinessFormProps { interface BusinessFormProps {
industry: string[]; industry: string[];
handleSubmit: SubmitHandler<businessSchema>; onSubmit: SubmitHandler<businessSchema>;
onSubmit: () => void;
} }
const BusinessForm = ({ const BusinessForm = ({
onSubmit, onSubmit,
@ -53,7 +52,12 @@ const BusinessForm = ({
return ( return (
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8"> <form
onSubmit={form.handleSubmit(
onSubmit as unknown as SubmitHandler<businessSchema>
)}
className="space-y-8"
>
<div className="grid grid-flow-row auto-rows-max w-3/4 ml-1/2 lg:ml-[10%]"> <div className="grid grid-flow-row auto-rows-max w-3/4 ml-1/2 lg:ml-[10%]">
<h1 className="text-3xl font-bold mt-10 ml-96">About your company</h1> <h1 className="text-3xl font-bold mt-10 ml-96">About your company</h1>
<p className="ml-96 mt-5 text-neutral-500"> <p className="ml-96 mt-5 text-neutral-500">
@ -358,6 +362,12 @@ const BusinessForm = ({
</FormItem> </FormItem>
)} )}
/> */} /> */}
<Button
className="mt-12 mb-20 h-10 text-base font-bold py-6 px-5"
type="submit"
>
Submit application
</Button>
</div> </div>
</form> </form>
</Form> </Form>

View File

@ -3,140 +3,153 @@ import { z } from "zod";
const MAX_FILE_SIZE = 500000; const MAX_FILE_SIZE = 500000;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/jpg", "image/png"]; const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/jpg", "image/png"];
const imageSchema = z.custom<File>( const imageSchema = z
.custom<File>(
(val) => val && typeof val === "object" && "size" in val && "type" in val, (val) => val && typeof val === "object" && "size" in val && "type" in val,
{ {
message: "Input must be a file.", message: "Input must be a file.",
}, }
).refine((file) => file.size < MAX_FILE_SIZE, { )
.refine((file) => file.size < MAX_FILE_SIZE, {
message: "File can't be bigger than 5MB.", message: "File can't be bigger than 5MB.",
}).refine((file) => ACCEPTED_IMAGE_TYPES.includes(file.type), { })
.refine((file) => ACCEPTED_IMAGE_TYPES.includes(file.type), {
message: "File format must be either jpg, jpeg, or png.", message: "File format must be either jpg, jpeg, or png.",
}); });
const projectFormSchema = z.object({ const projectFormSchema = z.object({
projectName: z.string().min(5, { projectName: z.string().min(5, {
message: "Project name must be at least 5 characters.", message: "Project name must be at least 5 characters.",
}),
projectType: z.string({
required_error: "Please select one of the option",
}),
shortDescription: z
.string({
required_error: "Please provide a brief description for your project",
})
.min(10, {
message: "Short description must be at least 10 characters.",
}), }),
projectType: z.string({ projectPitchDeck: z.union([
required_error: "Please select one of the option", z
.string()
.url("Pitch deck must be a valid URL.")
.refine((url) => url.endsWith(".md"), {
message: "Pitch deck URL must link to a markdown file (.md).",
}),
z
.custom<File>((val) => val instanceof File, {
message: "Input must be a file.",
})
.refine((file) => file.size < MAX_FILE_SIZE, {
message: "File can't be bigger than 5MB.",
})
.refine((file) => file.name.endsWith(".md"), {
message: "File must be a markdown file (.md).",
}),
]),
projectLogo: imageSchema,
projectPhotos: z.custom(
(value) => {
if (value instanceof FileList || Array.isArray(value)) {
if (value.length === 1) {
return false;
}
return Array.from(value).every((item) => item instanceof File);
}
return false;
},
{
message:
"Must be a FileList or an array of File objects with at least one file.",
}
),
minInvest: z
.number({
required_error: "Minimum investment must be a number.",
invalid_type_error: "Minimum investment must be a valid number.",
})
.positive()
.max(9999999999, "Minimum investment must be a realistic amount."),
targetInvest: z
.number({
required_error: "Target investment must be a number.",
invalid_type_error: "Target investment must be a valid number.",
})
.positive()
.max(9999999999, "Target investment must be a realistic amount."),
deadline: z
.string()
.min(1, "Deadline is required.")
.refine((value) => !isNaN(Date.parse(value)), {
message: "Invalid date-time format.",
})
.transform((value) => new Date(value))
.refine((date) => date > new Date(), {
message: "Deadline must be in the future.",
}), }),
shortDescription: z.string({
required_error: "Please provide a brief description for your project",
}).min(10, {
message: "Short description must be at least 10 characters.",
}),
projectPitchDeck: z.union([
z.string().url("Pitch deck must be a valid URL.").refine(
(url) => url.endsWith(".md"),
{
message: "Pitch deck URL must link to a markdown file (.md).",
},
),
z.custom<File>(
(val) => val instanceof File,
{
message: "Input must be a file.",
},
).refine((file) => file.size < MAX_FILE_SIZE, {
message: "File can't be bigger than 5MB.",
}).refine((file) => file.name.endsWith(".md"), {
message: "File must be a markdown file (.md).",
}),
]),
projectLogo: imageSchema,
projectPhotos: z.custom(
(value) => {
if (value instanceof FileList || Array.isArray(value)) {
if (value.length === 1) {
return false;
}
return Array.from(value).every((item) => item instanceof File);
}
return false;
},
{
message:
"Must be a FileList or an array of File objects with at least one file.",
},
),
minInvest: z.number({
required_error: "Minimum investment must be a number.",
invalid_type_error: "Minimum investment must be a valid number.",
}).positive().max(
9999999999,
"Minimum investment must be a realistic amount.",
),
targetInvest: z.number({
required_error: "Target investment must be a number.",
invalid_type_error: "Target investment must be a valid number.",
}).positive().max(
9999999999,
"Target investment must be a realistic amount.",
),
deadline: z.string().min(1, "Deadline is required.")
.refine((value) => !isNaN(Date.parse(value)), {
message: "Invalid date-time format.",
})
.transform((value) => new Date(value))
.refine((date) => date > new Date(), {
message: "Deadline must be in the future.",
}),
}); });
const businessFormSchema = z.object({ const businessFormSchema = z.object({
companyName: z.string().min(5, { companyName: z.string().min(5, {
message: "Company name must be at least 5 characters.", message: "Company name must be at least 5 characters.",
}), }),
industry: z.string({ industry: z.string({
required_error: "Please select one of the option", required_error: "Please select one of the option",
}), }),
isInUS: z.string({ isInUS: z
required_error: "Please select either 'Yes' or 'No'.", .string({
required_error: "Please select either 'Yes' or 'No'.",
}) })
.transform((val) => val.toLowerCase()) .transform((val) => val.toLowerCase())
.refine((val) => val === "yes" || val === "no", { .refine((val) => val === "yes" || val === "no", {
message: "Please select either 'Yes' or 'No'.", message: "Please select either 'Yes' or 'No'.",
}),
isForSale: z.string({
required_error: "Please select either 'Yes' or 'No'.",
})
.transform((val) => val.toLowerCase())
.refine((val) => val === "yes" || val === "no", {
message: "Please select either 'Yes' or 'No'.",
}),
isGenerating: z.string({
required_error: "Please select either 'Yes' or 'No'.",
})
.transform((val) => val.toLowerCase())
.refine((val) => val === "yes" || val === "no", {
message: "Please select either 'Yes' or 'No'.",
}),
totalRaised: z.number({
required_error: "Total raised must be a number.",
invalid_type_error: "Total raised must be a valid number.",
}).positive().max(9999999999, "Total raised must be a realistic amount."),
communitySize: z.string({
required_error: "Please select one of the option",
}), }),
businessPitchDeck: z.union([ isForSale: z
z.string().url("Pitch deck must be a valid URL.").refine( .string({
(url) => url.endsWith(".md"), required_error: "Please select either 'Yes' or 'No'.",
{ })
message: "Pitch deck URL must link to a markdown file (.md).", .transform((val) => val.toLowerCase())
}, .refine((val) => val === "yes" || val === "no", {
), message: "Please select either 'Yes' or 'No'.",
z.custom<File>( }),
(val) => val instanceof File, isGenerating: z
{ .string({
message: "Input must be a file.", required_error: "Please select either 'Yes' or 'No'.",
}, })
).refine((file) => file.size < MAX_FILE_SIZE, { .transform((val) => val.toLowerCase())
message: "File can't be bigger than 5MB.", .refine((val) => val === "yes" || val === "no", {
}).refine((file) => file.name.endsWith(".md"), { message: "Please select either 'Yes' or 'No'.",
message: "File must be a markdown file (.md).", }),
}), totalRaised: z
]), .number({
required_error: "Total raised must be a number.",
invalid_type_error: "Total raised must be a valid number.",
})
.positive()
.max(9999999999, "Total raised must be a realistic amount."),
communitySize: z.string({
required_error: "Please select one of the option",
}),
businessPitchDeck: z.union([
z
.string()
.url("Pitch deck must be a valid URL.")
.refine((url) => url.endsWith(".md"), {
message: "Pitch deck URL must link to a markdown file (.md).",
}),
z
.custom<File>((val) => val instanceof File, {
message: "Input must be a file.",
})
.refine((file) => file.size < MAX_FILE_SIZE, {
message: "File can't be bigger than 5MB.",
})
.refine((file) => file.name.endsWith(".md"), {
message: "File must be a markdown file (.md).",
}),
]),
}); });
export { businessFormSchema, projectFormSchema }; export { businessFormSchema, projectFormSchema };