Skip to content

Commit d7dc5f0

Browse files
authored
feat: add instant meeting expiry input (#15555)
* feat: add instant meeting expiry input * fix: type err * chore: remove type * chore: update label
1 parent ec755b1 commit d7dc5f0

File tree

15 files changed

+67
-13
lines changed

15 files changed

+67
-13
lines changed

apps/web/components/eventtype/InstantEventController.tsx

+39-6
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Webhook } from "@prisma/client";
22
import { useSession } from "next-auth/react";
33
import type { EventTypeSetup } from "pages/event-types/[type]";
44
import { useState } from "react";
5-
import { useFormContext } from "react-hook-form";
5+
import { useFormContext, Controller } from "react-hook-form";
66

77
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
88
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
@@ -15,7 +15,17 @@ import { classNames } from "@calcom/lib";
1515
import { useLocale } from "@calcom/lib/hooks/useLocale";
1616
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
1717
import { trpc } from "@calcom/trpc/react";
18-
import { Alert, Button, EmptyScreen, SettingsToggle, Dialog, DialogContent, showToast } from "@calcom/ui";
18+
import {
19+
Alert,
20+
Button,
21+
EmptyScreen,
22+
SettingsToggle,
23+
Dialog,
24+
DialogContent,
25+
showToast,
26+
TextField,
27+
Label,
28+
} from "@calcom/ui";
1929

2030
type InstantEventControllerProps = {
2131
eventType: EventTypeSetup;
@@ -85,7 +95,33 @@ export default function InstantEventController({
8595
}
8696
}}>
8797
<div className="border-subtle rounded-b-lg border border-t-0 p-6">
88-
{instantEventState && <InstantMeetingWebhooks eventType={eventType} />}
98+
{instantEventState && (
99+
<div className="flex flex-col gap-2">
100+
<Controller
101+
name="instantMeetingExpiryTimeOffset"
102+
render={({ field: { value, onChange } }) => (
103+
<>
104+
<Label>{t("set_instant_meeting_expiry_time_offset_description")}</Label>
105+
<TextField
106+
required
107+
name="instantMeetingExpiryTimeOffset"
108+
labelSrOnly
109+
type="number"
110+
defaultValue={value}
111+
min={10}
112+
containerClassName="max-w-80"
113+
addOnSuffix={<>{t("seconds")}</>}
114+
onChange={(e) => {
115+
onChange(Math.abs(Number(e.target.value)));
116+
}}
117+
data-testid="instant-meeting-expiry-time-offset"
118+
/>
119+
</>
120+
)}
121+
/>
122+
<InstantMeetingWebhooks eventType={eventType} />
123+
</div>
124+
)}
89125
</div>
90126
</SettingsToggle>
91127
</>
@@ -213,9 +249,6 @@ const InstantMeetingWebhooks = ({ eventType }: { eventType: EventTypeSetup }) =>
213249
</>
214250
) : (
215251
<>
216-
<p className="text-default mb-4 text-sm font-normal">
217-
{t("warning_payment_instant_meeting_event")}
218-
</p>
219252
<EmptyScreen
220253
Icon="webhook"
221254
headline={t("create_your_first_webhook")}

apps/web/modules/event-types/views/event-types-single-view.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ const EventTypePage = (props: EventTypeSetupProps) => {
251251
destinationCalendar: eventType.destinationCalendar,
252252
recurringEvent: eventType.recurringEvent || null,
253253
isInstantEvent: eventType.isInstantEvent,
254+
instantMeetingExpiryTimeOffset: eventType.instantMeetingExpiryTimeOffset,
254255
description: eventType.description ?? undefined,
255256
schedule: eventType.schedule || undefined,
256257
bookingLimits: eventType.bookingLimits || undefined,

apps/web/public/static/locales/en/common.json

+1
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,7 @@
10431043
"seats_nearly_full": "Seats almost full",
10441044
"seats_half_full": "Seats filling fast",
10451045
"number_of_seats": "Number of seats per booking",
1046+
"set_instant_meeting_expiry_time_offset_description": "Set meeting join window (seconds): The time frame in seconds within which host can join and start the meeting. After this period, the meeting join url will expire.",
10461047
"enter_number_of_seats": "Enter number of seats",
10471048
"you_can_manage_your_schedules": "You can manage your schedules on the Availability page.",
10481049
"booking_full": "No more seats available",

packages/features/bookings/Booker/components/hooks/useBookings.ts

-5
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,6 @@ export const useBookings = ({ event, hashedLink, bookingForm, metadata, teamMemb
204204
});
205205
},
206206
onError: (err, _, ctx) => {
207-
// TODO:
208-
// const vercelId = ctx?.meta?.headers?.get("x-vercel-id");
209-
// if (vercelId) {
210-
// setResponseVercelIdHeader(vercelId);
211-
// }
212207
bookerFormErrorRef && bookerFormErrorRef.current?.scrollIntoView({ behavior: "smooth" });
213208
},
214209
});

packages/features/eventtypes/lib/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export type FormValues = {
3434
eventName: string;
3535
slug: string;
3636
isInstantEvent: boolean;
37+
instantMeetingExpiryTimeOffset: number;
3738
length: number;
3839
offsetStart: number;
3940
description: string;

packages/features/instant-meeting/handleInstantMeeting.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -194,12 +194,25 @@ async function handler(req: NextApiRequest) {
194194
const newBooking = await prisma.booking.create(createBookingObj);
195195

196196
// Create Instant Meeting Token
197+
197198
const token = randomBytes(32).toString("hex");
199+
200+
const eventTypeWithExpiryTimeOffset = await prisma.eventType.findUniqueOrThrow({
201+
where: {
202+
id: req.body.eventTypeId,
203+
},
204+
select: {
205+
instantMeetingExpiryTimeOffset: true,
206+
},
207+
});
208+
209+
const instantMeetingExpiryTimeOffset = eventTypeWithExpiryTimeOffset?.instantMeetingExpiryTimeOffset ?? 90;
210+
198211
const instantMeetingToken = await prisma.instantMeetingToken.create({
199212
data: {
200213
token,
201-
// 90 Seconds
202-
expires: new Date(new Date().getTime() + 1000 * 90),
214+
// current time + offset Seconds
215+
expires: new Date(new Date().getTime() + 1000 * instantMeetingExpiryTimeOffset),
203216
team: {
204217
connect: {
205218
id: eventType.team.id,

packages/lib/event-types/getEventTypeById.ts

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const getEventTypeById = async ({
8484
description: true,
8585
length: true,
8686
isInstantEvent: true,
87+
instantMeetingExpiryTimeOffset: true,
8788
aiPhoneCallConfig: true,
8889
offsetStart: true,
8990
hidden: true,

packages/lib/server/eventTypeSelect.ts

+1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const eventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
4040
slotInterval: true,
4141
successRedirectUrl: true,
4242
isInstantEvent: true,
43+
instantMeetingExpiryTimeOffset: true,
4344
aiPhoneCallConfig: true,
4445
assignAllTeamMembers: true,
4546
recurringEvent: true,

packages/lib/test/builder.ts

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export const buildEventType = (eventType?: Partial<EventType>): EventType => {
8282
description: faker.lorem.paragraph(),
8383
position: 1,
8484
isInstantEvent: false,
85+
instantMeetingExpiryTimeOffset: 90,
8586
locations: null,
8687
length: 15,
8788
offsetStart: 0,

packages/platform/sdk/src/endpoints/events/event-types/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export type EventType = {
5959
bookingLimits: number | null;
6060
durationLimits: number | null;
6161
isInstantEvent: boolean;
62+
instantMeetingExpiryTimeOffset: number;
6263
assignAllTeamMembers: boolean;
6364
useEventTypeDestinationCalendarEmail: boolean;
6465
};

packages/platform/sdk/src/endpoints/events/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export type Event = {
4545
eventName: string;
4646
slug: string;
4747
isInstantEvent: boolean;
48+
instantMeetingExpiryTimeOffset: number;
4849
aiPhoneCallConfig: {
4950
eventTypeId: number;
5051
enabled: boolean;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- AlterTable
2+
ALTER TABLE "EventType" ADD COLUMN "instantMeetingExpiryTimeOffset" INTEGER NOT NULL DEFAULT 90;

packages/prisma/schema.prisma

+1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ model EventType {
133133
/// @zod.custom(imports.intervalLimitsType)
134134
durationLimits Json?
135135
isInstantEvent Boolean @default(false)
136+
instantMeetingExpiryTimeOffset Int @default(90)
136137
assignAllTeamMembers Boolean @default(false)
137138
useEventTypeDestinationCalendarEmail Boolean @default(false)
138139
aiPhoneCallConfig AIPhoneCallConfiguration?

packages/prisma/zod-utils.ts

+1
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ export const allManagedEventTypeProps: { [k in keyof Omit<Prisma.EventTypeSelect
608608
title: true,
609609
description: true,
610610
isInstantEvent: true,
611+
instantMeetingExpiryTimeOffset: true,
611612
aiPhoneCallConfig: true,
612613
currency: true,
613614
periodDays: true,

packages/trpc/server/routers/viewer/eventTypes/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const EventTypeUpdateInput = _EventTypeModel
88
/** Optional fields */
99
.extend({
1010
isInstantEvent: z.boolean().optional(),
11+
instantMeetingExpiryTimeOffset: z.number().optional(),
1112
aiPhoneCallConfig: z
1213
.object({
1314
generalPrompt: z.string(),

0 commit comments

Comments
 (0)