From 574a3a20ce406a08caca829065013a3f2ad77adc Mon Sep 17 00:00:00 2001 From: AmirReza Jamali Date: Sun, 9 Nov 2025 15:02:26 +0330 Subject: [PATCH] refactor(marketing): Standardize step components with forwardRef and data collection - Add forwardRef to all marketing wizard step components - Implement useImperativeHandle for consistent data collection across steps - Remove redundant collectData static methods - Add displayName to each component for better debugging - Simplify data retrieval pattern using ref-based getData method - Remove duplicate code and improve component consistency - Ensure each step component follows the same structural pattern --- .../create/_components/BenefitsStep.tsx | 13 ++- .../create/_components/ChallengesStep.tsx | 18 +-- .../create/_components/ChartStep.tsx | 18 +-- .../create/_components/ContactsStep.tsx | 24 ++-- .../create/_components/FeaturesStep.tsx | 18 +-- .../create/_components/FooterStep.tsx | 19 ++-- .../marketing/create/_components/HeroStep.tsx | 17 +-- .../_components/useBenefitsStepPresenter.ts | 7 ++ src/app/marketing/create/page.tsx | 105 +++++++++++++----- 9 files changed, 160 insertions(+), 79 deletions(-) diff --git a/src/app/marketing/create/_components/BenefitsStep.tsx b/src/app/marketing/create/_components/BenefitsStep.tsx index 3ca295d..9265bba 100644 --- a/src/app/marketing/create/_components/BenefitsStep.tsx +++ b/src/app/marketing/create/_components/BenefitsStep.tsx @@ -1,10 +1,11 @@ "use client"; +import { forwardRef, useImperativeHandle } from "react"; import SectionStep from "./SectionStep"; import useBenefitsStepPresenter, { IBenefitsStepFields, } from "./useBenefitsStepPresenter"; -const BenefitsStep = () => { +const BenefitsStep = forwardRef((props, ref) => { const { errors, handleSubmit, @@ -14,7 +15,13 @@ const BenefitsStep = () => { append, remove, control, + getValues, } = useBenefitsStepPresenter(); + + useImperativeHandle(ref, () => ({ + getData: () => getValues(), + })); + return ( title="Benefits" @@ -30,6 +37,8 @@ const BenefitsStep = () => { defaultValues={{ icon: "", title: "", description: "" }} /> ); -}; +}); + +BenefitsStep.displayName = "BenefitsStep"; export default BenefitsStep; diff --git a/src/app/marketing/create/_components/ChallengesStep.tsx b/src/app/marketing/create/_components/ChallengesStep.tsx index 9ed7a3f..4cca940 100644 --- a/src/app/marketing/create/_components/ChallengesStep.tsx +++ b/src/app/marketing/create/_components/ChallengesStep.tsx @@ -1,10 +1,11 @@ "use client"; +import { forwardRef, useImperativeHandle } from "react"; import SectionStep from "./SectionStep"; import useChallengesStepPresenter, { IChallengesStepFields, } from "./useChallengesStepPresenter"; -const ChallengesStep = () => { +const ChallengesStep = forwardRef((props, ref) => { const { errors, handleSubmit, @@ -14,7 +15,13 @@ const ChallengesStep = () => { append, remove, control, + getValues, } = useChallengesStepPresenter(); + + useImperativeHandle(ref, () => ({ + getData: () => getValues(), + })); + return ( title="Challenges" @@ -30,11 +37,8 @@ const ChallengesStep = () => { defaultValues={{ icon: "", title: "", description: "" }} /> ); -}; +}); + +ChallengesStep.displayName = "ChallengesStep"; export default ChallengesStep; - -ChallengesStep.collectData = async () => { - const { getValues } = useChallengesStepPresenter(); - return getValues(); -} diff --git a/src/app/marketing/create/_components/ChartStep.tsx b/src/app/marketing/create/_components/ChartStep.tsx index d69733d..14d5eb5 100644 --- a/src/app/marketing/create/_components/ChartStep.tsx +++ b/src/app/marketing/create/_components/ChartStep.tsx @@ -3,12 +3,12 @@ import InputGroup from "@/components/FormElements/InputGroup"; import { ShowcaseSection } from "@/components/Layouts/showcase-section"; import { TrashIcon } from "@/assets/icons"; -import FormFooter from "@/components/ui/FormFooter"; import { FormErrorMessages } from "@/constants/Texts"; +import { forwardRef, useImperativeHandle } from "react"; import { ChartPreview } from "./ChartPreview"; import useChartStepPresenter from "./useChartStepPresenter"; -const ChartStep = () => { +const ChartStep = forwardRef((props, ref) => { const { errors, handleSubmit, @@ -18,8 +18,13 @@ const ChartStep = () => { append, remove, watch, + getValues, } = useChartStepPresenter(); + useImperativeHandle(ref, () => ({ + getData: () => getValues(), + })); + const chartTitle = watch("chart_title"); const chartPoints = watch("chart_points"); @@ -122,11 +127,8 @@ const ChartStep = () => {
); -}; +}); + +ChartStep.displayName = "ChartStep"; export default ChartStep; - -ChartStep.collectData = async () => { - const { getValues } = useChartStepPresenter(); - return getValues(); -} diff --git a/src/app/marketing/create/_components/ContactsStep.tsx b/src/app/marketing/create/_components/ContactsStep.tsx index 4f11b2e..1ab4257 100644 --- a/src/app/marketing/create/_components/ContactsStep.tsx +++ b/src/app/marketing/create/_components/ContactsStep.tsx @@ -4,11 +4,17 @@ import InputGroup from "@/components/FormElements/InputGroup"; import { TextAreaGroup } from "@/components/FormElements/InputGroup/text-area"; import { ShowcaseSection } from "@/components/Layouts/showcase-section"; import { FormErrorMessages } from "@/constants/Texts"; +import { forwardRef, useImperativeHandle } from "react"; import useContactsStepPresenter from "./useContactsStepPresenter"; -const ContactsStep = () => { - const { errors, handleSubmit, onSubmit, register, fields } = +const ContactsStep = forwardRef((props, ref) => { + const { errors, handleSubmit, onSubmit, register, fields, getValues } = useContactsStepPresenter(); + + useImperativeHandle(ref, () => ({ + getData: () => getValues(), + })); + return (
{
); -}; +}); + +ContactsStep.displayName = "ContactsStep"; export default ContactsStep; - -ContactsStep.collectData = async () => { - const { getValues } = useContactsStepPresenter(); - return getValues(); -} - -ContactsStep.collectData = async () => { - const { getValues } = useContactsStepPresenter(); - return getValues(); -} diff --git a/src/app/marketing/create/_components/FeaturesStep.tsx b/src/app/marketing/create/_components/FeaturesStep.tsx index 7e592b6..394a237 100644 --- a/src/app/marketing/create/_components/FeaturesStep.tsx +++ b/src/app/marketing/create/_components/FeaturesStep.tsx @@ -1,10 +1,11 @@ "use client"; +import { forwardRef, useImperativeHandle } from "react"; import SectionStep from "./SectionStep"; import useFeaturesStepPresenter, { IFeaturesStepFields, } from "./useFeaturesStepPresenter"; -const FeaturesStep = () => { +const FeaturesStep = forwardRef((props, ref) => { const { errors, handleSubmit, @@ -14,7 +15,13 @@ const FeaturesStep = () => { append, remove, control, + getValues, } = useFeaturesStepPresenter(); + + useImperativeHandle(ref, () => ({ + getData: () => getValues(), + })); + return ( title="Features" @@ -30,11 +37,8 @@ const FeaturesStep = () => { defaultValues={{ icon: "", title: "", description: "" }} /> ); -}; +}); + +FeaturesStep.displayName = "FeaturesStep"; export default FeaturesStep; - -FeaturesStep.collectData = async () => { - const { getValues } = useFeaturesStepPresenter(); - return getValues(); -} diff --git a/src/app/marketing/create/_components/FooterStep.tsx b/src/app/marketing/create/_components/FooterStep.tsx index 02733ca..002d50f 100644 --- a/src/app/marketing/create/_components/FooterStep.tsx +++ b/src/app/marketing/create/_components/FooterStep.tsx @@ -3,13 +3,17 @@ import InputGroup from "@/components/FormElements/InputGroup"; import { TextAreaGroup } from "@/components/FormElements/InputGroup/text-area"; import { ShowcaseSection } from "@/components/Layouts/showcase-section"; import { FormErrorMessages } from "@/constants/Texts"; +import { forwardRef, useImperativeHandle } from "react"; import useFooterStepPresenter from "./useFooterStepPresenter"; - -const FooterStep = () => { - const { errors, handleSubmit, onSubmit, register } = +const FooterStep = forwardRef((props, ref) => { + const { errors, handleSubmit, onSubmit, register, getValues } = useFooterStepPresenter(); + useImperativeHandle(ref, () => ({ + getData: () => getValues(), + })); + return (
{
); -}; +}); -export default FooterStep; +FooterStep.displayName = "FooterStep"; -FooterStep.collectData = async () => { - const { getValues } = useFooterStepPresenter(); - return getValues(); -} \ No newline at end of file +export default FooterStep; \ No newline at end of file diff --git a/src/app/marketing/create/_components/HeroStep.tsx b/src/app/marketing/create/_components/HeroStep.tsx index 4fb4c94..431039d 100644 --- a/src/app/marketing/create/_components/HeroStep.tsx +++ b/src/app/marketing/create/_components/HeroStep.tsx @@ -3,10 +3,16 @@ import InputGroup from "@/components/FormElements/InputGroup"; import { TextAreaGroup } from "@/components/FormElements/InputGroup/text-area"; import { ShowcaseSection } from "@/components/Layouts/showcase-section"; import { FormErrorMessages } from "@/constants/Texts"; +import { forwardRef, useImperativeHandle } from "react"; import useHeroStepPresenter from "./useHeroStepPresenter"; -const HeroStep = () => { - const { errors, handleSubmit, onSubmit, register } = useHeroStepPresenter(); +const HeroStep = forwardRef((props, ref) => { + const { errors, handleSubmit, onSubmit, register, getValues } = useHeroStepPresenter(); + + useImperativeHandle(ref, () => ({ + getData: () => getValues(), + })); + return (
{
); -}; +}); -HeroStep.collectData = async () => { - const { getValues } = useHeroStepPresenter(); - return getValues(); -} +HeroStep.displayName = "HeroStep"; export default HeroStep; diff --git a/src/app/marketing/create/_components/useBenefitsStepPresenter.ts b/src/app/marketing/create/_components/useBenefitsStepPresenter.ts index 58414a1..922c832 100644 --- a/src/app/marketing/create/_components/useBenefitsStepPresenter.ts +++ b/src/app/marketing/create/_components/useBenefitsStepPresenter.ts @@ -18,6 +18,7 @@ const useBenefitsStepPresenter = () => { control, watch, formState: { errors }, + getValues, } = useForm({ defaultValues: { benefits: [{ icon: "", title: "", description: "" }], @@ -33,6 +34,10 @@ const useBenefitsStepPresenter = () => { console.log(data); }; + const collectData = async () => { + return getValues(); + }; + return { register, handleSubmit, @@ -43,6 +48,8 @@ const useBenefitsStepPresenter = () => { remove, control, watch, + getValues, + collectData, }; }; diff --git a/src/app/marketing/create/page.tsx b/src/app/marketing/create/page.tsx index 079af26..42c6507 100644 --- a/src/app/marketing/create/page.tsx +++ b/src/app/marketing/create/page.tsx @@ -2,8 +2,10 @@ import Breadcrumb from "@/components/Breadcrumbs/Breadcrumb"; import { ShowcaseSection } from "@/components/Layouts/showcase-section"; import Stepper, { Step } from "@/components/ui/Stepper"; +import { useCreateCms } from "@/hooks/queries/useCmsQueries"; import { BreadcrumbItem } from "@/types/shared"; import { useRef, useState } from "react"; +import BenefitsStep from "./_components/BenefitsStep"; import ChallengesStep from "./_components/ChallengesStep"; import ChartStep from "./_components/ChartStep"; import ContactsStep from "./_components/ContactsStep"; @@ -20,6 +22,9 @@ const Marketing = () => { const benefitsRef = useRef(null); const contactsRef = useRef(null); const footerRef = useRef(null); + + const { mutate: createCms, isPending } = useCreateCms(); + const breadcrumbItems: BreadcrumbItem[] = [ { href: "/", @@ -35,63 +40,104 @@ const Marketing = () => { }, ]; - const collectStepData = async (stepIndex: number) => { - let stepData = {}; - const refs = [heroRef, chartRef, featuresRef, challengesRef, benefitsRef, contactsRef, footerRef]; - - const currentRef = refs[stepIndex]; - if (currentRef?.current?.collectData) { - stepData = await currentRef.current.collectData(); - - // Submit data to backend - if (stepData) { - await submitData(stepData, stepIndex); + const collectAllStepsData = async () => { + const refs = [ + heroRef, + chartRef, + featuresRef, + challengesRef, + benefitsRef, + contactsRef, + footerRef, + ]; + + const allData: any = { + hero: {}, + chart: {}, + features: {}, + challenges: {}, + benefits: {}, + contacts: {}, + footer: {}, + }; + + const keys = [ + "hero", + "chart", + "features", + "challenges", + "benefits", + "contacts", + "footer", + ]; + + for (let i = 0; i < refs.length; i++) { + const ref = refs[i]; + if (ref?.current?.getData) { + try { + const data = await ref.current.getData(); + allData[keys[i]] = data; + } catch (error) { + console.error(`Error collecting data from step ${i}:`, error); + } } } + + return allData; }; - const submitData = async (data: any, stepIndex: number) => { - const apiUrl = "/api/marketing-data"; - console.log(`Submitting data for step ${stepIndex}:`, data); + const handleSubmitAll = async () => { + const allData = await collectAllStepsData(); + console.log("All collected data:", allData); + + createCms(allData, { + onSuccess: () => { + console.log("CMS created successfully"); + }, + onError: (error) => { + console.error("Error creating CMS:", error); + }, + }); }; const steps: Step[] = [ { title: "Hero", description: "Create the hero section", - content: , + content: , }, { title: "Chart", description: "Create the chart", - content: , + content: , }, { title: "Features", description: "Add features", - content: , + content: , }, { title: "Challenges", description: "Create challenges section", - content: , + content: , }, { title: "Benefits", description: "create benefits section", - content: , + content: , }, { title: "Contacts", description: "Create contacts section", - content: , + content: , }, { title: "Footer", description: "Add footer information", - content: , + content: , }, ]; + return ( <> @@ -99,11 +145,18 @@ const Marketing = () => { { - collectStepData(step); - setStep(newStep); - }} - > + onStepChange={setStep} + /> +
+ +
);