fix: common schedule toggling incorrectly (#17517)
* fix: common schedule toggling incorrectly * type fixes to util.ts * fix another type error
This commit is contained in:
+25
-27
@@ -54,7 +54,6 @@ type HostSchedulesQueryType =
|
||||
|
||||
type EventTypeTeamScheduleProps = {
|
||||
hostSchedulesQuery: HostSchedulesQueryType;
|
||||
hosts?: Host[];
|
||||
};
|
||||
|
||||
type TeamMember = Pick<TeamMembers[number], "avatar" | "name" | "id">;
|
||||
@@ -392,13 +391,13 @@ const TeamMemberSchedule = ({
|
||||
};
|
||||
|
||||
const TeamAvailability = ({
|
||||
hosts,
|
||||
teamMembers,
|
||||
hostSchedulesQuery,
|
||||
}: EventTypeTeamScheduleProps & { teamMembers: TeamMember[] }) => {
|
||||
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">
|
||||
@@ -439,42 +438,41 @@ const TeamAvailability = ({
|
||||
);
|
||||
};
|
||||
|
||||
const useCommonScheduleState = (initialScheduleId: number | null) => {
|
||||
const { setValue } = useFormContext<FormValues>();
|
||||
const [useHostSchedulesForTeamEvent, setUseHostSchedulesForTeamEvent] = useState(!initialScheduleId);
|
||||
// Reset the main schedule
|
||||
const clearMainSchedule = () => {
|
||||
setValue("schedule", null, { shouldDirty: Boolean(initialScheduleId) });
|
||||
};
|
||||
// Toggle function
|
||||
const toggleScheduleState = (checked: boolean) => {
|
||||
const useHostSchedulesForTeamEvent = !checked;
|
||||
setUseHostSchedulesForTeamEvent(useHostSchedulesForTeamEvent);
|
||||
if (useHostSchedulesForTeamEvent) clearMainSchedule();
|
||||
};
|
||||
return {
|
||||
useHostSchedulesForTeamEvent,
|
||||
toggleScheduleState,
|
||||
};
|
||||
};
|
||||
|
||||
const UseCommonScheduleSettingsToggle = ({ eventType, ...rest }: EventTypeScheduleProps) => {
|
||||
const { t } = useLocale();
|
||||
const { setValue, resetField, getValues, watch } = useFormContext<FormValues>();
|
||||
|
||||
const [useHostSchedulesForTeamEvent, setUseHostSchedulesForTeamEvent] = useState(
|
||||
!Boolean(eventType.schedule)
|
||||
);
|
||||
|
||||
const watchHosts = watch("hosts");
|
||||
|
||||
const { useHostSchedulesForTeamEvent, toggleScheduleState } = useCommonScheduleState(eventType.schedule);
|
||||
return (
|
||||
<>
|
||||
<SettingsToggle
|
||||
checked={!useHostSchedulesForTeamEvent}
|
||||
onCheckedChange={(checked) => {
|
||||
setUseHostSchedulesForTeamEvent(!checked);
|
||||
if (checked) {
|
||||
if (Boolean(eventType.schedule)) resetField("schedule");
|
||||
getValues("hosts").map((_, index) => {
|
||||
setValue(`hosts.${index}.scheduleId`, null, { shouldDirty: true });
|
||||
});
|
||||
} else {
|
||||
setValue("schedule", null, { shouldDirty: Boolean(eventType.schedule) });
|
||||
}
|
||||
}}
|
||||
onCheckedChange={toggleScheduleState}
|
||||
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} />
|
||||
</SettingsToggle>
|
||||
{useHostSchedulesForTeamEvent && (
|
||||
<div className="lg:ml-14">
|
||||
<TeamAvailability
|
||||
hosts={watchHosts}
|
||||
teamMembers={rest.teamMembers}
|
||||
hostSchedulesQuery={rest.hostSchedulesQuery}
|
||||
/>
|
||||
<TeamAvailability teamMembers={rest.teamMembers} hostSchedulesQuery={rest.hostSchedulesQuery} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -37,6 +37,33 @@ export type TeamMember = {
|
||||
defaultScheduleId: number | null;
|
||||
};
|
||||
|
||||
type EventLocation = {
|
||||
type: EventLocationType["type"];
|
||||
address?: string;
|
||||
attendeeAddress?: string;
|
||||
somewhereElse?: string;
|
||||
link?: string;
|
||||
hostPhoneNumber?: string;
|
||||
displayLocationPublicly?: boolean;
|
||||
phone?: string;
|
||||
hostDefault?: string;
|
||||
credentialId?: number;
|
||||
teamName?: string;
|
||||
};
|
||||
|
||||
type PhoneCallConfig = {
|
||||
generalPrompt: string;
|
||||
enabled: boolean;
|
||||
beginMessage: string;
|
||||
yourPhoneNumber: string;
|
||||
numberToCall: string;
|
||||
guestName?: string;
|
||||
guestEmail?: string;
|
||||
guestCompany?: string;
|
||||
templateType: string;
|
||||
schedulerName?: string;
|
||||
};
|
||||
|
||||
export type FormValues = {
|
||||
id: number;
|
||||
title: string;
|
||||
@@ -59,31 +86,8 @@ export type FormValues = {
|
||||
hideCalendarNotes: boolean;
|
||||
multiplePrivateLinks: string[] | undefined;
|
||||
eventTypeColor: z.infer<typeof eventTypeColor>;
|
||||
locations: {
|
||||
type: EventLocationType["type"];
|
||||
address?: string;
|
||||
attendeeAddress?: string;
|
||||
somewhereElse?: string;
|
||||
link?: string;
|
||||
hostPhoneNumber?: string;
|
||||
displayLocationPublicly?: boolean;
|
||||
phone?: string;
|
||||
hostDefault?: string;
|
||||
credentialId?: number;
|
||||
teamName?: string;
|
||||
}[];
|
||||
aiPhoneCallConfig: {
|
||||
generalPrompt: string;
|
||||
enabled: boolean;
|
||||
beginMessage: string;
|
||||
yourPhoneNumber: string;
|
||||
numberToCall: string;
|
||||
guestName?: string;
|
||||
guestEmail?: string;
|
||||
guestCompany?: string;
|
||||
templateType: string;
|
||||
schedulerName?: string;
|
||||
};
|
||||
locations: EventLocation[];
|
||||
aiPhoneCallConfig: PhoneCallConfig;
|
||||
customInputs: CustomInputParsed[];
|
||||
schedule: number | null;
|
||||
|
||||
|
||||
@@ -5,77 +5,84 @@ import { _DestinationCalendarModel, _EventTypeModel } from "@calcom/prisma/zod";
|
||||
import { customInputSchema, EventTypeMetaDataSchema, stringOrNumber } from "@calcom/prisma/zod-utils";
|
||||
import { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
|
||||
|
||||
export const EventTypeUpdateInput = _EventTypeModel
|
||||
/** Optional fields */
|
||||
const aiPhoneCallConfig = z
|
||||
.object({
|
||||
generalPrompt: z.string(),
|
||||
enabled: z.boolean(),
|
||||
beginMessage: z.string().nullable(),
|
||||
yourPhoneNumber: z.string(),
|
||||
numberToCall: z.string(),
|
||||
guestName: z.string().nullable().optional(),
|
||||
guestEmail: z.string().nullable().optional(),
|
||||
guestCompany: z.string().nullable().optional(),
|
||||
templateType: templateTypeEnum,
|
||||
})
|
||||
.optional();
|
||||
|
||||
const hostSchema = z.object({
|
||||
userId: z.number(),
|
||||
profileId: z.number().or(z.null()).optional(),
|
||||
isFixed: z.boolean().optional(),
|
||||
priority: z.number().min(0).max(4).optional().nullable(),
|
||||
weight: z.number().min(0).optional().nullable(),
|
||||
scheduleId: z.number().optional().nullable(),
|
||||
});
|
||||
|
||||
const childSchema = z.object({
|
||||
owner: z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
email: z.string(),
|
||||
eventTypeSlugs: z.array(z.string()),
|
||||
}),
|
||||
hidden: z.boolean(),
|
||||
});
|
||||
|
||||
/** Optional fields */
|
||||
const BaseEventTypeUpdateInput = _EventTypeModel
|
||||
.extend({
|
||||
isInstantEvent: z.boolean().optional(),
|
||||
instantMeetingExpiryTimeOffsetInSeconds: z.number().optional(),
|
||||
aiPhoneCallConfig: z
|
||||
.object({
|
||||
generalPrompt: z.string(),
|
||||
enabled: z.boolean(),
|
||||
beginMessage: z.string().nullable(),
|
||||
yourPhoneNumber: z.string().default(""),
|
||||
numberToCall: z.string().default(""),
|
||||
guestName: z
|
||||
.string()
|
||||
.nullable()
|
||||
.optional()
|
||||
.transform((val) => (!!val ? val : undefined)),
|
||||
guestEmail: z.string().nullable().default(null),
|
||||
guestCompany: z.string().nullable().default(null),
|
||||
templateType: templateTypeEnum,
|
||||
})
|
||||
.optional(),
|
||||
calAiPhoneScript: z.string().optional(),
|
||||
customInputs: z.array(customInputSchema).optional(),
|
||||
isInstantEvent: z.boolean(),
|
||||
instantMeetingExpiryTimeOffsetInSeconds: z.number(),
|
||||
aiPhoneCallConfig,
|
||||
calAiPhoneScript: z.string(),
|
||||
customInputs: z.array(customInputSchema),
|
||||
destinationCalendar: _DestinationCalendarModel
|
||||
.pick({
|
||||
integration: true,
|
||||
externalId: true,
|
||||
})
|
||||
.nullable(),
|
||||
users: z.array(stringOrNumber).optional(),
|
||||
children: z
|
||||
.array(
|
||||
z.object({
|
||||
owner: z.object({
|
||||
id: z.number(),
|
||||
name: z.string(),
|
||||
email: z.string(),
|
||||
eventTypeSlugs: z.array(z.string()),
|
||||
}),
|
||||
hidden: z.boolean(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
hosts: z
|
||||
.array(
|
||||
z.object({
|
||||
userId: z.number(),
|
||||
profileId: z.number().or(z.null()).optional(),
|
||||
isFixed: z.boolean().optional(),
|
||||
priority: z.number().min(0).max(4).optional().nullable(),
|
||||
weight: z.number().min(0).optional().nullable(),
|
||||
scheduleId: z.number().optional().nullable(),
|
||||
})
|
||||
)
|
||||
.optional(),
|
||||
schedule: z.number().nullable().optional(),
|
||||
instantMeetingSchedule: z.number().nullable().optional(),
|
||||
users: z.array(stringOrNumber),
|
||||
children: z.array(childSchema),
|
||||
hosts: z.array(hostSchema),
|
||||
schedule: z.number().nullable(),
|
||||
instantMeetingSchedule: z.number().nullable(),
|
||||
multiplePrivateLinks: z.array(z.string()),
|
||||
assignAllTeamMembers: z.boolean().optional(),
|
||||
isRRWeightsEnabled: z.boolean().optional(),
|
||||
assignAllTeamMembers: z.boolean(),
|
||||
isRRWeightsEnabled: z.boolean(),
|
||||
metadata: EventTypeMetaDataSchema,
|
||||
bookingFields: eventTypeBookingFields,
|
||||
})
|
||||
.partial()
|
||||
.extend({
|
||||
metadata: EventTypeMetaDataSchema.optional(),
|
||||
bookingFields: eventTypeBookingFields.optional(),
|
||||
})
|
||||
.merge(
|
||||
_EventTypeModel
|
||||
/** Required fields */
|
||||
.pick({
|
||||
id: true,
|
||||
})
|
||||
);
|
||||
.extend(_EventTypeModel.pick({ id: true }).shape);
|
||||
|
||||
const ZUpdateInputSchema = BaseEventTypeUpdateInput.extend({
|
||||
aiPhoneCallConfig: aiPhoneCallConfig.refine(
|
||||
(data) => {
|
||||
if (!data) return true;
|
||||
data.yourPhoneNumber = data.yourPhoneNumber || "";
|
||||
data.numberToCall = data.numberToCall || "";
|
||||
data.guestName = data.guestName ?? undefined;
|
||||
data.guestEmail = data.guestEmail ?? null;
|
||||
data.guestCompany = data.guestCompany ?? null;
|
||||
return true;
|
||||
},
|
||||
{
|
||||
message: "Applying default values and transformations",
|
||||
}
|
||||
),
|
||||
}).strict();
|
||||
// only run infer over the simple type, excluding refines/transforms.
|
||||
type TUpdateInputSchema = z.infer<typeof BaseEventTypeUpdateInput>;
|
||||
|
||||
export { ZUpdateInputSchema, type TUpdateInputSchema };
|
||||
|
||||
@@ -1,7 +1,2 @@
|
||||
import type { z } from "zod";
|
||||
|
||||
import { EventTypeUpdateInput } from "./types";
|
||||
|
||||
export const ZUpdateInputSchema = EventTypeUpdateInput.strict();
|
||||
|
||||
export type TUpdateInputSchema = z.infer<typeof ZUpdateInputSchema>;
|
||||
export { ZUpdateInputSchema } from "./types";
|
||||
export type { TUpdateInputSchema } from "./types";
|
||||
|
||||
@@ -12,7 +12,7 @@ import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
import authedProcedure from "../../../procedures/authedProcedure";
|
||||
import type { EventTypeUpdateInput } from "./types";
|
||||
import type { TUpdateInputSchema } from "./types";
|
||||
|
||||
type EventType = Awaited<ReturnType<typeof EventTypeRepository.findAllByUpId>>[number];
|
||||
|
||||
@@ -142,7 +142,7 @@ export function handleCustomInputs(customInputs: CustomInputSchema[], eventTypeI
|
||||
};
|
||||
}
|
||||
|
||||
export function ensureUniqueBookingFields(fields: z.infer<typeof EventTypeUpdateInput>["bookingFields"]) {
|
||||
export function ensureUniqueBookingFields(fields: TUpdateInputSchema["bookingFields"]) {
|
||||
if (!fields) {
|
||||
return;
|
||||
}
|
||||
@@ -161,9 +161,7 @@ export function ensureUniqueBookingFields(fields: z.infer<typeof EventTypeUpdate
|
||||
}, {} as Record<string, true>);
|
||||
}
|
||||
|
||||
export function ensureEmailOrPhoneNumberIsPresent(
|
||||
fields: z.infer<typeof EventTypeUpdateInput>["bookingFields"]
|
||||
) {
|
||||
export function ensureEmailOrPhoneNumberIsPresent(fields: TUpdateInputSchema["bookingFields"]) {
|
||||
if (!fields || fields.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user