fix: brand colors set by user not respected in embed (#17399)
Co-authored-by: Hariom <hariombalhara@gmail.com>
This commit is contained in:
@@ -17,7 +17,7 @@ import { useTimePreferences } from "@calcom/features/bookings/lib/timePreference
|
||||
import DatePicker from "@calcom/features/calendars/DatePicker";
|
||||
import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
|
||||
import { useSlotsForDate } from "@calcom/features/schedules/lib/use-schedule/useSlotsForDate";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import { APP_NAME, DEFAULT_LIGHT_BRAND_COLOR, DEFAULT_DARK_BRAND_COLOR } from "@calcom/lib/constants";
|
||||
import { weekdayToWeekIndex } from "@calcom/lib/date-fns";
|
||||
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
@@ -41,10 +41,17 @@ import {
|
||||
TimezoneSelect,
|
||||
} from "@calcom/ui";
|
||||
|
||||
import { buildCssVarsPerTheme } from "./lib/buildCssVarsPerTheme";
|
||||
import { getDimension } from "./lib/getDimension";
|
||||
import type { EmbedTabs, EmbedType, EmbedTypes, PreviewState } from "./types";
|
||||
|
||||
type EventType = RouterOutputs["viewer"]["eventTypes"]["get"]["eventType"] | undefined;
|
||||
type EmbedDialogProps = {
|
||||
types: EmbedTypes;
|
||||
tabs: EmbedTabs;
|
||||
eventTypeHideOptionDisabled: boolean;
|
||||
defaultBrandColor: { brandColor: string | null; darkBrandColor: string | null } | null;
|
||||
};
|
||||
|
||||
const enum Theme {
|
||||
auto = "auto",
|
||||
@@ -525,13 +532,12 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
namespace,
|
||||
eventTypeHideOptionDisabled,
|
||||
types,
|
||||
}: {
|
||||
defaultBrandColor,
|
||||
}: EmbedDialogProps & {
|
||||
embedType: EmbedType;
|
||||
embedUrl: string;
|
||||
tabs: EmbedTabs;
|
||||
namespace: string;
|
||||
eventTypeHideOptionDisabled: boolean;
|
||||
types: EmbedTypes;
|
||||
}) => {
|
||||
const { t } = useLocale();
|
||||
const searchParams = useCompatSearchParams();
|
||||
@@ -578,6 +584,19 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
const defaultConfig = {
|
||||
layout: BookerLayouts.MONTH_VIEW,
|
||||
};
|
||||
|
||||
const paletteDefaultValue = (paletteName: string) => {
|
||||
if (paletteName === "brandColor") {
|
||||
return defaultBrandColor?.brandColor ?? DEFAULT_LIGHT_BRAND_COLOR;
|
||||
}
|
||||
|
||||
if (paletteName === "darkBrandColor") {
|
||||
return defaultBrandColor?.darkBrandColor ?? DEFAULT_DARK_BRAND_COLOR;
|
||||
}
|
||||
|
||||
return "#000000";
|
||||
};
|
||||
|
||||
const [previewState, setPreviewState] = useState<PreviewState>({
|
||||
inline: {
|
||||
width: "100%",
|
||||
@@ -594,7 +613,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
} as PreviewState["elementClick"],
|
||||
hideEventTypeDetails: false,
|
||||
palette: {
|
||||
brandColor: "#000000",
|
||||
brandColor: defaultBrandColor?.brandColor ?? null,
|
||||
darkBrandColor: defaultBrandColor?.darkBrandColor ?? null,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -614,7 +634,7 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const addToPalette = (update: (typeof previewState)["palette"]) => {
|
||||
const addToPalette = (update: Partial<(typeof previewState)["palette"]>) => {
|
||||
setPreviewState((previewState) => {
|
||||
return {
|
||||
...previewState,
|
||||
@@ -657,11 +677,10 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
theme: previewState.theme,
|
||||
layout: previewState.layout,
|
||||
hideEventTypeDetails: previewState.hideEventTypeDetails,
|
||||
styles: {
|
||||
branding: {
|
||||
...previewState.palette,
|
||||
},
|
||||
},
|
||||
cssVarsPerTheme: buildCssVarsPerTheme({
|
||||
brandColor: previewState.palette.brandColor,
|
||||
darkBrandColor: previewState.palette.darkBrandColor,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -994,7 +1013,8 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
</div>
|
||||
) : null}
|
||||
{[
|
||||
{ name: "brandColor", title: "Brand Color" },
|
||||
{ name: "brandColor", title: "light_brand_color" },
|
||||
{ name: "darkBrandColor", title: "dark_brand_color" },
|
||||
// { name: "lightColor", title: "Light Color" },
|
||||
// { name: "lighterColor", title: "Lighter Color" },
|
||||
// { name: "lightestColor", title: "Lightest Color" },
|
||||
@@ -1002,12 +1022,12 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
// { name: "medianColor", title: "Median Color" },
|
||||
].map((palette) => (
|
||||
<Label key={palette.name} className="mb-6">
|
||||
<div className="mb-2">{palette.title}</div>
|
||||
<div className="mb-2">{t(palette.title)}</div>
|
||||
<div className="w-full">
|
||||
<ColorPicker
|
||||
popoverAlign="start"
|
||||
container={dialogContentRef?.current ?? undefined}
|
||||
defaultValue="#000000"
|
||||
defaultValue={paletteDefaultValue(palette.name)}
|
||||
onChange={(color) => {
|
||||
addToPalette({
|
||||
[palette.name as keyof (typeof previewState)["palette"]]: color,
|
||||
@@ -1164,11 +1184,8 @@ export const EmbedDialog = ({
|
||||
types,
|
||||
tabs,
|
||||
eventTypeHideOptionDisabled,
|
||||
}: {
|
||||
types: EmbedTypes;
|
||||
tabs: EmbedTabs;
|
||||
eventTypeHideOptionDisabled: boolean;
|
||||
}) => {
|
||||
defaultBrandColor,
|
||||
}: EmbedDialogProps) => {
|
||||
const searchParams = useCompatSearchParams();
|
||||
const embedUrl = (searchParams?.get("embedUrl") || "") as string;
|
||||
const namespace = (searchParams?.get("namespace") || "") as string;
|
||||
@@ -1184,6 +1201,7 @@ export const EmbedDialog = ({
|
||||
tabs={tabs}
|
||||
types={types}
|
||||
eventTypeHideOptionDisabled={eventTypeHideOptionDisabled}
|
||||
defaultBrandColor={defaultBrandColor}
|
||||
/>
|
||||
)}
|
||||
</Dialog>
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
import type { ComponentProps } from "react";
|
||||
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
import { EmbedButton, EmbedDialog } from "./Embed";
|
||||
import { tabs } from "./lib/EmbedTabs";
|
||||
import { useEmbedTypes } from "./lib/hooks";
|
||||
|
||||
export const EventTypeEmbedDialog = () => {
|
||||
const types = useEmbedTypes();
|
||||
const { data: user } = trpc.viewer.me.useQuery();
|
||||
|
||||
return <EmbedDialog types={types} tabs={tabs} eventTypeHideOptionDisabled={false} />;
|
||||
return (
|
||||
<EmbedDialog
|
||||
types={types}
|
||||
tabs={tabs}
|
||||
eventTypeHideOptionDisabled={false}
|
||||
defaultBrandColor={user ? { brandColor: user.brandColor, darkBrandColor: user.darkBrandColor } : null}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const EventTypeEmbedButton = (props: ComponentProps<typeof EmbedButton>) => {
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
import type { ComponentProps } from "react";
|
||||
|
||||
import { EmbedDialog, EmbedButton } from "@calcom/features/embed/Embed";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
import { tabs } from "./lib/EmbedTabs";
|
||||
import { useEmbedTypes } from "./lib/hooks";
|
||||
|
||||
export const RoutingFormEmbedDialog = () => {
|
||||
const types = useEmbedTypes();
|
||||
const { data: user } = trpc.viewer.me.useQuery();
|
||||
const routingFormTypes = types.filter((type) => type.type !== "email");
|
||||
return <EmbedDialog types={routingFormTypes} tabs={tabs} eventTypeHideOptionDisabled={true} />;
|
||||
return (
|
||||
<EmbedDialog
|
||||
types={routingFormTypes}
|
||||
tabs={tabs}
|
||||
eventTypeHideOptionDisabled={true}
|
||||
defaultBrandColor={user ? { brandColor: user.brandColor, darkBrandColor: user.darkBrandColor } : null}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const RoutingFormEmbedButton = (props: ComponentProps<typeof EmbedButton>) => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { TextArea } from "@calcom/ui";
|
||||
|
||||
import type { EmbedFramework, EmbedType, PreviewState } from "../types";
|
||||
import { Codes } from "./EmbedCodes";
|
||||
import { buildCssVarsPerTheme } from "./buildCssVarsPerTheme";
|
||||
import { embedLibUrl, EMBED_PREVIEW_HTML_URL } from "./constants";
|
||||
import { getApiNameForReactSnippet, getApiNameForVanillaJsSnippet } from "./getApiName";
|
||||
import { getDimension } from "./getDimension";
|
||||
@@ -175,25 +176,27 @@ const getEmbedTypeSpecificString = ({
|
||||
let uiInstructionStringArg: {
|
||||
apiName: string;
|
||||
theme: PreviewState["theme"];
|
||||
brandColor: string;
|
||||
brandColor: string | null;
|
||||
darkBrandColor: string | null;
|
||||
hideEventTypeDetails: boolean;
|
||||
layout?: BookerLayout;
|
||||
};
|
||||
const baseUiInstructionStringArg = {
|
||||
theme: previewState.theme,
|
||||
brandColor: previewState.palette.brandColor,
|
||||
darkBrandColor: previewState.palette.darkBrandColor,
|
||||
hideEventTypeDetails: previewState.hideEventTypeDetails,
|
||||
layout: previewState.layout,
|
||||
};
|
||||
if (embedFramework === "react") {
|
||||
uiInstructionStringArg = {
|
||||
...baseUiInstructionStringArg,
|
||||
apiName: getApiNameForReactSnippet({ mainApiName: "cal" }),
|
||||
theme: previewState.theme,
|
||||
brandColor: previewState.palette.brandColor,
|
||||
hideEventTypeDetails: previewState.hideEventTypeDetails,
|
||||
layout: previewState.layout,
|
||||
};
|
||||
} else {
|
||||
uiInstructionStringArg = {
|
||||
...baseUiInstructionStringArg,
|
||||
apiName: getApiNameForVanillaJsSnippet({ namespace, mainApiName: "Cal" }),
|
||||
theme: previewState.theme,
|
||||
brandColor: previewState.palette.brandColor,
|
||||
hideEventTypeDetails: previewState.hideEventTypeDetails,
|
||||
layout: previewState.layout,
|
||||
};
|
||||
}
|
||||
if (!frameworkCodes[embedType]) {
|
||||
@@ -230,27 +233,26 @@ const getEmbedUIInstructionString = ({
|
||||
apiName,
|
||||
theme,
|
||||
brandColor,
|
||||
darkBrandColor,
|
||||
hideEventTypeDetails,
|
||||
layout,
|
||||
}: {
|
||||
apiName: string;
|
||||
theme?: string;
|
||||
brandColor: string;
|
||||
brandColor: string | null;
|
||||
darkBrandColor: string | null;
|
||||
hideEventTypeDetails: boolean;
|
||||
layout?: string;
|
||||
}) => {
|
||||
theme = theme !== "auto" ? theme : undefined;
|
||||
|
||||
return getInstructionString({
|
||||
apiName,
|
||||
instructionName: "ui",
|
||||
instructionArg: {
|
||||
theme,
|
||||
styles: {
|
||||
branding: {
|
||||
brandColor,
|
||||
},
|
||||
},
|
||||
hideEventTypeDetails: hideEventTypeDetails,
|
||||
cssVarsPerTheme: buildCssVarsPerTheme({ brandColor, darkBrandColor }),
|
||||
hideEventTypeDetails,
|
||||
layout,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
function _buildCssVarsPerTheme({
|
||||
light,
|
||||
dark,
|
||||
}: {
|
||||
light: { "cal-brand": string | null };
|
||||
dark: { "cal-brand": string | null };
|
||||
}) {
|
||||
// Setting this value should remove it from the API
|
||||
const VALUE_WHEN_WE_DONT_WANT_IT_IN_API = undefined;
|
||||
const cssVarsPerTheme = Object.entries({ light, dark }).reduce((acc, [theme, themeCssVars]) => {
|
||||
if (!Object.values(themeCssVars).some(Boolean)) return acc;
|
||||
|
||||
const truthyValues = Object.fromEntries(
|
||||
Object.entries(themeCssVars).filter(([_, value]) => Boolean(value))
|
||||
);
|
||||
|
||||
return {
|
||||
...acc,
|
||||
[theme]: truthyValues,
|
||||
};
|
||||
}, {});
|
||||
|
||||
if (Object.keys(cssVarsPerTheme).length === 0) return VALUE_WHEN_WE_DONT_WANT_IT_IN_API;
|
||||
|
||||
return cssVarsPerTheme;
|
||||
}
|
||||
|
||||
export function buildCssVarsPerTheme({
|
||||
brandColor,
|
||||
darkBrandColor,
|
||||
}: {
|
||||
brandColor: string | null;
|
||||
darkBrandColor: string | null;
|
||||
}) {
|
||||
return _buildCssVarsPerTheme({
|
||||
light: { "cal-brand": brandColor },
|
||||
dark: { "cal-brand": darkBrandColor },
|
||||
});
|
||||
}
|
||||
+2
-1
@@ -36,7 +36,8 @@ export type PreviewState = {
|
||||
"element-click"
|
||||
>;
|
||||
palette: {
|
||||
brandColor: string;
|
||||
brandColor: string | null;
|
||||
darkBrandColor: string | null;
|
||||
};
|
||||
hideEventTypeDetails: boolean;
|
||||
layout: BookerLayouts;
|
||||
|
||||
Reference in New Issue
Block a user