feat: add isRecordingExist field and create api handler (#6777)
This commit is contained in:
parent
5c763389f7
commit
7c9012738a
|
@ -256,10 +256,9 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
},
|
||||
});
|
||||
};
|
||||
const showRecordingsButtons =
|
||||
(booking.location === "integrations:daily" || booking?.location?.trim() === "") && isPast && isConfirmed;
|
||||
|
||||
const title = booking.title;
|
||||
const showRecordingsButtons = booking.isRecorded && isPast && isConfirmed;
|
||||
return (
|
||||
<>
|
||||
<RescheduleDialog
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { z } from "zod";
|
||||
|
||||
import { DailyLocationType } from "@calcom/app-store/locations";
|
||||
import { getDownloadLinkOfCalVideoByRecordingId } from "@calcom/core/videoClient";
|
||||
import { sendDailyVideoRecordingEmails } from "@calcom/emails";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
|
||||
import { defaultHandler } from "@calcom/lib/server";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||
|
||||
const schema = z.object({
|
||||
recordingId: z.string(),
|
||||
bookingUID: z.string(),
|
||||
});
|
||||
|
||||
const downloadLinkSchema = z.object({
|
||||
download_link: z.string(),
|
||||
});
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!process.env.SENDGRID_API_KEY || !process.env.SENDGRID_EMAIL) {
|
||||
return res.status(405).json({ message: "No SendGrid API key or email" });
|
||||
}
|
||||
const response = schema.safeParse(JSON.parse(req.body));
|
||||
|
||||
if (!response.success) {
|
||||
return res.status(400).send({
|
||||
message: "Invalid Payload",
|
||||
});
|
||||
}
|
||||
|
||||
const { recordingId, bookingUID } = response.data;
|
||||
const session = await getServerSession({ req, res });
|
||||
|
||||
if (!session?.user) {
|
||||
return res.status(401).send({
|
||||
message: "User not logged in",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const booking = await prisma.booking.findFirst({
|
||||
where: {
|
||||
uid: bookingUID,
|
||||
},
|
||||
select: {
|
||||
...bookingMinimalSelect,
|
||||
uid: true,
|
||||
location: true,
|
||||
isRecorded: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
credentials: true,
|
||||
timeZone: true,
|
||||
email: true,
|
||||
name: true,
|
||||
locale: true,
|
||||
destinationCalendar: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!booking || booking.location !== DailyLocationType) {
|
||||
return res.status(404).send({
|
||||
message: `Booking of uid ${bookingUID} does not exist or does not contain daily video as location`,
|
||||
});
|
||||
}
|
||||
|
||||
const t = await getTranslation(booking?.user?.locale ?? "en", "common");
|
||||
const attendeesListPromises = booking.attendees.map(async (attendee) => {
|
||||
return {
|
||||
id: attendee.id,
|
||||
name: attendee.name,
|
||||
email: attendee.email,
|
||||
timeZone: attendee.timeZone,
|
||||
language: {
|
||||
translate: await getTranslation(attendee.locale ?? "en", "common"),
|
||||
locale: attendee.locale ?? "en",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const attendeesList = await Promise.all(attendeesListPromises);
|
||||
|
||||
const isUserAttendeeOrOrganiser =
|
||||
booking?.user?.id === session.user.id ||
|
||||
attendeesList.find((attendee) => attendee.id === session.user.id);
|
||||
|
||||
if (!isUserAttendeeOrOrganiser) {
|
||||
return res.status(403).send({
|
||||
message: "Unauthorised",
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.booking.update({
|
||||
where: {
|
||||
uid: booking.uid,
|
||||
},
|
||||
data: {
|
||||
isRecorded: true,
|
||||
},
|
||||
});
|
||||
|
||||
const isSendingEmailsAllowed = IS_SELF_HOSTED || session?.user?.belongsToActiveTeam;
|
||||
|
||||
// send emails to all attendees only when user has team plan
|
||||
if (isSendingEmailsAllowed) {
|
||||
const response = await getDownloadLinkOfCalVideoByRecordingId(recordingId);
|
||||
|
||||
const downloadLinkResponse = downloadLinkSchema.parse(response);
|
||||
const downloadLink = downloadLinkResponse.download_link;
|
||||
|
||||
const evt: CalendarEvent = {
|
||||
type: booking.title,
|
||||
title: booking.title,
|
||||
description: booking.description || undefined,
|
||||
startTime: booking.startTime.toISOString(),
|
||||
endTime: booking.endTime.toISOString(),
|
||||
organizer: {
|
||||
email: booking.user?.email || "Email-less",
|
||||
name: booking.user?.name || "Nameless",
|
||||
timeZone: booking.user?.timeZone || "Europe/London",
|
||||
language: { translate: t, locale: booking?.user?.locale ?? "en" },
|
||||
},
|
||||
attendees: attendeesList,
|
||||
uid: booking.uid,
|
||||
};
|
||||
|
||||
await sendDailyVideoRecordingEmails(evt, downloadLink);
|
||||
return res.status(200).json({ message: "Success" });
|
||||
}
|
||||
|
||||
return res.status(403).json({ message: "User does not have team plan to send out emails" });
|
||||
} catch (err) {
|
||||
console.warn("something_went_wrong", err);
|
||||
return res.status(500).json({ message: "something went wrong" });
|
||||
}
|
||||
}
|
||||
|
||||
export default defaultHandler({
|
||||
POST: Promise.resolve({ default: handler }),
|
||||
});
|
|
@ -1,8 +1,10 @@
|
|||
import type { DailyEventObjectRecordingStarted } from "@daily-co/daily-js";
|
||||
import DailyIframe from "@daily-co/daily-js";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import Head from "next/head";
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import z from "zod";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
|
@ -16,12 +18,19 @@ import { ChevronRight } from "@calcom/ui/components/icon";
|
|||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
const recordingStartedEventResponse = z
|
||||
.object({
|
||||
recordingId: z.string(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export type JoinCallPageProps = inferSSRProps<typeof getServerSideProps>;
|
||||
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
||||
|
||||
export default function JoinCall(props: JoinCallPageProps) {
|
||||
const { t } = useLocale();
|
||||
const { meetingUrl, meetingPassword, booking } = props;
|
||||
const recordingId = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const callFrame = DailyIframe.createFrame({
|
||||
|
@ -49,11 +58,30 @@ export default function JoinCall(props: JoinCallPageProps) {
|
|||
...(typeof meetingPassword === "string" && { token: meetingPassword }),
|
||||
});
|
||||
callFrame.join();
|
||||
callFrame.on("recording-started", onRecordingStarted).on("recording-stopped", onRecordingStopped);
|
||||
return () => {
|
||||
callFrame.destroy();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onRecordingStopped = () => {
|
||||
const data = { recordingId: recordingId.current, bookingUID: booking.uid };
|
||||
|
||||
fetch("/api/recorded-daily-video", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(data),
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
recordingId.current = null;
|
||||
};
|
||||
|
||||
const onRecordingStarted = (event?: DailyEventObjectRecordingStarted | undefined) => {
|
||||
const response = recordingStartedEventResponse.parse(event);
|
||||
recordingId.current = response.recordingId;
|
||||
};
|
||||
|
||||
const title = `${APP_NAME} Video`;
|
||||
return (
|
||||
<>
|
||||
|
@ -246,6 +274,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
...bookingMinimalSelect,
|
||||
uid: true,
|
||||
description: true,
|
||||
isRecorded: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
|
@ -69,6 +69,8 @@
|
|||
"event_awaiting_approval_subject": "Awaiting Approval: {{title}} at {{date}}",
|
||||
"event_still_awaiting_approval": "An event is still waiting for your approval",
|
||||
"booking_submitted_subject": "Booking Submitted: {{title}} at {{date}}",
|
||||
"download_recording_subject": "Download Recording: {{title}} at {{date}}",
|
||||
"download_your_recording": "Download your recording",
|
||||
"your_meeting_has_been_booked": "Your meeting has been booked",
|
||||
"event_type_has_been_rescheduled_on_time_date": "Your {{title}} has been rescheduled to {{date}}.",
|
||||
"event_has_been_rescheduled": "Updated - Your event has been rescheduled",
|
||||
|
@ -1199,6 +1201,7 @@
|
|||
"start_of_week": "Start of week",
|
||||
"recordings_title": "Recordings",
|
||||
"recording": "Recording",
|
||||
"happy_scheduling": "Happy Scheduling",
|
||||
"select_calendars": "Select which calendars you want to check for conflicts to prevent double bookings.",
|
||||
"check_for_conflicts": "Check for conflicts",
|
||||
"view_recordings": "View recordings",
|
||||
|
@ -1277,6 +1280,8 @@
|
|||
"download_responses": "Download responses",
|
||||
"download_responses_description": "Download all responses to your form in CSV format.",
|
||||
"download": "Download",
|
||||
"download_recording": "Download Recording",
|
||||
"recording_from_your_recent_call":"A recording from your recent call on Cal.com is ready for download",
|
||||
"create_your_first_form": "Create your first form",
|
||||
"create_your_first_form_description": "With Routing Forms you can ask qualifying questions and route to the correct person or event type.",
|
||||
"create_your_first_webhook": "Create your first Webhook",
|
||||
|
@ -1465,6 +1470,7 @@
|
|||
"fixed_round_robin": "Fixed round robin",
|
||||
"add_one_fixed_attendee": "Add one fixed attendee and round robin through a number of attendees.",
|
||||
"calcom_is_better_with_team": "Cal.com is better with teams",
|
||||
"the_calcom_team":"The Cal.com team",
|
||||
"add_your_team_members": "Add your team members to your event types. Use collective scheduling to include everyone or find the most suitable person with round robin scheduling.",
|
||||
"booking_limit_reached": "Booking Limit for this event type has been reached",
|
||||
"duration_limit_reached": "Duration Limit for this event type has been reached",
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
Don't modify this file manually.
|
||||
**/
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
export const InstallAppButtonMap = {
|
||||
exchange2013calendar: dynamic(() => import("./exchange2013calendar/components/InstallAppButton")),
|
||||
exchange2016calendar: dynamic(() => import("./exchange2016calendar/components/InstallAppButton")),
|
||||
|
|
|
@ -25,7 +25,6 @@ import { appKeysSchema as vital_zod_ts } from "./vital/zod";
|
|||
import { appKeysSchema as wordpress_zod_ts } from "./wordpress/zod";
|
||||
import { appKeysSchema as zapier_zod_ts } from "./zapier/zod";
|
||||
import { appKeysSchema as zoomvideo_zod_ts } from "./zoomvideo/zod";
|
||||
|
||||
export const appKeysSchemas = {
|
||||
dailyvideo: dailyvideo_zod_ts,
|
||||
fathom: fathom_zod_ts,
|
||||
|
|
|
@ -61,7 +61,6 @@ import wordpress_config_json from "./wordpress/config.json";
|
|||
import { metadata as zapier__metadata_ts } from "./zapier/_metadata";
|
||||
import zohocrm_config_json from "./zohocrm/config.json";
|
||||
import { metadata as zoomvideo__metadata_ts } from "./zoomvideo/_metadata";
|
||||
|
||||
export const appStoreMetadata = {
|
||||
amie: amie_config_json,
|
||||
applecalendar: applecalendar__metadata_ts,
|
||||
|
|
|
@ -25,7 +25,6 @@ import { appDataSchema as vital_zod_ts } from "./vital/zod";
|
|||
import { appDataSchema as wordpress_zod_ts } from "./wordpress/zod";
|
||||
import { appDataSchema as zapier_zod_ts } from "./zapier/zod";
|
||||
import { appDataSchema as zoomvideo_zod_ts } from "./zoomvideo/zod";
|
||||
|
||||
export const appDataSchemas = {
|
||||
dailyvideo: dailyvideo_zod_ts,
|
||||
fathom: fathom_zod_ts,
|
||||
|
|
|
@ -161,7 +161,7 @@ const DailyVideoApiAdapter = (): VideoApiAdapter => {
|
|||
},
|
||||
getRecordingDownloadLink: async (recordingId: string): Promise<GetAccessLinkResponseSchema> => {
|
||||
try {
|
||||
const res = await fetcher(`/recordings/${recordingId}/access-link`).then(
|
||||
const res = await fetcher(`/recordings/${recordingId}/access-link?valid_for_secs=172800`).then(
|
||||
getAccessLinkResponseSchema.parse
|
||||
);
|
||||
return Promise.resolve(res);
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { CalendarEvent, Person } from "@calcom/types/Calendar";
|
|||
import AttendeeAwaitingPaymentEmail from "./templates/attendee-awaiting-payment-email";
|
||||
import AttendeeCancelledEmail from "./templates/attendee-cancelled-email";
|
||||
import AttendeeCancelledSeatEmail from "./templates/attendee-cancelled-seat-email";
|
||||
import AttendeeDailyVideoDownloadRecordingEmail from "./templates/attendee-daily-video-download-recording-email";
|
||||
import AttendeeDeclinedEmail from "./templates/attendee-declined-email";
|
||||
import AttendeeLocationChangeEmail from "./templates/attendee-location-change-email";
|
||||
import AttendeeRequestEmail from "./templates/attendee-request-email";
|
||||
|
@ -307,3 +308,14 @@ export const sendSlugReplacementEmail = async ({
|
|||
export const sendNoShowFeeChargedEmail = async (attendee: Person, evt: CalendarEvent) => {
|
||||
await sendEmail(() => new NoShowFeeChargedEmail(evt, attendee));
|
||||
};
|
||||
|
||||
export const sendDailyVideoRecordingEmails = async (calEvent: CalendarEvent, downloadLink: string) => {
|
||||
const emailsToSend: Promise<unknown>[] = [];
|
||||
|
||||
for (const attendee of calEvent.attendees) {
|
||||
emailsToSend.push(
|
||||
sendEmail(() => new AttendeeDailyVideoDownloadRecordingEmail(calEvent, attendee, downloadLink))
|
||||
);
|
||||
}
|
||||
await Promise.all(emailsToSend);
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { IS_PRODUCTION, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
|
||||
import RawHtml from "./RawHtml";
|
||||
import Row from "./Row";
|
||||
|
@ -6,9 +6,7 @@ import Row from "./Row";
|
|||
const CommentIE = ({ html = "" }) => <RawHtml html={`<!--[if mso | IE]>${html}<![endif]-->`} />;
|
||||
|
||||
const EmailBodyLogo = () => {
|
||||
const image = IS_PRODUCTION
|
||||
? WEBAPP_URL + "/emails/CalLogo@2x.png"
|
||||
: "https://app.cal.com/emails/CalLogo@2x.png";
|
||||
const image = WEBAPP_URL + "/emails/logo.png";
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
import type { TFunction } from "next-i18next";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
|
||||
import { V2BaseEmailHtml, CallToAction } from "../components";
|
||||
|
||||
interface AttendeeDailyVideoDownloadRecordingEmailProps {
|
||||
language: TFunction;
|
||||
downloadLink: string;
|
||||
title: string;
|
||||
date: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const AttendeeDailyVideoDownloadRecordingEmail = (
|
||||
props: AttendeeDailyVideoDownloadRecordingEmailProps & Partial<React.ComponentProps<typeof V2BaseEmailHtml>>
|
||||
) => {
|
||||
const image = WEBAPP_URL + "/emails/logo.png";
|
||||
return (
|
||||
<V2BaseEmailHtml
|
||||
subject={props.language("download_your_recording", {
|
||||
title: props.title,
|
||||
date: props.date,
|
||||
})}>
|
||||
<div style={{ width: "89px", marginBottom: "35px" }}>
|
||||
<a href={WEBAPP_URL} target="_blank" rel="noreferrer">
|
||||
<img
|
||||
height="19"
|
||||
src={image}
|
||||
style={{
|
||||
border: "0",
|
||||
display: "block",
|
||||
outline: "none",
|
||||
textDecoration: "none",
|
||||
height: "19px",
|
||||
width: "100%",
|
||||
fontSize: "13px",
|
||||
}}
|
||||
width="89"
|
||||
alt=""
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "32px",
|
||||
fontWeight: "600",
|
||||
lineHeight: "38.5px",
|
||||
marginBottom: "40px",
|
||||
color: "black",
|
||||
}}>
|
||||
<>{props.language("download_your_recording")}</>
|
||||
</p>
|
||||
<p style={{ fontWeight: 400, lineHeight: "24px" }}>
|
||||
<>{props.language("hi_user_name", { name: props.name })},</>
|
||||
</p>
|
||||
<p style={{ fontWeight: 400, lineHeight: "24px", marginBottom: "40px" }}>
|
||||
<>{props.language("recording_from_your_recent_call")}</>
|
||||
</p>
|
||||
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: "#F3F4F6",
|
||||
padding: "32px",
|
||||
marginBottom: "40px",
|
||||
}}>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "18px",
|
||||
lineHeight: "20px",
|
||||
fontWeight: 600,
|
||||
marginBottom: "8px",
|
||||
color: "black",
|
||||
}}>
|
||||
<>{props.title}</>
|
||||
</p>
|
||||
<p
|
||||
style={{
|
||||
fontWeight: 400,
|
||||
lineHeight: "24px",
|
||||
marginBottom: "24px",
|
||||
marginTop: "0px",
|
||||
color: "black",
|
||||
}}>
|
||||
{props.date}
|
||||
</p>
|
||||
<CallToAction label={props.language("download_recording")} href={props.downloadLink} />
|
||||
</div>
|
||||
|
||||
<p style={{ fontWeight: 400, lineHeight: "24px", marginTop: "32px", marginBottom: "8px" }}>
|
||||
<>{props.language("happy_scheduling")},</>
|
||||
</p>
|
||||
<p style={{ fontWeight: 400, lineHeight: "24px", marginTop: "0px" }}>
|
||||
<>{props.language("the_calcom_team")}</>
|
||||
</p>
|
||||
</V2BaseEmailHtml>
|
||||
);
|
||||
};
|
|
@ -24,3 +24,4 @@ export { BrokenIntegrationEmail } from "./BrokenIntegrationEmail";
|
|||
export { OrganizerAttendeeCancelledSeatEmail } from "./OrganizerAttendeeCancelledSeatEmail";
|
||||
export { NoShowFeeChargedEmail } from "./NoShowFeeChargedEmail";
|
||||
export * from "@calcom/app-store/routing-forms/emails/components";
|
||||
export { AttendeeDailyVideoDownloadRecordingEmail } from "./AttendeeDailyVideoDownloadRecordingEmail";
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
// TODO: We should find a way to keep App specific email templates within the App itself
|
||||
import type { TFunction } from "next-i18next";
|
||||
|
||||
import type { CalendarEvent, Person } from "@calcom/types/Calendar";
|
||||
|
||||
import { renderEmail } from "../";
|
||||
import BaseEmail from "./_base-email";
|
||||
|
||||
export default class AttendeeDailyVideoDownloadRecordingEmail extends BaseEmail {
|
||||
calEvent: CalendarEvent;
|
||||
attendee: Person;
|
||||
downloadLink: string;
|
||||
t: TFunction;
|
||||
|
||||
constructor(calEvent: CalendarEvent, attendee: Person, downloadLink: string) {
|
||||
super();
|
||||
this.name = "SEND_RECORDING_DOWNLOAD_LINK";
|
||||
this.calEvent = calEvent;
|
||||
this.attendee = attendee;
|
||||
this.downloadLink = downloadLink;
|
||||
this.t = attendee.language.translate;
|
||||
}
|
||||
protected getNodeMailerPayload(): Record<string, unknown> {
|
||||
return {
|
||||
to: `${this.attendee.name} <${this.attendee.email}>`,
|
||||
from: `${this.calEvent.organizer.name} <${this.getMailerOptions().from}>`,
|
||||
replyTo: [...this.calEvent.attendees.map(({ email }) => email), this.calEvent.organizer.email],
|
||||
subject: `${this.t("download_recording_subject", {
|
||||
title: this.calEvent.title,
|
||||
date: this.getFormattedDate(),
|
||||
})}`,
|
||||
html: renderEmail("AttendeeDailyVideoDownloadRecordingEmail", {
|
||||
title: this.calEvent.title,
|
||||
date: this.getFormattedDate(),
|
||||
downloadLink: this.downloadLink,
|
||||
language: this.t,
|
||||
name: this.attendee.name,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
protected getTimezone(): string {
|
||||
return this.attendee.timeZone;
|
||||
}
|
||||
|
||||
protected getInviteeStart(format: string) {
|
||||
return this.getRecipientTime(this.calEvent.startTime, format);
|
||||
}
|
||||
|
||||
protected getInviteeEnd(format: string) {
|
||||
return this.getRecipientTime(this.calEvent.endTime, format);
|
||||
}
|
||||
|
||||
protected getFormattedDate() {
|
||||
return `${this.getInviteeStart("h:mma")} - ${this.getInviteeEnd("h:mma")}, ${this.t(
|
||||
this.getInviteeStart("dddd").toLowerCase()
|
||||
)}, ${this.t(this.getInviteeStart("MMMM").toLowerCase())} ${this.getInviteeStart("D, YYYY")}`;
|
||||
}
|
||||
}
|
|
@ -474,7 +474,7 @@
|
|||
<a href="{{base_url}}" target="_blank">
|
||||
<img
|
||||
height="19"
|
||||
src="https://app.cal.com/emails/CalLogo@2x.png"
|
||||
src="https://app.cal.com/emails/logo.png"
|
||||
style="
|
||||
border: 0;
|
||||
display: block;
|
||||
|
|
|
@ -57,6 +57,7 @@ export const buildBooking = (booking?: Partial<Booking>): Booking => {
|
|||
scheduledJobs: [],
|
||||
metadata: null,
|
||||
responses: null,
|
||||
isRecorded: false,
|
||||
...booking,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "Booking" ADD COLUMN "isRecorded" BOOLEAN NOT NULL DEFAULT false;
|
|
@ -335,6 +335,7 @@ model Booking {
|
|||
seatsReferences BookingSeat[]
|
||||
/// @zod.custom(imports.bookingMetadataSchema)
|
||||
metadata Json?
|
||||
isRecorded Boolean @default(false)
|
||||
}
|
||||
|
||||
model Schedule {
|
||||
|
|
|
@ -450,6 +450,15 @@ export const getAccessLinkResponseSchema = z.object({
|
|||
|
||||
export type GetAccessLinkResponseSchema = z.infer<typeof getAccessLinkResponseSchema>;
|
||||
|
||||
export const sendDailyVideoRecordingEmailsSchema = z.object({
|
||||
recordingId: z.string(),
|
||||
bookingUID: z.string(),
|
||||
});
|
||||
|
||||
export const downloadLinkSchema = z.object({
|
||||
download_link: z.string(),
|
||||
});
|
||||
|
||||
// All properties within event type that can and will be updated if needed
|
||||
export const allManagedEventTypeProps: { [k in keyof Omit<Prisma.EventTypeSelect, "id">]: true } = {
|
||||
title: true,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { BookingReference, EventType, User, WebhookTriggerEvents } from "@prisma/client";
|
||||
import { BookingStatus, MembershipRole, Prisma, SchedulingType, WorkflowMethods } from "@prisma/client";
|
||||
import type { BookingReference, EventType, User, WebhookTriggerEvents } from "@prisma/client";
|
||||
import type { TFunction } from "next-i18next";
|
||||
import { z } from "zod";
|
||||
|
||||
|
@ -285,6 +285,7 @@ export const bookingsRouter = router({
|
|||
},
|
||||
rescheduled: true,
|
||||
references: true,
|
||||
isRecorded: true,
|
||||
seatsReferences: {
|
||||
where: {
|
||||
attendee: {
|
||||
|
|
Loading…
Reference in New Issue
Block a user