feat(inquires): Make contact form configurable via CMS data
- Add CmsContact type support to FooterForm, Inquires, and InquiresForm components - Make form fields dynamic by accepting contactData prop with configurable Fields array - Add placeholder prop to InputGroup component for enhanced field customization - Replace hardcoded form fields with dynamic field mapping from CMS data - Support dynamic form title and description from contactData - Use CMS-provided submit button text with fallback to default "Submit" - Implement field-level validation configuration including required field handling - Auto-detect input type based on field name (email, phone, text) - Pass contact data from marketing page to FooterForm component - Maintain backward compatibility with default fields when contactData is not provided
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import { CmsContact } from "@/types/TCms";
|
||||
import Inquires from "../_inquires/page";
|
||||
|
||||
const FooterForm = () => {
|
||||
const FooterForm = ({ contactData }: { contactData?: CmsContact }) => {
|
||||
return (
|
||||
<div className="absolute w-full -top-[420px] lg:-top-72 z-10">
|
||||
<Inquires />
|
||||
<Inquires contactData={contactData} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ function InputGroup({
|
||||
type,
|
||||
validation,
|
||||
error,
|
||||
placeholder,
|
||||
}: {
|
||||
label: string;
|
||||
id: string;
|
||||
@@ -16,6 +17,7 @@ function InputGroup({
|
||||
type: HTMLInputTypeAttribute;
|
||||
validation?: RegisterOptions;
|
||||
error?: string;
|
||||
placeholder?: string;
|
||||
}) {
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const [hasValue, setHasValue] = useState(false);
|
||||
@@ -40,6 +42,7 @@ function InputGroup({
|
||||
: "border-gray-200"
|
||||
} p-2 py-3 rounded-lg w-full outline-none`}
|
||||
id={id}
|
||||
placeholder={placeholder}
|
||||
{...rest}
|
||||
onChange={handleChange}
|
||||
onFocus={() => setIsFocused(true)}
|
||||
|
||||
+66
-37
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
import api from "@/service/api";
|
||||
import { CmsContact } from "@/types/TCms";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { toast } from "react-toastify";
|
||||
@@ -7,20 +8,19 @@ import InputGroup from "../_components/InputGroup";
|
||||
import Loading from "../_components/Loading";
|
||||
|
||||
type TInquiresForm = {
|
||||
full_name: string;
|
||||
company_name: string;
|
||||
work_email: string;
|
||||
phone_number: string;
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
type InquiresFormProps = {
|
||||
columnsPerRow?: 1 | 2 | 3 | 4;
|
||||
buttonText?: string;
|
||||
contactData?: CmsContact;
|
||||
};
|
||||
|
||||
const InquiresForm = ({
|
||||
columnsPerRow = 2,
|
||||
buttonText = "Submit",
|
||||
buttonText,
|
||||
contactData,
|
||||
}: InquiresFormProps) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const {
|
||||
@@ -59,44 +59,73 @@ const InquiresForm = ({
|
||||
4: "lg:col-span-4",
|
||||
}[columnsPerRow];
|
||||
|
||||
const defaultFields = [
|
||||
{ Name: "full_name", Label: "Full Name", Placeholder: "", Required: false },
|
||||
{
|
||||
Name: "company_name",
|
||||
Label: "Company Name",
|
||||
Placeholder: "",
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "work_email",
|
||||
Label: "Work Email",
|
||||
Placeholder: "",
|
||||
Required: false,
|
||||
},
|
||||
{
|
||||
Name: "phone_number",
|
||||
Label: "Phone Number",
|
||||
Placeholder: "",
|
||||
Required: false,
|
||||
},
|
||||
];
|
||||
|
||||
const fields = contactData?.Fields || defaultFields;
|
||||
const submitText = contactData?.SubmitButtonText || buttonText || "Submit";
|
||||
|
||||
return (
|
||||
<form
|
||||
className={`w-full px-4 lg:px-16 py-10 grid ${gridColsClass} gap-4`}
|
||||
onSubmit={handleSubmit(onSubmit)}>
|
||||
<InputGroup
|
||||
id="full_name"
|
||||
label="Full Name"
|
||||
register={register}
|
||||
type="text"
|
||||
/>
|
||||
<InputGroup
|
||||
id="company_name"
|
||||
label="Company Name"
|
||||
register={register}
|
||||
type="text"
|
||||
/>
|
||||
<InputGroup
|
||||
id="work_email"
|
||||
label="Work Email"
|
||||
register={register}
|
||||
type="email"
|
||||
/>
|
||||
<InputGroup
|
||||
id="phone_number"
|
||||
label="Phone Number"
|
||||
register={register}
|
||||
type="text"
|
||||
error={errors.phone_number?.message}
|
||||
validation={{
|
||||
pattern: {
|
||||
value: /^[+\d]+$/,
|
||||
message: "Please enter a valid phone number",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{fields.map((field) => {
|
||||
const inputType =
|
||||
field.Name.includes("email") || field.Name === "work_email"
|
||||
? "email"
|
||||
: "text";
|
||||
const isPhoneField =
|
||||
field.Name.includes("phone") || field.Name === "phone_number";
|
||||
|
||||
return (
|
||||
<InputGroup
|
||||
key={field.Name}
|
||||
id={field.Name}
|
||||
label={field.Label}
|
||||
placeholder={field.Placeholder}
|
||||
register={register}
|
||||
type={inputType}
|
||||
error={errors[field.Name]?.message}
|
||||
validation={
|
||||
isPhoneField
|
||||
? {
|
||||
pattern: {
|
||||
value: /^[+\d]+$/,
|
||||
message: "Please enter a valid phone number",
|
||||
},
|
||||
required: field.Required
|
||||
? "This field is required"
|
||||
: undefined,
|
||||
}
|
||||
: field.Required
|
||||
? { required: "This field is required" }
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<div className={`${colSpanClass} flex justify-end`}>
|
||||
<button className="bg-(--primary) cursor-pointer w-fit text-white rounded-full py-4 px-6">
|
||||
{isLoading ? <Loading /> : buttonText}
|
||||
{isLoading ? <Loading /> : submitText}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import { CmsContact } from "@/types/TCms";
|
||||
import InquiresForm from "./form";
|
||||
|
||||
const Inquires = () => {
|
||||
const Inquires = ({ contactData }: { contactData?: CmsContact }) => {
|
||||
return (
|
||||
<div className="flex flex-col items-center mt-16 px-8 ">
|
||||
<div className="lg:w-2/3 m-auto flex flex-col gap-4 mb-12">
|
||||
<h2 className="font-bold text-4xl">Unlock Your Growth Potential</h2>
|
||||
<h2 className="font-bold text-4xl">
|
||||
{contactData?.Title || "Unlock Your Growth Potential"}
|
||||
</h2>
|
||||
<p className="font-normal text-[16px] text-gray-600">
|
||||
Register for early access and a free consultation. Let's start winning
|
||||
together.
|
||||
{contactData?.Description ||
|
||||
"Register for early access and a free consultation. Let's start winning together."}
|
||||
</p>
|
||||
</div>
|
||||
<section className="border border-gray-300 rounded-4xl w-full lg:max-w-2/3 flex flex-col items-center bg-white">
|
||||
<InquiresForm />
|
||||
<InquiresForm contactData={contactData} />
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -39,6 +39,7 @@ export default async function Home({ params }: IProps) {
|
||||
} catch (error) {
|
||||
console.error("Error fetching CMS data:", error);
|
||||
}
|
||||
console.log(cmsData);
|
||||
|
||||
const centerFrames = [
|
||||
{
|
||||
@@ -247,7 +248,7 @@ export default async function Home({ params }: IProps) {
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
<FooterForm />
|
||||
<FooterForm contactData={cmsData?.data.contact} />
|
||||
|
||||
<div className="relative z-10 container px-4 py-4 m-auto flex flex-col justify-center items-center mt-80 ">
|
||||
<div className="flex justify-between flex-col lg:flex-row gap-14 w-full lg:w-1/2">
|
||||
|
||||
Reference in New Issue
Block a user