Merge branch 'main' into minimum-booking-notice-will-allow-hours-and-days
This commit is contained in:
commit
0384058c90
|
@ -137,3 +137,7 @@ CLOSECOM_API_KEY=
|
|||
|
||||
# Sendgrid internal email sender
|
||||
SENDGRID_API_KEY=
|
||||
|
||||
# Sentry
|
||||
NEXT_PUBLIC_SENTRY_DSN=
|
||||
SENTRY_IGNORE_API_RESOLUTION_ERROR=
|
||||
|
|
|
@ -67,4 +67,6 @@ tsconfig.tsbuildinfo
|
|||
public/embed
|
||||
|
||||
# Copied app-store images
|
||||
public/app-store
|
||||
public/app-store
|
||||
# Sentry
|
||||
.sentryclirc
|
||||
|
|
|
@ -176,11 +176,12 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
setLocationMutation.mutate({ bookingId: booking.id, newLocation });
|
||||
};
|
||||
|
||||
// Extract recurring dates is intensive to run, so use useMemo.
|
||||
// Calculate the booking date(s) and setup recurring event data to show
|
||||
// @FIXME: This is importing the RRULE library which is already heavy. Find out a more optimal way do this.
|
||||
const [recurringStrings, recurringDates] = useMemo(() => {
|
||||
if (booking.recurringBookings !== undefined && booking.eventType.recurringEvent?.freq !== undefined) {
|
||||
const [, recurringDates] = useMemo(() => {
|
||||
if (
|
||||
booking.recurringBookings !== undefined &&
|
||||
booking.eventType.recurringEvent?.freq !== undefined &&
|
||||
booking.recurringEventId
|
||||
) {
|
||||
return extractRecurringDates(booking, user?.timeZone, i18n);
|
||||
}
|
||||
return [[], []];
|
||||
|
@ -275,13 +276,11 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
attendees={booking.attendees}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isPending && (
|
||||
<Badge className="ltr:mr-2 rtl:ml-2" variant="orange">
|
||||
{t("unconfirmed")}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{booking.eventType?.team && (
|
||||
<Badge className="ltr:mr-2 rtl:ml-2" variant="gray">
|
||||
{booking.eventType.team.name}
|
||||
|
@ -292,7 +291,6 @@ function BookingListItem(booking: BookingItemProps) {
|
|||
{t("pending_payment")}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
<div className="mt-2 text-sm text-gray-400">
|
||||
<RecurringBookingsTooltip booking={booking} recurringDates={recurringDates} />
|
||||
</div>
|
||||
|
|
|
@ -7,8 +7,9 @@ import classNames from "@calcom/lib/classNames";
|
|||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { weekdayNames } from "@calcom/lib/weekday";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
|
||||
import { Icon } from "@calcom/ui";
|
||||
import { Badge } from "@calcom/ui/v2";
|
||||
import { Badge, SettingsToggle } from "@calcom/ui/v2";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import Select from "@calcom/ui/v2/core/form/select";
|
||||
import { SkeletonText } from "@calcom/ui/v2/core/skeleton";
|
||||
|
@ -91,93 +92,119 @@ const AvailabilitySelect = ({
|
|||
);
|
||||
};
|
||||
|
||||
const format = (date: Date) =>
|
||||
Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "numeric" }).format(
|
||||
const format = (date: Date, hour12: boolean) =>
|
||||
Intl.DateTimeFormat(undefined, { hour: "numeric", minute: "numeric", hour12 }).format(
|
||||
new Date(dayjs.utc(date).format("YYYY-MM-DDTHH:mm:ss"))
|
||||
);
|
||||
|
||||
export const AvailabilityTab = () => {
|
||||
export const AvailabilityTab = ({ isTeamEvent }: { isTeamEvent: boolean }) => {
|
||||
const { t, i18n } = useLocale();
|
||||
const { watch } = useFormContext<FormValues>();
|
||||
|
||||
const EventTypeSchedule = () => {
|
||||
const me = useMeQuery();
|
||||
const timeFormat = me?.data?.timeFormat;
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="min-w-4 mb-2">
|
||||
<label htmlFor="availability" className="mt-0 flex text-sm font-medium text-neutral-700">
|
||||
{t("availability")}
|
||||
</label>
|
||||
</div>
|
||||
<Controller
|
||||
name="schedule"
|
||||
render={({ field }) => (
|
||||
<AvailabilitySelect
|
||||
value={field.value}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
onChange={(selected) => {
|
||||
field.onChange(selected?.value || null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-4 rounded border p-4 py-6 pt-2 md:p-8">
|
||||
<ol className="table border-collapse text-sm">
|
||||
{weekdayNames(i18n.language, 1, "long").map((day, index) => {
|
||||
const isAvailable = !!filterDays(index).length;
|
||||
return (
|
||||
<li key={day} className="my-6 flex border-transparent last:mb-2">
|
||||
<span
|
||||
className={classNames(
|
||||
"w-20 font-medium sm:w-32",
|
||||
!isAvailable && "text-gray-500 opacity-50"
|
||||
)}>
|
||||
{day}
|
||||
</span>
|
||||
{isLoading ? (
|
||||
<SkeletonText className="block h-5 w-60" />
|
||||
) : isAvailable ? (
|
||||
<div className="space-y-3 text-right">
|
||||
{filterDays(index).map((dayRange, i) => (
|
||||
<div key={i} className="flex items-center leading-4">
|
||||
<span className="w-16 sm:w-28 sm:text-left">
|
||||
{format(dayRange.startTime, timeFormat === 12)}
|
||||
</span>
|
||||
<span className="ml-4">-</span>
|
||||
<div className="ml-6">{format(dayRange.endTime, timeFormat === 12)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="ml-6 text-gray-500 opacity-50 sm:ml-0">{t("unavailable")}</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
<hr />
|
||||
<div className="flex flex-col justify-center gap-2 sm:flex-row sm:justify-between">
|
||||
<span className="flex items-center justify-center text-sm text-gray-600 sm:justify-start">
|
||||
<Icon.FiGlobe className="mr-2" />
|
||||
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
|
||||
</span>
|
||||
<Button
|
||||
href={`/availability/${schedule?.schedule.id}`}
|
||||
color="minimal"
|
||||
EndIcon={Icon.FiExternalLink}
|
||||
target="_blank"
|
||||
className="justify-center border sm:border-0"
|
||||
rel="noopener noreferrer">
|
||||
{t("edit_availability")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const scheduleId = watch("schedule");
|
||||
const { isLoading, data: schedule } = trpc.useQuery(["viewer.availability.schedule", { scheduleId }]);
|
||||
|
||||
const filterDays = (dayNum: number) =>
|
||||
schedule?.schedule.availability.filter((item) => item.days.includes((dayNum + 1) % 7)) || [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div className="min-w-4 mb-2">
|
||||
<label htmlFor="availability" className="mt-0 flex text-sm font-medium text-neutral-700">
|
||||
{t("availability")}
|
||||
</label>
|
||||
</div>
|
||||
<Controller
|
||||
name="schedule"
|
||||
render={({ field }) => (
|
||||
<AvailabilitySelect
|
||||
value={field.value}
|
||||
onBlur={field.onBlur}
|
||||
name={field.name}
|
||||
onChange={(selected) => {
|
||||
field.onChange(selected?.value || null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
if (!isTeamEvent) {
|
||||
return <EventTypeSchedule />;
|
||||
}
|
||||
|
||||
<div className="space-y-4 rounded border p-4 py-6 pt-2 md:p-8">
|
||||
<ol className="table border-collapse text-sm">
|
||||
{weekdayNames(i18n.language, 1, "long").map((day, index) => {
|
||||
const isAvailable = !!filterDays(index).length;
|
||||
return (
|
||||
<li key={day} className="my-6 flex border-transparent last:mb-2">
|
||||
<span
|
||||
className={classNames(
|
||||
"w-20 font-medium sm:w-32",
|
||||
!isAvailable && "text-gray-500 opacity-50"
|
||||
)}>
|
||||
{day}
|
||||
</span>
|
||||
{isLoading ? (
|
||||
<SkeletonText className="block h-5 w-60" />
|
||||
) : isAvailable ? (
|
||||
<div className="space-y-3 text-right">
|
||||
{filterDays(index).map((dayRange, i) => (
|
||||
<div key={i} className="flex items-center leading-4">
|
||||
<span className="w-16 sm:w-28 sm:text-left">{format(dayRange.startTime)}</span>
|
||||
<span className="ml-4">-</span>
|
||||
<div className="ml-6">{format(dayRange.endTime)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<span className="ml-6 text-gray-500 opacity-50 sm:ml-0">{t("unavailable")}</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
<hr />
|
||||
<div className="flex flex-col justify-center gap-2 sm:flex-row sm:justify-between">
|
||||
<span className="flex items-center justify-center text-sm text-gray-600 sm:justify-start">
|
||||
<Icon.FiGlobe className="mr-2" />
|
||||
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
|
||||
</span>
|
||||
<Button
|
||||
href={`/availability/${schedule?.schedule.id}`}
|
||||
color="minimal"
|
||||
EndIcon={Icon.FiExternalLink}
|
||||
target="_blank"
|
||||
className="justify-center border sm:border-0"
|
||||
rel="noopener noreferrer">
|
||||
{t("edit_availability")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
return (
|
||||
<Controller
|
||||
name="metadata.config.useHostSchedulesForTeamEvent"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<SettingsToggle
|
||||
checked={!value}
|
||||
onCheckedChange={(checked) => {
|
||||
onChange(!checked);
|
||||
}}
|
||||
title={t("choose_common_schedule_team_event")}
|
||||
description={t("choose_common_schedule_team_event_description")}>
|
||||
<EventTypeSchedule />
|
||||
</SettingsToggle>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -75,11 +75,16 @@ export const extractRecurringDates = (
|
|||
const recurringInfo = booking.recurringBookings.find(
|
||||
(val) => val.recurringEventId === booking.recurringEventId
|
||||
);
|
||||
if (!recurringInfo) {
|
||||
// something went wrong, fail here before RRule.
|
||||
return [[], []];
|
||||
}
|
||||
const allDates = new RRule({
|
||||
...rest,
|
||||
count: recurringInfo?._count.recurringEventId,
|
||||
dtstart: recurringInfo?._min.startTime,
|
||||
}).all();
|
||||
|
||||
const utcOffset = dayjs(recurringInfo?._min.startTime).tz(timeZone).utcOffset();
|
||||
const dateStrings = allDates.map((r) => {
|
||||
return processDate(dayjs.utc(r).utcOffset(utcOffset), i18n);
|
||||
|
|
|
@ -39,6 +39,10 @@ const middleware: NextMiddleware = async (req) => {
|
|||
return NextResponse.next();
|
||||
};
|
||||
|
||||
export const config = {
|
||||
matcher: ["/api/collect-events/:path*", "/api/auth/:path*", "/apps/routing_forms/:path*", "/:path*/embed"],
|
||||
};
|
||||
|
||||
export default collectEvents({
|
||||
middleware,
|
||||
...nextCollectBasicSettings,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
require("dotenv").config({ path: "../../.env" });
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const { withSentryConfig } = require("@sentry/nextjs");
|
||||
|
||||
const withTM = require("next-transpile-modules")([
|
||||
"@calcom/app-store",
|
||||
|
@ -228,6 +229,18 @@ const nextConfig = {
|
|||
|
||||
return redirects;
|
||||
},
|
||||
sentry: {
|
||||
hideSourceMaps: true,
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = () => plugins.reduce((acc, next) => next(acc), nextConfig);
|
||||
const sentryWebpackPluginOptions = {
|
||||
silent: true, // Suppresses all logs
|
||||
};
|
||||
|
||||
const moduleExports = () => plugins.reduce((acc, next) => next(acc), nextConfig);
|
||||
|
||||
// Sentry should be the last thing to export to catch everything right
|
||||
module.exports = process.env.NEXT_PUBLIC_SENTRY_DSN
|
||||
? withSentryConfig(moduleExports, sentryWebpackPluginOptions)
|
||||
: moduleExports;
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
"@radix-ui/react-switch": "^1.0.0",
|
||||
"@radix-ui/react-toggle-group": "^1.0.0",
|
||||
"@radix-ui/react-tooltip": "^1.0.0",
|
||||
"@sentry/nextjs": "^7.17.3",
|
||||
"@stripe/react-stripe-js": "^1.10.0",
|
||||
"@stripe/stripe-js": "^1.35.0",
|
||||
"@tanstack/react-query": "^4.3.9",
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Typescript class based component for custom-error
|
||||
* @link https://nextjs.org/docs/advanced-features/custom-error-page
|
||||
*/
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { NextPage, NextPageContext } from "next";
|
||||
import NextError, { ErrorProps } from "next/error";
|
||||
import React from "react";
|
||||
|
@ -46,7 +47,9 @@ const CustomError: NextPage<CustomErrorProps> = (props) => {
|
|||
* Partially adapted from the example in
|
||||
* https://github.com/vercel/next.js/tree/canary/examples/with-sentry
|
||||
*/
|
||||
CustomError.getInitialProps = async ({ res, err, asPath }: AugmentedNextPageContext) => {
|
||||
CustomError.getInitialProps = async (ctx: AugmentedNextPageContext) => {
|
||||
const { res, err, asPath } = ctx;
|
||||
await Sentry.captureUnderscoreErrorException(ctx);
|
||||
const errorInitialProps = (await NextError.getInitialProps({
|
||||
res,
|
||||
err,
|
||||
|
|
|
@ -9,6 +9,8 @@ import { AppGetServerSideProps } from "@calcom/types/AppGetServerSideProps";
|
|||
import { AppProps } from "@lib/app-providers";
|
||||
import { getSession } from "@lib/auth";
|
||||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
type AppPageType = {
|
||||
getServerSideProps: AppGetServerSideProps;
|
||||
// A component than can accept any properties
|
||||
|
@ -128,7 +130,8 @@ export async function getServerSideProps(
|
|||
appPages: string[];
|
||||
}>,
|
||||
prisma,
|
||||
user
|
||||
user,
|
||||
ssrInit
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
|
|
|
@ -201,7 +201,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
|||
teamMembers={teamMembers}
|
||||
/>
|
||||
),
|
||||
availability: <AvailabilityTab />,
|
||||
availability: <AvailabilityTab isTeamEvent={!!team} />,
|
||||
team: (
|
||||
<EventTeamTab
|
||||
eventType={eventType}
|
||||
|
|
|
@ -1225,6 +1225,8 @@
|
|||
"exchange_authentication_ntlm": "NTLM authentication",
|
||||
"exchange_compression": "GZip compression",
|
||||
"routing_forms_description": "You can see all forms and routes you have created here.",
|
||||
"routing_forms_send_email_owner": "Send Email to Owner",
|
||||
"routing_forms_send_email_owner_description": "Sends an email to the owner when the form is submitted",
|
||||
"add_new_form": "Add new form",
|
||||
"form_description": "Create your form to route a booker",
|
||||
"copy_link_to_form": "Copy link to form",
|
||||
|
@ -1339,5 +1341,7 @@
|
|||
"number_sms_notifications": "Phone number (SMS\u00a0notifications)",
|
||||
"attendee_email_workflow": "Attendee email",
|
||||
"attendee_email_info": "The person booking's email",
|
||||
"invalid_credential": "Oh no! Looks like permission expired or was revoked. Please reinstall again."
|
||||
"invalid_credential": "Oh no! Looks like permission expired or was revoked. Please reinstall again.",
|
||||
"choose_common_schedule_team_event": "Choose a common schedule",
|
||||
"choose_common_schedule_team_event_description": "Enable this if you want to use a common schedule between hosts. When disabled, each host will be booked based on their default schedule."
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// This file configures the initialization of Sentry on the browser.
|
||||
// The config you add here will be used whenever a page is visited.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN;
|
||||
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN,
|
||||
// Adjust this value in production, or use tracesSampler for greater control
|
||||
tracesSampleRate: 1.0,
|
||||
// ...
|
||||
// Note: if you want to override the automatic release value, do not set a
|
||||
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
|
||||
// that it will also get attached to your source maps
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
defaults.url=https://sentry.io/
|
||||
defaults.org=calcom
|
||||
defaults.project=cal
|
|
@ -0,0 +1,16 @@
|
|||
// This file configures the initialization of Sentry on the server.
|
||||
// The config you add here will be used whenever the server handles a request.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
|
||||
const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN;
|
||||
|
||||
Sentry.init({
|
||||
dsn: SENTRY_DSN,
|
||||
// Adjust this value in production, or use tracesSampler for greater control
|
||||
tracesSampleRate: 1.0,
|
||||
// ...
|
||||
// Note: if you want to override the automatic release value, do not set a
|
||||
// `release` value here - use the environment variable `SENTRY_RELEASE`, so
|
||||
// that it will also get attached to your source maps
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
import { App_RoutingForms_Form } from "@prisma/client";
|
||||
import { useEffect } from "react";
|
||||
import { useForm, UseFormReturn } from "react-hook-form";
|
||||
import { useForm, UseFormReturn, Controller } from "react-hook-form";
|
||||
|
||||
import useApp from "@calcom/lib/hooks/useApp";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
@ -10,6 +10,7 @@ import { Form } from "@calcom/ui/form/fields";
|
|||
import { showToast, DropdownMenuSeparator } from "@calcom/ui/v2";
|
||||
import { ButtonGroup, TextAreaField, TextField, Tooltip, Button, VerticalDivider } from "@calcom/ui/v2";
|
||||
import Meta from "@calcom/ui/v2/core/Meta";
|
||||
import SettingsToggle from "@calcom/ui/v2/core/SettingsToggle";
|
||||
import { ShellMain } from "@calcom/ui/v2/core/Shell";
|
||||
import Banner from "@calcom/ui/v2/core/banner";
|
||||
|
||||
|
@ -187,6 +188,7 @@ type SingleFormComponentProps = {
|
|||
|
||||
function SingleForm({ form, appUrl, Page }: SingleFormComponentProps) {
|
||||
const utils = trpc.useContext();
|
||||
const { t } = useLocale();
|
||||
|
||||
const hookForm = useForm({
|
||||
defaultValues: form,
|
||||
|
@ -241,6 +243,23 @@ function SingleForm({ form, appUrl, Page }: SingleFormComponentProps) {
|
|||
{...hookForm.register("description")}
|
||||
defaultValue={form.description || ""}
|
||||
/>
|
||||
|
||||
<div className="mt-6">
|
||||
<Controller
|
||||
name="settings.emailOwnerOnSubmission"
|
||||
control={hookForm.control}
|
||||
render={({ field: { value, onChange } }) => {
|
||||
return (
|
||||
<SettingsToggle
|
||||
title={t("routing_forms_send_email_owner")}
|
||||
description={t("routing_forms_send_email_owner_description")}
|
||||
checked={value}
|
||||
onCheckedChange={(val) => onChange(val)}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{!form._count?.responses && (
|
||||
<Banner
|
||||
className="mt-6"
|
||||
|
|
|
@ -49,7 +49,7 @@ const TextWidget = (props: TextWidgetProps & { type?: string }) => {
|
|||
const textValue = value || "";
|
||||
return (
|
||||
<TextField
|
||||
containerClassName="w-full mt-2"
|
||||
containerClassName="w-full"
|
||||
type={type}
|
||||
className="dark:border-darkgray-300 flex flex-grow border-gray-300 text-sm dark:bg-transparent dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500"
|
||||
value={textValue}
|
||||
|
@ -66,6 +66,7 @@ function NumberWidget({ value, setValue, ...remainingProps }: NumberWidgetProps)
|
|||
return (
|
||||
<TextField
|
||||
type="number"
|
||||
containerClassName="w-full"
|
||||
className="dark:border-darkgray-300 mt-0 border-gray-300 text-sm dark:bg-transparent dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500"
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
|
@ -101,7 +102,7 @@ const MultiSelectWidget = ({
|
|||
|
||||
return (
|
||||
<Select
|
||||
className="dark:border-darkgray-300 block w-full min-w-0 flex-1 rounded-none rounded-r-sm border-gray-300 dark:bg-transparent dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500 sm:text-sm"
|
||||
className="dark:border-darkgray-300 mb-2 block w-full min-w-0 flex-1 rounded-none rounded-r-sm border-gray-300 dark:bg-transparent dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500 sm:text-sm"
|
||||
menuPosition="fixed"
|
||||
onChange={(items) => {
|
||||
setValue(items?.map((item) => item.value));
|
||||
|
@ -135,7 +136,7 @@ function SelectWidget({
|
|||
|
||||
return (
|
||||
<Select
|
||||
className="data-testid-select dark:border-darkgray-300 block w-full min-w-0 flex-1 rounded-none rounded-r-sm border-gray-300 dark:bg-transparent dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500 sm:text-sm"
|
||||
className="data-testid-select dark:border-darkgray-300 mb-2 block w-full min-w-0 flex-1 rounded-none rounded-r-sm border-gray-300 dark:bg-transparent dark:text-white dark:selection:bg-green-500 disabled:dark:text-gray-500 sm:text-sm"
|
||||
menuPosition="fixed"
|
||||
onChange={(item) => {
|
||||
if (!item) {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import { BaseEmailHtml, Info } from "@calcom/emails/src/components";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
|
||||
import { Response } from "../../types/types";
|
||||
import { App_RoutingForms_Form } from ".prisma/client";
|
||||
|
||||
export const ResponseEmail = ({
|
||||
form,
|
||||
response,
|
||||
...props
|
||||
}: {
|
||||
form: Pick<App_RoutingForms_Form, "id" | "name">;
|
||||
response: Response;
|
||||
subject: string;
|
||||
} & Partial<React.ComponentProps<typeof BaseEmailHtml>>) => {
|
||||
return (
|
||||
<BaseEmailHtml
|
||||
callToAction={
|
||||
<div
|
||||
style={{
|
||||
fontFamily: "Roboto, Helvetica, sans-serif",
|
||||
fontSize: "16px",
|
||||
fontWeight: 500,
|
||||
lineHeight: "0px",
|
||||
textAlign: "left",
|
||||
color: "#3e3e3e",
|
||||
}}>
|
||||
<p style={{ fontWeight: 400, lineHeight: "24px" }}>
|
||||
<a href={`${WEBAPP_URL}/apps/routing-forms/form-edit/${form.id}`} style={{ color: "#3e3e3e" }}>
|
||||
<>Manage this form</>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
title={form.name}
|
||||
subtitle="New Response Received"
|
||||
{...props}>
|
||||
{Object.entries(response).map(([fieldId, fieldResponse]) => {
|
||||
return (
|
||||
<Info
|
||||
withSpacer
|
||||
key={fieldId}
|
||||
label={fieldResponse.label}
|
||||
description={
|
||||
fieldResponse.value instanceof Array ? fieldResponse.value.join(",") : fieldResponse.value
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</BaseEmailHtml>
|
||||
);
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export { ResponseEmail } from "./ResponseEmail";
|
|
@ -0,0 +1,33 @@
|
|||
import { renderEmail } from "@calcom/emails";
|
||||
import BaseEmail from "@calcom/emails/templates/_base-email";
|
||||
|
||||
import { Response } from "../../types/types";
|
||||
import { App_RoutingForms_Form } from ".prisma/client";
|
||||
|
||||
type Form = Pick<App_RoutingForms_Form, "id" | "name">;
|
||||
export default class ResponseEmail extends BaseEmail {
|
||||
response: Response;
|
||||
toAddresses: string[];
|
||||
form: Form;
|
||||
constructor({ toAddresses, response, form }: { form: Form; toAddresses: string[]; response: Response }) {
|
||||
super();
|
||||
this.form = form;
|
||||
this.response = response;
|
||||
this.toAddresses = toAddresses;
|
||||
}
|
||||
|
||||
protected getNodeMailerPayload(): Record<string, unknown> {
|
||||
const toAddresses = this.toAddresses;
|
||||
const subject = `${this.form.name} has a new response`;
|
||||
return {
|
||||
from: `Cal.com <${this.getMailerOptions().from}>`,
|
||||
to: toAddresses.join(","),
|
||||
subject,
|
||||
html: renderEmail("ResponseEmail", {
|
||||
form: this.form,
|
||||
response: this.response,
|
||||
subject,
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
import { App_RoutingForms_Form } from "@prisma/client";
|
||||
|
||||
import { RoutingFormSettings } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { SerializableForm } from "../types/types";
|
||||
import { zodFields, zodRoutes } from "../zod";
|
||||
|
||||
|
@ -13,10 +15,17 @@ export function getSerializableForm<TForm extends App_RoutingForms_Form>(form: T
|
|||
if (!fieldsParsed.success) {
|
||||
throw new Error("Error parsing fields");
|
||||
}
|
||||
const settings = RoutingFormSettings.parse(
|
||||
form.settings || {
|
||||
// Would have really loved to do it using zod. But adding .default(true) throws type error in prisma/zod/app_routingforms_form.ts
|
||||
emailOwnerOnSubmission: true,
|
||||
}
|
||||
);
|
||||
|
||||
// Ideally we shouldb't have needed to explicitly type it but due to some reason it's not working reliably with VSCode TypeCheck
|
||||
const serializableForm: SerializableForm<TForm> = {
|
||||
...form,
|
||||
settings: settings,
|
||||
fields: fieldsParsed.data,
|
||||
routes: routesParsed.data,
|
||||
createdAt: form.createdAt.toString(),
|
||||
|
|
|
@ -6,7 +6,12 @@ import { UseFormReturn } from "react-hook-form";
|
|||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { AppGetServerSidePropsContext, AppPrisma, AppUser } from "@calcom/types/AppGetServerSideProps";
|
||||
import {
|
||||
AppGetServerSidePropsContext,
|
||||
AppPrisma,
|
||||
AppUser,
|
||||
AppSsrInit,
|
||||
} from "@calcom/types/AppGetServerSideProps";
|
||||
import { Icon } from "@calcom/ui";
|
||||
import { Button, EmptyScreen, SelectField, TextAreaField, TextField, Shell } from "@calcom/ui/v2";
|
||||
import { BooleanToggleGroupField } from "@calcom/ui/v2/core/form/BooleanToggleGroup";
|
||||
|
@ -300,8 +305,10 @@ FormEditPage.getLayout = (page: React.ReactElement) => {
|
|||
export const getServerSideProps = async function getServerSideProps(
|
||||
context: AppGetServerSidePropsContext,
|
||||
prisma: AppPrisma,
|
||||
user: AppUser
|
||||
user: AppUser,
|
||||
ssrInit: AppSsrInit
|
||||
) {
|
||||
const ssr = await ssrInit(context);
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
|
@ -349,6 +356,8 @@ export const getServerSideProps = async function getServerSideProps(
|
|||
}
|
||||
return {
|
||||
props: {
|
||||
trpcState: ssr.dehydrate(),
|
||||
|
||||
form: getSerializableForm(form),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -6,7 +6,12 @@ import { Query, Config, Builder, Utils as QbUtils } from "react-awesome-query-bu
|
|||
import { JsonTree, ImmutableTree, BuilderProps } from "react-awesome-query-builder";
|
||||
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { AppGetServerSidePropsContext, AppPrisma, AppUser } from "@calcom/types/AppGetServerSideProps";
|
||||
import {
|
||||
AppGetServerSidePropsContext,
|
||||
AppPrisma,
|
||||
AppUser,
|
||||
AppSsrInit,
|
||||
} from "@calcom/types/AppGetServerSideProps";
|
||||
import { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||
import { Icon } from "@calcom/ui";
|
||||
import { Button, TextField, SelectWithValidation as Select, TextArea, Shell } from "@calcom/ui/v2";
|
||||
|
@ -463,8 +468,11 @@ RouteBuilder.getLayout = (page: React.ReactElement) => {
|
|||
export const getServerSideProps = async function getServerSideProps(
|
||||
context: AppGetServerSidePropsContext,
|
||||
prisma: AppPrisma,
|
||||
user: AppUser
|
||||
user: AppUser,
|
||||
ssrInit: AppSsrInit
|
||||
) {
|
||||
const ssr = await ssrInit(context);
|
||||
|
||||
if (!user) {
|
||||
return {
|
||||
redirect: {
|
||||
|
@ -513,6 +521,7 @@ export const getServerSideProps = async function getServerSideProps(
|
|||
|
||||
return {
|
||||
props: {
|
||||
trpcState: ssr.dehydrate(),
|
||||
form: getSerializableForm(form),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,16 +1,77 @@
|
|||
import { Prisma, WebhookTriggerEvents } from "@prisma/client";
|
||||
import { App_RoutingForms_Form, Prisma, User, WebhookTriggerEvents } from "@prisma/client";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { z } from "zod";
|
||||
|
||||
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
|
||||
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { RoutingFormSettings } from "@calcom/prisma/zod-utils";
|
||||
import { TRPCError } from "@calcom/trpc/server";
|
||||
import { createProtectedRouter, createRouter } from "@calcom/trpc/server/createRouter";
|
||||
import { Ensure } from "@calcom/types/utils";
|
||||
|
||||
import ResponseEmail from "./emails/templates/response-email";
|
||||
import { getSerializableForm } from "./lib/getSerializableForm";
|
||||
import { isAllowed } from "./lib/isAllowed";
|
||||
import { Response, SerializableForm } from "./types/types";
|
||||
import { zodFields, zodRoutes } from "./zod";
|
||||
|
||||
async function onFormSubmission(
|
||||
form: Ensure<SerializableForm<App_RoutingForms_Form> & { user: User }, "fields">,
|
||||
response: Response
|
||||
) {
|
||||
const fieldResponsesByName: Record<string, typeof response[keyof typeof response]["value"]> = {};
|
||||
|
||||
for (const [fieldId, fieldResponse] of Object.entries(response)) {
|
||||
// Use the label lowercased as the key to identify a field.
|
||||
const key =
|
||||
form.fields.find((f) => f.id === fieldId)?.identifier ||
|
||||
(fieldResponse.label as keyof typeof fieldResponsesByName);
|
||||
fieldResponsesByName[key] = fieldResponse.value;
|
||||
}
|
||||
|
||||
const subscriberOptions = {
|
||||
userId: form.user.id,
|
||||
// It isn't an eventType webhook
|
||||
eventTypeId: -1,
|
||||
triggerEvent: WebhookTriggerEvents.FORM_SUBMITTED,
|
||||
};
|
||||
|
||||
const webhooks = await getWebhooks(subscriberOptions);
|
||||
const promises = webhooks.map((webhook) => {
|
||||
sendGenericWebhookPayload(
|
||||
webhook.secret,
|
||||
"FORM_SUBMITTED",
|
||||
new Date().toISOString(),
|
||||
webhook,
|
||||
fieldResponsesByName
|
||||
).catch((e) => {
|
||||
console.error(`Error executing routing form webhook`, webhook, e);
|
||||
});
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
if (form.settings?.emailOwnerOnSubmission) {
|
||||
logger.debug(
|
||||
`Preparing to send Form Response email for Form:${form.id} to form owner: ${form.user.email}`
|
||||
);
|
||||
await sendResponseEmail(form, response, form.user.email);
|
||||
}
|
||||
}
|
||||
|
||||
const sendResponseEmail = async (
|
||||
form: Pick<App_RoutingForms_Form, "id" | "name">,
|
||||
response: Response,
|
||||
ownerEmail: string
|
||||
) => {
|
||||
try {
|
||||
const email = new ResponseEmail({ form: form, toAddresses: [ownerEmail], response: response });
|
||||
await email.sendEmail();
|
||||
} catch (e) {
|
||||
logger.error("Error sending response email", e);
|
||||
}
|
||||
};
|
||||
|
||||
const app_RoutingForms = createRouter()
|
||||
.merge(
|
||||
"public.",
|
||||
|
@ -41,24 +102,21 @@ const app_RoutingForms = createRouter()
|
|||
code: "NOT_FOUND",
|
||||
});
|
||||
}
|
||||
const fieldsParsed = zodFields.safeParse(form.fields);
|
||||
if (!fieldsParsed.success) {
|
||||
// This should not be possible normally as before saving the form it is verified by zod
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
});
|
||||
}
|
||||
|
||||
const fields = fieldsParsed.data;
|
||||
|
||||
if (!fields) {
|
||||
const serializableForm = getSerializableForm(form);
|
||||
if (!serializableForm.fields) {
|
||||
// There is no point in submitting a form that doesn't have fields defined
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
});
|
||||
}
|
||||
|
||||
const missingFields = fields
|
||||
const serializableFormWithFields = {
|
||||
...serializableForm,
|
||||
fields: serializableForm.fields,
|
||||
};
|
||||
|
||||
const missingFields = serializableFormWithFields.fields
|
||||
.filter((field) => !(field.required ? response[field.id]?.value : true))
|
||||
.map((f) => f.label);
|
||||
|
||||
|
@ -68,7 +126,7 @@ const app_RoutingForms = createRouter()
|
|||
message: `Missing required fields ${missingFields.join(", ")}`,
|
||||
});
|
||||
}
|
||||
const invalidFields = fields
|
||||
const invalidFields = serializableFormWithFields.fields
|
||||
.filter((field) => {
|
||||
const fieldValue = response[field.id]?.value;
|
||||
// The field isn't required at this point. Validate only if it's set
|
||||
|
@ -94,39 +152,12 @@ const app_RoutingForms = createRouter()
|
|||
});
|
||||
}
|
||||
|
||||
const fieldResponsesByName: Record<string, typeof response[keyof typeof response]["value"]> = {};
|
||||
|
||||
for (const [fieldId, fieldResponse] of Object.entries(response)) {
|
||||
// Use the label lowercased as the key to identify a field.
|
||||
const key =
|
||||
fields.find((f) => f.id === fieldId)?.identifier ||
|
||||
(fieldResponse.label as keyof typeof fieldResponsesByName);
|
||||
fieldResponsesByName[key] = fieldResponse.value;
|
||||
}
|
||||
|
||||
const subscriberOptions = {
|
||||
userId: form.user.id,
|
||||
// It isn't an eventType webhook
|
||||
eventTypeId: -1,
|
||||
triggerEvent: WebhookTriggerEvents.FORM_SUBMITTED,
|
||||
};
|
||||
const webhooks = await getWebhooks(subscriberOptions);
|
||||
const promises = webhooks.map((webhook) => {
|
||||
sendGenericWebhookPayload(
|
||||
webhook.secret,
|
||||
"FORM_SUBMITTED",
|
||||
new Date().toISOString(),
|
||||
webhook,
|
||||
fieldResponsesByName
|
||||
).catch((e) => {
|
||||
console.error(`Error executing routing form webhook`, webhook, e);
|
||||
});
|
||||
});
|
||||
await Promise.all(promises);
|
||||
|
||||
return await prisma.app_RoutingForms_FormResponse.create({
|
||||
const dbFormResponse = await prisma.app_RoutingForms_FormResponse.create({
|
||||
data: input,
|
||||
});
|
||||
|
||||
await onFormSubmission(serializableFormWithFields, dbFormResponse.response as Response);
|
||||
return dbFormResponse;
|
||||
} catch (e) {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (e.code === "P2002") {
|
||||
|
@ -201,9 +232,10 @@ const app_RoutingForms = createRouter()
|
|||
routes: zodRoutes,
|
||||
addFallback: z.boolean().optional(),
|
||||
duplicateFrom: z.string().nullable().optional(),
|
||||
settings: RoutingFormSettings.optional(),
|
||||
}),
|
||||
async resolve({ ctx: { user, prisma }, input }) {
|
||||
const { name, id, description, disabled, addFallback, duplicateFrom } = input;
|
||||
const { name, id, description, settings, disabled, addFallback, duplicateFrom } = input;
|
||||
if (!(await isAllowed({ userId: user.id, formId: id }))) {
|
||||
throw new TRPCError({
|
||||
code: "FORBIDDEN",
|
||||
|
@ -284,6 +316,7 @@ const app_RoutingForms = createRouter()
|
|||
fields: fields,
|
||||
name: name,
|
||||
description,
|
||||
settings: settings === null ? Prisma.JsonNull : settings,
|
||||
routes: routes === null ? Prisma.JsonNull : routes,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { App_RoutingForms_Form } from "@prisma/client";
|
||||
import z from "zod";
|
||||
|
||||
import { RoutingFormSettings } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { zodFields, zodRoutes } from "../zod";
|
||||
|
||||
export type Response = Record<
|
||||
|
@ -17,10 +19,11 @@ export type Routes = z.infer<typeof zodRoutes>;
|
|||
export type Route = Routes[0];
|
||||
export type SerializableForm<T extends App_RoutingForms_Form> = Omit<
|
||||
T,
|
||||
"fields" | "routes" | "createdAt" | "updatedAt"
|
||||
"fields" | "routes" | "createdAt" | "updatedAt" | "settings"
|
||||
> & {
|
||||
routes: Routes;
|
||||
fields: Fields;
|
||||
settings: z.infer<typeof RoutingFormSettings>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
|
|
@ -9,7 +9,7 @@ import logger from "@calcom/lib/logger";
|
|||
import { checkLimit } from "@calcom/lib/server";
|
||||
import { performance } from "@calcom/lib/server/perfObserver";
|
||||
import prisma, { availabilityUserSelect } from "@calcom/prisma";
|
||||
import { stringToDayjs } from "@calcom/prisma/zod-utils";
|
||||
import { EventTypeMetaDataSchema, stringToDayjs } from "@calcom/prisma/zod-utils";
|
||||
import { BookingLimit, EventBusyDetails } from "@calcom/types/Calendar";
|
||||
|
||||
import { getBusyTimes } from "./getBusyTimes";
|
||||
|
@ -27,14 +27,15 @@ const availabilitySchema = z
|
|||
})
|
||||
.refine((data) => !!data.username || !!data.userId, "Either username or userId should be filled in.");
|
||||
|
||||
const getEventType = (id: number) =>
|
||||
prisma.eventType.findUnique({
|
||||
const getEventType = async (id: number) => {
|
||||
const eventType = await prisma.eventType.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true,
|
||||
seatsPerTimeSlot: true,
|
||||
bookingLimits: true,
|
||||
timeZone: true,
|
||||
metadata: true,
|
||||
schedule: {
|
||||
select: {
|
||||
availability: true,
|
||||
|
@ -50,6 +51,14 @@ const getEventType = (id: number) =>
|
|||
},
|
||||
},
|
||||
});
|
||||
if (!eventType) {
|
||||
return eventType;
|
||||
}
|
||||
return {
|
||||
...eventType,
|
||||
metadata: EventTypeMetaDataSchema.parse(eventType.metadata),
|
||||
};
|
||||
};
|
||||
|
||||
type EventType = Awaited<ReturnType<typeof getEventType>>;
|
||||
|
||||
|
@ -200,13 +209,15 @@ export async function getUserAvailability(
|
|||
});
|
||||
}
|
||||
}
|
||||
const schedule = eventType?.schedule
|
||||
? { ...eventType?.schedule }
|
||||
: {
|
||||
...currentUser.schedules.filter(
|
||||
(schedule) => !currentUser.defaultScheduleId || schedule.id === currentUser.defaultScheduleId
|
||||
)[0],
|
||||
};
|
||||
|
||||
const schedule =
|
||||
!eventType?.metadata?.config?.useHostSchedulesForTeamEvent && eventType?.schedule
|
||||
? { ...eventType?.schedule }
|
||||
: {
|
||||
...currentUser.schedules.filter(
|
||||
(schedule) => !currentUser.defaultScheduleId || schedule.id === currentUser.defaultScheduleId
|
||||
)[0],
|
||||
};
|
||||
|
||||
const startGetWorkingHours = performance.now();
|
||||
|
||||
|
|
|
@ -18,3 +18,4 @@ export { OrganizerRescheduledEmail } from "./OrganizerRescheduledEmail";
|
|||
export { OrganizerScheduledEmail } from "./OrganizerScheduledEmail";
|
||||
export { TeamInviteEmail } from "./TeamInviteEmail";
|
||||
export { BrokenIntegrationEmail } from "./BrokenIntegrationEmail";
|
||||
export * from "@calcom/app-store/ee/routing-forms/emails/components";
|
||||
|
|
|
@ -20,7 +20,7 @@ const customTemplate = async (text: string, variables: VariablesType, locale: st
|
|||
let locationString = variables.location || "";
|
||||
|
||||
if (text.includes("{LOCATION}")) {
|
||||
locationString = guessEventLocationType(locationString)?.label || "";
|
||||
locationString = guessEventLocationType(locationString)?.label || locationString;
|
||||
}
|
||||
|
||||
let dynamicText = text
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterTable
|
||||
ALTER TABLE "App_RoutingForms_Form" ADD COLUMN "settings" JSONB;
|
|
@ -518,6 +518,8 @@ model App_RoutingForms_Form {
|
|||
userId Int
|
||||
responses App_RoutingForms_FormResponse[]
|
||||
disabled Boolean @default(false)
|
||||
/// @zod.custom(imports.RoutingFormSettings)
|
||||
settings Json?
|
||||
}
|
||||
|
||||
model App_RoutingForms_FormResponse {
|
||||
|
|
|
@ -32,6 +32,11 @@ export const EventTypeMetaDataSchema = z
|
|||
giphyThankYouPage: z.string().optional(),
|
||||
apps: z.object(appDataSchemas).partial().optional(),
|
||||
additionalNotesRequired: z.boolean().optional(),
|
||||
config: z
|
||||
.object({
|
||||
useHostSchedulesForTeamEvent: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.nullable();
|
||||
|
||||
|
@ -208,6 +213,12 @@ export const successRedirectUrl = z
|
|||
])
|
||||
.optional();
|
||||
|
||||
export const RoutingFormSettings = z
|
||||
.object({
|
||||
emailOwnerOnSubmission: z.boolean(),
|
||||
})
|
||||
.nullable();
|
||||
|
||||
export type ZodDenullish<T extends ZodTypeAny> = T extends ZodNullable<infer U> | ZodOptional<infer U>
|
||||
? ZodDenullish<U>
|
||||
: T;
|
||||
|
|
|
@ -11,6 +11,7 @@ import logger from "@calcom/lib/logger";
|
|||
import { performance } from "@calcom/lib/server/perfObserver";
|
||||
import getTimeSlots from "@calcom/lib/slots";
|
||||
import prisma, { availabilityUserSelect } from "@calcom/prisma";
|
||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
import { EventBusyDate } from "@calcom/types/Calendar";
|
||||
import { TimeRange } from "@calcom/types/schedule";
|
||||
|
||||
|
@ -104,7 +105,7 @@ export const slotsRouter = createRouter().query("getSchedule", {
|
|||
});
|
||||
|
||||
async function getEventType(ctx: { prisma: typeof prisma }, input: z.infer<typeof getScheduleSchema>) {
|
||||
return ctx.prisma.eventType.findUnique({
|
||||
const eventType = await ctx.prisma.eventType.findUnique({
|
||||
where: {
|
||||
id: input.eventTypeId,
|
||||
},
|
||||
|
@ -124,6 +125,7 @@ async function getEventType(ctx: { prisma: typeof prisma }, input: z.infer<typeo
|
|||
periodEndDate: true,
|
||||
periodCountCalendarDays: true,
|
||||
periodDays: true,
|
||||
metadata: true,
|
||||
schedule: {
|
||||
select: {
|
||||
availability: true,
|
||||
|
@ -144,6 +146,14 @@ async function getEventType(ctx: { prisma: typeof prisma }, input: z.infer<typeo
|
|||
},
|
||||
},
|
||||
});
|
||||
if (!eventType) {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
return {
|
||||
...eventType,
|
||||
metadata: EventTypeMetaDataSchema.parse(eventType.metadata),
|
||||
};
|
||||
}
|
||||
|
||||
async function getDynamicEventType(ctx: { prisma: typeof prisma }, input: z.infer<typeof getScheduleSchema>) {
|
||||
|
|
|
@ -3,14 +3,17 @@ import { CalendsoSessionUser } from "next-auth";
|
|||
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import type { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
export type AppUser = CalendsoSessionUser | undefined;
|
||||
export type AppPrisma = typeof prisma;
|
||||
export type AppGetServerSidePropsContext = GetServerSidePropsContext<{
|
||||
appPages: string[];
|
||||
}>;
|
||||
|
||||
export type AppSsrInit = ssrInit;
|
||||
export type AppGetServerSideProps = (
|
||||
context: AppGetServerSidePropsContext,
|
||||
prisma: AppPrisma,
|
||||
user: AppUser
|
||||
user: AppUser,
|
||||
ssrInit: AppSsrInit
|
||||
) => GetServerSidePropsResult;
|
||||
|
|
|
@ -44,8 +44,8 @@ function SettingsToggle({
|
|||
</div>
|
||||
</div>
|
||||
{children && (
|
||||
<div className="mt-4 lg:ml-14" ref={animateRef}>
|
||||
{checked && children}
|
||||
<div className="lg:ml-14" ref={animateRef}>
|
||||
{checked && <div className="mt-4">{children}</div>}
|
||||
</div>
|
||||
)}
|
||||
</fieldset>
|
||||
|
|
|
@ -228,6 +228,8 @@
|
|||
"$SAML_ADMINS",
|
||||
"$SAML_DATABASE_URL",
|
||||
"$SEND_FEEDBACK_EMAIL",
|
||||
"$SENTRY_DSN",
|
||||
"$NEXT_PUBLIC_SENTRY_DSN",
|
||||
"$SLACK_CLIENT_ID",
|
||||
"$SLACK_CLIENT_SECRET",
|
||||
"$SLACK_SIGNING_SECRET",
|
||||
|
|
177
yarn.lock
177
yarn.lock
|
@ -4806,7 +4806,20 @@
|
|||
resolved "https://registry.yarnpkg.com/@resvg/resvg-wasm/-/resvg-wasm-2.0.0-alpha.4.tgz#fc2f86186a9641df030d8f9f3f9d995899cd1ecb"
|
||||
integrity sha512-pWIG9a/x1ky8gXKRhPH1OPKpHFoMN1ISLbJ+O+gPXQHIAKhNd5I28RlWf7q576hAOQA9JZTlo3p/M2uyLzJmmw==
|
||||
|
||||
"@rollup/pluginutils@^4.2.1":
|
||||
"@rollup/plugin-sucrase@4.0.4":
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-sucrase/-/plugin-sucrase-4.0.4.tgz#0a3b3d97cdc239ec3399f5a10711f751e9f95d98"
|
||||
integrity sha512-YH4J8yoJb5EVnLhAwWxYAQNh2SJOR+SdZ6XdgoKEv6Kxm33riYkM8MlMaggN87UoISP52qAFyZ5ey56wu6umGg==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^4.1.1"
|
||||
sucrase "^3.20.0"
|
||||
|
||||
"@rollup/plugin-virtual@3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-virtual/-/plugin-virtual-3.0.0.tgz#8c3f54b4ab4b267d9cd3dcbaedc58d4fd1deddca"
|
||||
integrity sha512-K9KORe1myM62o0lKkNR4MmCxjwuAXsZEtIHpaILfv4kILXTOrXt/R2ha7PzMcCHPYdnkWPiBZK8ed4Zr3Ll5lQ==
|
||||
|
||||
"@rollup/pluginutils@^4.1.1", "@rollup/pluginutils@^4.2.1":
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
|
||||
integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==
|
||||
|
@ -4852,6 +4865,16 @@
|
|||
"@sentry/utils" "6.19.7"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/browser@7.17.4":
|
||||
version "7.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.17.4.tgz#2e4ad09180905929b823d01e2a029f10933c0a03"
|
||||
integrity sha512-cNLQ/6ea0KOZyLKXGLTdgfqWYdg43+T/uF9D/hmc6kp/5qXm2dR3FcFRZX6OicaENM3dXvSoBIF6bSWmcszRtQ==
|
||||
dependencies:
|
||||
"@sentry/core" "7.17.4"
|
||||
"@sentry/types" "7.17.4"
|
||||
"@sentry/utils" "7.17.4"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/cli@^1.73.0":
|
||||
version "1.74.5"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.5.tgz#4a5c622913087c9ab6f82994da9a7526423779b8"
|
||||
|
@ -4865,6 +4888,19 @@
|
|||
proxy-from-env "^1.1.0"
|
||||
which "^2.0.2"
|
||||
|
||||
"@sentry/cli@^1.74.6":
|
||||
version "1.74.6"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.6.tgz#c4f276e52c6f5e8c8d692845a965988068ebc6f5"
|
||||
integrity sha512-pJ7JJgozyjKZSTjOGi86chIngZMLUlYt2HOog+OJn+WGvqEkVymu8m462j1DiXAnex9NspB4zLLNuZ/R6rTQHg==
|
||||
dependencies:
|
||||
https-proxy-agent "^5.0.0"
|
||||
mkdirp "^0.5.5"
|
||||
node-fetch "^2.6.7"
|
||||
npmlog "^4.1.2"
|
||||
progress "^2.0.3"
|
||||
proxy-from-env "^1.1.0"
|
||||
which "^2.0.2"
|
||||
|
||||
"@sentry/core@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-6.19.7.tgz#156aaa56dd7fad8c89c145be6ad7a4f7209f9785"
|
||||
|
@ -4876,6 +4912,15 @@
|
|||
"@sentry/utils" "6.19.7"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/core@7.17.4":
|
||||
version "7.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.17.4.tgz#62686e1b2baf4e7ba3d9ca57d91d2dfc643b8250"
|
||||
integrity sha512-U3ABSJBKGK8dJ01nEG2+qNOb6Wv7U3VqoajiZxfV4lpPWNFGCoEhiTytxBlFTOCmdUH8209zSZiWJZaDLy+TSA==
|
||||
dependencies:
|
||||
"@sentry/types" "7.17.4"
|
||||
"@sentry/utils" "7.17.4"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/hub@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.19.7.tgz#58ad7776bbd31e9596a8ec46365b45cd8b9cfd11"
|
||||
|
@ -4895,6 +4940,16 @@
|
|||
localforage "^1.8.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/integrations@7.17.4":
|
||||
version "7.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.17.4.tgz#facdc4abb1d4714175c7d66bab634a898cc7eead"
|
||||
integrity sha512-9vL0RRLuMnj0mj61THgRhDUCgTGYbRoCT6HmLGrErHjyTG3d7lUgUwTBQPgw9qYLss1uLKoEtRvcxgtW7ef0BA==
|
||||
dependencies:
|
||||
"@sentry/types" "7.17.4"
|
||||
"@sentry/utils" "7.17.4"
|
||||
localforage "^1.8.1"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/minimal@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.19.7.tgz#b3ee46d6abef9ef3dd4837ebcb6bdfd01b9aa7b4"
|
||||
|
@ -4919,6 +4974,25 @@
|
|||
"@sentry/webpack-plugin" "1.18.8"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/nextjs@^7.17.3":
|
||||
version "7.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/nextjs/-/nextjs-7.17.4.tgz#9c6d71d17e37da3fcbe753a374e93307a56dbf02"
|
||||
integrity sha512-l4aHhkYjiC49yvvu/iG0CZGpAfmWdVQ4PlcnzqO9XY40So1LixY7idJEDefqx6LSZ1+VadpZT0o6iRwHfuOllQ==
|
||||
dependencies:
|
||||
"@rollup/plugin-sucrase" "4.0.4"
|
||||
"@rollup/plugin-virtual" "3.0.0"
|
||||
"@sentry/core" "7.17.4"
|
||||
"@sentry/integrations" "7.17.4"
|
||||
"@sentry/node" "7.17.4"
|
||||
"@sentry/react" "7.17.4"
|
||||
"@sentry/tracing" "7.17.4"
|
||||
"@sentry/types" "7.17.4"
|
||||
"@sentry/utils" "7.17.4"
|
||||
"@sentry/webpack-plugin" "1.20.0"
|
||||
chalk "3.0.0"
|
||||
rollup "2.78.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/node@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-6.19.7.tgz#32963b36b48daebbd559e6f13b1deb2415448592"
|
||||
|
@ -4933,6 +5007,19 @@
|
|||
lru_map "^0.3.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/node@7.17.4":
|
||||
version "7.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.17.4.tgz#647ebcbd7228e5fa326dc3f95aceab34771205d3"
|
||||
integrity sha512-cR+Gsir9c/tzFWxvk4zXkMQy6tNRHEYixHrb88XIjZVYDqDS9l2/bKs5nJusdmaUeLtmPp5Et2o7RJyS7gvKTQ==
|
||||
dependencies:
|
||||
"@sentry/core" "7.17.4"
|
||||
"@sentry/types" "7.17.4"
|
||||
"@sentry/utils" "7.17.4"
|
||||
cookie "^0.4.1"
|
||||
https-proxy-agent "^5.0.0"
|
||||
lru_map "^0.3.3"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/react@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-6.19.7.tgz#58cc2d6da20f7d3b0df40638dfbbbc86c9c85caf"
|
||||
|
@ -4945,6 +5032,17 @@
|
|||
hoist-non-react-statics "^3.3.2"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/react@7.17.4":
|
||||
version "7.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.17.4.tgz#1d2c752841ace0561043be6a863de9fc7437f64c"
|
||||
integrity sha512-Hw8lgeCgUthgVQ5OG24/iZWGNXnxodVfCmfngeIfqUWeFgQUae1V833GNkYZCiE5j2yjNVh3LL2bXA8PnvMCEg==
|
||||
dependencies:
|
||||
"@sentry/browser" "7.17.4"
|
||||
"@sentry/types" "7.17.4"
|
||||
"@sentry/utils" "7.17.4"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/tracing@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.19.7.tgz#54bb99ed5705931cd33caf71da347af769f02a4c"
|
||||
|
@ -4956,11 +5054,26 @@
|
|||
"@sentry/utils" "6.19.7"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/tracing@7.17.4":
|
||||
version "7.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-7.17.4.tgz#1254f4eb7397e1134248de1dade33d2fec864005"
|
||||
integrity sha512-9Fz6DI16ddnd970mlB5MiCNRSmSXp4SVZ1Yv3L22oS3kQeNxjBTE+htYNwJzSPrQp9aL/LqTYwlnrCy24u9XQA==
|
||||
dependencies:
|
||||
"@sentry/core" "7.17.4"
|
||||
"@sentry/types" "7.17.4"
|
||||
"@sentry/utils" "7.17.4"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/types@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.19.7.tgz#c6b337912e588083fc2896eb012526cf7cfec7c7"
|
||||
integrity sha512-jH84pDYE+hHIbVnab3Hr+ZXr1v8QABfhx39KknxqKWr2l0oEItzepV0URvbEhB446lk/S/59230dlUUIBGsXbg==
|
||||
|
||||
"@sentry/types@7.17.4":
|
||||
version "7.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.17.4.tgz#476522bc988989101e7aee9eee3c3f8f16fa59ea"
|
||||
integrity sha512-QJj8vO4AtxuzQfJIzDnECSmoxwnS+WJsm1Ta2Cwdy+TUCBJyWpW7aIJJGta76zb9gNPGb3UcAbeEjhMJBJeRMQ==
|
||||
|
||||
"@sentry/utils@6.19.7":
|
||||
version "6.19.7"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.19.7.tgz#6edd739f8185fd71afe49cbe351c1bbf5e7b7c79"
|
||||
|
@ -4969,6 +5082,14 @@
|
|||
"@sentry/types" "6.19.7"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/utils@7.17.4":
|
||||
version "7.17.4"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.17.4.tgz#b8f4fb6a329765b97668b34f6d397e8de2169fef"
|
||||
integrity sha512-ioG0ANy8uiWzig82/e7cc+6C9UOxkyBzJDi1luoQVDH6P0/PvM8GzVU+1iUVUipf8+OL1Jh09GrWnd5wLm3XNQ==
|
||||
dependencies:
|
||||
"@sentry/types" "7.17.4"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@sentry/webpack-plugin@1.18.8":
|
||||
version "1.18.8"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.18.8.tgz#247a73a0aa9e28099a736bbe89ca0d35cbac7636"
|
||||
|
@ -4976,6 +5097,14 @@
|
|||
dependencies:
|
||||
"@sentry/cli" "^1.73.0"
|
||||
|
||||
"@sentry/webpack-plugin@1.20.0":
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-1.20.0.tgz#e7add76122708fb6b4ee7951294b521019720e58"
|
||||
integrity sha512-Ssj1mJVFsfU6vMCOM2d+h+KQR7QHSfeIP16t4l20Uq/neqWXZUQ2yvQfe4S3BjdbJXz/X4Rw8Hfy1Sd0ocunYw==
|
||||
dependencies:
|
||||
"@sentry/cli" "^1.74.6"
|
||||
webpack-sources "^2.0.0 || ^3.0.0"
|
||||
|
||||
"@shuding/opentype.js@1.4.0-beta.0":
|
||||
version "1.4.0-beta.0"
|
||||
resolved "https://registry.yarnpkg.com/@shuding/opentype.js/-/opentype.js-1.4.0-beta.0.tgz#5d1e7e9e056f546aad41df1c5043f8f85d39e24b"
|
||||
|
@ -9665,6 +9794,14 @@ chalk@2.3.0:
|
|||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^4.0.0"
|
||||
|
||||
chalk@3.0.0, chalk@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
|
||||
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
|
||||
|
@ -9701,14 +9838,6 @@ chalk@^2.0.0, chalk@^2.4.1:
|
|||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
|
||||
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
change-case@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/change-case/-/change-case-2.3.1.tgz#2c4fde3f063bb41d00cd68e0d5a09db61cbe894f"
|
||||
|
@ -18289,7 +18418,7 @@ mysql2@2.3.3:
|
|||
seq-queue "^0.0.5"
|
||||
sqlstring "^2.3.2"
|
||||
|
||||
mz@^2.4.0:
|
||||
mz@^2.4.0, mz@^2.7.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
|
||||
integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
|
||||
|
@ -21711,6 +21840,13 @@ rlp@^2.0.0, rlp@^2.2.3, rlp@^2.2.4:
|
|||
dependencies:
|
||||
bn.js "^5.2.0"
|
||||
|
||||
rollup@2.78.0:
|
||||
version "2.78.0"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.78.0.tgz#00995deae70c0f712ea79ad904d5f6b033209d9e"
|
||||
integrity sha512-4+YfbQC9QEVvKTanHhIAFVUFSRsezvQF8vFOJwtGfb9Bb+r014S+qryr9PSmw8x6sMnPkmFBGAvIFVQxvJxjtg==
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
"rollup@>=2.59.0 <2.78.0":
|
||||
version "2.77.3"
|
||||
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12"
|
||||
|
@ -23021,6 +23157,18 @@ stylis@4.0.13:
|
|||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91"
|
||||
integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==
|
||||
|
||||
sucrase@^3.20.0:
|
||||
version "3.28.0"
|
||||
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.28.0.tgz#7fd8b3118d2155fcdf291088ab77fa6eefd63c4c"
|
||||
integrity sha512-TK9600YInjuiIhVM3729rH4ZKPOsGeyXUwY+Ugu9eilNbdTFyHr6XcAGYbRVZPDgWj6tgI7bx95aaJjHnbffag==
|
||||
dependencies:
|
||||
commander "^4.0.0"
|
||||
glob "7.1.6"
|
||||
lines-and-columns "^1.1.6"
|
||||
mz "^2.7.0"
|
||||
pirates "^4.0.1"
|
||||
ts-interface-checker "^0.1.9"
|
||||
|
||||
superagent@^5.1.1:
|
||||
version "5.3.1"
|
||||
resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.3.1.tgz#d62f3234d76b8138c1320e90fa83dc1850ccabf1"
|
||||
|
@ -23723,6 +23871,11 @@ ts-essentials@^7.0.3:
|
|||
resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38"
|
||||
integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==
|
||||
|
||||
ts-interface-checker@^0.1.9:
|
||||
version "0.1.13"
|
||||
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
|
||||
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
|
||||
|
||||
ts-jest@^28.0.8:
|
||||
version "28.0.8"
|
||||
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-28.0.8.tgz#cd204b8e7a2f78da32cf6c95c9a6165c5b99cc73"
|
||||
|
@ -25223,7 +25376,7 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
|
|||
source-list-map "^2.0.0"
|
||||
source-map "~0.6.1"
|
||||
|
||||
webpack-sources@^3.2.3:
|
||||
"webpack-sources@^2.0.0 || ^3.0.0", webpack-sources@^3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
|
||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||
|
@ -25845,7 +25998,7 @@ zod-prisma@^0.5.4:
|
|||
parenthesis "^3.1.8"
|
||||
ts-morph "^13.0.2"
|
||||
|
||||
zod@^3.17.3, zod@^3.19.1:
|
||||
zod@^3.17.3, zod@^3.18.0, zod@^3.19.1:
|
||||
version "3.19.1"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.19.1.tgz#112f074a97b50bfc4772d4ad1576814bd8ac4473"
|
||||
integrity sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==
|
||||
|
|
Loading…
Reference in New Issue
Block a user