feat: added customClassnames to event-availability tab (#17268)
* feat: added customClassnames to event-availability tab * added classNames to common types * use common classnames type * Update types.ts * update name * fix: better classnames * better calssnames * Update types.ts
This commit is contained in:
@@ -161,20 +161,44 @@ describe("TestFormDialog", () => {
|
||||
});
|
||||
|
||||
it("renders the dialog when open", () => {
|
||||
render(<TestFormDialog form={mockTeamForm} isTestPreviewOpen={true} setIsTestPreviewOpen={() => {}} />);
|
||||
render(
|
||||
<TestFormDialog
|
||||
form={mockTeamForm}
|
||||
isTestPreviewOpen={true}
|
||||
setIsTestPreviewOpen={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText("test_routing_form")).toBeInTheDocument();
|
||||
expect(screen.getByText("test_preview_description")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("doesn't render the dialog when closed", () => {
|
||||
render(<TestFormDialog form={mockTeamForm} isTestPreviewOpen={false} setIsTestPreviewOpen={() => {}} />);
|
||||
render(
|
||||
<TestFormDialog
|
||||
form={mockTeamForm}
|
||||
isTestPreviewOpen={false}
|
||||
setIsTestPreviewOpen={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.queryByText("test_routing_form")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders form fields", () => {
|
||||
render(<TestFormDialog form={mockTeamForm} isTestPreviewOpen={true} setIsTestPreviewOpen={() => {}} />);
|
||||
render(
|
||||
<TestFormDialog
|
||||
form={mockTeamForm}
|
||||
isTestPreviewOpen={true}
|
||||
setIsTestPreviewOpen={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId("form-field-name")).toBeInTheDocument();
|
||||
});
|
||||
@@ -182,7 +206,15 @@ describe("TestFormDialog", () => {
|
||||
describe("Team Form", () => {
|
||||
it("submits the form and shows test results for Custom Page", async () => {
|
||||
mockCustomPageMessageMatchingRoute();
|
||||
render(<TestFormDialog form={mockTeamForm} isTestPreviewOpen={true} setIsTestPreviewOpen={() => {}} />);
|
||||
render(
|
||||
<TestFormDialog
|
||||
form={mockTeamForm}
|
||||
isTestPreviewOpen={true}
|
||||
setIsTestPreviewOpen={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
fireEvent.change(screen.getByTestId("form-field-name"), { target: { value: "John Doe" } });
|
||||
fireEvent.click(screen.getByText("test_routing"));
|
||||
|
||||
@@ -193,7 +225,15 @@ describe("TestFormDialog", () => {
|
||||
|
||||
it("submits the form and shows test results for Event Type", async () => {
|
||||
mockEventTypeRedirectUrlMatchingRoute();
|
||||
render(<TestFormDialog form={mockTeamForm} isTestPreviewOpen={true} setIsTestPreviewOpen={() => {}} />);
|
||||
render(
|
||||
<TestFormDialog
|
||||
form={mockTeamForm}
|
||||
isTestPreviewOpen={true}
|
||||
setIsTestPreviewOpen={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
fireEvent.change(screen.getByTestId("form-field-name"), { target: { value: "John Doe" } });
|
||||
fireEvent.click(screen.getByText("test_routing"));
|
||||
expect(screen.getByText("route_to:")).toBeInTheDocument();
|
||||
@@ -215,7 +255,15 @@ describe("TestFormDialog", () => {
|
||||
},
|
||||
checkedFallback: false,
|
||||
});
|
||||
render(<TestFormDialog form={mockTeamForm} isTestPreviewOpen={true} setIsTestPreviewOpen={() => {}} />);
|
||||
render(
|
||||
<TestFormDialog
|
||||
form={mockTeamForm}
|
||||
isTestPreviewOpen={true}
|
||||
setIsTestPreviewOpen={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
fireEvent.change(screen.getByTestId("form-field-name"), { target: { value: "John Doe" } });
|
||||
fireEvent.click(screen.getByText("test_routing"));
|
||||
expect(screen.getByText("route_to:")).toBeInTheDocument();
|
||||
@@ -237,7 +285,15 @@ describe("TestFormDialog", () => {
|
||||
mainWarnings: ["Main-Error-1", "Main-Error-2"],
|
||||
fallbackWarnings: ["Fallback-Error-1", "Fallback-Error-2"],
|
||||
});
|
||||
render(<TestFormDialog form={mockTeamForm} isTestPreviewOpen={true} setIsTestPreviewOpen={() => {}} />);
|
||||
render(
|
||||
<TestFormDialog
|
||||
form={mockTeamForm}
|
||||
isTestPreviewOpen={true}
|
||||
setIsTestPreviewOpen={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
fireEvent.change(screen.getByTestId("form-field-name"), { target: { value: "John Doe" } });
|
||||
fireEvent.click(screen.getByText("test_routing"));
|
||||
screen.logTestingPlaygroundURL();
|
||||
@@ -255,7 +311,15 @@ describe("TestFormDialog", () => {
|
||||
mainWarnings: null,
|
||||
fallbackWarnings: null,
|
||||
});
|
||||
render(<TestFormDialog form={mockTeamForm} isTestPreviewOpen={true} setIsTestPreviewOpen={() => {}} />);
|
||||
render(
|
||||
<TestFormDialog
|
||||
form={mockTeamForm}
|
||||
isTestPreviewOpen={true}
|
||||
setIsTestPreviewOpen={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
fireEvent.change(screen.getByTestId("form-field-name"), { target: { value: "John Doe" } });
|
||||
fireEvent.click(screen.getByText("test_routing"));
|
||||
screen.logTestingPlaygroundURL();
|
||||
@@ -273,7 +337,15 @@ describe("TestFormDialog", () => {
|
||||
mainWarnings: null,
|
||||
fallbackWarnings: null,
|
||||
});
|
||||
render(<TestFormDialog form={mockTeamForm} isTestPreviewOpen={true} setIsTestPreviewOpen={() => {}} />);
|
||||
render(
|
||||
<TestFormDialog
|
||||
form={mockTeamForm}
|
||||
isTestPreviewOpen={true}
|
||||
setIsTestPreviewOpen={() => {
|
||||
return;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
fireEvent.change(screen.getByTestId("form-field-name"), { target: { value: "John Doe" } });
|
||||
fireEvent.click(screen.getByText("test_routing"));
|
||||
expect(screen.getByTestId("attribute-logic-matched")).toHaveTextContent("no");
|
||||
|
||||
@@ -1166,26 +1166,25 @@ async function handler(
|
||||
await usersRepository.updateLastActiveAt(booking.userId);
|
||||
}
|
||||
|
||||
// If it's a round robin event, record the reason for the host assignment
|
||||
if (eventType.schedulingType === SchedulingType.ROUND_ROBIN) {
|
||||
if (reqBody.crmOwnerRecordType && reqBody.crmAppSlug && contactOwnerEmail && routingFormResponseId) {
|
||||
await AssignmentReasonRecorder.CRMOwnership({
|
||||
bookingId: booking.id,
|
||||
crmAppSlug: reqBody.crmAppSlug,
|
||||
teamMemberEmail: contactOwnerEmail,
|
||||
recordType: reqBody.crmOwnerRecordType,
|
||||
routingFormResponseId,
|
||||
});
|
||||
} else if (routingFormResponseId && teamId) {
|
||||
await AssignmentReasonRecorder.routingFormRoute({
|
||||
bookingId: booking.id,
|
||||
routingFormResponseId,
|
||||
organizerId: organizerUser.id,
|
||||
teamId,
|
||||
});
|
||||
// If it's a round robin event, record the reason for the host assignment
|
||||
if (eventType.schedulingType === SchedulingType.ROUND_ROBIN) {
|
||||
if (reqBody.crmOwnerRecordType && reqBody.crmAppSlug && contactOwnerEmail && routingFormResponseId) {
|
||||
await AssignmentReasonRecorder.CRMOwnership({
|
||||
bookingId: booking.id,
|
||||
crmAppSlug: reqBody.crmAppSlug,
|
||||
teamMemberEmail: contactOwnerEmail,
|
||||
recordType: reqBody.crmOwnerRecordType,
|
||||
routingFormResponseId,
|
||||
});
|
||||
} else if (routingFormResponseId && teamId) {
|
||||
await AssignmentReasonRecorder.routingFormRoute({
|
||||
bookingId: booking.id,
|
||||
routingFormResponseId,
|
||||
organizerId: organizerUser.id,
|
||||
teamId,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
evt.uid = booking.uid ?? null;
|
||||
evt.oneTimePassword = booking.oneTimePassword ?? null;
|
||||
|
||||
+160
-33
@@ -16,6 +16,7 @@ import type {
|
||||
FormValues,
|
||||
EventTypeSetup,
|
||||
Host,
|
||||
SelectClassNames,
|
||||
} from "@calcom/features/eventtypes/lib/types";
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
@@ -28,6 +29,42 @@ import { Spinner } from "@calcom/ui/components/icon/Spinner";
|
||||
|
||||
type ScheduleQueryData = RouterOutputs["viewer"]["availability"]["schedule"]["get"];
|
||||
|
||||
type AvailabilityTableCustomClassNames = {
|
||||
tableContainer?: string;
|
||||
table?: string;
|
||||
tableRow?: string;
|
||||
day?: string;
|
||||
dayUnavailable?: string;
|
||||
dayAvailabilityContainer?: string;
|
||||
dayAvailabilityFrom?: string;
|
||||
dayAvailabilityTo?: string;
|
||||
dayAvailabilitySeperator?: string;
|
||||
};
|
||||
|
||||
type TeamMemmberScheduelCustomClassNames = SelectClassNames & {
|
||||
labelAvatar?: string;
|
||||
labelContainer?: string;
|
||||
};
|
||||
|
||||
type UserAvailabilityCustomClassNames = {
|
||||
availabilitySectionContainer?: string;
|
||||
availabilitySelect?: SelectClassNames;
|
||||
availabilityTable?: AvailabilityTableCustomClassNames;
|
||||
};
|
||||
|
||||
type TeamAvailabilityCustomClassNames = {
|
||||
chooseHostSchedulesLabelContainer?: string;
|
||||
chooseHostSchedulesLabel?: string;
|
||||
chooseHostSchedulesLabelDescription?: string;
|
||||
teamAvailibilityContainer?: string;
|
||||
teamMemberSchedule: TeamMemmberScheduelCustomClassNames;
|
||||
};
|
||||
|
||||
export type EventAvailabilityTabCustomClassNames = {
|
||||
teamAvailability: TeamAvailabilityCustomClassNames;
|
||||
userAvailability: UserAvailabilityCustomClassNames;
|
||||
};
|
||||
|
||||
type EventTypeScheduleDetailsProps = {
|
||||
scheduleQueryData?: Pick<ScheduleQueryData, "timeZone" | "id" | "isManaged" | "readOnly"> & {
|
||||
schedule: Array<Pick<ScheduleQueryData["schedule"][number], "days" | "startTime" | "endTime">>;
|
||||
@@ -35,6 +72,7 @@ type EventTypeScheduleDetailsProps = {
|
||||
isSchedulePending?: boolean;
|
||||
user?: Pick<RouterOutputs["viewer"]["me"], "timeFormat" | "weekStart">;
|
||||
editAvailabilityRedirectUrl?: string;
|
||||
customClassNames?: AvailabilityTableCustomClassNames;
|
||||
};
|
||||
|
||||
type HostSchedulesQueryType =
|
||||
@@ -54,6 +92,8 @@ type HostSchedulesQueryType =
|
||||
|
||||
type EventTypeTeamScheduleProps = {
|
||||
hostSchedulesQuery: HostSchedulesQueryType;
|
||||
hosts?: Host[];
|
||||
customClassNames?: TeamAvailabilityCustomClassNames;
|
||||
};
|
||||
|
||||
type TeamMember = Pick<TeamMembers[number], "avatar" | "name" | "id">;
|
||||
@@ -65,14 +105,22 @@ type EventTypeScheduleProps = {
|
||||
isSchedulesPending?: boolean;
|
||||
eventType: EventTypeSetup;
|
||||
teamMembers: TeamMember[];
|
||||
} & EventTypeScheduleDetailsProps &
|
||||
EventTypeTeamScheduleProps;
|
||||
customClassNames?: UserAvailabilityCustomClassNames;
|
||||
} & Omit<EventTypeScheduleDetailsProps, "customClassNames"> &
|
||||
Omit<EventTypeTeamScheduleProps, "customClassNames">;
|
||||
|
||||
export type EventAvailabilityTabBaserProps = {
|
||||
isTeamEvent: boolean;
|
||||
};
|
||||
|
||||
type EventAvailabilityTabProps = EventAvailabilityTabBaserProps & EventTypeScheduleProps;
|
||||
type UseCommonScheduleSettingsToggle = Omit<EventTypeScheduleProps, "customClassNames"> & {
|
||||
customClassNames?: EventAvailabilityTabCustomClassNames;
|
||||
};
|
||||
|
||||
type EventAvailabilityTabProps = EventAvailabilityTabBaserProps &
|
||||
Omit<EventTypeScheduleProps, "customClassNames"> & {
|
||||
customClassNames?: EventAvailabilityTabCustomClassNames;
|
||||
};
|
||||
|
||||
const Option = ({ ...props }: OptionProps<AvailabilityOption>) => {
|
||||
const { label, isDefault, isManaged = false } = props.data;
|
||||
@@ -127,6 +175,7 @@ const EventTypeScheduleDetails = memo(
|
||||
isSchedulePending,
|
||||
user,
|
||||
editAvailabilityRedirectUrl,
|
||||
customClassNames,
|
||||
}: EventTypeScheduleDetailsProps) => {
|
||||
const timeFormat = user?.timeFormat;
|
||||
const { t, i18n } = useLocale();
|
||||
@@ -138,16 +187,22 @@ const EventTypeScheduleDetails = memo(
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="border-subtle space-y-4 border-x p-6">
|
||||
<ol className="table border-collapse text-sm">
|
||||
<div className={classNames("border-subtle space-y-4 border-x p-6", customClassNames?.tableContainer)}>
|
||||
<ol className={classNames("table border-collapse text-sm", customClassNames?.table)}>
|
||||
{weekdayNames(i18n.language, weekStart, "long").map((day, index) => {
|
||||
const isAvailable = !!filterDays(index).length;
|
||||
return (
|
||||
<li key={day} className="my-6 flex border-transparent last:mb-2">
|
||||
<li
|
||||
key={day}
|
||||
className={classNames(
|
||||
"my-6 flex border-transparent last:mb-2",
|
||||
customClassNames?.tableRow
|
||||
)}>
|
||||
<span
|
||||
className={classNames(
|
||||
"w-20 font-medium sm:w-32 ",
|
||||
!isAvailable ? "text-subtle line-through" : "text-default"
|
||||
!isAvailable ? "text-subtle line-through" : "text-default",
|
||||
customClassNames?.day
|
||||
)}>
|
||||
{day}
|
||||
</span>
|
||||
@@ -156,17 +211,33 @@ const EventTypeScheduleDetails = memo(
|
||||
) : isAvailable ? (
|
||||
<div className="space-y-3 text-right">
|
||||
{filterDays(index).map((dayRange, i) => (
|
||||
<div key={i} className="text-default flex items-center leading-4">
|
||||
<span className="w-16 sm:w-28 sm:text-left">
|
||||
<div
|
||||
key={i}
|
||||
className={classNames(
|
||||
"text-default flex items-center leading-4",
|
||||
customClassNames?.dayAvailabilityContainer
|
||||
)}>
|
||||
<span
|
||||
className={classNames(
|
||||
"w-16 sm:w-28 sm:text-left",
|
||||
customClassNames?.dayAvailabilityFrom
|
||||
)}>
|
||||
{format(dayRange.startTime, timeFormat === 12)}
|
||||
</span>
|
||||
<span className="ms-4">-</span>
|
||||
<div className="ml-6 sm:w-28">{format(dayRange.endTime, timeFormat === 12)}</div>
|
||||
<span className={classNames("ms-4", customClassNames?.dayAvailabilitySeperator)}>
|
||||
-
|
||||
</span>
|
||||
<div className={classNames("ml-6 sm:w-28", customClassNames?.dayAvailabilityTo)}>
|
||||
{format(dayRange.endTime, timeFormat === 12)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-subtle ml-6 sm:ml-0">{t("unavailable")}</span>
|
||||
<span
|
||||
className={classNames("text-subtle ml-6 sm:ml-0", customClassNames?.dayUnavailable)}>
|
||||
{t("unavailable")}
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
@@ -204,6 +275,7 @@ const EventTypeSchedule = ({
|
||||
eventType,
|
||||
schedulesQueryData,
|
||||
isSchedulesPending,
|
||||
customClassNames,
|
||||
...rest
|
||||
}: EventTypeScheduleProps) => {
|
||||
const { t } = useLocale();
|
||||
@@ -272,8 +344,17 @@ const EventTypeSchedule = ({
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="border-subtle rounded-t-md border p-6">
|
||||
<label htmlFor="availability" className="text-default mb-2 block text-sm font-medium leading-none">
|
||||
<div
|
||||
className={classNames(
|
||||
"border-subtle rounded-t-md border p-6",
|
||||
customClassNames?.availabilitySectionContainer
|
||||
)}>
|
||||
<label
|
||||
htmlFor="availability"
|
||||
className={classNames(
|
||||
"text-default mb-2 block text-sm font-medium leading-none",
|
||||
customClassNames?.availabilitySelect?.label
|
||||
)}>
|
||||
{t("availability")}
|
||||
{(isManagedEventType || isChildrenManagedEventType) && shouldLockIndicator("schedule")}
|
||||
</label>
|
||||
@@ -292,17 +373,21 @@ const EventTypeSchedule = ({
|
||||
onChange={(selected) => {
|
||||
if (selected) onChange(selected.value);
|
||||
}}
|
||||
className="block w-full min-w-0 flex-1 rounded-sm text-sm"
|
||||
className={classNames(
|
||||
"block w-full min-w-0 flex-1 rounded-sm text-sm",
|
||||
customClassNames?.availabilitySelect?.select
|
||||
)}
|
||||
value={optionValue}
|
||||
components={{ Option, SingleValue }}
|
||||
isMulti={false}
|
||||
innerClassNames={customClassNames?.availabilitySelect?.innerClassNames}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{scheduleId !== 0 ? (
|
||||
<EventTypeScheduleDetails {...rest} />
|
||||
<EventTypeScheduleDetails {...rest} customClassNames={customClassNames?.availabilityTable} />
|
||||
) : (
|
||||
isManagedEventType && (
|
||||
<p className="!mt-2 ml-1 text-sm text-gray-600">{t("members_default_schedule_description")}</p>
|
||||
@@ -311,17 +396,18 @@ const EventTypeSchedule = ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TeamMemberSchedule = ({
|
||||
host,
|
||||
index,
|
||||
teamMembers,
|
||||
hostScheduleQuery,
|
||||
customClassNames,
|
||||
}: {
|
||||
host: Host;
|
||||
index: number;
|
||||
teamMembers: TeamMember[];
|
||||
hostScheduleQuery: HostSchedulesQueryType;
|
||||
customClassNames?: TeamMemmberScheduelCustomClassNames;
|
||||
}) => {
|
||||
const { t } = useLocale();
|
||||
const isPlatform = useIsPlatform();
|
||||
@@ -355,10 +441,10 @@ const TeamMemberSchedule = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex w-full items-center">
|
||||
<div className={classNames("flex w-full items-center", customClassNames?.labelContainer)}>
|
||||
{!isPlatform && <Avatar size="sm" imageSrc={avatar} alt={label || ""} />}
|
||||
{isPlatform && <Icon name="user" className="h-4 w-4" />}
|
||||
<p className="text-emphasis my-auto ms-3 text-sm">{label}</p>
|
||||
{isPlatform && <Icon name="user" className={classNames("h-4 w-4", customClassNames?.labelAvatar)} />}
|
||||
<p className={classNames("text-emphasis my-auto ms-3 text-sm", customClassNames?.label)}>{label}</p>
|
||||
</div>
|
||||
<div className="flex w-full flex-col pt-2 ">
|
||||
{isPending ? (
|
||||
@@ -375,7 +461,11 @@ const TeamMemberSchedule = ({
|
||||
onChange={(selected) => {
|
||||
field.onChange(selected?.value || null);
|
||||
}}
|
||||
className="block w-full min-w-0 flex-1 rounded-sm text-sm"
|
||||
className={classNames(
|
||||
"block w-full min-w-0 flex-1 rounded-sm text-sm",
|
||||
customClassNames?.select
|
||||
)}
|
||||
innerClassNames={customClassNames?.innerClassNames}
|
||||
value={value as AvailabilityOption}
|
||||
components={{ Option, SingleValue }}
|
||||
isMulti={false}
|
||||
@@ -393,17 +483,36 @@ const TeamMemberSchedule = ({
|
||||
const TeamAvailability = ({
|
||||
teamMembers,
|
||||
hostSchedulesQuery,
|
||||
}: EventTypeTeamScheduleProps & { teamMembers: TeamMember[] }) => {
|
||||
customClassNames,
|
||||
}: EventTypeTeamScheduleProps & {
|
||||
teamMembers: TeamMember[];
|
||||
customClassNames?: TeamAvailabilityCustomClassNames;
|
||||
}) => {
|
||||
const { t } = useLocale();
|
||||
const { watch } = useFormContext<FormValues>();
|
||||
const [animationRef] = useAutoAnimate<HTMLUListElement>();
|
||||
const hosts = watch("hosts");
|
||||
return (
|
||||
<>
|
||||
<div className="border-subtle flex flex-col rounded-md">
|
||||
<div className="border-subtle mt-5 rounded-t-md border p-6 pb-5">
|
||||
<Label className="mb-1 text-sm font-semibold">{t("choose_hosts_schedule")}</Label>
|
||||
<p className="text-subtle max-w-full break-words text-sm leading-tight">
|
||||
<div
|
||||
className={classNames(
|
||||
"border-subtle flex flex-col rounded-md",
|
||||
customClassNames?.teamAvailibilityContainer
|
||||
)}>
|
||||
<div
|
||||
className={classNames(
|
||||
"border-subtle mt-5 rounded-t-md border p-6 pb-5",
|
||||
customClassNames?.chooseHostSchedulesLabelContainer
|
||||
)}>
|
||||
<Label
|
||||
className={classNames("mb-1 text-sm font-semibold", customClassNames?.chooseHostSchedulesLabel)}>
|
||||
{t("choose_hosts_schedule")}
|
||||
</Label>
|
||||
<p
|
||||
className={classNames(
|
||||
"text-subtle max-w-full break-words text-sm leading-tight",
|
||||
customClassNames?.chooseHostSchedulesLabelDescription
|
||||
)}>
|
||||
{t("hosts_schedule_description")}
|
||||
</p>
|
||||
</div>
|
||||
@@ -415,14 +524,16 @@ const TeamAvailability = ({
|
||||
{hosts?.map((host, index) => (
|
||||
<li
|
||||
key={host.userId}
|
||||
className={`flex flex-col px-3 py-2 ${
|
||||
index === hosts.length - 1 ? "" : "border-subtle border-b"
|
||||
}`}>
|
||||
className={classNames(
|
||||
`flex flex-col px-3 py-2 ${index === hosts.length - 1 ? "" : "border-subtle border-b"}`,
|
||||
customClassNames?.teamMemberSchedule?.container
|
||||
)}>
|
||||
<TeamMemberSchedule
|
||||
host={host}
|
||||
index={index}
|
||||
teamMembers={teamMembers}
|
||||
hostScheduleQuery={hostSchedulesQuery}
|
||||
customClassNames={customClassNames?.teamMemberSchedule}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
@@ -457,7 +568,11 @@ const useCommonScheduleState = (initialScheduleId: number | null) => {
|
||||
};
|
||||
};
|
||||
|
||||
const UseCommonScheduleSettingsToggle = ({ eventType, ...rest }: EventTypeScheduleProps) => {
|
||||
const UseCommonScheduleSettingsToggle = ({
|
||||
eventType,
|
||||
customClassNames,
|
||||
...rest
|
||||
}: UseCommonScheduleSettingsToggle) => {
|
||||
const { t } = useLocale();
|
||||
const { useHostSchedulesForTeamEvent, toggleScheduleState } = useCommonScheduleState(eventType.schedule);
|
||||
return (
|
||||
@@ -468,11 +583,19 @@ const UseCommonScheduleSettingsToggle = ({ eventType, ...rest }: EventTypeSchedu
|
||||
title={t("choose_common_schedule_team_event")}
|
||||
description={t("choose_common_schedule_team_event_description")}>
|
||||
{/* handles the state for which 'schedule' ID is set, as it's unknown until the Select dropdown is loaded */}
|
||||
<EventTypeSchedule eventType={eventType} {...rest} />
|
||||
<EventTypeSchedule
|
||||
customClassNames={customClassNames?.userAvailability}
|
||||
eventType={eventType}
|
||||
{...rest}
|
||||
/>
|
||||
</SettingsToggle>
|
||||
{useHostSchedulesForTeamEvent && (
|
||||
<div className="lg:ml-14">
|
||||
<TeamAvailability teamMembers={rest.teamMembers} hostSchedulesQuery={rest.hostSchedulesQuery} />
|
||||
<TeamAvailability
|
||||
teamMembers={rest.teamMembers}
|
||||
hostSchedulesQuery={rest.hostSchedulesQuery}
|
||||
customClassNames={customClassNames?.teamAvailability}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
@@ -483,6 +606,10 @@ export const EventAvailabilityTab = ({ eventType, isTeamEvent, ...rest }: EventA
|
||||
return isTeamEvent && eventType.schedulingType !== SchedulingType.MANAGED ? (
|
||||
<UseCommonScheduleSettingsToggle eventType={eventType} {...rest} />
|
||||
) : (
|
||||
<EventTypeSchedule eventType={eventType} {...rest} />
|
||||
<EventTypeSchedule
|
||||
eventType={eventType}
|
||||
{...rest}
|
||||
customClassNames={rest?.customClassNames?.userAvailability}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import type { EventAvailabilityTabCustomClassNames } from "@calcom/features/eventtypes/components/tabs/availability/EventAvailabilityTab";
|
||||
import { EventAvailabilityTab } from "@calcom/features/eventtypes/components/tabs/availability/EventAvailabilityTab";
|
||||
import type { EventTypeSetup, FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import type { User } from "@calcom/prisma/client";
|
||||
@@ -16,6 +17,7 @@ type EventAvailabilityTabPlatformWrapperProps = {
|
||||
eventType: EventTypeSetup;
|
||||
isTeamEvent: boolean;
|
||||
teamId?: number;
|
||||
customClassNames?: EventAvailabilityTabCustomClassNames;
|
||||
};
|
||||
|
||||
const EventAvailabilityTabPlatformWrapper = ({
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useRef, useState, useEffect } from "react";
|
||||
import type { ChildrenEventType } from "@calcom/features/eventtypes/components/ChildrenEventTypeSelect";
|
||||
import { EventType as EventTypeComponent } from "@calcom/features/eventtypes/components/EventType";
|
||||
import ManagedEventTypeDialog from "@calcom/features/eventtypes/components/dialogs/ManagedEventDialog";
|
||||
import type { EventAvailabilityTabCustomClassNames } from "@calcom/features/eventtypes/components/tabs/availability/EventAvailabilityTab";
|
||||
import type { EventRecurringTabCustomClassNames } from "@calcom/features/eventtypes/components/tabs/recurring/RecurringEventController";
|
||||
import type { EventTypeSetupProps, FormValues, TabMap } from "@calcom/features/eventtypes/lib/types";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
@@ -30,6 +31,10 @@ import SetupTab from "./EventSetupTabPlatformWrapper";
|
||||
import EventTeamAssignmentTabPlatformWrapper from "./EventTeamAssignmentTabPlatformWrapper";
|
||||
|
||||
export type PlatformTabs = keyof Omit<TabMap, "workflows" | "webhooks" | "instant" | "ai" | "apps">;
|
||||
export type eventTypeCustomClassNames = {
|
||||
atomsWrapper?: string;
|
||||
eventAvailabilityTab?: EventAvailabilityTabCustomClassNames;
|
||||
};
|
||||
|
||||
export type EventTypeCustomClassNames = {
|
||||
atomsWrapper?: string;
|
||||
@@ -44,6 +49,7 @@ export type EventTypePlatformWrapperProps = {
|
||||
onDeleteSuccess?: () => void;
|
||||
onDeleteError?: (msg: string) => void;
|
||||
allowDelete: boolean;
|
||||
customClassNames?: eventTypeCustomClassNames;
|
||||
customClassNames?: EventTypeCustomClassNames;
|
||||
disableToasts?: boolean;
|
||||
};
|
||||
@@ -158,6 +164,7 @@ const EventType = ({
|
||||
isTeamEvent={!!team}
|
||||
user={user?.data}
|
||||
teamId={team?.id}
|
||||
customClassNames={customClassNames?.eventAvailabilityTab}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
|
||||
@@ -97,6 +97,60 @@ export default function Bookings(props: { calUsername: string; calEmail: string
|
||||
<EventTypeSettings
|
||||
customClassNames={{
|
||||
atomsWrapper: "!w-[50vw] !m-auto",
|
||||
eventAvailabilityTab: {
|
||||
teamAvailability: {
|
||||
chooseHostSchedulesLabelContainer: "border border-purple-200 bg-purple-50 rounded-md p-4",
|
||||
chooseHostSchedulesLabel: "text-purple-700 font-semibold",
|
||||
chooseHostSchedulesLabelDescription: "text-gray-600",
|
||||
teamAvailibilityContainer: "mt-4 border border-purple-200 bg-white rounded-lg p-6",
|
||||
teamMemberSchedule: {
|
||||
labelAvatar: "rounded-full border border-purple-300",
|
||||
labelContainer: "flex items-center gap-2",
|
||||
innerClassNames: {
|
||||
input: "text-purple-900",
|
||||
option: "hover:bg-purple-100",
|
||||
control: "border-purple-300",
|
||||
singleValue: "text-purple-700",
|
||||
valueContainer: "",
|
||||
multiValue: "",
|
||||
menu: "border border-purple-200 rounded-md",
|
||||
menuList: "",
|
||||
},
|
||||
select: "border border-purple-300 rounded-md bg-white",
|
||||
label: "text-purple-700",
|
||||
container: "",
|
||||
},
|
||||
},
|
||||
userAvailability: {
|
||||
availabilitySectionContainer: "border border-purple-200 bg-white rounded-lg p-6",
|
||||
availabilitySelect: {
|
||||
innerClassNames: {
|
||||
input: "text-purple-900",
|
||||
option: "hover:bg-purple-100",
|
||||
control: "border-purple-300",
|
||||
singleValue: "text-purple-700",
|
||||
valueContainer: "",
|
||||
multiValue: "",
|
||||
menu: "border border-purple-200 rounded-md",
|
||||
menuList: "",
|
||||
},
|
||||
select: "border border-purple-300 rounded-md bg-white",
|
||||
label: "text-purple-700",
|
||||
container: "",
|
||||
},
|
||||
availabilityTable: {
|
||||
tableContainer: "border border-purple-200 rounded-md bg-white mt-4",
|
||||
table: "w-full table-auto",
|
||||
tableRow: "hover:bg-purple-50",
|
||||
day: "text-purple-800 font-semibold",
|
||||
dayUnavailable: "text-gray-400",
|
||||
dayAvailabilityContainer: "flex items-center gap-2",
|
||||
dayAvailabilityFrom: "text-purple-700",
|
||||
dayAvailabilityTo: "text-purple-700",
|
||||
dayAvailabilitySeperator: "text-purple-500",
|
||||
},
|
||||
},
|
||||
},
|
||||
eventRecurringTab: {
|
||||
container: "border border-purple-200 bg-purple-50 rounded-lg shadow-sm",
|
||||
recurringToggle: {
|
||||
|
||||
+2
-1
@@ -461,6 +461,7 @@
|
||||
"NEXT_PUBLIC_POSTHOG_KEY",
|
||||
"NEXT_PUBLIC_POSTHOG_HOST",
|
||||
"VAPID_PRIVATE_KEY",
|
||||
"HUDDLE01_API_TOKEN"
|
||||
"HUDDLE01_API_TOKEN",
|
||||
"REPLEXICA_API_KEY"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user