From 09b1643478f60ec4f997dd21d161874d52df1883 Mon Sep 17 00:00:00 2001 From: Peer Richelsen Date: Fri, 8 Aug 2025 08:18:59 +0200 Subject: [PATCH] chore: added headless routing link to embed (#22921) * added headless routing link to embed * Update apps/web/public/static/locales/en/common.json * Update apps/web/public/static/locales/en/common.json * fix: add missing headless embed type handlers to resolve TypeScript errors - Add headless handlers for react, react-atom, and HTML frameworks in EmbedCodes.tsx - Add headless case handling in EmbedTabs.tsx getEmbedTypeSpecificString function - Add embedCalOrigin parameter to HTML framework handlers for type consistency - All handlers return documentation comments directing users to headless routing docs Co-Authored-By: hariom@cal.com * temp: enable IS_CALCOM for localhost testing Co-Authored-By: hariom@cal.com * fix: change headless handlers to return null instead of documentation code Co-Authored-By: hariom@cal.com * revert: remove temporary IS_CALCOM localhost change Co-Authored-By: hariom@cal.com * refactor: remove unnecessary parameter destructuring from headless embed handlers - Headless handlers now take no parameters since they only return null - Simplified function signatures to avoid unused parameter warnings - Maintains existing functionality while improving code clarity Co-Authored-By: hariom@cal.com --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: hariom@cal.com --- apps/web/public/static/locales/en/common.json | 2 + packages/features/embed/Embed.tsx | 10 +++- packages/features/embed/RoutingFormEmbed.tsx | 56 ++++++++++++++++++- packages/features/embed/lib/EmbedCodes.tsx | 16 +++++- packages/features/embed/lib/EmbedTabs.tsx | 2 + packages/features/embed/types/index.d.ts | 2 +- 6 files changed, 82 insertions(+), 6 deletions(-) diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index e9e7a07c8d..c6d26045b9 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -2251,6 +2251,8 @@ "floating_button_trigger_modal": "Puts a floating button on your site that triggers a modal with your event type.", "pop_up_element_click": "Pop up via element click", "open_dialog_with_element_click": "Open your calendar as a dialog when someone clicks an element.", + "use_my_own_form": "Use my own form", + "use_our_headless_routing_api": "Use our headless routing API and use your own form for submissions.", "need_help_embedding": "Need help? See our guides for embedding Cal on Wix, Squarespace, or WordPress, check our common questions, or explore advanced embed options.", "book_my_cal": "Book my Cal", "first_name": "First name", diff --git a/packages/features/embed/Embed.tsx b/packages/features/embed/Embed.tsx index 7cbb32acf2..6feddac825 100644 --- a/packages/features/embed/Embed.tsx +++ b/packages/features/embed/Embed.tsx @@ -207,9 +207,13 @@ const ChooseEmbedTypesDialogContent = ({ key={index} data-testid={embed.type} onClick={() => { - gotoState({ - embedType: embed.type as EmbedType, - }); + if (embed.type === "headless") { + window.open("https://cal.com/help/routing/headless-routing", "_blank"); + } else { + gotoState({ + embedType: embed.type as EmbedType, + }); + } }}>
{embed.illustration} diff --git a/packages/features/embed/RoutingFormEmbed.tsx b/packages/features/embed/RoutingFormEmbed.tsx index c62de54b88..a1283aa16e 100644 --- a/packages/features/embed/RoutingFormEmbed.tsx +++ b/packages/features/embed/RoutingFormEmbed.tsx @@ -1,6 +1,8 @@ import type { ComponentProps } from "react"; import { EmbedDialog, EmbedButton } from "@calcom/features/embed/Embed"; +import { IS_CALCOM } from "@calcom/lib/constants"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; import { tabs } from "./lib/EmbedTabs"; @@ -8,11 +10,63 @@ import { useEmbedTypes } from "./lib/hooks"; export const RoutingFormEmbedDialog = () => { const types = useEmbedTypes(); + const { t } = useLocale(); const { data: user } = trpc.viewer.me.get.useQuery(); const routingFormTypes = types.filter((type) => type.type !== "email"); + + // Add the headless option specifically for routing forms + const headlessType = { + title: t("use_my_own_form"), + subtitle: t("use_our_headless_routing_api"), + type: "headless", + illustration: ( + + + + + + + + + + + + + + + + API + + + + + + + ), + }; + + const routingFormTypesWithHeadless = IS_CALCOM + ? [...routingFormTypes, headlessType] + : routingFormTypes; + return ( Click me; };`; }, + headless: () => { + return null; + }, }, "react-atom": { inline: ({ @@ -230,17 +233,22 @@ export default function Booker( props : BookerProps ) { ); };`; }, + headless: () => { + return null; + }, }, HTML: { inline: ({ calLink, uiInstructionCode, previewState, + embedCalOrigin, namespace, }: { calLink: string; uiInstructionCode: string; previewState: PreviewState["inline"]; + embedCalOrigin: string; namespace: string; }) => { return code`${getApiNameForVanillaJsSnippet({ namespace, mainApiName: "Cal" })}("inline", { @@ -251,16 +259,17 @@ export default function Booker( props : BookerProps ) { ${uiInstructionCode}`; }, - "floating-popup": ({ calLink, uiInstructionCode, previewState, + embedCalOrigin, namespace, }: { calLink: string; uiInstructionCode: string; previewState: PreviewState["floatingPopup"]; + embedCalOrigin: string; namespace: string; }) => { const floatingButtonArg = JSON.stringify({ @@ -277,11 +286,13 @@ export default function Booker( props : BookerProps ) { calLink, uiInstructionCode, previewState, + embedCalOrigin, namespace, }: { calLink: string; uiInstructionCode: string; previewState: PreviewState["elementClick"]; + embedCalOrigin: string; namespace: string; }) => { return code` @@ -292,6 +303,9 @@ export default function Booker( props : BookerProps ) { ${uiInstructionCode}`; }, + headless: () => { + return null; + }, }, }; diff --git a/packages/features/embed/lib/EmbedTabs.tsx b/packages/features/embed/lib/EmbedTabs.tsx index b7882d8047..6bda5cf3fb 100644 --- a/packages/features/embed/lib/EmbedTabs.tsx +++ b/packages/features/embed/lib/EmbedTabs.tsx @@ -283,6 +283,8 @@ const getEmbedTypeSpecificString = ({ ...codeGeneratorInput, previewState: previewState.elementClick, }); + } else if (embedType === "headless") { + return frameworkCodes[embedType](); } return ""; }; diff --git a/packages/features/embed/types/index.d.ts b/packages/features/embed/types/index.d.ts index 879bc347ce..f4f60709af 100644 --- a/packages/features/embed/types/index.d.ts +++ b/packages/features/embed/types/index.d.ts @@ -3,7 +3,7 @@ import type { Brand } from "@calcom/types/utils"; import type { tabs } from "../lib/EmbedTabs"; import type { useEmbedTypes } from "../lib/hooks"; -export type EmbedType = "inline" | "floating-popup" | "element-click" | "email"; +export type EmbedType = "inline" | "floating-popup" | "element-click" | "email" | "headless"; type EmbedConfig = { layout?: BookerLayouts; theme?: Theme;