import { useState } from "react"; import { Controller, useForm } from "react-hook-form"; import type { z } from "zod"; import { BookerLayoutSelector } from "@calcom/features/settings/BookerLayoutSelector"; import ThemeLabel from "@calcom/features/settings/ThemeLabel"; import { getLayout } from "@calcom/features/settings/layouts/SettingsLayout"; import { APP_NAME } from "@calcom/lib/constants"; import { checkWCAGContrastColor } from "@calcom/lib/getBrandColours"; import { useHasPaidPlan } from "@calcom/lib/hooks/useHasPaidPlan"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { validateBookerLayouts } from "@calcom/lib/validateBookerLayouts"; import type { userMetadata } from "@calcom/prisma/zod-utils"; import { trpc } from "@calcom/trpc/react"; import { Alert, Button, ColorPicker, Form, Meta, showToast, SkeletonButton, SkeletonContainer, SkeletonText, Switch, UpgradeTeamsBadge, } from "@calcom/ui"; import PageWrapper from "@components/PageWrapper"; const SkeletonLoader = ({ title, description }: { title: string; description: string }) => { return (
); }; const AppearanceView = () => { const { t } = useLocale(); const utils = trpc.useContext(); const { data: user, isLoading } = trpc.viewer.me.useQuery(); const [darkModeError, setDarkModeError] = useState(false); const [lightModeError, setLightModeError] = useState(false); const { isLoading: isTeamPlanStatusLoading, hasPaidPlan } = useHasPaidPlan(); const formMethods = useForm({ defaultValues: { theme: user?.theme, brandColor: user?.brandColor || "#292929", darkBrandColor: user?.darkBrandColor || "#fafafa", hideBranding: user?.hideBranding, metadata: user?.metadata as z.infer, }, }); const selectedTheme = formMethods.watch("theme"); const selectedThemeIsDark = selectedTheme === "dark" || (selectedTheme === "" && typeof document !== "undefined" && document.documentElement.classList.contains("dark")); const { formState: { isSubmitting, isDirty }, reset, } = formMethods; const mutation = trpc.viewer.updateProfile.useMutation({ onSuccess: async (data) => { await utils.viewer.me.invalidate(); showToast(t("settings_updated_successfully"), "success"); reset(data); }, onError: (error) => { if (error.message) { showToast(error.message, "error"); } else { showToast(t("error_updating_settings"), "error"); } }, }); if (isLoading || isTeamPlanStatusLoading) return ; if (!user) return null; const isDisabled = isSubmitting || !isDirty; return (
{ const layoutError = validateBookerLayouts(values?.metadata?.defaultBookerLayouts || null); if (layoutError) throw new Error(t(layoutError)); mutation.mutate({ ...values, // Radio values don't support null as values, therefore we convert an empty string // back to null here. theme: values.theme || null, }); }}>

{t("theme")}

{t("theme_applies_note")}



{t("custom_brand_colors")}

{t("customize_your_brand_colors")}

(

{t("light_brand_color")}

{ if (!checkWCAGContrastColor("#ffffff", value)) { setLightModeError(true); } else { setLightModeError(false); } formMethods.setValue("brandColor", value, { shouldDirty: true }); }} />
)} /> (

{t("dark_brand_color")}

{ if (!checkWCAGContrastColor("#101010", value)) { setDarkModeError(true); } else { setDarkModeError(false); } formMethods.setValue("darkBrandColor", value, { shouldDirty: true }); }} />
)} />
{darkModeError ? (
) : null} {lightModeError ? (
) : null} {/* TODO future PR to preview brandColors */} {/* */}
( <>

{t("disable_cal_branding", { appName: APP_NAME })}

{t("removes_cal_branding", { appName: APP_NAME })}

formMethods.setValue("hideBranding", checked, { shouldDirty: true }) } checked={hasPaidPlan ? value : false} />
)} /> ); }; AppearanceView.getLayout = getLayout; AppearanceView.PageWrapper = PageWrapper; export default AppearanceView;