feat: add instant meeting parameter (#17647)
* feat: add instant meeting parameter * feat: show modal only when parameter present * chore: remove logs * fix: type err * fix: UI * fix: remove useFieldArray
This commit is contained in:
1 parent
e6633050ba
commit
0f3773d724
16 files changed
+97
-16
No files matched your search
@@ -2218,6 +2218,8 @@
|
||||
"add_times_to_your_email": "Select a few available times and embed them in your Email",
|
||||
"select_time": "Select Time",
|
||||
"select_date": "Select Date",
|
||||
"only_show_if_parameter_set": "Only show if a parameter is set",
|
||||
"add_parameter": "Add parameter",
|
||||
"connecting_you_to_someone": "We are connecting you to someone from {{orgName}}.",
|
||||
"please_do_not_close_this_tab": "Please do not close or refresh this page",
|
||||
"see_all_available_times": "See all available times",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AnimatePresence, LazyMotion, m } from "framer-motion";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import StickyBox from "react-sticky-box";
|
||||
@@ -68,6 +69,13 @@ const BookerComponent = ({
|
||||
customClassNames,
|
||||
}: BookerProps & WrappedBookerProps) => {
|
||||
const { t } = useLocale();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const areInstantMeetingParametersSet = !!event.data?.instantMeetingParameters
|
||||
? searchParams &&
|
||||
event.data.instantMeetingParameters.every((param) => Array.from(searchParams.values()).includes(param))
|
||||
: true;
|
||||
|
||||
const [bookerState, setBookerState] = useBookerStore((state) => [state.state, state.setState], shallow);
|
||||
const selectedDate = useBookerStore((state) => state.selectedDate);
|
||||
const {
|
||||
@@ -441,21 +449,23 @@ const BookerComponent = ({
|
||||
}}
|
||||
/>
|
||||
|
||||
{bookerState !== "booking" && event.data?.showInstantEventConnectNowModal && (
|
||||
<div
|
||||
className={classNames(
|
||||
"animate-fade-in-up z-40 my-2 opacity-0",
|
||||
layout === BookerLayouts.MONTH_VIEW && isEmbed ? "" : "fixed bottom-2"
|
||||
)}
|
||||
style={{ animationDelay: "1s" }}>
|
||||
<InstantBooking
|
||||
event={event.data}
|
||||
onConnectNow={() => {
|
||||
onConnectNowInstantMeeting();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{bookerState !== "booking" &&
|
||||
event.data?.showInstantEventConnectNowModal &&
|
||||
areInstantMeetingParametersSet && (
|
||||
<div
|
||||
className={classNames(
|
||||
"animate-fade-in-up z-40 my-2 opacity-0",
|
||||
layout === BookerLayouts.MONTH_VIEW && isEmbed ? "" : "fixed bottom-2"
|
||||
)}
|
||||
style={{ animationDelay: "1s" }}>
|
||||
<InstantBooking
|
||||
event={event.data}
|
||||
onConnectNow={() => {
|
||||
onConnectNowInstantMeeting();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!hideBranding && !isPlatform && (
|
||||
<m.span
|
||||
key="logo"
|
||||
|
||||
@@ -51,6 +51,7 @@ export type BookerEvent = Pick<
|
||||
| "bookingFields"
|
||||
| "seatsShowAvailabilityCount"
|
||||
| "isInstantEvent"
|
||||
| "instantMeetingParameters"
|
||||
| "fieldTranslations"
|
||||
| "autoTranslateDescriptionEnabled"
|
||||
> & { users: BookerEventUser[]; showInstantEventConnectNowModal: boolean } & { profile: BookerEventProfile };
|
||||
|
||||
@@ -76,6 +76,10 @@ export default function InstantEventController({
|
||||
const [instantEventState, setInstantEventState] = useState<boolean>(eventType?.isInstantEvent ?? false);
|
||||
const formMethods = useFormContext<FormValues>();
|
||||
|
||||
const [parameters, setParameters] = useState<string[]>(
|
||||
formMethods.getValues("instantMeetingParameters") || []
|
||||
);
|
||||
|
||||
const { shouldLockDisableProps } = useLockedFieldsManager({
|
||||
eventType,
|
||||
translate: t,
|
||||
@@ -171,6 +175,56 @@ export default function InstantEventController({
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<Label>{t("only_show_if_parameter_set")}</Label>
|
||||
<div className="space-y-2">
|
||||
{parameters.map((parameter, index) => (
|
||||
<div key={index} className="flex gap-2">
|
||||
<TextField
|
||||
required
|
||||
name={`parameter-${index}`}
|
||||
labelSrOnly
|
||||
type="text"
|
||||
value={parameter}
|
||||
containerClassName="flex-1 max-w-80"
|
||||
onChange={(e) => {
|
||||
const newParameters = [...parameters];
|
||||
newParameters[index] = e.target.value;
|
||||
setParameters(newParameters);
|
||||
formMethods.setValue("instantMeetingParameters", newParameters, {
|
||||
shouldDirty: true,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
color="destructive"
|
||||
variant="icon"
|
||||
StartIcon="trash"
|
||||
onClick={() => {
|
||||
const newParameters = parameters.filter((_, i) => i !== index);
|
||||
setParameters(newParameters);
|
||||
formMethods.setValue("instantMeetingParameters", newParameters, {
|
||||
shouldDirty: true,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Button
|
||||
color="minimal"
|
||||
StartIcon="plus"
|
||||
onClick={() => {
|
||||
const newParameters = [...parameters, ""];
|
||||
setParameters(newParameters);
|
||||
formMethods.setValue("instantMeetingParameters", newParameters, {
|
||||
shouldDirty: true,
|
||||
});
|
||||
}}>
|
||||
{t("add_parameter")}
|
||||
</Button>
|
||||
</div>
|
||||
<Controller
|
||||
name="instantMeetingExpiryTimeOffsetInSeconds"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
|
||||
@@ -57,6 +57,7 @@ const publicEventSelect = Prisma.validator<Prisma.EventTypeSelect>()({
|
||||
eventName: true,
|
||||
slug: true,
|
||||
isInstantEvent: true,
|
||||
instantMeetingParameters: true,
|
||||
aiPhoneCallConfig: true,
|
||||
schedulingType: true,
|
||||
length: true,
|
||||
@@ -299,6 +300,7 @@ export const getPublicEvent = async (
|
||||
logoUrl: null,
|
||||
},
|
||||
isInstantEvent: false,
|
||||
instantMeetingParameters: [],
|
||||
showInstantEventConnectNowModal: false,
|
||||
autoTranslateDescriptionEnabled: false,
|
||||
fieldTranslations: [],
|
||||
@@ -455,7 +457,6 @@ export const getPublicEvent = async (
|
||||
if (event.team?.isPrivate && !isTeamAdminOrOwner && !isOrgAdminOrOwner) {
|
||||
users = [];
|
||||
}
|
||||
|
||||
return {
|
||||
...eventWithUserProfiles,
|
||||
bookerLayouts: bookerLayoutsSchema.parse(eventMetaData?.bookerLayouts || null),
|
||||
@@ -495,6 +496,7 @@ export const getPublicEvent = async (
|
||||
isDynamic: false,
|
||||
isInstantEvent: eventWithUserProfiles.isInstantEvent,
|
||||
showInstantEventConnectNowModal,
|
||||
instantMeetingParameters: eventWithUserProfiles.instantMeetingParameters,
|
||||
aiPhoneCallConfig: eventWithUserProfiles.aiPhoneCallConfig,
|
||||
assignAllTeamMembers: event.assignAllTeamMembers,
|
||||
};
|
||||
|
||||
@@ -71,6 +71,7 @@ export type FormValues = {
|
||||
eventName: string;
|
||||
slug: string;
|
||||
isInstantEvent: boolean;
|
||||
instantMeetingParameters: string[];
|
||||
instantMeetingExpiryTimeOffsetInSeconds: number;
|
||||
length: number;
|
||||
offsetStart: number;
|
||||
|
||||
@@ -44,6 +44,7 @@ export const eventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
|
||||
successRedirectUrl: true,
|
||||
isInstantEvent: true,
|
||||
instantMeetingExpiryTimeOffsetInSeconds: true,
|
||||
instantMeetingParameters: true,
|
||||
aiPhoneCallConfig: true,
|
||||
assignAllTeamMembers: true,
|
||||
isRRWeightsEnabled: true,
|
||||
|
||||
@@ -428,6 +428,7 @@ export class EventTypeRepository {
|
||||
length: true,
|
||||
isInstantEvent: true,
|
||||
instantMeetingExpiryTimeOffsetInSeconds: true,
|
||||
instantMeetingParameters: true,
|
||||
aiPhoneCallConfig: true,
|
||||
offsetStart: true,
|
||||
hidden: true,
|
||||
|
||||
@@ -88,6 +88,7 @@ export const buildEventType = (eventType?: Partial<EventType>): EventType => {
|
||||
description: faker.lorem.paragraph(),
|
||||
position: 1,
|
||||
isInstantEvent: false,
|
||||
instantMeetingParameters: [],
|
||||
instantMeetingExpiryTimeOffsetInSeconds: 90,
|
||||
instantMeetingScheduleId: null,
|
||||
locations: null,
|
||||
|
||||
@@ -63,6 +63,7 @@ export const useEventTypeForm = ({
|
||||
destinationCalendar: eventType.destinationCalendar,
|
||||
recurringEvent: eventType.recurringEvent || null,
|
||||
isInstantEvent: eventType.isInstantEvent,
|
||||
instantMeetingParameters: eventType.instantMeetingParameters || [],
|
||||
instantMeetingExpiryTimeOffsetInSeconds: eventType.instantMeetingExpiryTimeOffsetInSeconds,
|
||||
description: eventType.description ?? undefined,
|
||||
schedule: eventType.schedule || undefined,
|
||||
|
||||
@@ -59,6 +59,7 @@ export type EventType = {
|
||||
bookingLimits: number | null;
|
||||
durationLimits: number | null;
|
||||
isInstantEvent: boolean;
|
||||
instantMeetingParameters: string[];
|
||||
instantMeetingExpiryTimeOffsetInSeconds: number;
|
||||
assignAllTeamMembers: boolean;
|
||||
useEventTypeDestinationCalendarEmail: boolean;
|
||||
|
||||
@@ -45,6 +45,7 @@ export type Event = {
|
||||
eventName: string;
|
||||
slug: string;
|
||||
isInstantEvent: boolean;
|
||||
instantMeetingParameters: string[];
|
||||
instantMeetingExpiryTimeOffsetInSeconds: number;
|
||||
aiPhoneCallConfig: {
|
||||
eventTypeId: number;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "EventType" ADD COLUMN "instantMeetingParameters" TEXT[];
|
||||
@@ -146,6 +146,7 @@ model EventType {
|
||||
instantMeetingExpiryTimeOffsetInSeconds Int @default(90)
|
||||
instantMeetingScheduleId Int?
|
||||
instantMeetingSchedule Schedule? @relation("InstantMeetingSchedule", fields: [instantMeetingScheduleId], references: [id])
|
||||
instantMeetingParameters String[]
|
||||
assignAllTeamMembers Boolean @default(false)
|
||||
useEventTypeDestinationCalendarEmail Boolean @default(false)
|
||||
aiPhoneCallConfig AIPhoneCallConfiguration?
|
||||
|
||||
@@ -649,6 +649,7 @@ export const allManagedEventTypeProps: { [k in keyof Omit<Prisma.EventTypeSelect
|
||||
title: true,
|
||||
description: true,
|
||||
isInstantEvent: true,
|
||||
instantMeetingParameters: true,
|
||||
instantMeetingExpiryTimeOffsetInSeconds: true,
|
||||
aiPhoneCallConfig: true,
|
||||
currency: true,
|
||||
|
||||
@@ -42,6 +42,7 @@ const childSchema = z.object({
|
||||
const BaseEventTypeUpdateInput = _EventTypeModel
|
||||
.extend({
|
||||
isInstantEvent: z.boolean(),
|
||||
instantMeetingParameters: z.array(z.string()),
|
||||
instantMeetingExpiryTimeOffsetInSeconds: z.number(),
|
||||
aiPhoneCallConfig,
|
||||
calAiPhoneScript: z.string(),
|
||||
|
||||
Reference in new issue
Block a user