fix: resolve hydration mismatch in analytics app installation (#23877)
- Wrap ConfigureStepCard content with NoSSR component - Prevents blank step 3 configuration page in production - Fixes hydration mismatch between server and client rendering - Ensures analytics apps (GTM, Fathom, GA4) installation works properly - Uses Cal.com's established NoSSR pattern for client-side only rendering Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Devin AI
parent
c2a44e9319
commit
81bd8033a9
@@ -7,6 +7,7 @@ import { useFieldArray, useFormContext } from "react-hook-form";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
|
||||
import NoSSR from "@calcom/lib/components/NoSSR";
|
||||
import { locationsResolver } from "@calcom/lib/event-types/utils/locationsResolver";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { LocationObject } from "@calcom/lib/location";
|
||||
@@ -188,7 +189,7 @@ const EventTypeGroup = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const ConfigureStepCard: FC<ConfigureStepCardProps> = (props) => {
|
||||
const ConfigureStepCardContent: FC<ConfigureStepCardProps> = (props) => {
|
||||
const { loading, formPortalRef, handleSetUpLater } = props;
|
||||
const { t } = useLocale();
|
||||
const { control, watch } = useFormContext<TEventTypesForm>();
|
||||
@@ -221,60 +222,69 @@ export const ConfigureStepCard: FC<ConfigureStepCardProps> = (props) => {
|
||||
}
|
||||
}, [submit, allUpdated, mainForSubmitRef]);
|
||||
|
||||
return (
|
||||
formPortalRef?.current &&
|
||||
createPortal(
|
||||
<div className="mt-8">
|
||||
{fields.map((group, groupIndex) => (
|
||||
<div key={group.fieldId}>
|
||||
{eventTypeGroups[groupIndex].eventTypes.some((eventType) => eventType.selected === true) && (
|
||||
<div className="mb-2 mt-4 flex items-center">
|
||||
<Avatar
|
||||
alt=""
|
||||
imageSrc={group.image} // if no image, use default avatar
|
||||
size="md"
|
||||
className="inline-flex justify-center"
|
||||
/>
|
||||
<p className="text-subtle block pl-2">{group.slug}</p>
|
||||
</div>
|
||||
)}
|
||||
<EventTypeGroup
|
||||
groupIndex={groupIndex}
|
||||
setUpdatedEventTypesStatus={setUpdatedEventTypesStatus}
|
||||
submitRefs={submitRefs}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<button form="outer-event-type-form" type="submit" className="hidden" ref={mainForSubmitRef}>
|
||||
Save
|
||||
</button>
|
||||
<Button
|
||||
className="text-md mt-6 w-full justify-center"
|
||||
type="button"
|
||||
data-testid="configure-step-save"
|
||||
onClick={() => {
|
||||
submitRefs.current.forEach((ref) => ref?.click());
|
||||
setSubmit(true);
|
||||
}}
|
||||
loading={loading}>
|
||||
{t("save")}
|
||||
</Button>
|
||||
if (!formPortalRef?.current) {
|
||||
return null;
|
||||
}
|
||||
|
||||
<div className="flex w-full flex-row justify-center">
|
||||
<Button
|
||||
color="minimal"
|
||||
data-testid="set-up-later"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
handleSetUpLater();
|
||||
}}
|
||||
className="mt-8 cursor-pointer px-4 py-2 font-sans text-sm font-medium">
|
||||
{t("set_up_later")}
|
||||
</Button>
|
||||
return createPortal(
|
||||
<div className="mt-8">
|
||||
{fields.map((group, groupIndex) => (
|
||||
<div key={group.fieldId}>
|
||||
{eventTypeGroups[groupIndex].eventTypes.some((eventType) => eventType.selected === true) && (
|
||||
<div className="mb-2 mt-4 flex items-center">
|
||||
<Avatar
|
||||
alt=""
|
||||
imageSrc={group.image} // if no image, use default avatar
|
||||
size="md"
|
||||
className="inline-flex justify-center"
|
||||
/>
|
||||
<p className="text-subtle block pl-2">{group.slug}</p>
|
||||
</div>
|
||||
)}
|
||||
<EventTypeGroup
|
||||
groupIndex={groupIndex}
|
||||
setUpdatedEventTypesStatus={setUpdatedEventTypesStatus}
|
||||
submitRefs={submitRefs}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
formPortalRef?.current
|
||||
)
|
||||
))}
|
||||
<button form="outer-event-type-form" type="submit" className="hidden" ref={mainForSubmitRef}>
|
||||
Save
|
||||
</button>
|
||||
<Button
|
||||
className="text-md mt-6 w-full justify-center"
|
||||
type="button"
|
||||
data-testid="configure-step-save"
|
||||
onClick={() => {
|
||||
submitRefs.current.forEach((ref) => ref?.click());
|
||||
setSubmit(true);
|
||||
}}
|
||||
loading={loading}>
|
||||
{t("save")}
|
||||
</Button>
|
||||
|
||||
<div className="flex w-full flex-row justify-center">
|
||||
<Button
|
||||
color="minimal"
|
||||
data-testid="set-up-later"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
handleSetUpLater();
|
||||
}}
|
||||
className="mt-8 cursor-pointer px-4 py-2 font-sans text-sm font-medium">
|
||||
{t("set_up_later")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>,
|
||||
formPortalRef.current
|
||||
);
|
||||
};
|
||||
|
||||
export const ConfigureStepCard: FC<ConfigureStepCardProps> = (props) => {
|
||||
return (
|
||||
<NoSSR>
|
||||
<ConfigureStepCardContent {...props} />
|
||||
</NoSSR>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user