191 lines
5.2 KiB
TypeScript

// @ts-nocheck
"use client";
import { useRouter } from "next/navigation";
import React, { useCallback, useState } from "react";
import { useForm } from "react-hook-form";
import { Form } from "@/payload-types";
import { Button } from "./Button";
import RichText from "./RichText";
import { buildInitialFormState } from "./buildInitialFormState";
import { fields } from "./fields";
import { toast } from "react-toastify";
export type Value = unknown;
export interface Property {
[key: string]: Value;
}
export interface Data {
[key: string]: Property | Property[] | Value;
}
export type FormBlockType = {
blockName?: string;
blockType?: "formBlock";
enableIntro?: boolean | null;
form: number | Form;
introContent?: {
root: {
type: string;
children: {
type: string;
version: number;
[k: string]: unknown;
}[];
direction: ("ltr" | "rtl") | null;
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
indent: number;
version: number;
};
[k: string]: unknown;
} | null;
};
export const FormBlock: React.FC<
FormBlockType & {
id?: string | null;
}
> = (props) => {
const {
enableIntro,
form: formFromProps,
form: { id: formID, confirmationMessage, confirmationType, redirect, submitButtonLabel } = {},
introContent,
} = props;
const formMethods = useForm({
defaultValues: buildInitialFormState(formFromProps.fields),
});
const {
control,
formState: { errors },
handleSubmit,
register,
reset,
setValue,
} = formMethods;
const [isLoading, setIsLoading] = useState(false);
const [hasSubmitted, setHasSubmitted] = useState<boolean>();
const [error, setError] = useState<{ message: string; status?: string } | undefined>();
const router = useRouter();
const onSubmit = useCallback(
(data: Data) => {
let loadingTimerID: ReturnType<typeof setTimeout>;
const submitForm = async () => {
setError(undefined);
const dataToSend = Object.entries(data).map(([name, value]) => ({
field: name,
value,
}));
const toastId = toast.loading("Sending...");
// delay loading indicator by 1s
loadingTimerID = setTimeout(() => {
setIsLoading(true);
}, 250);
try {
const req = await fetch(`/api/form-submissions`, {
body: JSON.stringify({
form: formID,
submissionData: dataToSend,
}),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});
const res = await req.json();
clearTimeout(loadingTimerID);
if (req.status >= 400) {
setIsLoading(false);
toast.error(res.errors?.[0]?.message || "Internal Server Error");
toast.dismiss(toastId);
setError({
message: res.errors?.[0]?.message || "Internal Server Error",
status: res.status,
});
return;
}
toast.success(<RichText content={confirmationMessage} />);
toast.dismiss(toastId);
setIsLoading(false);
setHasSubmitted(true);
reset();
if (confirmationType === "redirect" && redirect) {
const { url } = redirect;
const redirectUrl = url;
if (redirectUrl) router.push(redirectUrl);
}
} catch (err) {
console.warn(err);
toast.error("Something went wrong");
toast.dismiss(toastId);
setIsLoading(false);
setError({
message: "Something went wrong",
});
}
};
void submitForm();
},
[router, formID, redirect, confirmationType]
);
return (
<div>
{!!formFromProps?.title && <h2>{formFromProps.title}</h2>}
<div className="mt-4">
{!!enableIntro && introContent && !hasSubmitted && <RichText content={introContent} />}
{isLoading && !hasSubmitted && <p>Loading, please wait...</p>}
{error && <div className="text-red-500">{`${error.status || "500"}: ${error.message || ""}`}</div>}
<form id={formID} onSubmit={handleSubmit(onSubmit)} className="rd-form">
<div className="row space-y-4">
{formFromProps &&
formFromProps.fields &&
formFromProps.fields.map((field, index) => {
const Field: React.FC<any> = fields?.[field.blockType];
if (Field) {
return (
<React.Fragment key={index}>
<Field
form={formFromProps}
{...field}
{...formMethods}
control={control}
errors={errors}
register={register}
/>
</React.Fragment>
);
}
return null;
})}
</div>
<Button appearance="primary" el="button" form={formID} label={submitButtonLabel} isLoading={isLoading} />
</form>
</div>
</div>
);
};