chore: migrate /user embed pages (#19149)

* add user pages to config.matcher in middleware

* add app router user booking pages

* remove HeadSeo from users-public-view component

* remove pages router user booking pages

* fix type check

* fix embed

* fix test

* chore: remove BookerSEO from users-type-public-view

* fix

* migrate all /user embed pages to app router

* remove /future routes

* fix prefill not working with preload

* move to (booking-page-wrapper) route group

---------

Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
This commit is contained in:
Benny Joo
2025-02-26 11:50:27 +01:00
committed by GitHub
co-authored by Hariom Balhara
parent 8741714402
commit caaecf2f94
6 changed files with 95 additions and 45 deletions
@@ -0,0 +1,19 @@
import withEmbedSsrAppDir from "app/WithEmbedSSR";
import type { PageProps as ServerPageProps } from "app/_types";
import { cookies, headers } from "next/headers";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@server/lib/[user]/[type]/getServerSideProps";
import TypePage, { type PageProps as ClientPageProps } from "~/users/views/users-type-public-view";
const getData = withEmbedSsrAppDir<ClientPageProps>(getServerSideProps);
const ServerPage = async ({ params, searchParams }: ServerPageProps) => {
const context = buildLegacyCtx(headers(), cookies(), params, searchParams);
const props = await getData(context);
return <TypePage {...props} />;
};
export default ServerPage;
@@ -0,0 +1,19 @@
import withEmbedSsrAppDir from "app/WithEmbedSSR";
import type { PageProps as ServerPageProps } from "app/_types";
import { cookies, headers } from "next/headers";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@server/lib/[user]/getServerSideProps";
import User, { type PageProps as ClientPageProps } from "~/users/views/users-public-view";
const getData = withEmbedSsrAppDir<ClientPageProps>(getServerSideProps);
const ServerPage = async ({ params, searchParams }: ServerPageProps) => {
const context = buildLegacyCtx(headers(), cookies(), params, searchParams);
const props = await getData(context);
return <User {...props} />;
};
export default ServerPage;
-16
View File
@@ -1,16 +0,0 @@
import withEmbedSsr from "@lib/withEmbedSsr";
import PageWrapper from "@components/PageWrapper";
import { getServerSideProps as _getServerSideProps } from "@server/lib/[user]/[type]/getServerSideProps";
import type { PageProps } from "~/users/views/users-type-public-view";
import TypePage from "~/users/views/users-type-public-view";
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
const Type = (props: PageProps) => <TypePage {...props} />;
Type.PageWrapper = PageWrapper;
export default Type;
-15
View File
@@ -1,15 +0,0 @@
import withEmbedSsr from "@lib/withEmbedSsr";
import PageWrapper from "@components/PageWrapper";
import { getServerSideProps as _getServerSideProps } from "@server/lib/[user]/getServerSideProps";
import User, { type PageProps } from "~/users/views/users-public-view";
export const getServerSideProps = withEmbedSsr(_getServerSideProps);
const UserPage = (props: PageProps) => <User {...props} />;
UserPage.PageWrapper = PageWrapper;
export default UserPage;
@@ -64,7 +64,7 @@ export const useBookingForm = ({
};
const isRescheduling = !!rescheduleUid && !!bookingData;
const { initialValues, key } = useInitialFormValues({
const { values: initialValues, key } = useInitialFormValues({
eventType: event,
rescheduleUid,
isRescheduling,
@@ -24,6 +24,34 @@ type UseInitialFormValuesProps = {
lastBookingResponse?: Record<string, string>;
};
// Add this stable hash function
function getStableHash(obj: Record<string, string | string[]>) {
return Object.entries(obj)
.sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
.map(([key, value]) => {
if (Array.isArray(value)) {
return `${key}:${value.sort().join(",")}`;
}
return `${key}:${value}`;
})
.join("|");
}
const buildKey = ({
values,
hasSession,
stableHashExtraOptions,
}: {
values: Record<string, any>;
hasSession: boolean;
stableHashExtraOptions: string;
}) => {
// We could think about removing specific items like values.length, hasSession and bookingId and instead use a stableHash maybe .
return `${Object.keys(values).length}_${hasSession ? 1 : 0}_${
values.bookingId ?? 0
}_${stableHashExtraOptions}`;
};
export function useInitialFormValues({
eventType,
rescheduleUid,
@@ -36,16 +64,27 @@ export function useInitialFormValues({
prefillFormParams,
lastBookingResponse,
}: UseInitialFormValuesProps) {
const [initialValues, setDefaultValues] = useState<{
responses?: Partial<z.infer<ReturnType<typeof getBookingResponsesSchema>>>;
bookingId?: number;
}>({});
const stableHashExtraOptions = getStableHash(extraOptions);
const [initialValuesState, setInitialValuesState] = useState<{
values: {
responses?: Partial<z.infer<ReturnType<typeof getBookingResponsesSchema>>>;
bookingId?: number;
};
key: string;
}>({
values: {},
key: "",
});
const bookingData = useBookerStore((state) => state.bookingData);
const formValues = useBookerStore((state) => state.formValues);
useEffect(() => {
(async function () {
if (Object.keys(formValues).length) {
setDefaultValues(formValues);
setInitialValuesState({
values: formValues,
key: buildKey({ values: formValues, hasSession, stableHashExtraOptions }),
});
return;
}
@@ -99,7 +138,10 @@ export function useInitialFormValues({
email: defaultUserValues.email ?? "",
};
setDefaultValues(defaults);
setInitialValuesState({
values: defaults,
key: buildKey({ values: defaults, hasSession, stableHashExtraOptions }),
});
}
if (!rescheduleUid && !bookingData) {
@@ -124,9 +166,12 @@ export function useInitialFormValues({
name: defaultUserValues.name,
email: defaultUserValues.email ?? "",
};
setDefaultValues(defaults);
setInitialValuesState({
values: defaults,
key: buildKey({ values: defaults, hasSession, stableHashExtraOptions }),
});
})();
// do not add extraOptions as a dependency, it will cause infinite loop
// TODO: Remove it. It was initially added so that we don't add extraOptions in deps but that is handled using stableHashExtraOptions now.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
eventType?.bookingFields,
@@ -139,11 +184,9 @@ export function useInitialFormValues({
name,
username,
prefillFormParams,
// We need to have extraOptions as a dep so that any change in query params can update the form values, but extraOptions itself isn't stable and changes reference on every render
stableHashExtraOptions,
]);
// When initialValues is available(after doing async schema parsing) or session is available(so that we can prefill logged-in user email and name), we need to reset the form with the initialValues
// We also need the key to change if the bookingId changes, so that the form is reset and rerendered with the new initialValues
const key = `${Object.keys(initialValues).length}_${hasSession ? 1 : 0}_${initialValues?.bookingId ?? 0}`;
return { initialValues, key };
return initialValuesState;
}