import Head from "next/head"; import { useRouter } from "next/router"; import type { FormEvent } from "react"; import { useEffect, useRef, useState } from "react"; import { Toaster } from "react-hot-toast"; import { v4 as uuidv4 } from "uuid"; import { sdkActionManager, useIsEmbed } from "@calcom/embed-core/embed-iframe"; import CustomBranding from "@calcom/lib/CustomBranding"; import classNames from "@calcom/lib/classNames"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import useTheme from "@calcom/lib/hooks/useTheme"; import { trpc } from "@calcom/trpc/react"; import type { AppGetServerSidePropsContext, AppPrisma } from "@calcom/types/AppGetServerSideProps"; import type { inferSSRProps } from "@calcom/types/inferSSRProps"; import { Button, showToast } from "@calcom/ui"; import FormInputFields from "../../components/FormInputFields"; import { getSerializableForm } from "../../lib/getSerializableForm"; import { processRoute } from "../../lib/processRoute"; import type { Response, Route } from "../../types/types"; function RoutingForm({ form, profile, ...restProps }: inferSSRProps) { const [customPageMessage, setCustomPageMessage] = useState(""); const formFillerIdRef = useRef(uuidv4()); const isEmbed = useIsEmbed(restProps.isEmbed); useTheme(profile.theme); // TODO: We might want to prevent spam from a single user by having same formFillerId across pageviews // But technically, a user can fill form multiple times due to any number of reasons and we currently can't differentiate b/w that. // - like a network error // - or he abandoned booking flow in between const formFillerId = formFillerIdRef.current; const decidedActionRef = useRef(); const router = useRouter(); const onSubmit = (response: Response) => { const decidedAction = processRoute({ form, response }); if (!decidedAction) { // FIXME: Make sure that when a form is created, there is always a fallback route and then remove this. alert("Define atleast 1 route"); return; } responseMutation.mutate({ formId: form.id, formFillerId, response: response, }); decidedActionRef.current = decidedAction; }; useEffect(() => { // Custom Page doesn't actually change Route, so fake it so that embed can adjust the scroll to make the content visible sdkActionManager?.fire("__routeChanged", {}); }, [customPageMessage]); const responseMutation = trpc.viewer.appRoutingForms.public.response.useMutation({ onSuccess: () => { const decidedAction = decidedActionRef.current; if (!decidedAction) { return; } //TODO: Maybe take action after successful mutation if (decidedAction.type === "customPageMessage") { setCustomPageMessage(decidedAction.value); } else if (decidedAction.type === "eventTypeRedirectUrl") { router.push(`/${decidedAction.value}`); } else if (decidedAction.type === "externalRedirectUrl") { window.location.href = decidedAction.value; } // showToast("Form submitted successfully! Redirecting now ...", "success"); }, onError: (e) => { if (e?.message) { return void showToast(e?.message, "error"); } if (e?.data?.code === "CONFLICT") { return void showToast("Form already submitted", "error"); } // showToast("Something went wrong", "error"); }, }); const [response, setResponse] = useState({}); const handleOnSubmit = (e: FormEvent) => { e.preventDefault(); onSubmit(response); }; const { t } = useLocale(); return (
{!customPageMessage ? ( <> {`${form.name} | Cal.com Forms`}

{form.name}

{form.description ? (

{form.description}

) : null}
) : (
{customPageMessage}
)}
); } export default function RoutingLink(props: inferSSRProps) { return ; } RoutingLink.isThemeSupported = true; export const getServerSideProps = async function getServerSideProps( context: AppGetServerSidePropsContext, prisma: AppPrisma ) { const { params } = context; if (!params) { return { notFound: true, }; } const formId = params.appPages[0]; if (!formId || params.appPages.length > 2) { return { notFound: true, }; } const isEmbed = params.appPages[1] === "embed"; const form = await prisma.app_RoutingForms_Form.findUnique({ where: { id: formId, }, include: { user: { select: { theme: true, brandColor: true, darkBrandColor: true, }, }, }, }); if (!form || form.disabled) { return { notFound: true, }; } return { props: { isEmbed, profile: { theme: form.user.theme, brandColor: form.user.brandColor, darkBrandColor: form.user.darkBrandColor, }, form: await getSerializableForm(form), }, }; };