From 4cbe8b678dbed8f04f126c942618ecc7d8583eea Mon Sep 17 00:00:00 2001 From: supalarry Date: Thu, 7 Aug 2025 16:12:35 +0200 Subject: [PATCH 01/12] feat: availability override classnames --- .../availability/AvailabilitySettings.tsx | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/packages/platform/atoms/availability/AvailabilitySettings.tsx b/packages/platform/atoms/availability/AvailabilitySettings.tsx index 2192a48b003f45..afe153f9292eba 100644 --- a/packages/platform/atoms/availability/AvailabilitySettings.tsx +++ b/packages/platform/atoms/availability/AvailabilitySettings.tsx @@ -68,6 +68,9 @@ export type CustomClassNames = { subtitlesClassName?: string; scheduleClassNames?: scheduleClassNames; overridesModalClassNames?: string; + dateOverrideClassNames?: { + container?: string; + }; hiddenSwitchClassname?: { container?: string; thumb?: string; @@ -180,6 +183,7 @@ const DateOverride = ({ travelSchedules, weekStart, overridesModalClassNames, + classNames, handleSubmit, }: { workingHours: WorkingHours[]; @@ -187,6 +191,9 @@ const DateOverride = ({ travelSchedules?: RouterOutputs["viewer"]["travelSchedules"]["get"]; weekStart: 0 | 1 | 2 | 3 | 4 | 5 | 6; overridesModalClassNames?: string; + classNames?: { + container?: string; + }; handleSubmit: (data: AvailabilityFormValues) => Promise; }) => { const { append, replace, fields } = useFieldArray({ @@ -202,7 +209,7 @@ const DateOverride = ({ }; return ( -
+

{t("date_overrides")}{" "} @@ -318,24 +325,27 @@ export const AvailabilitySettings = forwardRef void; onError?: (error: Error) => void }>({}); - const handleFormSubmit = useCallback((customCallbacks?: { onSuccess?: () => void; onError?: (error: Error) => void }) => { - if (customCallbacks) { - callbacksRef.current = customCallbacks; - } + const handleFormSubmit = useCallback( + (customCallbacks?: { onSuccess?: () => void; onError?: (error: Error) => void }) => { + if (customCallbacks) { + callbacksRef.current = customCallbacks; + } - if (saveButtonRef.current) { - saveButtonRef.current.click(); - } else { - form.handleSubmit(async (data) => { - try { - await handleSubmit(data); - callbacksRef.current?.onSuccess?.(); - } catch (error) { - callbacksRef.current?.onError?.(error as Error); - } - })(); - } - }, [form, handleSubmit]); + if (saveButtonRef.current) { + saveButtonRef.current.click(); + } else { + form.handleSubmit(async (data) => { + try { + await handleSubmit(data); + callbacksRef.current?.onSuccess?.(); + } catch (error) { + callbacksRef.current?.onError?.(error as Error); + } + })(); + } + }, + [form, handleSubmit] + ); const validateForm = useCallback(async () => { const isValid = await form.trigger(); @@ -661,6 +671,7 @@ export const AvailabilitySettings = forwardRef )}

From 6ede440bc46259b9156e822f7d0c3d7d505e6881 Mon Sep 17 00:00:00 2001 From: supalarry Date: Fri, 8 Aug 2025 11:16:32 +0200 Subject: [PATCH 02/12] feat: availability time picker styles --- .../schedules/components/Schedule.tsx | 32 +++++++++++++++++-- packages/platform/atoms/availability/types.ts | 7 ++++ .../examples/base/src/pages/availability.tsx | 20 +++++++++++- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/packages/features/schedules/components/Schedule.tsx b/packages/features/schedules/components/Schedule.tsx index 388de8f32d6a1f..9a6ad2a8d0c0c7 100644 --- a/packages/features/schedules/components/Schedule.tsx +++ b/packages/features/schedules/components/Schedule.tsx @@ -20,7 +20,6 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; import { weekdayNames } from "@calcom/lib/weekday"; import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery"; import type { TimeRange } from "@calcom/types/schedule"; - import cn from "@calcom/ui/classNames"; import { Button } from "@calcom/ui/components/button"; import { Dropdown, DropdownMenuContent, DropdownMenuTrigger } from "@calcom/ui/components/dropdown"; @@ -37,6 +36,14 @@ export type ScheduleLabelsType = { deleteTime: string; }; +export type SelectInnerClassNames = { + control?: string; + singleValue?: string; + valueContainer?: string; + input?: string; + menu?: string; +}; + export type FieldPathByValue = { [Key in FieldPath]: FieldPathValue extends TValue ? Key : never; }[FieldPath]; @@ -106,6 +113,7 @@ export const ScheduleDay = ({ classNames={{ dayRanges: classNames?.dayRanges, timeRangeField: classNames?.timeRangeField, + timePicker: classNames?.timePicker, }} /> {!disabled &&
{CopyButton}
} @@ -238,7 +246,7 @@ export const DayRanges = ({ disabled?: boolean; labels?: ScheduleLabelsType; userTimeFormat: number | null; - classNames?: Pick; + classNames?: Pick; }) => { const { t } = useLocale(); const { getValues } = useFormContext(); @@ -261,6 +269,7 @@ export const DayRanges = ({ )} @@ -336,11 +345,27 @@ const TimeRangeField = ({ onChange, disabled, userTimeFormat, + timePickerClassNames, }: { className?: string; disabled?: boolean; userTimeFormat: number | null; + timePickerClassNames?: { + container?: string; + value?: string; + valueContainer?: string; + input?: string; + dropdown?: string; + }; } & ControllerRenderProps) => { + const innerClassNames: SelectInnerClassNames = { + control: timePickerClassNames?.container, + singleValue: timePickerClassNames?.value, + valueContainer: timePickerClassNames?.valueContainer, + input: timePickerClassNames?.input, + menu: timePickerClassNames?.dropdown, + }; + // this is a controlled component anyway given it uses LazySelect, so keep it RHF agnostic. return (
@@ -350,6 +375,7 @@ const TimeRangeField = ({ isDisabled={disabled} value={value.start} menuPlacement="bottom" + innerClassNames={innerClassNames} onChange={(option) => { const newStart = new Date(option?.value as number); if (newStart >= new Date(value.end)) { @@ -368,6 +394,7 @@ const TimeRangeField = ({ isDisabled={disabled} value={value.end} min={value.start} + innerClassNames={innerClassNames} menuPlacement="bottom" onChange={(option) => { onChange({ ...value, end: new Date(option?.value as number) }); @@ -389,6 +416,7 @@ const LazySelect = ({ min?: ConfigType; max?: ConfigType; userTimeFormat: number | null; + innerClassNames?: SelectInnerClassNames; }) => { // Lazy-loaded options, otherwise adding a field has a noticeable redraw delay. const { options, filter } = useOptions(userTimeFormat); diff --git a/packages/platform/atoms/availability/types.ts b/packages/platform/atoms/availability/types.ts index 8cdba22a344ba2..caf96f2f8e6b92 100644 --- a/packages/platform/atoms/availability/types.ts +++ b/packages/platform/atoms/availability/types.ts @@ -28,6 +28,13 @@ export type scheduleClassNames = { timeRangeField?: string; labelAndSwitchContainer?: string; scheduleContainer?: string; + timePicker?: { + container?: string; + valueContainer?: string; + value?: string; + input?: string; + dropdown?: string; + }; }; export type AvailabilityFormValidationResult = { diff --git a/packages/platform/examples/base/src/pages/availability.tsx b/packages/platform/examples/base/src/pages/availability.tsx index 3477bd256f2bc8..e896b367f853b0 100644 --- a/packages/platform/examples/base/src/pages/availability.tsx +++ b/packages/platform/examples/base/src/pages/availability.tsx @@ -1,6 +1,6 @@ import { Navbar } from "@/components/Navbar"; import { Inter } from "next/font/google"; -import { useRef, useCallback, useState } from "react"; +import { useRef, useCallback } from "react"; import type { AvailabilitySettingsFormRef } from "@calcom/atoms"; import { AvailabilitySettings } from "@calcom/atoms"; @@ -57,6 +57,24 @@ export default function Availability(props: { calUsername: string; calEmail: str ctaClassName: "border p-4 rounded-md", editableHeadingClassName: "underline font-semibold", hiddenSwitchClassname: { thumb: "bg-red-500" }, + scheduleClassNames: { + schedule: "bg-blue-50 border-2 border-blue-200 rounded-lg p-4", + scheduleDay: "bg-green-50 border border-green-300 rounded-md mb-2", + dayRanges: "bg-yellow-50 p-3 rounded border-l-4 border-yellow-400", + timeRangeField: "!text-2xl bg-red-50 border-2 border-red-300 rounded-xl px-4 py-2", + labelAndSwitchContainer: "bg-purple-50 border border-purple-200 rounded p-2", + scheduleContainer: "bg-gray-100 border-4 border-gray-400 rounded-2xl shadow-lg", + timePicker: { + container: "!bg-blue-900", + valueContainer: "!bg-pink-500", + value: "!bg-yellow-500", + input: "!bg-green-500", + dropdown: "!bg-cyan-500", + }, + }, + dateOverrideClassNames: { + container: "p-0", + }, }} onFormStateChange={handleFormStateChange} onUpdateSuccess={() => { From 6bc892b5c042011dce3c78f79b96f0d4e9f4e599 Mon Sep 17 00:00:00 2001 From: supalarry Date: Fri, 8 Aug 2025 12:03:13 +0200 Subject: [PATCH 03/12] feat: calendar settings headers styles --- .../CalendarSettingsPlatformWrapper.tsx | 11 +++++++++- .../DestinationCalendar.tsx | 19 +++++++++++++----- ...inationCalendarSettingsPlatformWrapper.tsx | 4 ++++ ...lectedCalendarsSettingsPlatformWrapper.tsx | 20 +++++++++++++++++-- 4 files changed, 46 insertions(+), 8 deletions(-) diff --git a/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx b/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx index 917c25bdeae2d2..4a3dd1e821b7d6 100644 --- a/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx +++ b/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx @@ -1,12 +1,19 @@ +import type { DestinationHeaderClassnames } from "destination-calendar/DestinationCalendar"; + import { DestinationCalendarSettingsPlatformWrapper } from "../../destination-calendar/index"; import { SelectedCalendarsSettingsPlatformWrapper } from "../../selected-calendars/index"; -import type { CalendarRedirectUrls } from "../../selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper"; +import type { + CalendarRedirectUrls, + SelectedCalendarsHeaderClassnames, +} from "../../selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper"; type CalendarSettingsPlatformWrapperProps = { classNames?: { calendarSettingsCustomClassnames?: string; destinationCalendarSettingsCustomClassnames?: string; selectedCalendarSettingsCustomClassnames?: string; + selectedCalendarSettingsHeaderClassNames?: SelectedCalendarsHeaderClassnames; + destinationCalendarSettingsHeaderClassNames?: DestinationHeaderClassnames; }; calendarRedirectUrls?: CalendarRedirectUrls; allowDelete?: boolean; @@ -24,6 +31,7 @@ export const CalendarSettingsPlatformWrapper = ({ } classNames={classNames?.destinationCalendarSettingsCustomClassnames} + headerClassNames={classNames?.destinationCalendarSettingsHeaderClassNames} isDryRun={isDryRun} />
); diff --git a/packages/platform/atoms/destination-calendar/DestinationCalendar.tsx b/packages/platform/atoms/destination-calendar/DestinationCalendar.tsx index a024956e6ca4d3..b48504dd9b9bf1 100644 --- a/packages/platform/atoms/destination-calendar/DestinationCalendar.tsx +++ b/packages/platform/atoms/destination-calendar/DestinationCalendar.tsx @@ -5,12 +5,19 @@ import { cn } from "../src/lib/utils"; import type { DestinationCalendarProps } from "./DestinationCalendarSelector"; import { DestinationCalendarSelector } from "./DestinationCalendarSelector"; -export const DestinationCalendarSettings = (props: DestinationCalendarProps & { classNames?: string }) => { +export type DestinationHeaderClassnames = { + title?: string; + description?: string; +}; + +export const DestinationCalendarSettings = ( + props: DestinationCalendarProps & { classNames?: string; headerClassNames?: DestinationHeaderClassnames } +) => { const { t } = useLocale(); return (
- +
@@ -23,15 +30,17 @@ export const DestinationCalendarSettings = (props: DestinationCalendarProps & { ); }; -const DestinationCalendarSettingsHeading = () => { +const DestinationCalendarSettingsHeading = ({ classNames }: { classNames?: DestinationHeaderClassnames }) => { const { t } = useLocale(); return (
-

+

{t("add_to_calendar")}

-

{t("add_to_calendar_description")}

+

+ {t("add_to_calendar_description")} +

); }; diff --git a/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx b/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx index cc1db9571b21ab..6f34e2f7b8d8ed 100644 --- a/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx +++ b/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx @@ -1,15 +1,18 @@ import { useUpdateDestinationCalendars } from "../../hooks/calendars/useUpdateDestinationCalendars"; import { useConnectedCalendars } from "../../hooks/useConnectedCalendars"; import { AtomsWrapper } from "../../src/components/atoms-wrapper"; +import type { DestinationHeaderClassnames } from "../DestinationCalendar"; import { DestinationCalendarSettings } from "../DestinationCalendar"; export const DestinationCalendarSettingsPlatformWrapper = ({ statusLoader, classNames = "mx-5", + headerClassNames, isDryRun = false, }: { statusLoader?: JSX.Element; classNames?: string; + headerClassNames?: DestinationHeaderClassnames; isDryRun?: boolean; }) => { const calendars = useConnectedCalendars({}); @@ -35,6 +38,7 @@ export const DestinationCalendarSettingsPlatformWrapper = ({ { const { t } = useLocale(); const query = useConnectedCalendars({}); @@ -57,6 +60,7 @@ export const SelectedCalendarsSettingsPlatformWrapper = ({ return ( @@ -68,6 +72,7 @@ export const SelectedCalendarsSettingsPlatformWrapper = ({ return ( @@ -156,12 +161,19 @@ export const SelectedCalendarsSettingsPlatformWrapper = ({ ); }; +export type SelectedCalendarsHeaderClassnames = { + title?: string; + description?: string; +}; + const SelectedCalendarsSettingsHeading = ({ calendarRedirectUrls, isDryRun, + classNames, }: { calendarRedirectUrls?: CalendarRedirectUrls; isDryRun?: boolean; + classNames?: SelectedCalendarsHeaderClassnames; }) => { const { t } = useLocale(); @@ -169,8 +181,12 @@ const SelectedCalendarsSettingsHeading = ({
-

{t("check_for_conflicts")}

-

{t("select_calendars")}

+

+ {t("check_for_conflicts")} +

+

+ {t("select_calendars")} +

From 133114bc541de1d1b8e534116fcd48da45054141 Mon Sep 17 00:00:00 2001 From: supalarry Date: Fri, 8 Aug 2025 13:16:10 +0200 Subject: [PATCH 04/12] feat: destination and connected calendars --- .../CalendarSettingsPlatformWrapper.tsx | 12 +++--- .../DestinationCalendar.tsx | 20 ++++++--- ...inationCalendarSettingsPlatformWrapper.tsx | 8 ++-- ...lectedCalendarsSettingsPlatformWrapper.tsx | 43 +++++++++++++------ .../examples/base/src/pages/calendars.tsx | 23 +++++++++- 5 files changed, 76 insertions(+), 30 deletions(-) diff --git a/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx b/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx index 4a3dd1e821b7d6..00ae256c4dc93c 100644 --- a/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx +++ b/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx @@ -1,10 +1,10 @@ -import type { DestinationHeaderClassnames } from "destination-calendar/DestinationCalendar"; +import type { DestinationCalendarClassNames } from "destination-calendar/DestinationCalendar"; import { DestinationCalendarSettingsPlatformWrapper } from "../../destination-calendar/index"; import { SelectedCalendarsSettingsPlatformWrapper } from "../../selected-calendars/index"; import type { CalendarRedirectUrls, - SelectedCalendarsHeaderClassnames, + SelectedCalendarsClassNames, } from "../../selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper"; type CalendarSettingsPlatformWrapperProps = { @@ -12,8 +12,8 @@ type CalendarSettingsPlatformWrapperProps = { calendarSettingsCustomClassnames?: string; destinationCalendarSettingsCustomClassnames?: string; selectedCalendarSettingsCustomClassnames?: string; - selectedCalendarSettingsHeaderClassNames?: SelectedCalendarsHeaderClassnames; - destinationCalendarSettingsHeaderClassNames?: DestinationHeaderClassnames; + selectedCalendarSettingsClassNames?: SelectedCalendarsClassNames; + destinationCalendarSettingsClassNames?: DestinationCalendarClassNames; }; calendarRedirectUrls?: CalendarRedirectUrls; allowDelete?: boolean; @@ -31,7 +31,7 @@ export const CalendarSettingsPlatformWrapper = ({ } classNames={classNames?.destinationCalendarSettingsCustomClassnames} - headerClassNames={classNames?.destinationCalendarSettingsHeaderClassNames} + classNamesObject={classNames?.destinationCalendarSettingsClassNames} isDryRun={isDryRun} />
); diff --git a/packages/platform/atoms/destination-calendar/DestinationCalendar.tsx b/packages/platform/atoms/destination-calendar/DestinationCalendar.tsx index b48504dd9b9bf1..00907c8da34e8a 100644 --- a/packages/platform/atoms/destination-calendar/DestinationCalendar.tsx +++ b/packages/platform/atoms/destination-calendar/DestinationCalendar.tsx @@ -5,19 +5,29 @@ import { cn } from "../src/lib/utils"; import type { DestinationCalendarProps } from "./DestinationCalendarSelector"; import { DestinationCalendarSelector } from "./DestinationCalendarSelector"; -export type DestinationHeaderClassnames = { +type DestinationHeaderClassnames = { + container?: string; title?: string; description?: string; }; +export type DestinationCalendarClassNames = { + container?: string; + header?: DestinationHeaderClassnames; +}; + export const DestinationCalendarSettings = ( - props: DestinationCalendarProps & { classNames?: string; headerClassNames?: DestinationHeaderClassnames } + props: DestinationCalendarProps & { classNames?: string; classNamesObject?: DestinationCalendarClassNames } ) => { const { t } = useLocale(); return ( -
- +
+
@@ -34,7 +44,7 @@ const DestinationCalendarSettingsHeading = ({ classNames }: { classNames?: Desti const { t } = useLocale(); return ( -
+

{t("add_to_calendar")}

diff --git a/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx b/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx index 6f34e2f7b8d8ed..402d69b80b7dad 100644 --- a/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx +++ b/packages/platform/atoms/destination-calendar/wrappers/DestinationCalendarSettingsPlatformWrapper.tsx @@ -1,18 +1,18 @@ import { useUpdateDestinationCalendars } from "../../hooks/calendars/useUpdateDestinationCalendars"; import { useConnectedCalendars } from "../../hooks/useConnectedCalendars"; import { AtomsWrapper } from "../../src/components/atoms-wrapper"; -import type { DestinationHeaderClassnames } from "../DestinationCalendar"; +import type { DestinationCalendarClassNames } from "../DestinationCalendar"; import { DestinationCalendarSettings } from "../DestinationCalendar"; export const DestinationCalendarSettingsPlatformWrapper = ({ statusLoader, classNames = "mx-5", - headerClassNames, + classNamesObject, isDryRun = false, }: { statusLoader?: JSX.Element; classNames?: string; - headerClassNames?: DestinationHeaderClassnames; + classNamesObject?: DestinationCalendarClassNames; isDryRun?: boolean; }) => { const calendars = useConnectedCalendars({}); @@ -38,7 +38,7 @@ export const DestinationCalendarSettingsPlatformWrapper = ({ { const { t } = useLocale(); const query = useConnectedCalendars({}); @@ -58,21 +68,27 @@ export const SelectedCalendarsSettingsPlatformWrapper = ({ if (!data.connectedCalendars.length) { return ( - + -

No connected calendars found.

+

+ No connected calendars found. +

); } return ( - + @@ -161,11 +177,6 @@ export const SelectedCalendarsSettingsPlatformWrapper = ({ ); }; -export type SelectedCalendarsHeaderClassnames = { - title?: string; - description?: string; -}; - const SelectedCalendarsSettingsHeading = ({ calendarRedirectUrls, isDryRun, @@ -173,12 +184,16 @@ const SelectedCalendarsSettingsHeading = ({ }: { calendarRedirectUrls?: CalendarRedirectUrls; isDryRun?: boolean; - classNames?: SelectedCalendarsHeaderClassnames; + classNames?: { + container?: string; + title?: string; + description?: string; + }; }) => { const { t } = useLocale(); return ( -
+

diff --git a/packages/platform/examples/base/src/pages/calendars.tsx b/packages/platform/examples/base/src/pages/calendars.tsx index 4aded1786b2930..87b5d197d93342 100644 --- a/packages/platform/examples/base/src/pages/calendars.tsx +++ b/packages/platform/examples/base/src/pages/calendars.tsx @@ -10,7 +10,28 @@ export default function Calendars(props: { calUsername: string; calEmail: string
- +
); From 0f5ce0ce8e30a72aff11be5000e82c8fd08f0227 Mon Sep 17 00:00:00 2001 From: supalarry Date: Mon, 11 Aug 2025 12:25:16 +0200 Subject: [PATCH 05/12] feat: date override title,description,button --- .../availability/AvailabilitySettings.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/platform/atoms/availability/AvailabilitySettings.tsx b/packages/platform/atoms/availability/AvailabilitySettings.tsx index afe153f9292eba..051c4d46194e15 100644 --- a/packages/platform/atoms/availability/AvailabilitySettings.tsx +++ b/packages/platform/atoms/availability/AvailabilitySettings.tsx @@ -70,6 +70,9 @@ export type CustomClassNames = { overridesModalClassNames?: string; dateOverrideClassNames?: { container?: string; + title?: string; + description?: string; + button?: string; }; hiddenSwitchClassname?: { container?: string; @@ -193,6 +196,9 @@ const DateOverride = ({ overridesModalClassNames?: string; classNames?: { container?: string; + title?: string; + description?: string; + button?: string; }; handleSubmit: (data: AvailabilityFormValues) => Promise; }) => { @@ -210,7 +216,7 @@ const DateOverride = ({ return (
-

+

{t("date_overrides")}{" "} @@ -218,7 +224,9 @@ const DateOverride = ({

-

{t("date_overrides_subtitle")}

+

+ {t("date_overrides_subtitle")} +

+ } From f2b0627b5d4b3360ba306feeb5bc62607b6915c8 Mon Sep 17 00:00:00 2001 From: supalarry Date: Mon, 11 Aug 2025 12:26:57 +0200 Subject: [PATCH 06/12] feat: selected calendars list --- ...lectedCalendarsSettingsPlatformWrapper.tsx | 48 +++++++++++++++++-- .../components/app-list-card/AppListCard.tsx | 23 +++++++-- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx b/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx index 616f679956561e..c6bf73f33472d1 100644 --- a/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx +++ b/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx @@ -37,6 +37,21 @@ export type SelectedCalendarsClassNames = { title?: string; description?: string; }; + selectedCalendarsListClassNames?: { + container?: string; + selectedCalendar: { + container?: string; + header?: { + container: string; + title?: string; + description?: string; + }; + body?: { + container?: string; + description?: string; + }; + }; + }; noSelectedCalendarsMessage?: string; }; @@ -92,7 +107,9 @@ export const SelectedCalendarsSettingsPlatformWrapper = ({ calendarRedirectUrls={calendarRedirectUrls} isDryRun={isDryRun} /> - + {data.connectedCalendars.map((connectedCalendar) => { if (!!connectedCalendar.calendars && connectedCalendar.calendars.length > 0) { return ( @@ -105,7 +122,18 @@ export const SelectedCalendarsSettingsPlatformWrapper = ({ description={ connectedCalendar.primary?.email ?? connectedCalendar.integration.description } - className="border-subtle mt-4 rounded-lg border" + classNameObject={{ + container: cn( + "border-subtle mt-4 rounded-lg border", + classNamesObject?.selectedCalendarsListClassNames?.selectedCalendar?.container + ), + title: + classNamesObject?.selectedCalendarsListClassNames?.selectedCalendar?.header + ?.title, + description: + classNamesObject?.selectedCalendarsListClassNames?.selectedCalendar?.header + ?.description, + }} actions={
{allowDelete && !connectedCalendar.delegationCredentialId && ( @@ -119,8 +147,20 @@ export const SelectedCalendarsSettingsPlatformWrapper = ({ )}
}> -
-

{t("toggle_calendars_conflict")}

+
+

+ {t("toggle_calendars_conflict")} +

    {connectedCalendar.calendars?.map((cal) => { return ( diff --git a/packages/ui/components/app-list-card/AppListCard.tsx b/packages/ui/components/app-list-card/AppListCard.tsx index 78df7ba8292a61..e770d394d06fd3 100644 --- a/packages/ui/components/app-list-card/AppListCard.tsx +++ b/packages/ui/components/app-list-card/AppListCard.tsx @@ -22,6 +22,12 @@ type ShouldHighlight = slug?: never; }; +export type AppCardClassNames = { + container: string; + title?: string; + description?: string; +}; + export type AppListCardProps = { logo?: string; title: string; @@ -33,6 +39,7 @@ export type AppListCardProps = { children?: ReactNode; credentialOwner?: CredentialOwner; className?: string; + classNameObject?: AppCardClassNames; } & ShouldHighlight; export const AppListCard = (props: AppListCardProps & { highlight?: boolean }) => { @@ -48,11 +55,16 @@ export const AppListCard = (props: AppListCardProps & { highlight?: boolean }) = children, credentialOwner, className, + classNameObject, highlight, } = props; return ( -
    +
    {logo ? (
    -

    {title}

    +

    + {title} +

    {isDefault && {t("default")}} {isTemplate && Template}
    - + {description} {invalidCredential && ( From 2ef847359ca38753da5fa0f962237e07fa611c73 Mon Sep 17 00:00:00 2001 From: supalarry Date: Mon, 11 Aug 2025 12:31:56 +0200 Subject: [PATCH 07/12] example styles --- .../examples/base/src/pages/availability.tsx | 5 ++++- .../examples/base/src/pages/calendars.tsx | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/platform/examples/base/src/pages/availability.tsx b/packages/platform/examples/base/src/pages/availability.tsx index e896b367f853b0..876e2f916bb03b 100644 --- a/packages/platform/examples/base/src/pages/availability.tsx +++ b/packages/platform/examples/base/src/pages/availability.tsx @@ -73,7 +73,10 @@ export default function Availability(props: { calUsername: string; calEmail: str }, }, dateOverrideClassNames: { - container: "p-0", + container: "p-4 bg-gray-900 rounded-md", + title: "text-red-500 font-bold", + description: "text-white", + button: "text-black", }, }} onFormStateChange={handleFormStateChange} diff --git a/packages/platform/examples/base/src/pages/calendars.tsx b/packages/platform/examples/base/src/pages/calendars.tsx index 87b5d197d93342..2cd2e8d59a20cf 100644 --- a/packages/platform/examples/base/src/pages/calendars.tsx +++ b/packages/platform/examples/base/src/pages/calendars.tsx @@ -22,13 +22,28 @@ export default function Calendars(props: { calUsername: string; calEmail: string }, }, selectedCalendarSettingsClassNames: { - container: "bg-red-200 mx-5 mb-6", + container: "mx-5 mb-6", header: { - container: "bg-gray-200", + container: "bg-gray-200 rounded-md", title: "text-green-500", description: "text-red-500", }, noSelectedCalendarsMessage: "text-blue-500", + selectedCalendarsListClassNames: { + container: "bg-yellow-100", + selectedCalendar: { + container: "bg-yellow-200", + header: { + container: "bg-yellow-500", + title: "text-green-500", + description: "text-red-500", + }, + body: { + container: "bg-yellow-500", + description: "text-red-500", + }, + }, + }, }, }} /> From 8a889bd2738f7d89e2fdf72c490798875088f0ac Mon Sep 17 00:00:00 2001 From: supalarry Date: Mon, 11 Aug 2025 14:45:19 +0200 Subject: [PATCH 08/12] docs --- docs/platform/atoms/availability-settings.mdx | 12 +++++++ docs/platform/atoms/calendar-settings.mdx | 31 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/docs/platform/atoms/availability-settings.mdx b/docs/platform/atoms/availability-settings.mdx index fbbe8b75f30a3a..62839c41915d4a 100644 --- a/docs/platform/atoms/availability-settings.mdx +++ b/docs/platform/atoms/availability-settings.mdx @@ -131,6 +131,18 @@ Along with the props, Availability settings atom accepts custom styles via the * | timeRanges | Time ranges in the availability settings can be customized | | labelAndSwitchContainer | Adds styling to label and switches | | overridesModalClassNames | Adds styling to the date overrides modal | +| dateOverrideClassNames | An object for granular styling of date override components (see detailed table below) | + +### dateOverrideClassNames Object Structure + +The `dateOverrideClassNames` prop accepts an object with the following structure for granular styling of date override components: + +| Property Path | Description | +|:--------------|:------------| +| `container` | Styles the main container | +| `title` | Styles the title| +| `description` | Styles the description | +| `button` | Styles the button to add date override | Please ensure all custom classnames are valid [Tailwind CSS](https://tailwindcss.com/) classnames. diff --git a/docs/platform/atoms/calendar-settings.mdx b/docs/platform/atoms/calendar-settings.mdx index 82c3cae1f727ae..c7ca09b1d9aecf 100644 --- a/docs/platform/atoms/calendar-settings.mdx +++ b/docs/platform/atoms/calendar-settings.mdx @@ -47,6 +47,37 @@ Along with the props, calendar settings atom accepts custom styles via the **cla | calendarSettingsCustomClassnames | Adds styling to the entire calendar settings atom | | destinationCalendarSettingsCustomClassnames | Adds styling only to the destination calendar container | | selectedCalendarSettingsCustomClassnames | Adds styling only to the selected calendar container | +| selectedCalendarSettingsClassNames | An object for granular styling of selected calendars component (see detailed table below) | +| destinationCalendarSettingsClassNames | An object for granular styling of destination calendar component (see detailed table below) | + +### selectedCalendarSettingsClassNames Object Structure + +The `selectedCalendarSettingsClassNames` prop accepts an object with the following nested structure for granular styling of the selected calendars component: + +| Property Path | Description | +|:--------------|:------------| +| `container` | Styles the main container of the selected calendars section | +| `header.container` | Styles the header container | +| `header.title` | Styles the header title | +| `header.description` | Styles the header description | +| `selectedCalendarsListClassNames.container` | Styles the container that holds all selected calendar items | +| `selectedCalendarsListClassNames.selectedCalendar.container` | Styles each individual calendar item container | +| `selectedCalendarsListClassNames.selectedCalendar.header.container` | Styles the header section of each calendar item | +| `selectedCalendarsListClassNames.selectedCalendar.header.title` | Styles the title of each calendar item | +| `selectedCalendarsListClassNames.selectedCalendar.header.description` | Styles the description of each calendar item | +| `selectedCalendarsListClassNames.selectedCalendar.body.container` | Styles the body section of each calendar item | +| `selectedCalendarsListClassNames.selectedCalendar.body.description` | Styles the body description of each calendar item | +| `noSelectedCalendarsMessage` | Styles the message shown when no calendars are connected | + +### destinationCalendarSettingsClassNames Object Structure + +The `destinationCalendarSettingsClassNames` prop accepts an object with the following nested structure for granular styling of the destination calendar component: + +| Property Path | Description | +|:--------------|:------------| +| `container` | Styles the main container of the destination calendar section | +| `header.title` | Styles the header title text | +| `header.description` | Styles the header description text |

    Additionally, if you wish to select either the Destination Calendar or the Selected Calendar, we also provide atoms specifically designed for this purpose. From e9987a4ea0693432737c2881f6238262b8ae068a Mon Sep 17 00:00:00 2001 From: supalarry Date: Mon, 11 Aug 2025 14:46:18 +0200 Subject: [PATCH 09/12] chanegest --- .changeset/mighty-wings-create.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/mighty-wings-create.md diff --git a/.changeset/mighty-wings-create.md b/.changeset/mighty-wings-create.md new file mode 100644 index 00000000000000..091fe4eebbcafe --- /dev/null +++ b/.changeset/mighty-wings-create.md @@ -0,0 +1,5 @@ +--- +"@calcom/atoms": minor +--- + +feat: style calendar settings and availability overrides From 03fc14c7862edad381b7b89f03747b186ee38bc1 Mon Sep 17 00:00:00 2001 From: supalarry Date: Tue, 12 Aug 2025 10:36:45 +0200 Subject: [PATCH 10/12] refactor: change import path to relative --- .../wrappers/CalendarSettingsPlatformWrapper.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx b/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx index 00ae256c4dc93c..67008cc99c8d6c 100644 --- a/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx +++ b/packages/platform/atoms/calendar-settings/wrappers/CalendarSettingsPlatformWrapper.tsx @@ -1,5 +1,4 @@ -import type { DestinationCalendarClassNames } from "destination-calendar/DestinationCalendar"; - +import type { DestinationCalendarClassNames } from "../../destination-calendar/DestinationCalendar"; import { DestinationCalendarSettingsPlatformWrapper } from "../../destination-calendar/index"; import { SelectedCalendarsSettingsPlatformWrapper } from "../../selected-calendars/index"; import type { From a0db1d8709fc7edcb5cf5e2c8e7425cb7cb24199 Mon Sep 17 00:00:00 2001 From: supalarry Date: Tue, 12 Aug 2025 10:37:58 +0200 Subject: [PATCH 11/12] fix: make all classnames optional --- .../wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx b/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx index c6bf73f33472d1..becd316df141c4 100644 --- a/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx +++ b/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsPlatformWrapper.tsx @@ -39,10 +39,10 @@ export type SelectedCalendarsClassNames = { }; selectedCalendarsListClassNames?: { container?: string; - selectedCalendar: { + selectedCalendar?: { container?: string; header?: { - container: string; + container?: string; title?: string; description?: string; }; From 6bef75fce5988cdd741300f55623493447f783b2 Mon Sep 17 00:00:00 2001 From: supalarry Date: Tue, 12 Aug 2025 10:49:48 +0200 Subject: [PATCH 12/12] docs: time picker docs --- docs/platform/atoms/availability-settings.mdx | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/docs/platform/atoms/availability-settings.mdx b/docs/platform/atoms/availability-settings.mdx index 62839c41915d4a..6ff260f0db41b9 100644 --- a/docs/platform/atoms/availability-settings.mdx +++ b/docs/platform/atoms/availability-settings.mdx @@ -125,14 +125,35 @@ Along with the props, Availability settings atom accepts custom styles via the * | formClassName | Form which contains the days and toggles | | timezoneSelectClassName | Adds styling to the timezone select component | | subtitlesClassName | Styles the subtitle | -| scheduleContainer | Styles the entire schedule component | -| scheduleDay | Adds styling to just the day of a particular schedule | -| dayRanges | Adds styling to day ranges | -| timeRanges | Time ranges in the availability settings can be customized | -| labelAndSwitchContainer | Adds styling to label and switches | -| overridesModalClassNames | Adds styling to the date overrides modal | +| scheduleClassNames | An object for granular styling of schedule components (see detailed table below) | | dateOverrideClassNames | An object for granular styling of date override components (see detailed table below) | +### scheduleClassNames Object Structure + +The `scheduleClassNames` prop accepts an object with the following structure for granular styling of schedule components: + +| Property Path | Description | +|:--------------|:------------| +| `schedule` | Styles the entire schedule component | +| `scheduleContainer` | Styles the schedule container | +| `scheduleDay` | Adds styling to just the day of a particular schedule | +| `dayRanges` | Adds styling to day ranges | +| `timeRangeField` | Styles the time range input fields | +| `labelAndSwitchContainer` | Adds styling to label and switches | +| `timePicker` | An object for granular styling of time picker dropdown components (see nested table below) | + +#### timePicker Object Structure (nested within scheduleClassNames) + +The `timePicker` prop accepts an object with the following structure for granular styling of time picker dropdown components. This applies to the time selection dropdowns (e.g., [ 9:00 ]) that users can click to open a dropdown with available times or click to manually enter a time: + +| Property Path | Description | +|:--------------|:------------| +| `container` | Styles the main time picker container | +| `valueContainer` | Styles the container that holds the selected value | +| `value` | Styles the displayed selected time value | +| `input` | Styles the input field for manual time entry | +| `dropdown` | Styles the dropdown menu with time options | + ### dateOverrideClassNames Object Structure The `dateOverrideClassNames` prop accepts an object with the following structure for granular styling of date override components: