chore: booker atom username / teams props enabled queries (#16514)
This commit is contained in:
@@ -1,10 +1,20 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsEnum } from "class-validator";
|
||||
import { IsEnum, IsNumber, IsString } from "class-validator";
|
||||
|
||||
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
|
||||
|
||||
export class ProviderVerifyClientData {
|
||||
@IsString()
|
||||
clientId!: string;
|
||||
@IsNumber()
|
||||
organizationId!: number;
|
||||
@IsString()
|
||||
name!: string;
|
||||
}
|
||||
export class ProviderVerifyClientOutput {
|
||||
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
|
||||
@IsEnum([SUCCESS_STATUS, ERROR_STATUS])
|
||||
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
|
||||
|
||||
data!: ProviderVerifyClientData;
|
||||
}
|
||||
|
||||
@@ -40,6 +40,11 @@ export class CalProviderController {
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: {
|
||||
clientId: oAuthClient.id,
|
||||
organizationId: oAuthClient.organizationId,
|
||||
name: oAuthClient.name,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -9313,6 +9313,25 @@
|
||||
"status"
|
||||
]
|
||||
},
|
||||
"ProviderVerifyClientData": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"clientId": {
|
||||
"type": "string"
|
||||
},
|
||||
"organizationId": {
|
||||
"type": "number"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"clientId",
|
||||
"organizationId",
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"ProviderVerifyClientOutput": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -9323,10 +9342,14 @@
|
||||
"success",
|
||||
"error"
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"$ref": "#/components/schemas/ProviderVerifyClientData"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"status"
|
||||
"status",
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"ProviderVerifyAccessTokenOutput": {
|
||||
|
||||
@@ -45,11 +45,13 @@ import { useMe } from "../hooks/useMe";
|
||||
import { useSlots } from "../hooks/useSlots";
|
||||
import { AtomsWrapper } from "../src/components/atoms-wrapper";
|
||||
|
||||
export type BookerPlatformWrapperAtomProps = Omit<BookerProps, "username" | "entity"> & {
|
||||
export type BookerPlatformWrapperAtomProps = Omit<
|
||||
BookerProps,
|
||||
"username" | "entity" | "isTeamEvent" | "teamId"
|
||||
> & {
|
||||
rescheduleUid?: string;
|
||||
rescheduledBy?: string;
|
||||
bookingUid?: string;
|
||||
username: string | string[] | undefined;
|
||||
entity?: BookerProps["entity"];
|
||||
// values for the booking form and booking fields
|
||||
defaultFormValues?: {
|
||||
@@ -73,11 +75,24 @@ export type BookerPlatformWrapperAtomProps = Omit<BookerProps, "username" | "ent
|
||||
onDeleteSlotSuccess?: (data: ApiSuccessResponseWithoutData) => void;
|
||||
onDeleteSlotError?: (data: ApiErrorResponse) => void;
|
||||
locationUrl?: string;
|
||||
teamId?: number;
|
||||
};
|
||||
|
||||
export const BookerPlatformWrapper = (props: BookerPlatformWrapperAtomProps) => {
|
||||
type BookerPlatformWrapperAtomPropsForIndividual = BookerPlatformWrapperAtomProps & {
|
||||
username: string | string[];
|
||||
isTeamEvent?: false;
|
||||
};
|
||||
|
||||
type BookerPlatformWrapperAtomPropsForTeam = BookerPlatformWrapperAtomProps & {
|
||||
username?: string | string[];
|
||||
isTeamEvent: true;
|
||||
teamId: number;
|
||||
};
|
||||
|
||||
export const BookerPlatformWrapper = (
|
||||
props: BookerPlatformWrapperAtomPropsForIndividual | BookerPlatformWrapperAtomPropsForTeam
|
||||
) => {
|
||||
const { clientId } = useAtomsContext();
|
||||
const teamId: number | undefined = props.isTeamEvent ? props.teamId : undefined;
|
||||
const [bookerState, setBookerState] = useBookerStore((state) => [state.state, state.setState], shallow);
|
||||
const setSelectedDate = useBookerStore((state) => state.setSelectedDate);
|
||||
const setSelectedDuration = useBookerStore((state) => state.setSelectedDuration);
|
||||
@@ -112,18 +127,18 @@ export const BookerPlatformWrapper = (props: BookerPlatformWrapperAtomProps) =>
|
||||
isSuccess: isTeamSuccess,
|
||||
isError: isTeamError,
|
||||
isPending: isTeamPending,
|
||||
data: teamData,
|
||||
} = useTeamEventType(props.teamId, props.eventSlug, props.isTeamEvent);
|
||||
data: teamEventTypeData,
|
||||
} = useTeamEventType(teamId, props.eventSlug, props.isTeamEvent);
|
||||
|
||||
const event = useMemo(() => {
|
||||
if (props.isTeamEvent) {
|
||||
if (props.isTeamEvent && !isTeamPending && teamId && teamEventTypeData && teamEventTypeData.length > 0) {
|
||||
return {
|
||||
isSuccess: isTeamSuccess,
|
||||
isError: isTeamError,
|
||||
isPending: isTeamPending,
|
||||
data:
|
||||
teamData && teamData.length > 0
|
||||
? transformApiTeamEventTypeForAtom(teamData[0], props.entity)
|
||||
teamEventTypeData && teamEventTypeData.length > 0
|
||||
? transformApiTeamEventTypeForAtom(teamEventTypeData[0], props.entity)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
@@ -136,15 +151,16 @@ export const BookerPlatformWrapper = (props: BookerPlatformWrapperAtomProps) =>
|
||||
};
|
||||
}, [
|
||||
props.isTeamEvent,
|
||||
teamId,
|
||||
props.entity,
|
||||
teamEventTypeData,
|
||||
isSuccess,
|
||||
isError,
|
||||
isPending,
|
||||
data,
|
||||
isTeamPending,
|
||||
isTeamSuccess,
|
||||
isTeamError,
|
||||
isTeamPending,
|
||||
teamData,
|
||||
]);
|
||||
|
||||
if (isDynamic && props.duration && event.data) {
|
||||
@@ -212,19 +228,25 @@ export const BookerPlatformWrapper = (props: BookerPlatformWrapperAtomProps) =>
|
||||
});
|
||||
|
||||
const schedule = useAvailableSlots({
|
||||
usernameList: getUsernameList(username ?? ""),
|
||||
usernameList: getUsernameList(username),
|
||||
eventTypeId: event?.data?.id ?? 0,
|
||||
startTime,
|
||||
endTime,
|
||||
timeZone: session?.data?.timeZone,
|
||||
duration: selectedDuration ?? undefined,
|
||||
rescheduleUid: props.rescheduleUid,
|
||||
...(props.isTeamEvent
|
||||
? {
|
||||
isTeamEvent: props.isTeamEvent,
|
||||
teamId: teamId,
|
||||
}
|
||||
: {}),
|
||||
enabled:
|
||||
Boolean(username) &&
|
||||
Boolean(teamId || username) &&
|
||||
Boolean(month) &&
|
||||
Boolean(timezone) &&
|
||||
// Should only wait for one or the other, not both.
|
||||
(Boolean(eventSlug) || Boolean(event?.data?.id) || event?.data?.id === 0),
|
||||
(props.isTeamEvent ? !isTeamPending : !isPending) &&
|
||||
Boolean(event?.data?.id),
|
||||
orgSlug: props.entity?.orgSlug ?? undefined,
|
||||
eventTypeSlug: isDynamic ? "dynamic" : eventSlug || "",
|
||||
});
|
||||
|
||||
@@ -37,9 +37,12 @@ export function BaseCalProvider({
|
||||
labels,
|
||||
autoUpdateTimezone,
|
||||
language = EN,
|
||||
organizationId,
|
||||
onTimezoneChange,
|
||||
}: CalProviderProps) {
|
||||
const [error, setError] = useState<string>("");
|
||||
const [stateOrgId, setOrganizationId] = useState<number>(0);
|
||||
|
||||
const { data: me } = useMe();
|
||||
|
||||
const { mutateAsync } = useUpdateUserTimezone();
|
||||
@@ -64,8 +67,9 @@ export function BaseCalProvider({
|
||||
apiUrl: options.apiUrl,
|
||||
refreshUrl: options.refreshUrl,
|
||||
onError: setError,
|
||||
onSuccess: () => {
|
||||
onSuccess: (data) => {
|
||||
setError("");
|
||||
setOrganizationId(data.organizationId);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -120,7 +124,7 @@ export function BaseCalProvider({
|
||||
isInit: isInit,
|
||||
isValidClient: Boolean(!error && clientId && isInit),
|
||||
isAuth: Boolean(isInit && !error && clientId && currentAccessToken && http.getAuthorizationHeader()),
|
||||
organizationId: me?.data.organizationId || 0,
|
||||
organizationId: organizationId || stateOrgId || me?.data.organizationId || 0,
|
||||
...translations,
|
||||
}}>
|
||||
<TooltipProvider>{children}</TooltipProvider>
|
||||
|
||||
@@ -71,6 +71,7 @@ export type CalProviderProps = {
|
||||
autoUpdateTimezone?: boolean;
|
||||
onTimezoneChange?: () => void;
|
||||
version?: API_VERSIONS_ENUM;
|
||||
organizationId?: number;
|
||||
} & i18nProps;
|
||||
|
||||
/**
|
||||
@@ -97,6 +98,7 @@ export function CalProvider({
|
||||
language = "en",
|
||||
onTimezoneChange,
|
||||
version = VERSION_2024_06_14,
|
||||
organizationId,
|
||||
}: CalProviderProps) {
|
||||
useEffect(() => {
|
||||
http.setVersionHeader(version);
|
||||
@@ -118,7 +120,8 @@ export function CalProvider({
|
||||
options={options}
|
||||
version={version}
|
||||
labels={labels as Record<translationKeys, string>}
|
||||
language={language}>
|
||||
language={language}
|
||||
organizationId={organizationId}>
|
||||
{children}
|
||||
</BaseCalProvider>
|
||||
</QueryClientProvider>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useMemo } from "react";
|
||||
import { shallow } from "zustand/shallow";
|
||||
|
||||
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
|
||||
import { getUsernameList } from "@calcom/lib/defaultEvents";
|
||||
import { SUCCESS_STATUS, V2_ENDPOINTS } from "@calcom/platform-constants";
|
||||
import type { EventTypeOutput_2024_06_14 } from "@calcom/platform-types";
|
||||
@@ -14,20 +12,17 @@ export const QUERY_KEY = "use-event-type";
|
||||
export type UsePublicEventReturnType = ReturnType<typeof useEventType>;
|
||||
|
||||
export const useEventType = (username: string, eventSlug: string, isTeamEvent: boolean | undefined) => {
|
||||
const [stateUsername, stateEventSlug] = useBookerStore(
|
||||
(state) => [state.username, state.eventSlug],
|
||||
shallow
|
||||
);
|
||||
|
||||
const requestUsername = stateUsername ?? username;
|
||||
const requestEventSlug = stateEventSlug ?? eventSlug;
|
||||
|
||||
const requestUsername = username;
|
||||
const requestEventSlug = eventSlug;
|
||||
|
||||
const isDynamic = useMemo(() => {
|
||||
return getUsernameList(requestUsername ?? "").length > 1;
|
||||
}, [requestUsername]);
|
||||
|
||||
const event = useQuery({
|
||||
queryKey: [QUERY_KEY, stateUsername ?? username, stateEventSlug ?? eventSlug],
|
||||
queryKey: [QUERY_KEY, username, eventSlug],
|
||||
queryFn: async () => {
|
||||
if (isTeamEvent) {
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { shallow } from "zustand/shallow";
|
||||
|
||||
import { useBookerStore } from "@calcom/features/bookings/Booker/store";
|
||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import type { ApiSuccessResponse, TeamEventTypeOutput_2024_06_14 } from "@calcom/platform-types";
|
||||
import type { ApiResponse } from "@calcom/platform-types";
|
||||
@@ -14,12 +12,8 @@ export const QUERY_KEY = "use-team-event-type";
|
||||
export const useTeamEventType = (teamId: number | undefined, eventSlug: string, isTeamEvent: boolean | undefined) => {
|
||||
const { organizationId } = useAtomsContext();
|
||||
|
||||
const [stateEventSlug] = useBookerStore(
|
||||
(state) => [state.eventSlug],
|
||||
shallow
|
||||
);
|
||||
|
||||
const requestEventSlug = stateEventSlug ?? eventSlug;
|
||||
const requestEventSlug = eventSlug;
|
||||
|
||||
const pathname = `/organizations/${organizationId}/teams/${teamId}/event-types?eventSlug=${requestEventSlug}`;
|
||||
|
||||
@@ -35,6 +29,7 @@ export const useTeamEventType = (teamId: number | undefined, eventSlug: string,
|
||||
});
|
||||
}
|
||||
},
|
||||
enabled: Boolean(organizationId) && Boolean(teamId) && Boolean(eventSlug) && Boolean(isTeamEvent),
|
||||
});
|
||||
|
||||
return event;
|
||||
|
||||
@@ -8,9 +8,21 @@ import http from "../lib/http";
|
||||
|
||||
export const QUERY_KEY = "get-available-slots";
|
||||
|
||||
export const useAvailableSlots = ({ enabled, ...rest }: GetAvailableSlotsInput & { enabled: boolean }) => {
|
||||
export const useAvailableSlots = ({
|
||||
enabled,
|
||||
...rest
|
||||
}: GetAvailableSlotsInput & { enabled: boolean; isTeamEvent?: boolean; teamId?: number }) => {
|
||||
const availableSlots = useQuery({
|
||||
queryKey: [QUERY_KEY, rest.startTime, rest.endTime, rest.eventTypeId, rest.eventTypeSlug],
|
||||
queryKey: [
|
||||
QUERY_KEY,
|
||||
rest.startTime,
|
||||
rest.endTime,
|
||||
rest.eventTypeId,
|
||||
rest.eventTypeSlug,
|
||||
rest.isTeamEvent ?? false,
|
||||
rest.teamId ?? false,
|
||||
rest.usernameList,
|
||||
],
|
||||
queryFn: () => {
|
||||
return http
|
||||
.get<ApiResponse<AvailableSlotsType>>("/slots/available", {
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { AxiosError } from "axios";
|
||||
import { useState, useEffect } from "react";
|
||||
import { usePrevious } from "react-use";
|
||||
|
||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import type { ApiResponse } from "@calcom/platform-types";
|
||||
|
||||
import http from "../lib/http";
|
||||
@@ -11,12 +12,11 @@ export interface useOAuthClientProps {
|
||||
apiUrl?: string;
|
||||
refreshUrl?: string;
|
||||
onError: (error: string) => void;
|
||||
onSuccess: () => void;
|
||||
onSuccess: (data: { client: string; organizationId: number; name: string }) => void;
|
||||
}
|
||||
export const useOAuthClient = ({ clientId, apiUrl, refreshUrl, onError, onSuccess }: useOAuthClientProps) => {
|
||||
const prevClientId = usePrevious(clientId);
|
||||
const [isInit, setIsInit] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (apiUrl && http.getUrl() !== apiUrl) {
|
||||
http.setUrl(apiUrl);
|
||||
@@ -31,9 +31,11 @@ export const useOAuthClient = ({ clientId, apiUrl, refreshUrl, onError, onSucces
|
||||
if (clientId && http.getUrl() && prevClientId !== clientId) {
|
||||
try {
|
||||
http
|
||||
.get<ApiResponse>(`/provider/${clientId}`)
|
||||
.then(() => {
|
||||
onSuccess();
|
||||
.get<ApiResponse<{ client: string; organizationId: number; name: string }>>(`/provider/${clientId}`)
|
||||
.then((response) => {
|
||||
if (response.data.status === SUCCESS_STATUS) {
|
||||
onSuccess(response.data.data);
|
||||
}
|
||||
http.setClientIdHeader(clientId);
|
||||
})
|
||||
.catch((err: AxiosError) => {
|
||||
|
||||
@@ -92,13 +92,10 @@ export default function Bookings(props: { calUsername: string; calEmail: string
|
||||
<h1>{eventTypeSlug}</h1>
|
||||
<Booker
|
||||
eventSlug={eventTypeSlug}
|
||||
username={props.calUsername ?? ""}
|
||||
onCreateBookingSuccess={(data) => {
|
||||
setBookingTitle(data.data.title ?? "");
|
||||
router.push(`/${data.data.uid}`);
|
||||
}}
|
||||
teamId={teams?.[0]?.id || 0}
|
||||
isTeamEvent={isTeamEvent}
|
||||
duration={eventTypeDuration}
|
||||
customClassNames={{
|
||||
bookerContainer: "!bg-[#F5F2FE] [&_button:!rounded-full] border-subtle border",
|
||||
@@ -113,6 +110,9 @@ export default function Bookings(props: { calUsername: string; calEmail: string
|
||||
availableTimes: "!bg-[#D7CEF5]",
|
||||
},
|
||||
}}
|
||||
{...(isTeamEvent
|
||||
? { isTeamEvent: true, teamId: teams?.[0]?.id || 0 }
|
||||
: { username: props.calUsername })}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -19,6 +19,7 @@ export class GetAvailableSlotsInput {
|
||||
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
usernameList?: string[];
|
||||
|
||||
@IsBoolean()
|
||||
|
||||
Reference in New Issue
Block a user