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 { 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 */} {/* window.open(`${WEBAPP_URL}/${user.username}/${user.eventTypes[0].title}`, "_blank")}> Preview */} ( <> {t("disable_cal_branding", { appName: APP_NAME })} {t("removes_cal_branding", { appName: APP_NAME })} formMethods.setValue("hideBranding", checked, { shouldDirty: true }) } checked={hasPaidPlan ? value : false} /> > )} /> {t("update")} ); }; AppearanceView.getLayout = getLayout; AppearanceView.PageWrapper = PageWrapper; export default AppearanceView;
{t("theme")}
{t("theme_applies_note")}
{t("custom_brand_colors")}
{t("customize_your_brand_colors")}
{t("light_brand_color")}
{t("dark_brand_color")}
{t("disable_cal_branding", { appName: APP_NAME })}
{t("removes_cal_branding", { appName: APP_NAME })}