refactor(forms): memoize dynamic form sections with stable ids
continuous-integration/drone/push Build is passing

Add uuid-based section keys and memoized DynamicSection to reduce re-renders, fix dashboard GSAP scoping, update logo sizing, and temporarily hide notice-type and closing-soon widgets.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
AmirReza Jamali
2026-06-23 09:50:27 +03:30
parent 90a42b9fc5
commit 31d80cd6ae
12 changed files with 169 additions and 81 deletions
+79 -51
View File
@@ -19,6 +19,7 @@ import type {
HTMLInputTypeAttribute,
ReactNode,
} from "react";
import { memo } from "react";
export interface DynamicFieldContext<TValues extends FieldValues> {
control: Control<TValues>;
@@ -61,6 +62,7 @@ export type DynamicFormField<TValues extends FieldValues> =
| DynamicCustomField<TValues>;
export interface DynamicFormSection<TValues extends FieldValues> {
id: string;
title?: ReactNode;
className?: string;
rootClassName?: string;
@@ -90,6 +92,70 @@ const getErrorMessage = <TValues extends FieldValues>(
return typeof message === "string" ? message : undefined;
};
interface DynamicSectionProps<TValues extends FieldValues> {
section: DynamicFormSection<TValues>;
register: UseFormRegister<TValues>;
control: Control<TValues>;
errors: FieldErrors<TValues>;
footer?: ReactNode;
}
const DynamicSectionComponent = <TValues extends FieldValues>({
section,
register,
control,
errors,
footer,
}: DynamicSectionProps<TValues>) => (
<ShowcaseSection
title={section.title}
rootClassName={section.rootClassName}
className={cn("grid grid-cols-1 gap-3 md:grid-cols-2", section.className)}
>
{section.fields.map((field) => {
if (field.hidden) return null;
if (field.kind === "custom") {
return (
<div key={field.name} className={field.className}>
{field.render({
control,
errors,
register,
error: getErrorMessage(errors, field.name),
})}
</div>
);
}
return (
<InputGroup<TValues>
key={field.name}
{...register(field.name, field.rules)}
name={field.name}
label={field.label}
type={field.type}
placeholder={field.placeholder}
required={field.required}
disabled={field.disabled}
autoComplete={field.autoComplete}
dataCy={field.dataCy}
icon={field.icon}
iconPosition={field.iconPosition}
className={field.className}
errors={errors}
/>
);
})}
{footer ? <div className="md:col-span-2">{footer}</div> : null}
</ShowcaseSection>
);
const DynamicSection = memo(
DynamicSectionComponent,
) as typeof DynamicSectionComponent;
const DynamicFormBuilder = <TValues extends FieldValues>({
sections,
register,
@@ -113,57 +179,19 @@ const DynamicFormBuilder = <TValues extends FieldValues>({
{header}
<div className={sectionsClassName}>
{sections.map((section, sectionIndex) => (
<ShowcaseSection
key={sectionIndex}
title={section.title}
rootClassName={section.rootClassName}
className={cn(
"grid grid-cols-1 gap-3 md:grid-cols-2",
section.className,
)}
>
{section.fields.map((field) => {
if (field.hidden) return null;
if (field.kind === "custom") {
return (
<div key={field.name} className={field.className}>
{field.render({
control,
errors,
register,
error: getErrorMessage(errors, field.name),
})}
</div>
);
}
return (
<InputGroup<TValues>
key={field.name}
{...register(field.name, field.rules)}
name={field.name}
label={field.label}
type={field.type}
placeholder={field.placeholder}
required={field.required}
disabled={field.disabled}
autoComplete={field.autoComplete}
dataCy={field.dataCy}
icon={field.icon}
iconPosition={field.iconPosition}
className={field.className}
errors={errors}
/>
);
})}
{footer &&
footerPlacement === "last-section" &&
sectionIndex === sections.length - 1 ? (
<div className="md:col-span-2">{footer}</div>
) : null}
</ShowcaseSection>
<DynamicSection
key={section.id}
section={section}
register={register}
control={control}
errors={errors}
footer={
footerPlacement === "last-section" &&
sectionIndex === sections.length - 1
? footer
: undefined
}
/>
))}
</div>
{footerPlacement === "form" ? footer : null}
@@ -8,8 +8,11 @@ import FileImageUploadCard from "@/components/ui/FileImageUploadCard";
import { FormErrorMessages } from "@/constants/Texts";
import type { Dispatch, SetStateAction } from "react";
import { Controller, type UseFormRegisterReturn } from "react-hook-form";
import { v4 as uuidv4 } from "uuid";
import type { CreateAdminFormValues } from "./createAdminForm.types";
const adminSectionId = uuidv4();
interface CreateAdminFormSectionsOptions {
editMode?: boolean;
passwordFieldInputType: "password" | "text";
@@ -36,6 +39,7 @@ export const createAdminFormSections = ({
clearSelectedProfileImage,
}: CreateAdminFormSectionsOptions): DynamicFormSection<CreateAdminFormValues>[] => [
{
id: adminSectionId,
title: "Input Fields",
fields: [
{
@@ -2,11 +2,15 @@ import { TextAreaGroup } from "@/components/FormElements/InputGroup/text-area";
import type { DynamicFormSection } from "@/components/forms/DynamicFormBuilder";
import { FormErrorMessages } from "@/constants/Texts";
import type { TCreateCompanyCategoryCredentials } from "@/lib/api";
import { v4 as uuidv4 } from "uuid";
const companyCategorySectionId = uuidv4();
export const createCompanyCategoryFormSections = (
editMode?: boolean,
): DynamicFormSection<TCreateCompanyCategoryCredentials>[] => [
{
id: companyCategorySectionId,
title: editMode ? "Edit Company Category" : "Create Company Category",
className: "gap-5",
fields: [
@@ -9,8 +9,16 @@ import { FormErrorMessages } from "@/constants/Texts";
import type { ICreateCompanyCredentials } from "@/lib/api";
import type { ILabelValue } from "@/types/shared";
import type { UseFormSetValue, UseFormWatch } from "react-hook-form";
import { v4 as uuidv4 } from "uuid";
import CompanyDocuments from "./documents/CompanyDocuments";
const companyInformationSectionId = uuidv4();
const businessInformationSectionId = uuidv4();
const settingsSectionId = uuidv4();
const addressSectionId = uuidv4();
const tagsSectionId = uuidv4();
const documentsSectionId = uuidv4();
interface CompanyFormSectionsOptions {
editMode?: boolean;
id?: string;
@@ -79,6 +87,7 @@ export const createCompanyFormSections = ({
setValue,
}: CompanyFormSectionsOptions): DynamicFormSection<ICreateCompanyCredentials>[] => [
{
id: companyInformationSectionId,
title: "Company information",
rootClassName: "lg:col-span-2",
className: "gap-x-5 gap-y-5 sm:grid-cols-2 md:grid-cols-2",
@@ -235,6 +244,7 @@ export const createCompanyFormSections = ({
],
},
{
id: businessInformationSectionId,
title: "Business information",
className: "gap-x-5 gap-y-5 sm:grid-cols-2 md:grid-cols-2",
fields: [
@@ -276,6 +286,7 @@ export const createCompanyFormSections = ({
],
},
{
id: settingsSectionId,
title: "Settings",
className: "gap-x-5 gap-y-5 sm:grid-cols-2 md:grid-cols-2",
fields: [
@@ -323,6 +334,7 @@ export const createCompanyFormSections = ({
],
},
{
id: addressSectionId,
title: "Address",
className: "gap-x-5 gap-y-5 sm:grid-cols-2 md:grid-cols-2",
fields: [
@@ -395,6 +407,7 @@ export const createCompanyFormSections = ({
],
},
{
id: tagsSectionId,
title: "Tags",
className: "gap-x-5 gap-y-5 sm:grid-cols-2 md:grid-cols-2",
fields: [
@@ -479,6 +492,7 @@ export const createCompanyFormSections = ({
],
},
{
id: documentsSectionId,
title: "Documents",
rootClassName: "lg:col-span-2",
className: "md:grid-cols-1",
@@ -14,6 +14,9 @@ import type { ILabelValue } from "@/types/shared";
import { getEnumAsArray } from "@/utils/shared";
import type { Dispatch, SetStateAction } from "react";
import { Controller, type UseFormWatch } from "react-hook-form";
import { v4 as uuidv4 } from "uuid";
const customerSectionId = uuidv4();
interface CustomerFormSectionsOptions {
editMode?: boolean;
@@ -31,6 +34,7 @@ export const createCustomerFormSections = ({
setShowPassword,
}: CustomerFormSectionsOptions): DynamicFormSection<CreateCustomerCredentials>[] => [
{
id: customerSectionId,
title: "Create Customer",
className: "gap-5",
fields: [
@@ -8,6 +8,9 @@ import { FormErrorMessages } from "@/constants/Texts";
import type { TCreateNotificationCredentials } from "@/lib/api/types/NotificationHistory";
import type { ILabelValue } from "@/types/shared";
import type { ChangeEvent } from "react";
import { v4 as uuidv4 } from "uuid";
const notificationSectionId = uuidv4();
interface NotificationFormSectionsOptions {
recipient: ILabelValue[] | null;
@@ -37,6 +40,7 @@ export const createNotificationFormSections = ({
channels,
}: NotificationFormSectionsOptions): DynamicFormSection<TCreateNotificationCredentials>[] => [
{
id: notificationSectionId,
title: "Create Notification",
className: "gap-5",
fields: [
+3 -2
View File
@@ -4,8 +4,9 @@ export function Logo() {
return (
<Image
src="/images/main-logo.svg"
width={174}
height={32}
width={1217}
height={192}
className="h-auto w-[174px]"
alt="Opp lens logo"
role="presentation"
quality={100}