Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7b4d5932d | ||
|
|
3c7fc85b34 | ||
|
|
af018f0e8b | ||
|
|
6573f75a83 | ||
|
|
a3b26cd1e7 | ||
|
|
4f99746f68 | ||
|
|
fd76bc9cab | ||
|
|
602c813928 | ||
|
|
2c54ea90f9 | ||
|
+17 |
2698cde311 | ||
|
|
1433ec6946 | ||
|
|
48e5d18ea1 |
No files matched your search
@@ -2,4 +2,9 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@import "../../../packages/ui/styles/shared-globals.css"
|
@import "../../../packages/ui/styles/shared-globals.css";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--font-inter: "Inter var";
|
||||||
|
--font-cal: "Cal Sans";
|
||||||
|
}
|
||||||
@@ -20,10 +20,10 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
TextArea,
|
TextArea,
|
||||||
TextField,
|
TextField,
|
||||||
|
ColorPicker,
|
||||||
} from "@calcom/ui";
|
} from "@calcom/ui";
|
||||||
import { FiCode, FiTrello, FiSun, FiArrowLeft, FiChevronRight } from "@calcom/ui/components/icon";
|
import { FiCode, FiTrello, FiSun, FiArrowLeft, FiChevronRight } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import ColorPicker from "@components/ui/colorpicker";
|
|
||||||
import Select from "@components/ui/form/Select";
|
import Select from "@components/ui/form/Select";
|
||||||
|
|
||||||
type EmbedType = "inline" | "floating-popup" | "element-click";
|
type EmbedType = "inline" | "floating-popup" | "element-click";
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
@@ -39,6 +40,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
|
|||||||
seatsPerTimeSlot,
|
seatsPerTimeSlot,
|
||||||
ethSignature,
|
ethSignature,
|
||||||
}) => {
|
}) => {
|
||||||
|
const [slotPickerRef] = useAutoAnimate<HTMLDivElement>();
|
||||||
const { t, i18n } = useLocale();
|
const { t, i18n } = useLocale();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { rescheduleUid } = router.query;
|
const { rescheduleUid } = router.query;
|
||||||
@@ -49,122 +51,124 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
|
|||||||
setBrand(getComputedStyle(document.documentElement).getPropertyValue("--brand-color").trim());
|
setBrand(getComputedStyle(document.documentElement).getPropertyValue("--brand-color").trim());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!date) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dark:bg-darkgray-100 mt-8 flex h-full w-full flex-col px-4 text-center sm:mt-0 sm:p-5 md:-mb-5 md:min-w-[200px] lg:min-w-[300px]">
|
<div ref={slotPickerRef}>
|
||||||
<div className="mb-6 flex items-center text-left text-base">
|
{!!date ? (
|
||||||
<div className="mr-4">
|
<div className="dark:bg-darkgray-100 mt-8 flex h-full w-full flex-col px-4 text-center sm:mt-0 sm:p-5 md:-mb-5 md:min-w-[200px] lg:min-w-[300px]">
|
||||||
<span className="text-bookingdarker dark:text-darkgray-800 font-semibold text-gray-900">
|
<div className="mb-6 flex items-center text-left text-base">
|
||||||
{nameOfDay(i18n.language, Number(date.format("d")), "short")}
|
<div className="mr-4">
|
||||||
</span>
|
<span className="text-bookingdarker dark:text-darkgray-800 font-semibold text-gray-900">
|
||||||
<span className="text-bookinglight font-medium">
|
{nameOfDay(i18n.language, Number(date.format("d")), "short")}
|
||||||
, {date.toDate().toLocaleString(i18n.language, { month: "short" })} {date.format(" D ")}
|
</span>
|
||||||
</span>
|
<span className="text-bookinglight font-medium">
|
||||||
</div>
|
, {date.toDate().toLocaleString(i18n.language, { month: "short" })} {date.format(" D ")}
|
||||||
<div className="ml-auto">
|
</span>
|
||||||
<ToggleGroup
|
</div>
|
||||||
onValueChange={(timeFormat) => onTimeFormatChange(timeFormat === "24")}
|
<div className="ml-auto">
|
||||||
defaultValue={timeFormat === TimeFormat.TWELVE_HOUR ? "12" : "24"}
|
<ToggleGroup
|
||||||
options={[
|
onValueChange={(timeFormat) => onTimeFormatChange(timeFormat === "24")}
|
||||||
{ value: "12", label: t("12_hour_short") },
|
defaultValue={timeFormat === TimeFormat.TWELVE_HOUR ? "12" : "24"}
|
||||||
{ value: "24", label: t("24_hour_short") },
|
options={[
|
||||||
]}
|
{ value: "12", label: t("12_hour_short") },
|
||||||
/>
|
{ value: "24", label: t("24_hour_short") },
|
||||||
</div>
|
]}
|
||||||
</div>
|
/>
|
||||||
<div className="flex-grow overflow-y-auto sm:block md:h-[364px]">
|
</div>
|
||||||
{slots.length > 0 &&
|
|
||||||
slots.map((slot) => {
|
|
||||||
type BookingURL = {
|
|
||||||
pathname: string;
|
|
||||||
query: Record<string, string | number | string[] | undefined>;
|
|
||||||
};
|
|
||||||
const bookingUrl: BookingURL = {
|
|
||||||
pathname: router.pathname.endsWith("/embed") ? "../book" : "book",
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
date: dayjs.utc(slot.time).tz(timeZone()).format(),
|
|
||||||
type: eventTypeId,
|
|
||||||
slug: eventTypeSlug,
|
|
||||||
/** Treat as recurring only when a count exist and it's not a rescheduling workflow */
|
|
||||||
count: recurringCount && !rescheduleUid ? recurringCount : undefined,
|
|
||||||
...(ethSignature ? { ethSignature } : {}),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (rescheduleUid) {
|
|
||||||
bookingUrl.query.rescheduleUid = rescheduleUid as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If event already has an attendee add booking id
|
|
||||||
if (slot.bookingUid) {
|
|
||||||
bookingUrl.query.bookingUid = slot.bookingUid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div data-slot-owner={(slot.userIds || []).join(",")} key={`${dayjs(slot.time).format()}`}>
|
|
||||||
{/* ^ data-slot-owner is helpful in debugging and used to identify the owners of the slot. Owners are the users which have the timeslot in their schedule. It doesn't consider if a user has that timeslot booked */}
|
|
||||||
{/* Current there is no way to disable Next.js Links */}
|
|
||||||
{seatsPerTimeSlot && slot.attendees && slot.attendees >= seatsPerTimeSlot ? (
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
"text-primary-500 dark:bg-darkgray-200 dark:text-darkgray-900 mb-2 block rounded-sm border bg-white py-2 font-medium opacity-25 dark:border-transparent ",
|
|
||||||
brand === "#fff" || brand === "#ffffff" ? "" : ""
|
|
||||||
)}>
|
|
||||||
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
|
|
||||||
{!!seatsPerTimeSlot && <p className="text-sm">{t("booking_full")}</p>}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Link
|
|
||||||
href={bookingUrl}
|
|
||||||
prefetch={false}
|
|
||||||
className={classNames(
|
|
||||||
"text-primary-500 hover:border-gray-900 hover:bg-gray-50",
|
|
||||||
"dark:bg-darkgray-200 dark:hover:bg-darkgray-300 dark:hover:border-darkmodebrand dark:text-darkgray-800 mb-2 block rounded-md border bg-white py-2 text-sm font-medium dark:border-transparent",
|
|
||||||
brand === "#fff" || brand === "#ffffff" ? "" : ""
|
|
||||||
)}
|
|
||||||
data-testid="time">
|
|
||||||
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
|
|
||||||
{!!seatsPerTimeSlot && (
|
|
||||||
<p
|
|
||||||
className={`${
|
|
||||||
slot.attendees && slot.attendees / seatsPerTimeSlot >= 0.8
|
|
||||||
? "text-rose-600"
|
|
||||||
: slot.attendees && slot.attendees / seatsPerTimeSlot >= 0.33
|
|
||||||
? "text-yellow-500"
|
|
||||||
: "text-emerald-400"
|
|
||||||
} text-sm`}>
|
|
||||||
{slot.attendees ? seatsPerTimeSlot - slot.attendees : seatsPerTimeSlot} /{" "}
|
|
||||||
{seatsPerTimeSlot} {t("seats_available")}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{!isLoading && !slots.length && (
|
|
||||||
<div className="-mt-4 flex h-full w-full flex-col content-center items-center justify-center">
|
|
||||||
<h1 className="my-6 text-xl text-black dark:text-white">{t("all_booked_today")}</h1>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<div className="flex-grow overflow-y-auto sm:block md:h-[364px]">
|
||||||
|
{slots.length > 0 &&
|
||||||
|
slots.map((slot) => {
|
||||||
|
type BookingURL = {
|
||||||
|
pathname: string;
|
||||||
|
query: Record<string, string | number | string[] | undefined>;
|
||||||
|
};
|
||||||
|
const bookingUrl: BookingURL = {
|
||||||
|
pathname: router.pathname.endsWith("/embed") ? "../book" : "book",
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
date: dayjs.utc(slot.time).tz(timeZone()).format(),
|
||||||
|
type: eventTypeId,
|
||||||
|
slug: eventTypeSlug,
|
||||||
|
/** Treat as recurring only when a count exist and it's not a rescheduling workflow */
|
||||||
|
count: recurringCount && !rescheduleUid ? recurringCount : undefined,
|
||||||
|
...(ethSignature ? { ethSignature } : {}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
{isLoading && !slots.length && (
|
if (rescheduleUid) {
|
||||||
<>
|
bookingUrl.query.rescheduleUid = rescheduleUid as string;
|
||||||
<SkeletonContainer className="mb-2">
|
}
|
||||||
<SkeletonText className="h-5 w-full" />
|
|
||||||
</SkeletonContainer>
|
// If event already has an attendee add booking id
|
||||||
<SkeletonContainer className="mb-2">
|
if (slot.bookingUid) {
|
||||||
<SkeletonText className="h-5 w-full" />
|
bookingUrl.query.bookingUid = slot.bookingUid;
|
||||||
</SkeletonContainer>
|
}
|
||||||
<SkeletonContainer className="mb-2">
|
|
||||||
<SkeletonText className="h-5 w-full" />
|
return (
|
||||||
</SkeletonContainer>
|
<div data-slot-owner={(slot.userIds || []).join(",")} key={`${dayjs(slot.time).format()}`}>
|
||||||
</>
|
{/* ^ data-slot-owner is helpful in debugging and used to identify the owners of the slot. Owners are the users which have the timeslot in their schedule. It doesn't consider if a user has that timeslot booked */}
|
||||||
)}
|
{/* Current there is no way to disable Next.js Links */}
|
||||||
</div>
|
{seatsPerTimeSlot && slot.attendees && slot.attendees >= seatsPerTimeSlot ? (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
"text-primary-500 dark:bg-darkgray-200 dark:text-darkgray-900 mb-2 block rounded-sm border bg-white py-2 font-medium opacity-25 dark:border-transparent ",
|
||||||
|
brand === "#fff" || brand === "#ffffff" ? "" : ""
|
||||||
|
)}>
|
||||||
|
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
|
||||||
|
{!!seatsPerTimeSlot && <p className="text-sm">{t("booking_full")}</p>}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Link
|
||||||
|
href={bookingUrl}
|
||||||
|
prefetch={false}
|
||||||
|
className={classNames(
|
||||||
|
"text-primary-500 hover:border-gray-900 hover:bg-gray-50",
|
||||||
|
"dark:bg-darkgray-200 dark:hover:bg-darkgray-300 dark:hover:border-darkmodebrand dark:text-darkgray-800 mb-2 block rounded-md border bg-white py-2 text-sm font-medium dark:border-transparent",
|
||||||
|
brand === "#fff" || brand === "#ffffff" ? "" : ""
|
||||||
|
)}
|
||||||
|
data-testid="time">
|
||||||
|
{dayjs(slot.time).tz(timeZone()).format(timeFormat)}
|
||||||
|
{!!seatsPerTimeSlot && (
|
||||||
|
<p
|
||||||
|
className={`${
|
||||||
|
slot.attendees && slot.attendees / seatsPerTimeSlot >= 0.8
|
||||||
|
? "text-rose-600"
|
||||||
|
: slot.attendees && slot.attendees / seatsPerTimeSlot >= 0.33
|
||||||
|
? "text-yellow-500"
|
||||||
|
: "text-emerald-400"
|
||||||
|
} text-sm`}>
|
||||||
|
{slot.attendees ? seatsPerTimeSlot - slot.attendees : seatsPerTimeSlot} /{" "}
|
||||||
|
{seatsPerTimeSlot} {t("seats_available")}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
{!isLoading && !slots.length && (
|
||||||
|
<div className="-mt-4 flex h-full w-full flex-col content-center items-center justify-center">
|
||||||
|
<h1 className="my-6 text-xl text-black dark:text-white">{t("all_booked_today")}</h1>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isLoading && !slots.length && (
|
||||||
|
<>
|
||||||
|
<SkeletonContainer className="mb-2">
|
||||||
|
<SkeletonText className="h-5 w-full" />
|
||||||
|
</SkeletonContainer>
|
||||||
|
<SkeletonContainer className="mb-2">
|
||||||
|
<SkeletonText className="h-5 w-full" />
|
||||||
|
</SkeletonContainer>
|
||||||
|
<SkeletonContainer className="mb-2">
|
||||||
|
<SkeletonText className="h-5 w-full" />
|
||||||
|
</SkeletonContainer>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { FC, ReactNode } from "react";
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import { classNames } from "@calcom/lib";
|
import classNames from "@calcom/lib/classNames";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { Badge } from "@calcom/ui";
|
import { Badge } from "@calcom/ui";
|
||||||
import { FiCheckSquare, FiClock, FiInfo } from "@calcom/ui/components/icon";
|
import { FiCheckSquare, FiClock, FiInfo } from "@calcom/ui/components/icon";
|
||||||
|
|||||||
@@ -0,0 +1,200 @@
|
|||||||
|
import type { EventType } from "@prisma/client";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
import type { Dayjs } from "@calcom/dayjs";
|
||||||
|
import dayjs from "@calcom/dayjs";
|
||||||
|
import DatePicker from "@calcom/features/calendars/DatePicker";
|
||||||
|
import classNames from "@calcom/lib/classNames";
|
||||||
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
import type { TimeFormat } from "@calcom/lib/timeFormat";
|
||||||
|
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||||
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
|
||||||
|
import useRouterQuery from "@lib/hooks/useRouterQuery";
|
||||||
|
|
||||||
|
const AvailableTimes = dynamic(() => import("@components/booking/AvailableTimes"));
|
||||||
|
|
||||||
|
const getRefetchInterval = (refetchCount: number): number => {
|
||||||
|
const intervals = [3000, 3000, 5000, 10000, 20000, 30000] as const;
|
||||||
|
return intervals[refetchCount] || intervals[intervals.length - 1];
|
||||||
|
};
|
||||||
|
|
||||||
|
const useSlots = ({
|
||||||
|
eventTypeId,
|
||||||
|
eventTypeSlug,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
usernameList,
|
||||||
|
timeZone,
|
||||||
|
duration,
|
||||||
|
enabled = true,
|
||||||
|
}: {
|
||||||
|
eventTypeId: number;
|
||||||
|
eventTypeSlug: string;
|
||||||
|
startTime?: Dayjs;
|
||||||
|
endTime?: Dayjs;
|
||||||
|
usernameList: string[];
|
||||||
|
timeZone?: string;
|
||||||
|
duration?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
}) => {
|
||||||
|
const [refetchCount, setRefetchCount] = useState(0);
|
||||||
|
const refetchInterval = getRefetchInterval(refetchCount);
|
||||||
|
const { data, isLoading, isPaused, fetchStatus } = trpc.viewer.public.slots.getSchedule.useQuery(
|
||||||
|
{
|
||||||
|
eventTypeId,
|
||||||
|
eventTypeSlug,
|
||||||
|
usernameList,
|
||||||
|
startTime: startTime?.toISOString() || "",
|
||||||
|
endTime: endTime?.toISOString() || "",
|
||||||
|
timeZone,
|
||||||
|
duration,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enabled: !!startTime && !!endTime && enabled,
|
||||||
|
refetchInterval,
|
||||||
|
trpc: { context: { skipBatch: true } },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!!data && fetchStatus === "idle") {
|
||||||
|
setRefetchCount(refetchCount + 1);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [fetchStatus, data]);
|
||||||
|
|
||||||
|
// The very first time isPaused is set if auto-fetch is disabled, so isPaused should also be considered a loading state.
|
||||||
|
return { slots: data?.slots || {}, isLoading: isLoading || isPaused };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SlotPicker = ({
|
||||||
|
eventType,
|
||||||
|
timeFormat,
|
||||||
|
onTimeFormatChange,
|
||||||
|
timeZone,
|
||||||
|
recurringEventCount,
|
||||||
|
users,
|
||||||
|
seatsPerTimeSlot,
|
||||||
|
weekStart = 0,
|
||||||
|
ethSignature,
|
||||||
|
}: {
|
||||||
|
eventType: Pick<
|
||||||
|
EventType & { metadata: z.infer<typeof EventTypeMetaDataSchema> },
|
||||||
|
"id" | "schedulingType" | "slug" | "length" | "metadata"
|
||||||
|
>;
|
||||||
|
timeFormat: TimeFormat;
|
||||||
|
onTimeFormatChange: (is24Hour: boolean) => void;
|
||||||
|
timeZone?: string;
|
||||||
|
seatsPerTimeSlot?: number;
|
||||||
|
recurringEventCount?: number;
|
||||||
|
users: string[];
|
||||||
|
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
|
ethSignature?: string;
|
||||||
|
}) => {
|
||||||
|
const [selectedDate, setSelectedDate] = useState<Dayjs>();
|
||||||
|
const [browsingDate, setBrowsingDate] = useState<Dayjs>();
|
||||||
|
let { duration = eventType.length.toString() } = useRouterQuery("duration");
|
||||||
|
const { date, setQuery: setDate } = useRouterQuery("date");
|
||||||
|
const { month, setQuery: setMonth } = useRouterQuery("month");
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
if (!eventType.metadata?.multipleDuration) {
|
||||||
|
duration = eventType.length.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!router.isReady) return;
|
||||||
|
|
||||||
|
// Etc/GMT is not actually a timeZone, so handle this select option explicitly to prevent a hard crash.
|
||||||
|
if (timeZone === "Etc/GMT") {
|
||||||
|
setBrowsingDate(dayjs.utc(month).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0));
|
||||||
|
if (date) {
|
||||||
|
setSelectedDate(dayjs.utc(date));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Set the start of the month without shifting time like startOf() may do.
|
||||||
|
setBrowsingDate(
|
||||||
|
dayjs.tz(month, timeZone).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0)
|
||||||
|
);
|
||||||
|
if (date) {
|
||||||
|
// It's important to set the date immediately to the timeZone, dayjs(date) will convert to browsertime.
|
||||||
|
setSelectedDate(dayjs.tz(date, timeZone));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [router.isReady, month, date, duration, timeZone]);
|
||||||
|
|
||||||
|
const { i18n, isLocaleReady } = useLocale();
|
||||||
|
const { slots: monthSlots, isLoading } = useSlots({
|
||||||
|
eventTypeId: eventType.id,
|
||||||
|
eventTypeSlug: eventType.slug,
|
||||||
|
usernameList: users,
|
||||||
|
startTime:
|
||||||
|
browsingDate === undefined || browsingDate.get("month") === dayjs.tz(undefined, timeZone).get("month")
|
||||||
|
? dayjs.tz(undefined, timeZone).subtract(2, "days").startOf("day")
|
||||||
|
: browsingDate?.startOf("month"),
|
||||||
|
endTime: browsingDate?.endOf("month"),
|
||||||
|
timeZone,
|
||||||
|
duration,
|
||||||
|
});
|
||||||
|
const { slots: selectedDateSlots, isLoading: _isLoadingSelectedDateSlots } = useSlots({
|
||||||
|
eventTypeId: eventType.id,
|
||||||
|
eventTypeSlug: eventType.slug,
|
||||||
|
usernameList: users,
|
||||||
|
startTime: selectedDate?.startOf("day"),
|
||||||
|
endTime: selectedDate?.endOf("day"),
|
||||||
|
timeZone,
|
||||||
|
duration,
|
||||||
|
/** Prevent refetching is we already have this data from month slots */
|
||||||
|
enabled: !!selectedDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Hide skeleton if we have the slot loaded in the month query */
|
||||||
|
const isLoadingSelectedDateSlots = (() => {
|
||||||
|
if (!selectedDate) return _isLoadingSelectedDateSlots;
|
||||||
|
if (!!selectedDateSlots[selectedDate.format("YYYY-MM-DD")]) return false;
|
||||||
|
if (!!monthSlots[selectedDate.format("YYYY-MM-DD")]) return false;
|
||||||
|
return false;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DatePicker
|
||||||
|
isLoading={isLoading}
|
||||||
|
className={classNames(
|
||||||
|
"mt-8 px-4 pb-4 sm:mt-0 md:min-w-[300px] md:px-5 lg:min-w-[455px]",
|
||||||
|
selectedDate ? "sm:dark:border-darkgray-200 border-gray-200 sm:border-r sm:p-4 sm:pr-6" : "sm:p-4"
|
||||||
|
)}
|
||||||
|
includedDates={Object.keys(monthSlots).filter((k) => monthSlots[k].length > 0)}
|
||||||
|
locale={isLocaleReady ? i18n.language : "en"}
|
||||||
|
selected={selectedDate}
|
||||||
|
onChange={(newDate) => {
|
||||||
|
setDate(newDate.format("YYYY-MM-DD"));
|
||||||
|
}}
|
||||||
|
onMonthChange={(newMonth) => {
|
||||||
|
setMonth(newMonth.format("YYYY-MM"));
|
||||||
|
}}
|
||||||
|
browsingDate={browsingDate}
|
||||||
|
weekStart={weekStart}
|
||||||
|
/>
|
||||||
|
<AvailableTimes
|
||||||
|
isLoading={isLoadingSelectedDateSlots}
|
||||||
|
slots={
|
||||||
|
selectedDate &&
|
||||||
|
(selectedDateSlots[selectedDate.format("YYYY-MM-DD")] ||
|
||||||
|
monthSlots[selectedDate.format("YYYY-MM-DD")])
|
||||||
|
}
|
||||||
|
date={selectedDate}
|
||||||
|
timeFormat={timeFormat}
|
||||||
|
onTimeFormatChange={onTimeFormatChange}
|
||||||
|
eventTypeId={eventType.id}
|
||||||
|
eventTypeSlug={eventType.slug}
|
||||||
|
seatsPerTimeSlot={seatsPerTimeSlot}
|
||||||
|
recurringCount={recurringEventCount}
|
||||||
|
ethSignature={ethSignature}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { FiGlobe } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
|
import { timeZone as localStorageTimeZone } from "@lib/clock";
|
||||||
|
|
||||||
|
import TimeOptions from "@components/booking/TimeOptions";
|
||||||
|
|
||||||
|
export function TimezoneDropdown({
|
||||||
|
onChangeTimeZone,
|
||||||
|
}: {
|
||||||
|
onChangeTimeZone: (newTimeZone: string) => void;
|
||||||
|
timeZone?: string;
|
||||||
|
}) {
|
||||||
|
const handleSelectTimeZone = (newTimeZone: string) => {
|
||||||
|
onChangeTimeZone(newTimeZone);
|
||||||
|
localStorageTimeZone(newTimeZone);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="dark:focus-within:bg-darkgray-200 dark:bg-darkgray-100 dark:hover:bg-darkgray-200 -mx-[2px] !mt-3 flex w-fit items-center rounded-[4px] px-1 py-[2px] text-sm font-medium focus-within:bg-gray-200 hover:bg-gray-100 [&_svg]:focus-within:text-gray-900 dark:[&_svg]:focus-within:text-white [&_p]:focus-within:text-gray-900 dark:[&_p]:focus-within:text-white">
|
||||||
|
<FiGlobe className="dark:text-darkgray-600 flex h-4 w-4 text-gray-600 ltr:mr-[2px] rtl:ml-[2px]" />
|
||||||
|
<TimeOptions onSelectTimeZone={handleSelectTimeZone} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
|
||||||
import type { EventType } from "@prisma/client";
|
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { useEffect, useMemo, useReducer, useState } from "react";
|
import { useEffect, useMemo, useReducer, useState } from "react";
|
||||||
import { Toaster } from "react-hot-toast";
|
|
||||||
import { FormattedNumber, IntlProvider } from "react-intl";
|
import { FormattedNumber, IntlProvider } from "react-intl";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
|
import BookingPageTagManager from "@calcom/app-store/BookingPageTagManager";
|
||||||
import { getEventTypeAppData } from "@calcom/app-store/utils";
|
import { getEventTypeAppData } from "@calcom/app-store/utils";
|
||||||
import type { Dayjs } from "@calcom/dayjs";
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import {
|
import {
|
||||||
useEmbedNonStylesConfig,
|
useEmbedNonStylesConfig,
|
||||||
@@ -18,7 +14,6 @@ import {
|
|||||||
useIsBackgroundTransparent,
|
useIsBackgroundTransparent,
|
||||||
useIsEmbed,
|
useIsEmbed,
|
||||||
} from "@calcom/embed-core/embed-iframe";
|
} from "@calcom/embed-core/embed-iframe";
|
||||||
import DatePicker from "@calcom/features/calendars/DatePicker";
|
|
||||||
import CustomBranding from "@calcom/lib/CustomBranding";
|
import CustomBranding from "@calcom/lib/CustomBranding";
|
||||||
import classNames from "@calcom/lib/classNames";
|
import classNames from "@calcom/lib/classNames";
|
||||||
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
|
import getPaymentAppData from "@calcom/lib/getPaymentAppData";
|
||||||
@@ -28,222 +23,30 @@ import notEmpty from "@calcom/lib/notEmpty";
|
|||||||
import { getRecurringFreq } from "@calcom/lib/recurringStrings";
|
import { getRecurringFreq } from "@calcom/lib/recurringStrings";
|
||||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||||
import { detectBrowserTimeFormat, setIs24hClockInLocalStorage, TimeFormat } from "@calcom/lib/timeFormat";
|
import { detectBrowserTimeFormat, setIs24hClockInLocalStorage, TimeFormat } from "@calcom/lib/timeFormat";
|
||||||
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
|
||||||
import { trpc } from "@calcom/trpc/react";
|
|
||||||
import { HeadSeo } from "@calcom/ui";
|
import { HeadSeo } from "@calcom/ui";
|
||||||
import { FiCreditCard, FiGlobe, FiRefreshCcw } from "@calcom/ui/components/icon";
|
import { FiCreditCard, FiRefreshCcw } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import { timeZone as localStorageTimeZone } from "@lib/clock";
|
import { timeZone as localStorageTimeZone } from "@lib/clock";
|
||||||
import useRouterQuery from "@lib/hooks/useRouterQuery";
|
|
||||||
|
|
||||||
import type { Gate, GateState } from "@components/Gates";
|
import type { Gate, GateState } from "@components/Gates";
|
||||||
import Gates from "@components/Gates";
|
import Gates from "@components/Gates";
|
||||||
import BookingDescription from "@components/booking/BookingDescription";
|
import BookingDescription from "@components/booking/BookingDescription";
|
||||||
import TimeOptions from "@components/booking/TimeOptions";
|
import { SlotPicker } from "@components/booking/SlotPicker";
|
||||||
|
|
||||||
import type { AvailabilityPageProps } from "../../../pages/[user]/[type]";
|
import type { AvailabilityPageProps } from "../../../pages/[user]/[type]";
|
||||||
import type { DynamicAvailabilityPageProps } from "../../../pages/d/[link]/[slug]";
|
import type { DynamicAvailabilityPageProps } from "../../../pages/d/[link]/[slug]";
|
||||||
import type { AvailabilityTeamPageProps } from "../../../pages/team/[slug]/[type]";
|
import type { AvailabilityTeamPageProps } from "../../../pages/team/[slug]/[type]";
|
||||||
|
|
||||||
const PoweredByCal = dynamic(() => import("@components/ui/PoweredByCal"));
|
const PoweredByCal = dynamic(() => import("@components/ui/PoweredByCal"));
|
||||||
const AvailableTimes = dynamic(() => import("@components/booking/AvailableTimes"));
|
|
||||||
|
|
||||||
const useSlots = ({
|
const Toaster = dynamic(() => import("react-hot-toast").then((mod) => mod.Toaster), { ssr: false });
|
||||||
eventTypeId,
|
/*const SlotPicker = dynamic(() => import("../SlotPicker").then((mod) => mod.SlotPicker), {
|
||||||
eventTypeSlug,
|
ssr: false,
|
||||||
startTime,
|
loading: () => <div className="mt-8 px-4 pb-4 sm:mt-0 sm:p-4 md:min-w-[300px] md:px-5 lg:min-w-[455px]" />,
|
||||||
endTime,
|
});*/
|
||||||
usernameList,
|
const TimezoneDropdown = dynamic(() => import("../TimezoneDropdown").then((mod) => mod.TimezoneDropdown), {
|
||||||
timeZone,
|
ssr: false,
|
||||||
duration,
|
});
|
||||||
enabled = true,
|
|
||||||
}: {
|
|
||||||
eventTypeId: number;
|
|
||||||
eventTypeSlug: string;
|
|
||||||
startTime?: Dayjs;
|
|
||||||
endTime?: Dayjs;
|
|
||||||
usernameList: string[];
|
|
||||||
timeZone?: string;
|
|
||||||
duration?: string;
|
|
||||||
enabled?: boolean;
|
|
||||||
}) => {
|
|
||||||
const { data, isLoading, isPaused } = trpc.viewer.public.slots.getSchedule.useQuery(
|
|
||||||
{
|
|
||||||
eventTypeId,
|
|
||||||
eventTypeSlug,
|
|
||||||
usernameList,
|
|
||||||
startTime: startTime?.toISOString() || "",
|
|
||||||
endTime: endTime?.toISOString() || "",
|
|
||||||
timeZone,
|
|
||||||
duration,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
enabled: !!startTime && !!endTime && enabled,
|
|
||||||
refetchInterval: 3000,
|
|
||||||
trpc: { context: { skipBatch: true } },
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// The very first time isPaused is set if auto-fetch is disabled, so isPaused should also be considered a loading state.
|
|
||||||
return { slots: data?.slots || {}, isLoading: isLoading || isPaused };
|
|
||||||
};
|
|
||||||
|
|
||||||
const SlotPicker = ({
|
|
||||||
eventType,
|
|
||||||
timeFormat,
|
|
||||||
onTimeFormatChange,
|
|
||||||
timeZone,
|
|
||||||
recurringEventCount,
|
|
||||||
users,
|
|
||||||
seatsPerTimeSlot,
|
|
||||||
weekStart = 0,
|
|
||||||
ethSignature,
|
|
||||||
}: {
|
|
||||||
eventType: Pick<
|
|
||||||
EventType & { metadata: z.infer<typeof EventTypeMetaDataSchema> },
|
|
||||||
"id" | "schedulingType" | "slug" | "length" | "metadata"
|
|
||||||
>;
|
|
||||||
timeFormat: TimeFormat;
|
|
||||||
onTimeFormatChange: (is24Hour: boolean) => void;
|
|
||||||
timeZone?: string;
|
|
||||||
seatsPerTimeSlot?: number;
|
|
||||||
recurringEventCount?: number;
|
|
||||||
users: string[];
|
|
||||||
weekStart?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
|
|
||||||
ethSignature?: string;
|
|
||||||
}) => {
|
|
||||||
const [selectedDate, setSelectedDate] = useState<Dayjs>();
|
|
||||||
const [browsingDate, setBrowsingDate] = useState<Dayjs>();
|
|
||||||
let { duration = eventType.length.toString() } = useRouterQuery("duration");
|
|
||||||
const { date, setQuery: setDate } = useRouterQuery("date");
|
|
||||||
const { month, setQuery: setMonth } = useRouterQuery("month");
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
if (!eventType.metadata?.multipleDuration) {
|
|
||||||
duration = eventType.length.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
const [slotPickerRef] = useAutoAnimate<HTMLDivElement>();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!router.isReady) return;
|
|
||||||
|
|
||||||
// Etc/GMT is not actually a timeZone, so handle this select option explicitly to prevent a hard crash.
|
|
||||||
if (timeZone === "Etc/GMT") {
|
|
||||||
setBrowsingDate(dayjs.utc(month).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0));
|
|
||||||
if (date) {
|
|
||||||
setSelectedDate(dayjs.utc(date));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Set the start of the month without shifting time like startOf() may do.
|
|
||||||
setBrowsingDate(
|
|
||||||
dayjs.tz(month, timeZone).set("date", 1).set("hour", 0).set("minute", 0).set("second", 0)
|
|
||||||
);
|
|
||||||
if (date) {
|
|
||||||
// It's important to set the date immediately to the timeZone, dayjs(date) will convert to browsertime.
|
|
||||||
setSelectedDate(dayjs.tz(date, timeZone));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [router.isReady, month, date, duration, timeZone]);
|
|
||||||
|
|
||||||
const { i18n, isLocaleReady } = useLocale();
|
|
||||||
const { slots: monthSlots, isLoading } = useSlots({
|
|
||||||
eventTypeId: eventType.id,
|
|
||||||
eventTypeSlug: eventType.slug,
|
|
||||||
usernameList: users,
|
|
||||||
startTime:
|
|
||||||
browsingDate === undefined || browsingDate.get("month") === dayjs.tz(undefined, timeZone).get("month")
|
|
||||||
? dayjs.tz(undefined, timeZone).subtract(2, "days").startOf("day")
|
|
||||||
: browsingDate?.startOf("month"),
|
|
||||||
endTime: browsingDate?.endOf("month"),
|
|
||||||
timeZone,
|
|
||||||
duration,
|
|
||||||
});
|
|
||||||
const { slots: selectedDateSlots, isLoading: _isLoadingSelectedDateSlots } = useSlots({
|
|
||||||
eventTypeId: eventType.id,
|
|
||||||
eventTypeSlug: eventType.slug,
|
|
||||||
usernameList: users,
|
|
||||||
startTime: selectedDate?.startOf("day"),
|
|
||||||
endTime: selectedDate?.endOf("day"),
|
|
||||||
timeZone,
|
|
||||||
duration,
|
|
||||||
/** Prevent refetching is we already have this data from month slots */
|
|
||||||
enabled: !!selectedDate,
|
|
||||||
});
|
|
||||||
|
|
||||||
/** Hide skeleton if we have the slot loaded in the month query */
|
|
||||||
const isLoadingSelectedDateSlots = (() => {
|
|
||||||
if (!selectedDate) return _isLoadingSelectedDateSlots;
|
|
||||||
if (!!selectedDateSlots[selectedDate.format("YYYY-MM-DD")]) return false;
|
|
||||||
if (!!monthSlots[selectedDate.format("YYYY-MM-DD")]) return false;
|
|
||||||
return false;
|
|
||||||
})();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<DatePicker
|
|
||||||
isLoading={isLoading}
|
|
||||||
className={classNames(
|
|
||||||
"mt-8 px-4 pb-4 sm:mt-0 md:min-w-[300px] md:px-5 lg:min-w-[455px]",
|
|
||||||
selectedDate ? "sm:dark:border-darkgray-200 border-gray-200 sm:border-r sm:p-4 sm:pr-6" : "sm:p-4"
|
|
||||||
)}
|
|
||||||
includedDates={Object.keys(monthSlots).filter((k) => monthSlots[k].length > 0)}
|
|
||||||
locale={isLocaleReady ? i18n.language : "en"}
|
|
||||||
selected={selectedDate}
|
|
||||||
onChange={(newDate) => {
|
|
||||||
setDate(newDate.format("YYYY-MM-DD"));
|
|
||||||
}}
|
|
||||||
onMonthChange={(newMonth) => {
|
|
||||||
setMonth(newMonth.format("YYYY-MM"));
|
|
||||||
}}
|
|
||||||
browsingDate={browsingDate}
|
|
||||||
weekStart={weekStart}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div ref={slotPickerRef}>
|
|
||||||
{selectedDate ? (
|
|
||||||
<AvailableTimes
|
|
||||||
isLoading={isLoadingSelectedDateSlots}
|
|
||||||
slots={
|
|
||||||
selectedDate &&
|
|
||||||
(selectedDateSlots[selectedDate.format("YYYY-MM-DD")] ||
|
|
||||||
monthSlots[selectedDate.format("YYYY-MM-DD")])
|
|
||||||
}
|
|
||||||
date={selectedDate}
|
|
||||||
timeFormat={timeFormat}
|
|
||||||
onTimeFormatChange={onTimeFormatChange}
|
|
||||||
eventTypeId={eventType.id}
|
|
||||||
eventTypeSlug={eventType.slug}
|
|
||||||
seatsPerTimeSlot={seatsPerTimeSlot}
|
|
||||||
recurringCount={recurringEventCount}
|
|
||||||
ethSignature={ethSignature}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
function TimezoneDropdown({
|
|
||||||
onChangeTimeZone,
|
|
||||||
}: {
|
|
||||||
onChangeTimeZone: (newTimeZone: string) => void;
|
|
||||||
timeZone?: string;
|
|
||||||
}) {
|
|
||||||
const handleSelectTimeZone = (newTimeZone: string) => {
|
|
||||||
onChangeTimeZone(newTimeZone);
|
|
||||||
localStorageTimeZone(newTimeZone);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="dark:focus-within:bg-darkgray-200 dark:bg-darkgray-100 dark:hover:bg-darkgray-200 -mx-[2px] !mt-3 flex w-fit items-center rounded-[4px] px-1 py-[2px] text-sm font-medium focus-within:bg-gray-200 hover:bg-gray-100 [&_svg]:focus-within:text-gray-900 dark:[&_svg]:focus-within:text-white [&_p]:focus-within:text-gray-900 dark:[&_p]:focus-within:text-white">
|
|
||||||
<FiGlobe className="dark:text-darkgray-600 flex h-4 w-4 text-gray-600 ltr:mr-[2px] rtl:ml-[2px]" />
|
|
||||||
<TimeOptions onSelectTimeZone={handleSelectTimeZone} />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const dateQuerySchema = z.object({
|
const dateQuerySchema = z.object({
|
||||||
rescheduleUid: z.string().optional().default(""),
|
rescheduleUid: z.string().optional().default(""),
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
import { useCallback, useRef, useState } from "react";
|
|
||||||
import { useEffect } from "react";
|
|
||||||
import { HexColorInput, HexColorPicker } from "react-colorful";
|
|
||||||
|
|
||||||
import { isValidHexCode, fallBackHex } from "@calcom/lib/CustomBranding";
|
|
||||||
import { Swatch } from "@calcom/ui";
|
|
||||||
|
|
||||||
type Handler = (event: MouseEvent | Event) => void;
|
|
||||||
function useEventListener<
|
|
||||||
KW extends keyof WindowEventMap,
|
|
||||||
KH extends keyof HTMLElementEventMap,
|
|
||||||
T extends HTMLElement | void = void
|
|
||||||
>(
|
|
||||||
eventName: KW | KH,
|
|
||||||
handler: (event: WindowEventMap[KW] | HTMLElementEventMap[KH] | Event) => void,
|
|
||||||
element?: React.RefObject<T>
|
|
||||||
) {
|
|
||||||
// Create a ref that stores handler
|
|
||||||
const savedHandler = useRef<typeof handler>();
|
|
||||||
useEffect(() => {
|
|
||||||
// Define the listening target
|
|
||||||
const targetElement: T | Window = element?.current || window;
|
|
||||||
if (!(targetElement && targetElement.addEventListener)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Update saved handler if necessary
|
|
||||||
if (savedHandler.current !== handler) {
|
|
||||||
savedHandler.current = handler;
|
|
||||||
}
|
|
||||||
// Create event listener that calls handler function stored in ref
|
|
||||||
const eventListener: typeof handler = (event) => {
|
|
||||||
// eslint-disable-next-line no-extra-boolean-cast
|
|
||||||
if (!!savedHandler?.current) {
|
|
||||||
savedHandler.current(event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
targetElement.addEventListener(eventName, eventListener);
|
|
||||||
// Remove event listener on cleanup
|
|
||||||
return () => {
|
|
||||||
targetElement.removeEventListener(eventName, eventListener);
|
|
||||||
};
|
|
||||||
}, [eventName, element, handler]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function useOnClickOutside<T extends HTMLElement = HTMLElement>(
|
|
||||||
ref: React.RefObject<T>,
|
|
||||||
handler: Handler,
|
|
||||||
mouseEvent: "mousedown" | "mouseup" = "mousedown"
|
|
||||||
): void {
|
|
||||||
useEventListener(mouseEvent, (event) => {
|
|
||||||
const el = ref?.current;
|
|
||||||
// Do nothing if clicking ref's element or descendent elements
|
|
||||||
if (!el || el.contains(event.target as Node)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handler(event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
export type ColorPickerProps = {
|
|
||||||
defaultValue: string;
|
|
||||||
onChange: (text: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const ColorPicker = (props: ColorPickerProps) => {
|
|
||||||
const init = !isValidHexCode(props.defaultValue)
|
|
||||||
? fallBackHex(props.defaultValue, false)
|
|
||||||
: props.defaultValue;
|
|
||||||
const [color, setColor] = useState(init);
|
|
||||||
const [isOpen, toggle] = useState(false);
|
|
||||||
const popover = useRef() as React.MutableRefObject<HTMLInputElement>;
|
|
||||||
const close = useCallback(() => toggle(false), []);
|
|
||||||
useOnClickOutside(popover, close);
|
|
||||||
return (
|
|
||||||
<div className="relative mt-1 flex items-center justify-center">
|
|
||||||
<Swatch size="sm" backgroundColor={color} onClick={() => toggle(!isOpen)} />
|
|
||||||
|
|
||||||
{isOpen && (
|
|
||||||
<div className="popover" ref={popover}>
|
|
||||||
<HexColorPicker
|
|
||||||
className="!absolute !top-10 !left-0 !z-10 !h-32 !w-32"
|
|
||||||
color={color}
|
|
||||||
onChange={(val) => {
|
|
||||||
setColor(val);
|
|
||||||
props.onChange(val);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<HexColorInput
|
|
||||||
className="ml-1 block w-full rounded-r-md border border-gray-300 px-3 py-2 sm:text-sm"
|
|
||||||
color={color}
|
|
||||||
onChange={(val) => {
|
|
||||||
setColor(val);
|
|
||||||
props.onChange(val);
|
|
||||||
}}
|
|
||||||
type="text"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ColorPicker;
|
|
||||||
@@ -29,7 +29,7 @@ function Select<
|
|||||||
IsMulti extends boolean = false,
|
IsMulti extends boolean = false,
|
||||||
Group extends GroupBase<Option> = GroupBase<Option>
|
Group extends GroupBase<Option> = GroupBase<Option>
|
||||||
>({ className, ...props }: SelectProps<Option, IsMulti, Group>) {
|
>({ className, ...props }: SelectProps<Option, IsMulti, Group>) {
|
||||||
const { resolvedTheme, forcedTheme, isReady } = useTheme();
|
const { resolvedTheme, forcedTheme /*isReady*/ } = useTheme();
|
||||||
const hasDarkTheme = !forcedTheme && resolvedTheme === "dark";
|
const hasDarkTheme = !forcedTheme && resolvedTheme === "dark";
|
||||||
const darkThemeColors = {
|
const darkThemeColors = {
|
||||||
/** Dark Theme starts */
|
/** Dark Theme starts */
|
||||||
@@ -74,9 +74,9 @@ function Select<
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Till we know in JS the theme is ready, we can't render react-select as it would render with light theme instead
|
// Till we know in JS the theme is ready, we can't render react-select as it would render with light theme instead
|
||||||
if (!isReady) {
|
// if (!isReady) {
|
||||||
return <input type="text" className={className} />;
|
// return <input type="text" className={className} />;
|
||||||
}
|
// }
|
||||||
return (
|
return (
|
||||||
<ReactSelect
|
<ReactSelect
|
||||||
theme={(theme) => ({
|
theme={(theme) => ({
|
||||||
|
|||||||
Binary file not shown.
@@ -44,7 +44,8 @@
|
|||||||
"@hookform/error-message": "^2.0.0",
|
"@hookform/error-message": "^2.0.0",
|
||||||
"@hookform/resolvers": "^2.9.7",
|
"@hookform/resolvers": "^2.9.7",
|
||||||
"@next-auth/prisma-adapter": "^1.0.4",
|
"@next-auth/prisma-adapter": "^1.0.4",
|
||||||
"@next/bundle-analyzer": "^12.2.5",
|
"@next/bundle-analyzer": "^13.1.6",
|
||||||
|
"@next/font": "^13.1.1",
|
||||||
"@radix-ui/react-avatar": "^1.0.0",
|
"@radix-ui/react-avatar": "^1.0.0",
|
||||||
"@radix-ui/react-collapsible": "^1.0.0",
|
"@radix-ui/react-collapsible": "^1.0.0",
|
||||||
"@radix-ui/react-dialog": "^1.0.0",
|
"@radix-ui/react-dialog": "^1.0.0",
|
||||||
@@ -80,6 +81,7 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lottie-react": "^2.3.1",
|
"lottie-react": "^2.3.1",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
|
"md5": "^2.3.0",
|
||||||
"memory-cache": "^0.2.0",
|
"memory-cache": "^0.2.0",
|
||||||
"micro": "^10.0.1",
|
"micro": "^10.0.1",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
@@ -139,6 +141,7 @@
|
|||||||
"@types/glidejs__glide": "^3.4.2",
|
"@types/glidejs__glide": "^3.4.2",
|
||||||
"@types/lodash": "^4.14.182",
|
"@types/lodash": "^4.14.182",
|
||||||
"@types/markdown-it": "^12.2.3",
|
"@types/markdown-it": "^12.2.3",
|
||||||
|
"@types/md5": "^2.3.2",
|
||||||
"@types/memory-cache": "^0.2.2",
|
"@types/memory-cache": "^0.2.2",
|
||||||
"@types/micro": "7.3.7",
|
"@types/micro": "7.3.7",
|
||||||
"@types/mime-types": "^2.1.1",
|
"@types/mime-types": "^2.1.1",
|
||||||
|
|||||||
@@ -1,21 +1,10 @@
|
|||||||
import MarkdownIt from "markdown-it";
|
|
||||||
import type { GetStaticPaths, GetStaticPropsContext } from "next";
|
import type { GetStaticPaths, GetStaticPropsContext } from "next";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import type { LocationObject } from "@calcom/app-store/locations";
|
import type { LocationObject } from "@calcom/app-store/locations";
|
||||||
import { privacyFilteredLocations } from "@calcom/app-store/locations";
|
|
||||||
import { getAppFromSlug } from "@calcom/app-store/utils";
|
|
||||||
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
|
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
|
||||||
import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents";
|
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
|
|
||||||
import prisma from "@calcom/prisma";
|
|
||||||
import type { User } from "@calcom/prisma/client";
|
import type { User } from "@calcom/prisma/client";
|
||||||
import {
|
|
||||||
EventTypeMetaDataSchema,
|
|
||||||
teamMetadataSchema,
|
|
||||||
userMetadata as userMetadataSchema,
|
|
||||||
} from "@calcom/prisma/zod-utils";
|
|
||||||
|
|
||||||
import { isBrandingHidden } from "@lib/isBrandingHidden";
|
import { isBrandingHidden } from "@lib/isBrandingHidden";
|
||||||
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||||
@@ -65,10 +54,19 @@ export default function Type(props: AvailabilityPageProps) {
|
|||||||
|
|
||||||
Type.isThemeSupported = true;
|
Type.isThemeSupported = true;
|
||||||
|
|
||||||
|
const paramsSchema = z.object({ type: z.string(), user: z.string() });
|
||||||
async function getUserPageProps(context: GetStaticPropsContext) {
|
async function getUserPageProps(context: GetStaticPropsContext) {
|
||||||
const { type: slug, user: username } = paramsSchema.parse(context.params);
|
// load server side dependencies
|
||||||
|
const MarkdownIt = await import("markdown-it").then((mod) => mod.default);
|
||||||
|
const prisma = await import("@calcom/prisma").then((mod) => mod.default);
|
||||||
|
const { privacyFilteredLocations } = await import("@calcom/app-store/locations");
|
||||||
|
const { parseRecurringEvent } = await import("@calcom/lib/isRecurringEvent");
|
||||||
|
const { EventTypeMetaDataSchema, teamMetadataSchema } = await import("@calcom/prisma/zod-utils");
|
||||||
const { ssgInit } = await import("@server/lib/ssg");
|
const { ssgInit } = await import("@server/lib/ssg");
|
||||||
|
|
||||||
|
const { type: slug, user: username } = paramsSchema.parse(context.params);
|
||||||
const ssg = await ssgInit(context);
|
const ssg = await ssgInit(context);
|
||||||
|
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: {
|
where: {
|
||||||
username,
|
username,
|
||||||
@@ -191,7 +189,17 @@ async function getUserPageProps(context: GetStaticPropsContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
|
async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
|
||||||
|
// load server side dependencies
|
||||||
|
const { getDefaultEvent, getGroupName, getUsernameList } = await import("@calcom/lib/defaultEvents");
|
||||||
|
const { privacyFilteredLocations } = await import("@calcom/app-store/locations");
|
||||||
|
const { parseRecurringEvent } = await import("@calcom/lib/isRecurringEvent");
|
||||||
|
const prisma = await import("@calcom/prisma").then((mod) => mod.default);
|
||||||
|
const { EventTypeMetaDataSchema, userMetadata: userMetadataSchema } = await import(
|
||||||
|
"@calcom/prisma/zod-utils"
|
||||||
|
);
|
||||||
const { ssgInit } = await import("@server/lib/ssg");
|
const { ssgInit } = await import("@server/lib/ssg");
|
||||||
|
const { getAppFromSlug } = await import("@calcom/app-store/utils");
|
||||||
|
|
||||||
const ssg = await ssgInit(context);
|
const ssg = await ssgInit(context);
|
||||||
const { type: typeParam, user: userParam } = paramsSchema.parse(context.params);
|
const { type: typeParam, user: userParam } = paramsSchema.parse(context.params);
|
||||||
const usernameList = getUsernameList(userParam);
|
const usernameList = getUsernameList(userParam);
|
||||||
@@ -310,8 +318,6 @@ async function getDynamicGroupPageProps(context: GetStaticPropsContext) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const paramsSchema = z.object({ type: z.string(), user: z.string() });
|
|
||||||
|
|
||||||
export const getStaticProps = async (context: GetStaticPropsContext) => {
|
export const getStaticProps = async (context: GetStaticPropsContext) => {
|
||||||
const { user: userParam } = paramsSchema.parse(context.params);
|
const { user: userParam } = paramsSchema.parse(context.params);
|
||||||
// dynamic groups are not generated at build time, but otherwise are probably cached until infinity.
|
// dynamic groups are not generated at build time, but otherwise are probably cached until infinity.
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { Inter } from "@next/font/google";
|
||||||
|
import localFont from "@next/font/local";
|
||||||
import { DefaultSeo } from "next-seo";
|
import { DefaultSeo } from "next-seo";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
@@ -14,6 +16,14 @@ import I18nLanguageHandler from "@components/I18nLanguageHandler";
|
|||||||
|
|
||||||
import "../styles/globals.css";
|
import "../styles/globals.css";
|
||||||
|
|
||||||
|
const interFont = Inter({ subsets: ["latin"], variable: "--font-inter", preload: true, display: "swap" });
|
||||||
|
const calFont = localFont({
|
||||||
|
src: "../fonts/CalSans-SemiBold.woff2",
|
||||||
|
variable: "--font-cal",
|
||||||
|
preload: true,
|
||||||
|
display: "swap",
|
||||||
|
});
|
||||||
|
|
||||||
function MyApp(props: AppProps) {
|
function MyApp(props: AppProps) {
|
||||||
const { Component, pageProps, err, router } = props;
|
const { Component, pageProps, err, router } = props;
|
||||||
let pageStatus = "200";
|
let pageStatus = "200";
|
||||||
@@ -46,6 +56,12 @@ function MyApp(props: AppProps) {
|
|||||||
id="page-status"
|
id="page-status"
|
||||||
dangerouslySetInnerHTML={{ __html: `window.CalComPageStatus = '${pageStatus}'` }}
|
dangerouslySetInnerHTML={{ __html: `window.CalComPageStatus = '${pageStatus}'` }}
|
||||||
/>
|
/>
|
||||||
|
<style jsx global>{`
|
||||||
|
:root {
|
||||||
|
--font-inter: ${interFont.style.fontFamily};
|
||||||
|
--font-cal: ${calFont.style.fontFamily};
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
<Head>
|
<Head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|||||||
@@ -39,14 +39,6 @@ class MyDocument extends Document<Props> {
|
|||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#000000" />
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#000000" />
|
||||||
<meta name="msapplication-TileColor" content="#ff0000" />
|
<meta name="msapplication-TileColor" content="#ff0000" />
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
href="/fonts/Inter-roman.var.woff2"
|
|
||||||
as="font"
|
|
||||||
type="font/woff2"
|
|
||||||
crossOrigin="anonymous"
|
|
||||||
/>
|
|
||||||
<link rel="preload" href="/fonts/cal.ttf" as="font" type="font/ttf" crossOrigin="anonymous" />
|
|
||||||
<Script src="/embed-init-iframe.js" strategy="beforeInteractive" />
|
<Script src="/embed-init-iframe.js" strategy="beforeInteractive" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
|
|||||||
@@ -507,6 +507,10 @@ export default NextAuth({
|
|||||||
existingUserWithEmail.identityProvider === IdentityProvider.CAL &&
|
existingUserWithEmail.identityProvider === IdentityProvider.CAL &&
|
||||||
(idP === IdentityProvider.GOOGLE || idP === IdentityProvider.SAML)
|
(idP === IdentityProvider.GOOGLE || idP === IdentityProvider.SAML)
|
||||||
) {
|
) {
|
||||||
|
await prisma.user.update({
|
||||||
|
where: { email: existingUserWithEmail.email },
|
||||||
|
data: { password: null },
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
} else if (existingUserWithEmail.identityProvider === IdentityProvider.CAL) {
|
} else if (existingUserWithEmail.identityProvider === IdentityProvider.CAL) {
|
||||||
return "/auth/error?error=use-password-login";
|
return "/auth/error?error=use-password-login";
|
||||||
|
|||||||
@@ -128,11 +128,17 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
|
|||||||
data-testid={"event-type-title-" + type.id}>
|
data-testid={"event-type-title-" + type.id}>
|
||||||
{type.title}
|
{type.title}
|
||||||
</span>
|
</span>
|
||||||
<small
|
{group.profile.slug ? (
|
||||||
className="hidden font-normal leading-4 text-gray-600 sm:inline"
|
<small
|
||||||
data-testid={"event-type-slug-" + type.id}>{`/${group.profile.slug}/${type.slug}`}</small>
|
className="hidden font-normal leading-4 text-gray-600 sm:inline"
|
||||||
|
data-testid={"event-type-slug-" + type.id}>
|
||||||
|
{`/${group.profile.slug}/${type.slug}`}
|
||||||
|
</small>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
{readOnly && (
|
{readOnly && (
|
||||||
<span className="rtl:ml-2inline items-center rounded-sm bg-gray-100 px-1.5 py-0.5 text-xs font-medium text-gray-800 ltr:ml-2 ltr:mr-2">
|
<span className="inline items-center rounded-sm bg-gray-100 px-1.5 py-0.5 text-xs font-medium text-gray-800 ltr:ml-2 ltr:mr-2 rtl:ml-2">
|
||||||
{t("readonly")}
|
{t("readonly")}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -557,6 +563,18 @@ const EventTypeListHeading = ({
|
|||||||
membershipCount,
|
membershipCount,
|
||||||
teamId,
|
teamId,
|
||||||
}: EventTypeListHeadingProps): JSX.Element => {
|
}: EventTypeListHeadingProps): JSX.Element => {
|
||||||
|
const { t } = useLocale();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const publishTeamMutation = trpc.viewer.teams.publish.useMutation({
|
||||||
|
onSuccess(data) {
|
||||||
|
router.push(data.url);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
showToast(error.message, "error");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-4 flex items-center space-x-2">
|
<div className="mb-4 flex items-center space-x-2">
|
||||||
<Avatar
|
<Avatar
|
||||||
@@ -588,6 +606,13 @@ const EventTypeListHeading = ({
|
|||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{!profile?.slug && !!teamId && (
|
||||||
|
<button onClick={() => publishTeamMutation.mutate({ teamId })}>
|
||||||
|
<Badge variant="gray" className="mb-1 -ml-2">
|
||||||
|
{t("upgrade")}
|
||||||
|
</Badge>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
@import "../../../packages/ui/styles/shared-globals.css";
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
|
|
||||||
|
import { getEventTypeAppData } from "@calcom/app-store/_utils/getEventTypeAppData";
|
||||||
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
|
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
|
||||||
import { getEventTypeAppData } from "@calcom/app-store/utils";
|
|
||||||
|
|
||||||
import type { appDataSchemas } from "./apps.schemas.generated";
|
import type { appDataSchemas } from "./apps.schemas.generated";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
import type { EventTypeModel } from "@calcom/prisma/zod";
|
||||||
|
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||||
|
|
||||||
|
export type EventTypeApps = NonNullable<NonNullable<z.infer<typeof EventTypeMetaDataSchema>>["apps"]>;
|
||||||
|
export type EventTypeAppsList = keyof EventTypeApps;
|
||||||
|
|
||||||
|
export const getEventTypeAppData = <T extends EventTypeAppsList>(
|
||||||
|
eventType: Pick<z.infer<typeof EventTypeModel>, "price" | "currency" | "metadata">,
|
||||||
|
appId: T,
|
||||||
|
forcedGet?: boolean
|
||||||
|
): EventTypeApps[T] => {
|
||||||
|
const metadata = eventType.metadata;
|
||||||
|
const appMetadata = metadata?.apps && metadata.apps[appId];
|
||||||
|
if (appMetadata) {
|
||||||
|
const allowDataGet = forcedGet ? true : appMetadata.enabled;
|
||||||
|
return allowDataGet ? appMetadata : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backward compatibility for existing event types.
|
||||||
|
// TODO: After the new AppStore EventType App flow is stable, write a migration to migrate metadata to new format which will let us remove this compatibility code
|
||||||
|
// Migration isn't being done right now, to allow a revert if needed
|
||||||
|
const legacyAppsData = {
|
||||||
|
stripe: {
|
||||||
|
enabled: eventType.price > 0,
|
||||||
|
// Price default is 0 in DB. So, it would always be non nullish.
|
||||||
|
price: eventType.price,
|
||||||
|
// Currency default is "usd" in DB.So, it would also be available always
|
||||||
|
currency: eventType.currency,
|
||||||
|
},
|
||||||
|
rainbow: {
|
||||||
|
enabled: !!(eventType.metadata?.smartContractAddress && eventType.metadata?.blockchainId),
|
||||||
|
smartContractAddress: eventType.metadata?.smartContractAddress || "",
|
||||||
|
blockchainId: eventType.metadata?.blockchainId || 0,
|
||||||
|
},
|
||||||
|
giphy: {
|
||||||
|
enabled: !!eventType.metadata?.giphyThankYouPage,
|
||||||
|
thankYouPage: eventType.metadata?.giphyThankYouPage || "",
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// TODO: This assertion helps typescript hint that only one of the app's data can be returned
|
||||||
|
const legacyAppData = legacyAppsData[appId as Extract<T, keyof typeof legacyAppsData>];
|
||||||
|
const allowDataGet = forcedGet ? true : legacyAppData?.enabled;
|
||||||
|
return allowDataGet ? legacyAppData : null;
|
||||||
|
};
|
||||||
@@ -13,6 +13,7 @@ import routing_forms_config_json from "./ee/routing-forms/config.json";
|
|||||||
import { metadata as exchange2013calendar__metadata_ts } from "./exchange2013calendar/_metadata";
|
import { metadata as exchange2013calendar__metadata_ts } from "./exchange2013calendar/_metadata";
|
||||||
import { metadata as exchange2016calendar__metadata_ts } from "./exchange2016calendar/_metadata";
|
import { metadata as exchange2016calendar__metadata_ts } from "./exchange2016calendar/_metadata";
|
||||||
import exchangecalendar_config_json from "./exchangecalendar/config.json";
|
import exchangecalendar_config_json from "./exchangecalendar/config.json";
|
||||||
|
import facetime_config_json from "./facetime/config.json";
|
||||||
import fathom_config_json from "./fathom/config.json";
|
import fathom_config_json from "./fathom/config.json";
|
||||||
import ga4_config_json from "./ga4/config.json";
|
import ga4_config_json from "./ga4/config.json";
|
||||||
import { metadata as giphy__metadata_ts } from "./giphy/_metadata";
|
import { metadata as giphy__metadata_ts } from "./giphy/_metadata";
|
||||||
@@ -68,6 +69,7 @@ export const appStoreMetadata = {
|
|||||||
exchange2013calendar: exchange2013calendar__metadata_ts,
|
exchange2013calendar: exchange2013calendar__metadata_ts,
|
||||||
exchange2016calendar: exchange2016calendar__metadata_ts,
|
exchange2016calendar: exchange2016calendar__metadata_ts,
|
||||||
exchangecalendar: exchangecalendar_config_json,
|
exchangecalendar: exchangecalendar_config_json,
|
||||||
|
facetime: facetime_config_json,
|
||||||
fathom: fathom_config_json,
|
fathom: fathom_config_json,
|
||||||
ga4: ga4_config_json,
|
ga4: ga4_config_json,
|
||||||
giphy: giphy__metadata_ts,
|
giphy: giphy__metadata_ts,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export const apiHandlers = {
|
|||||||
exchange2013calendar: import("./exchange2013calendar/api"),
|
exchange2013calendar: import("./exchange2013calendar/api"),
|
||||||
exchange2016calendar: import("./exchange2016calendar/api"),
|
exchange2016calendar: import("./exchange2016calendar/api"),
|
||||||
exchangecalendar: import("./exchangecalendar/api"),
|
exchangecalendar: import("./exchangecalendar/api"),
|
||||||
|
facetime: import("./facetime/api"),
|
||||||
fathom: import("./fathom/api"),
|
fathom: import("./fathom/api"),
|
||||||
ga4: import("./ga4/api"),
|
ga4: import("./ga4/api"),
|
||||||
giphy: import("./giphy/api"),
|
giphy: import("./giphy/api"),
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
items:
|
||||||
|
- /api/app-store/facetime/facetime1.png
|
||||||
|
- /api/app-store/facetime/facetime2.png
|
||||||
|
---
|
||||||
|
|
||||||
|
With FaceTime, it’s easy to stay in touch. You can make audio and video calls with up to 32 people, share your screen, enjoy films and music together, and more.
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { AppDeclarativeHandler } from "@calcom/types/AppHandler";
|
||||||
|
|
||||||
|
import { createDefaultInstallation } from "../../_utils/installation";
|
||||||
|
import appConfig from "../config.json";
|
||||||
|
|
||||||
|
const handler: AppDeclarativeHandler = {
|
||||||
|
appType: appConfig.type,
|
||||||
|
variant: appConfig.variant,
|
||||||
|
slug: appConfig.slug,
|
||||||
|
supportsMultipleInstalls: false,
|
||||||
|
handlerType: "add",
|
||||||
|
createCredential: ({ appType, user, slug }) =>
|
||||||
|
createDefaultInstallation({ appType, userId: user.id, slug, key: {} }),
|
||||||
|
};
|
||||||
|
|
||||||
|
export default handler;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default as add } from "./add";
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"/*": "Don't modify slug - If required, do it using cli edit command",
|
||||||
|
"name": "Facetime",
|
||||||
|
"title": "Facetime",
|
||||||
|
"slug": "facetime",
|
||||||
|
"type": "facetime_video",
|
||||||
|
"imageSrc": "/api/app-store/facetime/icon.svg",
|
||||||
|
"logo": "/api/app-store/facetime/icon.svg",
|
||||||
|
"url": "https://cal.com/apps/facetime",
|
||||||
|
"variant": "conferencing",
|
||||||
|
"categories": ["video"],
|
||||||
|
"publisher": "Mythie",
|
||||||
|
"email": "help@cal.com",
|
||||||
|
"description": "Facetime makes it super simple for collaborating teams to jump on a video call.",
|
||||||
|
"__createdUsingCli": true,
|
||||||
|
"appData": {
|
||||||
|
"location": {
|
||||||
|
"linkType": "static",
|
||||||
|
"type": "integrations:facetime_video",
|
||||||
|
"label": "Facetime",
|
||||||
|
"organizerInputPlaceholder": "https://facetime.apple.com/join... link copied from the FaceTime app",
|
||||||
|
"urlRegExp": "^https?:\\/\\/facetime\\.apple\\.com\\/join.+$"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"isTemplate": false
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * as api from "./api";
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/package.json",
|
||||||
|
"private": true,
|
||||||
|
"name": "@calcom/facetime",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"main": "./index.ts",
|
||||||
|
"description": "Facetime makes it super simple for collaborating teams to jump on a video call.",
|
||||||
|
"dependencies": {
|
||||||
|
"@calcom/lib": "*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@calcom/types": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 510 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,8 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 600 600">
|
||||||
|
<linearGradient id="a" x1="-137.5424" x2="-133.9618" y1="785.878" y2="197.7213" gradientTransform="matrix(1 0 0 -1 435.7924 798.4074)" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#5df777"/>
|
||||||
|
<stop offset="1" stop-color="#0abc28"/>
|
||||||
|
</linearGradient>
|
||||||
|
<path fill="url(#a)" d="M137.7 0h324.6C538.6 0 600 61.4 600 137.7v324.6c0 76.3-61.4 137.7-137.7 137.7H137.7C61.4 600 0 538.6 0 462.3V137.7C0 61.4 61.4 0 137.7 0z"/>
|
||||||
|
<path fill="#fff" d="M91.5 227.3v146.1c0 31.9 25.9 57.7 57.7 57.7H325c31.9 0 57.7-25.9 57.7-57.7V227.3c0-31.9-25.9-57.7-57.7-57.7H149.3c-31.9-.1-57.8 25.8-57.8 57.7zm379.3-39.1-66.2 54.6c-5.9 4.9-9.3 12.1-9.3 19.7v75.6c0 7.6 3.3 14.7 9.1 19.6l66.2 55.6c15.1 12.6 38 1.9 38-17.7V206c.1-19.5-22.7-30.3-37.8-17.8z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 851 B |
@@ -0,0 +1,8 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 600 600">
|
||||||
|
<linearGradient id="a" x1="-137.5424" x2="-133.9618" y1="785.878" y2="197.7213" gradientTransform="matrix(1 0 0 -1 435.7924 798.4074)" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop offset="0" stop-color="#5df777"/>
|
||||||
|
<stop offset="1" stop-color="#0abc28"/>
|
||||||
|
</linearGradient>
|
||||||
|
<path fill="url(#a)" d="M137.7 0h324.6C538.6 0 600 61.4 600 137.7v324.6c0 76.3-61.4 137.7-137.7 137.7H137.7C61.4 600 0 538.6 0 462.3V137.7C0 61.4 61.4 0 137.7 0z"/>
|
||||||
|
<path fill="#fff" d="M91.5 227.3v146.1c0 31.9 25.9 57.7 57.7 57.7H325c31.9 0 57.7-25.9 57.7-57.7V227.3c0-31.9-25.9-57.7-57.7-57.7H149.3c-31.9-.1-57.8 25.8-57.8 57.7zm379.3-39.1-66.2 54.6c-5.9 4.9-9.3 12.1-9.3 19.7v75.6c0 7.6 3.3 14.7 9.1 19.6l66.2 55.6c15.1 12.6 38 1.9 38-17.7V206c.1-19.5-22.7-30.3-37.8-17.8z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 851 B |
@@ -6,6 +6,7 @@ import * as dailyvideo from "./dailyvideo";
|
|||||||
import * as exchange2013calendar from "./exchange2013calendar";
|
import * as exchange2013calendar from "./exchange2013calendar";
|
||||||
import * as exchange2016calendar from "./exchange2016calendar";
|
import * as exchange2016calendar from "./exchange2016calendar";
|
||||||
import * as exchangecalendar from "./exchangecalendar";
|
import * as exchangecalendar from "./exchangecalendar";
|
||||||
|
import * as facetime from "./facetime";
|
||||||
import * as giphy from "./giphy";
|
import * as giphy from "./giphy";
|
||||||
import * as googlecalendar from "./googlecalendar";
|
import * as googlecalendar from "./googlecalendar";
|
||||||
import * as googlevideo from "./googlevideo";
|
import * as googlevideo from "./googlevideo";
|
||||||
@@ -52,6 +53,7 @@ const appStore = {
|
|||||||
exchange2013calendar,
|
exchange2013calendar,
|
||||||
exchange2016calendar,
|
exchange2016calendar,
|
||||||
exchangecalendar,
|
exchangecalendar,
|
||||||
|
facetime,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default appStore;
|
export default appStore;
|
||||||
@@ -1,16 +1,15 @@
|
|||||||
import { Prisma } from "@prisma/client";
|
import { Prisma } from "@prisma/client";
|
||||||
import type { TFunction } from "next-i18next";
|
import type { TFunction } from "next-i18next";
|
||||||
import type { z } from "zod";
|
|
||||||
|
|
||||||
// If you import this file on any app it should produce circular dependency
|
// If you import this file on any app it should produce circular dependency
|
||||||
// import appStore from "./index";
|
// import appStore from "./index";
|
||||||
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
|
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
|
||||||
import type { EventLocationType } from "@calcom/app-store/locations";
|
import type { EventLocationType } from "@calcom/app-store/locations";
|
||||||
import { defaultLocations } from "@calcom/app-store/locations";
|
import { defaultLocations } from "@calcom/app-store/locations";
|
||||||
import type { EventTypeModel } from "@calcom/prisma/zod";
|
|
||||||
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
|
||||||
import type { App, AppMeta } from "@calcom/types/App";
|
import type { App, AppMeta } from "@calcom/types/App";
|
||||||
|
|
||||||
|
export * from "./_utils/getEventTypeAppData";
|
||||||
|
|
||||||
type LocationOption = {
|
type LocationOption = {
|
||||||
label: string;
|
label: string;
|
||||||
value: EventLocationType["type"];
|
value: EventLocationType["type"];
|
||||||
@@ -18,9 +17,6 @@ type LocationOption = {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EventTypeApps = NonNullable<NonNullable<z.infer<typeof EventTypeMetaDataSchema>>["apps"]>;
|
|
||||||
export type EventTypeAppsList = keyof EventTypeApps;
|
|
||||||
|
|
||||||
const ALL_APPS_MAP = Object.keys(appStoreMetadata).reduce((store, key) => {
|
const ALL_APPS_MAP = Object.keys(appStoreMetadata).reduce((store, key) => {
|
||||||
const metadata = appStoreMetadata[key as keyof typeof appStoreMetadata] as AppMeta;
|
const metadata = appStoreMetadata[key as keyof typeof appStoreMetadata] as AppMeta;
|
||||||
if (metadata.logo && !metadata.logo.includes("/")) {
|
if (metadata.logo && !metadata.logo.includes("/")) {
|
||||||
@@ -193,44 +189,4 @@ export function getAppFromSlug(slug: string | undefined): AppMeta | undefined {
|
|||||||
return ALL_APPS.find((app) => app.slug === slug);
|
return ALL_APPS.find((app) => app.slug === slug);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getEventTypeAppData = <T extends EventTypeAppsList>(
|
|
||||||
eventType: Pick<z.infer<typeof EventTypeModel>, "price" | "currency" | "metadata">,
|
|
||||||
appId: T,
|
|
||||||
forcedGet?: boolean
|
|
||||||
): EventTypeApps[T] => {
|
|
||||||
const metadata = eventType.metadata;
|
|
||||||
const appMetadata = metadata?.apps && metadata.apps[appId];
|
|
||||||
if (appMetadata) {
|
|
||||||
const allowDataGet = forcedGet ? true : appMetadata.enabled;
|
|
||||||
return allowDataGet ? appMetadata : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backward compatibility for existing event types.
|
|
||||||
// TODO: After the new AppStore EventType App flow is stable, write a migration to migrate metadata to new format which will let us remove this compatibility code
|
|
||||||
// Migration isn't being done right now, to allow a revert if needed
|
|
||||||
const legacyAppsData = {
|
|
||||||
stripe: {
|
|
||||||
enabled: eventType.price > 0,
|
|
||||||
// Price default is 0 in DB. So, it would always be non nullish.
|
|
||||||
price: eventType.price,
|
|
||||||
// Currency default is "usd" in DB.So, it would also be available always
|
|
||||||
currency: eventType.currency,
|
|
||||||
},
|
|
||||||
rainbow: {
|
|
||||||
enabled: !!(eventType.metadata?.smartContractAddress && eventType.metadata?.blockchainId),
|
|
||||||
smartContractAddress: eventType.metadata?.smartContractAddress || "",
|
|
||||||
blockchainId: eventType.metadata?.blockchainId || 0,
|
|
||||||
},
|
|
||||||
giphy: {
|
|
||||||
enabled: !!eventType.metadata?.giphyThankYouPage,
|
|
||||||
thankYouPage: eventType.metadata?.giphyThankYouPage || "",
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
// TODO: This assertion helps typescript hint that only one of the app's data can be returned
|
|
||||||
const legacyAppData = legacyAppsData[appId as Extract<T, keyof typeof legacyAppsData>];
|
|
||||||
const allowDataGet = forcedGet ? true : legacyAppData?.enabled;
|
|
||||||
return allowDataGet ? legacyAppData : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getApps;
|
export default getApps;
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
const plugin = require("tailwindcss/plugin");
|
const plugin = require("tailwindcss/plugin");
|
||||||
|
const { fontFamily } = require("tailwindcss/defaultTheme");
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
|
||||||
@@ -12,10 +13,6 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
darkMode: "class",
|
darkMode: "class",
|
||||||
theme: {
|
theme: {
|
||||||
fontFamily: {
|
|
||||||
cal: ['"Cal Sans"', "sans-serif"],
|
|
||||||
sans: ['"Inter var"', "sans-serif"],
|
|
||||||
},
|
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
/* your primary brand color */
|
/* your primary brand color */
|
||||||
@@ -162,9 +159,9 @@ module.exports = {
|
|||||||
dropdown: "0px 2px 6px -1px rgba(0, 0, 0, 0.08)",
|
dropdown: "0px 2px 6px -1px rgba(0, 0, 0, 0.08)",
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
cal: ['"Cal Sans"', "sans-serif"],
|
cal: ["var(--font-cal)", ...fontFamily.serif],
|
||||||
|
sans: ["var(--font-inter)", ...fontFamily.sans],
|
||||||
mono: ["Roboto Mono", "monospace"],
|
mono: ["Roboto Mono", "monospace"],
|
||||||
sans: ['"Inter var"', "sans-serif"],
|
|
||||||
},
|
},
|
||||||
maxHeight: (theme) => ({
|
maxHeight: (theme) => ({
|
||||||
0: "0",
|
0: "0",
|
||||||
|
|||||||
@@ -8,14 +8,8 @@ import { getEventLocationTypeFromApp } from "@calcom/app-store/locations";
|
|||||||
import { MeetLocationType } from "@calcom/app-store/locations";
|
import { MeetLocationType } from "@calcom/app-store/locations";
|
||||||
import getApps from "@calcom/app-store/utils";
|
import getApps from "@calcom/app-store/utils";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { Attendee } from "@calcom/prisma/client";
|
|
||||||
import { createdEventSchema } from "@calcom/prisma/zod-utils";
|
import { createdEventSchema } from "@calcom/prisma/zod-utils";
|
||||||
import type {
|
import type { AdditionalInformation, CalendarEvent, NewCalendarEventType } from "@calcom/types/Calendar";
|
||||||
AdditionalInformation,
|
|
||||||
CalendarEvent,
|
|
||||||
NewCalendarEventType,
|
|
||||||
Person,
|
|
||||||
} from "@calcom/types/Calendar";
|
|
||||||
import { CredentialPayload, CredentialWithAppName } from "@calcom/types/Credential";
|
import { CredentialPayload, CredentialWithAppName } from "@calcom/types/Credential";
|
||||||
import type { Event } from "@calcom/types/Event";
|
import type { Event } from "@calcom/types/Event";
|
||||||
import type {
|
import type {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { BookingStatus, Credential, SelectedCalendar } from "@prisma/client";
|
import { BookingStatus, Credential } from "@prisma/client";
|
||||||
|
|
||||||
import { getBusyCalendarTimes } from "@calcom/core/CalendarManager";
|
import { getBusyCalendarTimes } from "@calcom/core/CalendarManager";
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { TFunction } from "next-i18next";
|
import type { TFunction } from "next-i18next";
|
||||||
import { ReactNode } from "react";
|
|
||||||
|
|
||||||
import { APP_NAME, WEBAPP_URL, IS_PRODUCTION } from "@calcom/lib/constants";
|
import { APP_NAME, WEBAPP_URL, IS_PRODUCTION } from "@calcom/lib/constants";
|
||||||
|
|
||||||
|
|||||||
@@ -37,26 +37,27 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
if (!team) {
|
if (!team) {
|
||||||
const prevTeam = await prisma.team.findFirstOrThrow({ where: { id } });
|
const prevTeam = await prisma.team.findFirstOrThrow({ where: { id } });
|
||||||
const metadata = teamMetadataSchema.parse(prevTeam.metadata);
|
const metadata = teamMetadataSchema.parse(prevTeam.metadata);
|
||||||
|
/** We save the metadata first to prevent duplicate payments */
|
||||||
|
team = await prisma.team.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
metadata: {
|
||||||
|
paymentId: checkoutSession.id,
|
||||||
|
subscriptionId: subscription.id || null,
|
||||||
|
subscriptionItemId: subscription.items.data[0].id || null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
/** Legacy teams already have a slug, this will allow them to upgrade as well */
|
/** Legacy teams already have a slug, this will allow them to upgrade as well */
|
||||||
const slug = prevTeam.slug || metadata?.requestedSlug;
|
const slug = prevTeam.slug || metadata?.requestedSlug;
|
||||||
if (!slug) throw new HttpError({ statusCode: 400, message: "Missing team slug" });
|
if (slug) {
|
||||||
try {
|
try {
|
||||||
/** We save the metadata first to prevent duplicate payments */
|
/** Then we try to upgrade the slug, which may fail if a conflict came up since team creation */
|
||||||
await prisma.team.update({
|
team = await prisma.team.update({ where: { id }, data: { slug } });
|
||||||
where: { id },
|
} catch (error) {
|
||||||
data: {
|
const { message, statusCode } = getRequestedSlugError(error, slug);
|
||||||
metadata: {
|
return res.status(statusCode).json({ message });
|
||||||
paymentId: checkoutSession.id,
|
}
|
||||||
subscriptionId: subscription.id || null,
|
|
||||||
subscriptionItemId: subscription.items.data[0].id || null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
/** Then we try to upgrade the slug, which may fail if a conflict came up since team creation */
|
|
||||||
team = await prisma.team.update({ where: { id }, data: { slug } });
|
|
||||||
} catch (error) {
|
|
||||||
const { message, statusCode } = getRequestedSlugError(error, slug);
|
|
||||||
return res.status(statusCode).json({ message });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sync Services: Close.com
|
// Sync Services: Close.com
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useState } from "react";
|
|||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
import { RouterOutputs, trpc } from "@calcom/trpc/react";
|
||||||
import { Card, showToast } from "@calcom/ui";
|
import { Card, showToast } from "@calcom/ui";
|
||||||
import { FiUserPlus, FiUsers, FiUnlock, FiEdit } from "@calcom/ui/components/icon";
|
import { FiUserPlus, FiUsers, FiEdit } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import TeamListItem from "./TeamListItem";
|
import TeamListItem from "./TeamListItem";
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import dayjs from "@calcom/dayjs";
|
|||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import { Prisma } from "@calcom/prisma/client";
|
import { Prisma } from "@calcom/prisma/client";
|
||||||
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
|
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||||
import { Person } from "@calcom/types/Calendar";
|
|
||||||
|
|
||||||
import { getSenderId } from "../alphanumericSenderIdSupport";
|
import { getSenderId } from "../alphanumericSenderIdSupport";
|
||||||
import * as twilio from "./smsProviders/twilioProvider";
|
import * as twilio from "./smsProviders/twilioProvider";
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import {
|
|||||||
TextAreaField,
|
TextAreaField,
|
||||||
TextField,
|
TextField,
|
||||||
} from "@calcom/ui";
|
} from "@calcom/ui";
|
||||||
import { FiPlus, FiChevronDown } from "@calcom/ui/components/icon";
|
import { FiPlus } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
import { DuplicateDialog } from "./DuplicateDialog";
|
import { DuplicateDialog } from "./DuplicateDialog";
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import {
|
|||||||
FiChevronRight,
|
FiChevronRight,
|
||||||
FiPlus,
|
FiPlus,
|
||||||
FiMenu,
|
FiMenu,
|
||||||
FiExternalLink,
|
|
||||||
} from "@calcom/ui/components/icon";
|
} from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
const tabs: VerticalTabItemProps[] = [
|
const tabs: VerticalTabItemProps[] = [
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
import { classNames } from "@calcom/lib";
|
import { classNames } from "@calcom/lib";
|
||||||
import { useHasPaidPlan } from "@calcom/lib/hooks/useHasPaidPlan";
|
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
|
||||||
|
import { useHasTeamPlan } from "@calcom/lib/hooks/useHasPaidPlan";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import isCalcom from "@calcom/lib/isCalcom";
|
|
||||||
import { EmptyScreen } from "@calcom/ui";
|
import { EmptyScreen } from "@calcom/ui";
|
||||||
import { FiUsers } from "@calcom/ui/components/icon";
|
import { FiUsers } from "@calcom/ui/components/icon";
|
||||||
|
|
||||||
@@ -33,13 +33,13 @@ export function UpgradeTip({
|
|||||||
isParentLoading?: ReactNode;
|
isParentLoading?: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const { isLoading, hasPaidPlan } = useHasPaidPlan();
|
const { isLoading, hasTeamPlan } = useHasTeamPlan();
|
||||||
|
|
||||||
if (hasPaidPlan) return children;
|
if (hasTeamPlan) return children;
|
||||||
|
|
||||||
if (isParentLoading || isLoading) return <>{isParentLoading}</>;
|
if (isParentLoading || isLoading) return <>{isParentLoading}</>;
|
||||||
|
|
||||||
if (!isCalcom)
|
if (IS_SELF_HOSTED)
|
||||||
return (
|
return (
|
||||||
<EmptyScreen
|
<EmptyScreen
|
||||||
Icon={FiUsers}
|
Icon={FiUsers}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { Prisma } from "@prisma/client";
|
|||||||
import ICAL from "ical.js";
|
import ICAL from "ical.js";
|
||||||
import type { Attendee, DateArray, DurationObject, Person } from "ics";
|
import type { Attendee, DateArray, DurationObject, Person } from "ics";
|
||||||
import { createEvent } from "ics";
|
import { createEvent } from "ics";
|
||||||
import type { DAVAccount } from "tsdav";
|
import type { DAVAccount, DAVCalendar } from "tsdav";
|
||||||
import {
|
import {
|
||||||
createAccount,
|
createAccount,
|
||||||
createCalendarObject,
|
createCalendarObject,
|
||||||
@@ -421,17 +421,20 @@ export default abstract class BaseCalendarService implements Calendar {
|
|||||||
try {
|
try {
|
||||||
const account = await this.getAccount();
|
const account = await this.getAccount();
|
||||||
|
|
||||||
const calendars = await fetchCalendars({
|
const calendars = (await fetchCalendars({
|
||||||
account,
|
account,
|
||||||
headers: this.headers,
|
headers: this.headers,
|
||||||
});
|
})) /** @url https://github.com/natelindev/tsdav/pull/139 */ as (Omit<DAVCalendar, "displayName"> & {
|
||||||
|
displayName?: string | Record<string, unknown>;
|
||||||
|
})[];
|
||||||
|
|
||||||
return calendars.reduce<IntegrationCalendar[]>((newCalendars, calendar) => {
|
return calendars.reduce<IntegrationCalendar[]>((newCalendars, calendar) => {
|
||||||
if (!calendar.components?.includes("VEVENT")) return newCalendars;
|
if (!calendar.components?.includes("VEVENT")) return newCalendars;
|
||||||
|
|
||||||
newCalendars.push({
|
newCalendars.push({
|
||||||
externalId: calendar.url,
|
externalId: calendar.url,
|
||||||
name: calendar.displayName ?? "",
|
/** @url https://github.com/calcom/cal.com/issues/7186 */
|
||||||
|
name: typeof calendar.displayName === "string" ? calendar.displayName : "",
|
||||||
primary: event?.destinationCalendar?.externalId
|
primary: event?.destinationCalendar?.externalId
|
||||||
? event.destinationCalendar.externalId === calendar.url
|
? event.destinationCalendar.externalId === calendar.url
|
||||||
: false,
|
: false,
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import crypto from "crypto";
|
import md5Parser from "md5";
|
||||||
|
|
||||||
export const defaultAvatarSrc = function ({ email, md5 }: { md5?: string; email?: string }) {
|
export const defaultAvatarSrc = function ({ email, md5 }: { md5?: string; email?: string }) {
|
||||||
if (!email && !md5) return "";
|
if (!email && !md5) return "";
|
||||||
|
|
||||||
if (email && !md5) {
|
if (email && !md5) {
|
||||||
md5 = crypto.createHash("md5").update(email).digest("hex");
|
md5 = md5Parser(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
return `https://www.gravatar.com/avatar/${md5}?s=160&d=mp&r=PG`;
|
return `https://www.gravatar.com/avatar/${md5}?s=160&d=mp&r=PG`;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { z } from "zod";
|
import type { z } from "zod";
|
||||||
|
|
||||||
|
import { getEventTypeAppData } from "@calcom/app-store/_utils/getEventTypeAppData";
|
||||||
import type { appDataSchemas } from "@calcom/app-store/apps.schemas.generated";
|
import type { appDataSchemas } from "@calcom/app-store/apps.schemas.generated";
|
||||||
import type { appDataSchema } from "@calcom/app-store/stripepayment/zod";
|
import type { appDataSchema } from "@calcom/app-store/stripepayment/zod";
|
||||||
import type { EventTypeAppsList } from "@calcom/app-store/utils";
|
import type { EventTypeAppsList } from "@calcom/app-store/utils";
|
||||||
import { getEventTypeAppData } from "@calcom/app-store/utils";
|
|
||||||
|
|
||||||
export default function getPaymentAppData(
|
export default function getPaymentAppData(
|
||||||
eventType: Parameters<typeof getEventTypeAppData>[0],
|
eventType: Parameters<typeof getEventTypeAppData>[0],
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
|
|
||||||
|
import { IS_SELF_HOSTED } from "../constants";
|
||||||
import hasKeyInMetadata from "../hasKeyInMetadata";
|
import hasKeyInMetadata from "../hasKeyInMetadata";
|
||||||
|
|
||||||
export function useHasPaidPlan() {
|
export function useHasPaidPlan() {
|
||||||
|
if (IS_SELF_HOSTED) return { isLoading: false, hasPaidPlan: true };
|
||||||
|
|
||||||
const { data: hasTeamPlan, isLoading: isLoadingTeamQuery } = trpc.viewer.teams.hasTeamPlan.useQuery();
|
const { data: hasTeamPlan, isLoading: isLoadingTeamQuery } = trpc.viewer.teams.hasTeamPlan.useQuery();
|
||||||
|
|
||||||
const { data: user, isLoading: isLoadingUserQuery } = trpc.viewer.me.useQuery();
|
const { data: user, isLoading: isLoadingUserQuery } = trpc.viewer.me.useQuery();
|
||||||
@@ -22,4 +25,10 @@ export function useTeamInvites() {
|
|||||||
return { isLoading: listInvites.isLoading, listInvites: listInvites.data };
|
return { isLoading: listInvites.isLoading, listInvites: listInvites.data };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useHasTeamPlan() {
|
||||||
|
const { data: hasTeamPlan, isLoading } = trpc.viewer.teams.hasTeamPlan.useQuery();
|
||||||
|
|
||||||
|
return { isLoading, hasTeamPlan: hasTeamPlan?.hasTeamPlan };
|
||||||
|
}
|
||||||
|
|
||||||
export default useHasPaidPlan;
|
export default useHasPaidPlan;
|
||||||
@@ -197,5 +197,12 @@
|
|||||||
"slug": "basic",
|
"slug": "basic",
|
||||||
"type": "basic_other",
|
"type": "basic_other",
|
||||||
"isTemplate": true
|
"isTemplate": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dirName": "facetime",
|
||||||
|
"categories": ["video"],
|
||||||
|
"slug": "facetime",
|
||||||
|
"type": "facetime_video",
|
||||||
|
"isTemplate": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -338,7 +338,7 @@ export const eventTypesRouter = router({
|
|||||||
profile: {
|
profile: {
|
||||||
name: membership.team.name,
|
name: membership.team.name,
|
||||||
image: `${CAL_URL}/team/${membership.team.slug}/avatar.png`,
|
image: `${CAL_URL}/team/${membership.team.slug}/avatar.png`,
|
||||||
slug: "team/" + membership.team.slug,
|
slug: membership.team.slug ? "team/" + membership.team.slug : null,
|
||||||
},
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
membershipCount: membership.team.members.length,
|
membershipCount: membership.team.members.length,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
|
import type { Prisma, PrismaPromise } from "@prisma/client";
|
||||||
import {
|
import {
|
||||||
Prisma,
|
|
||||||
PrismaPromise,
|
|
||||||
WorkflowTemplates,
|
WorkflowTemplates,
|
||||||
WorkflowActions,
|
WorkflowActions,
|
||||||
WorkflowTriggerEvents,
|
WorkflowTriggerEvents,
|
||||||
@@ -32,15 +31,15 @@ import {
|
|||||||
verifyPhoneNumber,
|
verifyPhoneNumber,
|
||||||
sendVerificationCode,
|
sendVerificationCode,
|
||||||
} from "@calcom/features/ee/workflows/lib/reminders/verifyPhoneNumber";
|
} from "@calcom/features/ee/workflows/lib/reminders/verifyPhoneNumber";
|
||||||
import { SENDER_ID } from "@calcom/lib/constants";
|
import { IS_SELF_HOSTED, SENDER_ID } from "@calcom/lib/constants";
|
||||||
import { SENDER_NAME } from "@calcom/lib/constants";
|
import { SENDER_NAME } from "@calcom/lib/constants";
|
||||||
// import { getErrorFromUnknown } from "@calcom/lib/errors";
|
// import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||||
import { WorkflowStep } from "@calcom/prisma/client";
|
import type { WorkflowStep } from "@calcom/prisma/client";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
import { router, authedProcedure, authedRateLimitedProcedure } from "../../trpc";
|
import { router, authedProcedure } from "../../trpc";
|
||||||
import { viewerTeamsRouter } from "./teams";
|
import { viewerTeamsRouter } from "./teams";
|
||||||
|
|
||||||
function getSender(
|
function getSender(
|
||||||
@@ -1127,6 +1126,6 @@ action === WorkflowActions.EMAIL_ADDRESS*/
|
|||||||
getWorkflowActionOptions: authedProcedure.query(async ({ ctx }) => {
|
getWorkflowActionOptions: authedProcedure.query(async ({ ctx }) => {
|
||||||
const { hasTeamPlan } = await viewerTeamsRouter.createCaller(ctx).hasTeamPlan();
|
const { hasTeamPlan } = await viewerTeamsRouter.createCaller(ctx).hasTeamPlan();
|
||||||
const t = await getTranslation(ctx.user.locale, "common");
|
const t = await getTranslation(ctx.user.locale, "common");
|
||||||
return getWorkflowActionOptions(t, !!hasTeamPlan);
|
return getWorkflowActionOptions(t, IS_SELF_HOSTED || !!hasTeamPlan);
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
@@ -50,13 +50,6 @@ export const Slider = <T extends string | unknown>({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`mb-2 ${className}`}>
|
<div className={`mb-2 ${className}`}>
|
||||||
<style jsx global>
|
|
||||||
{`
|
|
||||||
.glide__slide {
|
|
||||||
height: auto !important;
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
</style>
|
|
||||||
<div className="glide" ref={glide}>
|
<div className="glide" ref={glide}>
|
||||||
<div className="flex cursor-default items-center pb-3">
|
<div className="flex cursor-default items-center pb-3">
|
||||||
{isLocaleReady ? (
|
{isLocaleReady ? (
|
||||||
|
|||||||
@@ -1,61 +1,9 @@
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import * as Popover from "@radix-ui/react-popover";
|
||||||
|
import { useState } from "react";
|
||||||
import { HexColorInput, HexColorPicker } from "react-colorful";
|
import { HexColorInput, HexColorPicker } from "react-colorful";
|
||||||
|
|
||||||
import { fallBackHex, isValidHexCode } from "@calcom/lib/CustomBranding";
|
import { fallBackHex, isValidHexCode } from "@calcom/lib/CustomBranding";
|
||||||
|
|
||||||
import { Swatch } from "../../..";
|
|
||||||
|
|
||||||
type Handler = (event: MouseEvent | Event) => void;
|
|
||||||
function useEventListener<
|
|
||||||
KW extends keyof WindowEventMap,
|
|
||||||
KH extends keyof HTMLElementEventMap,
|
|
||||||
T extends HTMLElement | void = void
|
|
||||||
>(
|
|
||||||
eventName: KW | KH,
|
|
||||||
handler: (event: WindowEventMap[KW] | HTMLElementEventMap[KH] | Event) => void,
|
|
||||||
element?: React.RefObject<T>
|
|
||||||
) {
|
|
||||||
// Create a ref that stores handler
|
|
||||||
const savedHandler = useRef<typeof handler>();
|
|
||||||
useEffect(() => {
|
|
||||||
// Define the listening target
|
|
||||||
const targetElement: T | Window = element?.current || window;
|
|
||||||
if (!(targetElement && targetElement.addEventListener)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Update saved handler if necessary
|
|
||||||
if (savedHandler.current !== handler) {
|
|
||||||
savedHandler.current = handler;
|
|
||||||
}
|
|
||||||
// Create event listener that calls handler function stored in ref
|
|
||||||
const eventListener: typeof handler = (event) => {
|
|
||||||
// eslint-disable-next-line no-extra-boolean-cast
|
|
||||||
if (!!savedHandler?.current) {
|
|
||||||
savedHandler.current(event);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
targetElement.addEventListener(eventName, eventListener);
|
|
||||||
// Remove event listener on cleanup
|
|
||||||
return () => {
|
|
||||||
targetElement.removeEventListener(eventName, eventListener);
|
|
||||||
};
|
|
||||||
}, [eventName, element, handler]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function useOnClickOutside<T extends HTMLElement = HTMLElement>(
|
|
||||||
ref: React.RefObject<T>,
|
|
||||||
handler: Handler,
|
|
||||||
mouseEvent: "mousedown" | "mouseup" = "mousedown"
|
|
||||||
): void {
|
|
||||||
useEventListener(mouseEvent, (event) => {
|
|
||||||
const el = ref?.current;
|
|
||||||
// Do nothing if clicking ref's element or descendent elements
|
|
||||||
if (!el || el.contains(event.target as Node)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handler(event);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
export type ColorPickerProps = {
|
export type ColorPickerProps = {
|
||||||
defaultValue: string;
|
defaultValue: string;
|
||||||
onChange: (text: string) => void;
|
onChange: (text: string) => void;
|
||||||
@@ -66,27 +14,35 @@ const ColorPicker = (props: ColorPickerProps) => {
|
|||||||
? fallBackHex(props.defaultValue, false)
|
? fallBackHex(props.defaultValue, false)
|
||||||
: props.defaultValue;
|
: props.defaultValue;
|
||||||
const [color, setColor] = useState(init);
|
const [color, setColor] = useState(init);
|
||||||
const [isOpen, toggle] = useState(false);
|
|
||||||
const popover = useRef() as React.MutableRefObject<HTMLInputElement>;
|
|
||||||
const close = useCallback(() => toggle(false), []);
|
|
||||||
useOnClickOutside(popover, close);
|
|
||||||
return (
|
return (
|
||||||
<div className="relative mt-1 flex items-center justify-center">
|
<div className=" mt-1 flex items-center justify-center">
|
||||||
<Swatch size="sm" backgroundColor={color} onClick={() => toggle(!isOpen)} />
|
<Popover.Root>
|
||||||
{isOpen && (
|
<div className="flex items-center rounded-l-md border border-r-0 border-gray-300 p-1.5">
|
||||||
<div className="popover" ref={popover}>
|
<Popover.Trigger asChild>
|
||||||
<HexColorPicker
|
<button
|
||||||
className="!absolute !top-10 !left-0 !z-10 !h-32 !w-32"
|
className="h-6 w-6 rounded-sm border-2 border-gray-900"
|
||||||
color={color}
|
aria-label="pick colors"
|
||||||
onChange={(val) => {
|
style={{ backgroundColor: color }}
|
||||||
setColor(val);
|
/>
|
||||||
props.onChange(val);
|
</Popover.Trigger>
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
<Popover.Portal>
|
||||||
|
<Popover.Content align="center" sideOffset={10}>
|
||||||
|
<HexColorPicker
|
||||||
|
color={color}
|
||||||
|
className="!h-32 !w-32"
|
||||||
|
onChange={(val) => {
|
||||||
|
setColor(val);
|
||||||
|
props.onChange(val);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Popover.Content>
|
||||||
|
</Popover.Portal>
|
||||||
|
</Popover.Root>
|
||||||
|
|
||||||
<HexColorInput
|
<HexColorInput
|
||||||
className="block w-full border border-gray-300 px-3 py-2 ltr:ml-1 ltr:rounded-r-md rtl:mr-1 rtl:rounded-l-md sm:text-sm"
|
className="block w-full border border-gray-300 px-3 py-2 ltr:rounded-r-md rtl:rounded-l-md sm:text-sm"
|
||||||
color={color}
|
color={color}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
setColor(val);
|
setColor(val);
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import classNames from "@calcom/lib/classNames";
|
|
||||||
|
|
||||||
export type SwatchProps = {
|
|
||||||
size?: "base" | "sm" | "lg";
|
|
||||||
backgroundColor: string;
|
|
||||||
onClick: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const Swatch = (props: SwatchProps) => {
|
|
||||||
const { size, backgroundColor, onClick } = props;
|
|
||||||
return (
|
|
||||||
<div className="border-2 border-gray-200 p-1">
|
|
||||||
<div
|
|
||||||
onClick={onClick}
|
|
||||||
style={{ backgroundColor }}
|
|
||||||
className={classNames(
|
|
||||||
"cursor-pointer",
|
|
||||||
size === "sm" && "h-6 w-6 rounded-sm",
|
|
||||||
size === "base" && "h-16 w-16 rounded-sm",
|
|
||||||
size === "lg" && "h-24 w-24 rounded-sm"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Swatch;
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { default as Swatch } from "./Swatch";
|
|
||||||
@@ -109,7 +109,6 @@ export {
|
|||||||
export type { DialogProps, ConfirmationDialogContentProps } from "./components/dialog";
|
export type { DialogProps, ConfirmationDialogContentProps } from "./components/dialog";
|
||||||
export { showToast } from "./components/toast"; // We don't export the toast components as they are only used in local storybook file
|
export { showToast } from "./components/toast"; // We don't export the toast components as they are only used in local storybook file
|
||||||
export { Meta, MetaProvider, useMeta } from "./components/meta";
|
export { Meta, MetaProvider, useMeta } from "./components/meta";
|
||||||
export { Swatch } from "./components/swatch";
|
|
||||||
export { ShellSubHeading } from "./components/layout";
|
export { ShellSubHeading } from "./components/layout";
|
||||||
|
|
||||||
/** ⬇️ TODO - Move these to components */
|
/** ⬇️ TODO - Move these to components */
|
||||||
|
|||||||
@@ -3802,10 +3802,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-0.7.3.tgz#d274116678ffae87f6b60e90f88cc4083eefab86"
|
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-0.7.3.tgz#d274116678ffae87f6b60e90f88cc4083eefab86"
|
||||||
integrity sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==
|
integrity sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==
|
||||||
|
|
||||||
"@floating-ui/core@^1.2.0":
|
"@floating-ui/core@^1.2.1":
|
||||||
version "1.2.0"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.0.tgz#ae7ae7923d41f3d84cb2fd88740a89436610bbec"
|
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.2.1.tgz#074182a1d277f94569c50a6b456e62585d463c8e"
|
||||||
integrity sha512-GHUXPEhMEmTpnpIfesFA2KAoMJPb1SPQw964tToQwt+BbGXdhqTCWT1rOb0VURGylsxsYxiGMnseJ3IlclVpVA==
|
integrity sha512-LSqwPZkK3rYfD7GKoIeExXOyYx6Q1O4iqZWwIehDNuv3Dv425FIAE8PRwtAx1imEolFTHgBEcoFHm9MDnYgPCg==
|
||||||
|
|
||||||
"@floating-ui/dom@^0.5.3":
|
"@floating-ui/dom@^0.5.3":
|
||||||
version "0.5.4"
|
version "0.5.4"
|
||||||
@@ -3814,12 +3814,12 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@floating-ui/core" "^0.7.3"
|
"@floating-ui/core" "^0.7.3"
|
||||||
|
|
||||||
"@floating-ui/dom@^1.1.1":
|
"@floating-ui/dom@^1.2.1":
|
||||||
version "1.2.0"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.0.tgz#a60212069cc58961c478037c30eba4b191c75316"
|
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.2.1.tgz#8f93906e1a3b9f606ce78afb058e874344dcbe07"
|
||||||
integrity sha512-QXzg57o1cjLz3cGETzKXjI3kx1xyS49DW9l7kV2jw2c8Yftd434t2hllX0sVGn2Q8MtcW/4pNm8bfE1/4n6mng==
|
integrity sha512-Rt45SmRiV8eU+xXSB9t0uMYiQ/ZWGE/jumse2o3i5RGlyvcbqOF4q+1qBnzLE2kZ5JGhq0iMkcGXUKbFe7MpTA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@floating-ui/core" "^1.2.0"
|
"@floating-ui/core" "^1.2.1"
|
||||||
|
|
||||||
"@floating-ui/react-dom@0.7.2":
|
"@floating-ui/react-dom@0.7.2":
|
||||||
version "0.7.2"
|
version "0.7.2"
|
||||||
@@ -3830,11 +3830,11 @@
|
|||||||
use-isomorphic-layout-effect "^1.1.1"
|
use-isomorphic-layout-effect "^1.1.1"
|
||||||
|
|
||||||
"@floating-ui/react-dom@^1.0.0":
|
"@floating-ui/react-dom@^1.0.0":
|
||||||
version "1.2.2"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-1.2.2.tgz#ed256992fd44fcfcddc96da68b4b92f123d61871"
|
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-1.3.0.tgz#4d35d416eb19811c2b0e9271100a6aa18c1579b3"
|
||||||
integrity sha512-DbmFBLwFrZhtXgCI2ra7wXYT8L2BN4/4AMQKyu05qzsVji51tXOfF36VE2gpMB6nhJGHa85PdEg75FB4+vnLFQ==
|
integrity sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@floating-ui/dom" "^1.1.1"
|
"@floating-ui/dom" "^1.2.1"
|
||||||
|
|
||||||
"@formatjs/ecma402-abstract@1.11.4":
|
"@formatjs/ecma402-abstract@1.11.4":
|
||||||
version "1.11.4"
|
version "1.11.4"
|
||||||
@@ -3934,9 +3934,9 @@
|
|||||||
"@hapi/hoek" "^9.0.0"
|
"@hapi/hoek" "^9.0.0"
|
||||||
|
|
||||||
"@headlessui/react@^1.5.0":
|
"@headlessui/react@^1.5.0":
|
||||||
version "1.7.10"
|
version "1.7.11"
|
||||||
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.10.tgz#0971a33843a76f2bf4b801a43f76e3730fe15884"
|
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.11.tgz#1cc5750226abe5af2c94f72e975c0c8d2f5cc5a6"
|
||||||
integrity sha512-1m66h/5eayTEZVT2PI13/2PG3EVC7a9XalmUtVSC8X76pcyKYMuyX1XAL2RUtCr8WhoMa/KrDEyoeU5v+kSQOw==
|
integrity sha512-EaDbVgcyiylhtskZZf4Qb/JiiByY7cYbd0qgZ9xm2pm2X7hKojG0P4TaQYKgPOV3vojPhd/pZyQh3nmRkkcSyw==
|
||||||
dependencies:
|
dependencies:
|
||||||
client-only "^0.0.1"
|
client-only "^0.0.1"
|
||||||
|
|
||||||
@@ -4944,12 +4944,19 @@
|
|||||||
integrity sha512-jIOM6CzCbl2/Mzbx9kb2IjtHoJOeRN9wtQgLk4EUm5bhneSVGv1rtz5TDskvp2UfCa+EK9nDmug+lje41z80Gg==
|
integrity sha512-jIOM6CzCbl2/Mzbx9kb2IjtHoJOeRN9wtQgLk4EUm5bhneSVGv1rtz5TDskvp2UfCa+EK9nDmug+lje41z80Gg==
|
||||||
|
|
||||||
"@next/bundle-analyzer@^12.2.5":
|
"@next/bundle-analyzer@^12.2.5":
|
||||||
version "12.3.1"
|
version "12.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-12.3.1.tgz#df168652c43ed0dc3e91a5108e34cdcc8fc7f05a"
|
resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-12.3.4.tgz#37c587525288a3dea64213c991590532246e8bb8"
|
||||||
integrity sha512-2f/eei0YqZZBMTs4g1+HbgHyAFH5MbI/w9wLXmE8ly9SFze2D40sRH46JcC//EFVM/TIynVBh5sxn9CVO/vtxg==
|
integrity sha512-eKjgRICzbLTmod0UnJcArFVs5uEAiuZwB6NCf84m+btW7jdylUVoOYf1wi5tA14xk5L9Lho7Prm6/XJ8gxYzfQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
webpack-bundle-analyzer "4.3.0"
|
webpack-bundle-analyzer "4.3.0"
|
||||||
|
|
||||||
|
"@next/bundle-analyzer@^13.1.6":
|
||||||
|
version "13.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/bundle-analyzer/-/bundle-analyzer-13.1.6.tgz#36ff2f9c4b19aa75cd589ee080b1e2edb24f1192"
|
||||||
|
integrity sha512-rJS9CtLoGT58mL+v2ISKANosFFWP/0YKYByHQ3vTaZrbQP8b1rYRxd2QVMJmnSXaFkiP9URt1XJ6OdGyVq5b6g==
|
||||||
|
dependencies:
|
||||||
|
webpack-bundle-analyzer "4.7.0"
|
||||||
|
|
||||||
"@next/env@13.1.1":
|
"@next/env@13.1.1":
|
||||||
version "13.1.1"
|
version "13.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.1.1.tgz#6ff26488dc7674ef2bfdd1ca28fe43eed1113bea"
|
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.1.1.tgz#6ff26488dc7674ef2bfdd1ca28fe43eed1113bea"
|
||||||
@@ -4962,6 +4969,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
glob "7.1.7"
|
glob "7.1.7"
|
||||||
|
|
||||||
|
"@next/font@^13.1.1":
|
||||||
|
version "13.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@next/font/-/font-13.1.6.tgz#2bf99e3321ec9b4d65781c0d0ebff072e8752e1a"
|
||||||
|
integrity sha512-AITjmeb1RgX1HKMCiA39ztx2mxeAyxl4ljv2UoSBUGAbFFMg8MO7YAvjHCgFhD39hL7YTbFjol04e/BPBH5RzQ==
|
||||||
|
|
||||||
"@next/swc-android-arm-eabi@13.1.1":
|
"@next/swc-android-arm-eabi@13.1.1":
|
||||||
version "13.1.1"
|
version "13.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.1.tgz#b5c3cd1f79d5c7e6a3b3562785d4e5ac3555b9e1"
|
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.1.tgz#b5c3cd1f79d5c7e6a3b3562785d4e5ac3555b9e1"
|
||||||
@@ -5183,10 +5195,10 @@
|
|||||||
ms "2.1.3"
|
ms "2.1.3"
|
||||||
strip-ansi "6.0.1"
|
strip-ansi "6.0.1"
|
||||||
|
|
||||||
"@prisma/debug@4.10.0":
|
"@prisma/debug@4.10.1":
|
||||||
version "4.10.0"
|
version "4.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-4.10.0.tgz#5b1ab6b6d4812765a60ad8cccb7b5d38afb3b802"
|
resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-4.10.1.tgz#02d0f98aec5b0bb8b5358e1fcf6cb9462fbb6de2"
|
||||||
integrity sha512-rxVOZKsEyjlQCwN/pkkJO7wEdARt1yRyukSjLa+BF2QTvy2+VgtBmrfys4WDQSnj3jVWeHMpi5GeAoJjKkSKyA==
|
integrity sha512-NCWX+uJiEItzQsOS/4kiAKsT1hgSbN7n+1cNCmzoA6TsEn+WKzN0ZjBKyKuR937z57n2WVGXo5DfnCiW9ClqUg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/debug" "4.1.7"
|
"@types/debug" "4.1.7"
|
||||||
debug "4.3.4"
|
debug "4.3.4"
|
||||||
@@ -5203,11 +5215,11 @@
|
|||||||
integrity sha512-93tctjNXcIS+i/e552IO6tqw17sX8liivv8WX9lDMCpEEe3ci+nT9F+1oHtAafqruXLepKF80i/D20Mm+ESlOw==
|
integrity sha512-93tctjNXcIS+i/e552IO6tqw17sX8liivv8WX9lDMCpEEe3ci+nT9F+1oHtAafqruXLepKF80i/D20Mm+ESlOw==
|
||||||
|
|
||||||
"@prisma/generator-helper@^4.0.0":
|
"@prisma/generator-helper@^4.0.0":
|
||||||
version "4.10.0"
|
version "4.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/generator-helper/-/generator-helper-4.10.0.tgz#dc47265cbd55dc14ce08822f99c14d08f7e2cdb7"
|
resolved "https://registry.yarnpkg.com/@prisma/generator-helper/-/generator-helper-4.10.1.tgz#6a71043855ec670d9b8a497e4a77f181cedfbc20"
|
||||||
integrity sha512-NkQOfZpHUjVjqJ7NN2FymHSLkGd/E0fz5c3RkyESKvQqBy2sFBxt+aFxGsUbUy3FfwvkckC04HdQOXpisAko0A==
|
integrity sha512-OS2hAnRAiWpy+icccG6wp/yx/2jaeLdfYG6ZwD8RR0vZC2PC5VsrJz8tv6kYmuOs3qQwzVcPt9YSj3frWfD5WA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/debug" "4.10.0"
|
"@prisma/debug" "4.10.1"
|
||||||
"@types/cross-spawn" "6.0.2"
|
"@types/cross-spawn" "6.0.2"
|
||||||
chalk "4.1.2"
|
chalk "4.1.2"
|
||||||
cross-spawn "7.0.3"
|
cross-spawn "7.0.3"
|
||||||
@@ -6311,7 +6323,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^9.0.0"
|
"@hapi/hoek" "^9.0.0"
|
||||||
|
|
||||||
"@sideway/formula@^3.0.0":
|
"@sideway/formula@^3.0.1":
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
|
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
|
||||||
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
|
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
|
||||||
@@ -8248,6 +8260,11 @@
|
|||||||
"@types/linkify-it" "*"
|
"@types/linkify-it" "*"
|
||||||
"@types/mdurl" "*"
|
"@types/mdurl" "*"
|
||||||
|
|
||||||
|
"@types/md5@^2.3.2":
|
||||||
|
version "2.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/md5/-/md5-2.3.2.tgz#529bb3f8a7e9e9f621094eb76a443f585d882528"
|
||||||
|
integrity sha512-v+JFDu96+UYJ3/UWzB0mEglIS//MZXgRaJ4ubUPwOM0gvLc/kcQ3TWNYwENEK7/EcXGQVrW8h/XqednSjBd/Og==
|
||||||
|
|
||||||
"@types/mdast@^3.0.0":
|
"@types/mdast@^3.0.0":
|
||||||
version "3.0.10"
|
version "3.0.10"
|
||||||
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af"
|
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af"
|
||||||
@@ -8812,9 +8829,9 @@
|
|||||||
integrity sha512-aW6CfMMToX4a+baLuVxwcT0FSACjX3xrNt8wdi/3LLRlLAfhyue8OK7kJxhcYNZfydBeWTP59aRy8p5FUTIeew==
|
integrity sha512-aW6CfMMToX4a+baLuVxwcT0FSACjX3xrNt8wdi/3LLRlLAfhyue8OK7kJxhcYNZfydBeWTP59aRy8p5FUTIeew==
|
||||||
|
|
||||||
"@vercel/analytics@^0.1.6":
|
"@vercel/analytics@^0.1.6":
|
||||||
version "0.1.8"
|
version "0.1.9"
|
||||||
resolved "https://registry.yarnpkg.com/@vercel/analytics/-/analytics-0.1.8.tgz#71f1f8c7bb98ac0c5c47eb3fb8ccbe8141b9fe47"
|
resolved "https://registry.yarnpkg.com/@vercel/analytics/-/analytics-0.1.9.tgz#434f14a0dbe635a6d92c693a3607dbac0468dd47"
|
||||||
integrity sha512-PQrOI8BJ9qUiVJuQfnKiJd15eDjDJH9TBKsNeMrtelT4NAk7d9mBVz1CoZkvoFnHQ0OW7Xnqmr1F2nScfAnznQ==
|
integrity sha512-Lfus/6HAZXQBr4uv5eCtNKrEvthKWQFFcUNh5wC3fWn+4PbHxT8bqcm1TMQfYvGJ8O4TWzn0IfrQFNWh1DHDbQ==
|
||||||
|
|
||||||
"@vercel/edge-config@^0.1.1":
|
"@vercel/edge-config@^0.1.1":
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
@@ -11775,6 +11792,11 @@ commander@^6.2.0, commander@^6.2.1:
|
|||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
||||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||||
|
|
||||||
|
commander@^7.2.0:
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
|
||||||
|
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
|
||||||
|
|
||||||
commander@^8.3.0:
|
commander@^8.3.0:
|
||||||
version "8.3.0"
|
version "8.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
|
||||||
@@ -15443,9 +15465,9 @@ gifwrap@^0.9.2:
|
|||||||
omggif "^1.0.10"
|
omggif "^1.0.10"
|
||||||
|
|
||||||
github-buttons@^2.22.0:
|
github-buttons@^2.22.0:
|
||||||
version "2.22.3"
|
version "2.23.0"
|
||||||
resolved "https://registry.yarnpkg.com/github-buttons/-/github-buttons-2.22.3.tgz#ad0766618dadfbb46c12c5dca1f8c14c1e7d90f3"
|
resolved "https://registry.yarnpkg.com/github-buttons/-/github-buttons-2.23.0.tgz#b113ec9ed733f55bf5ab07652015bd0e5128dd1d"
|
||||||
integrity sha512-09/pJ7X6D1whEeFz+fKSjOBEEDnY/nxbPN7zduxAU5zGwo4RkXjL86AiOVHgIYDothbHAS43eaVk76a8Lp4YYw==
|
integrity sha512-2REUOV3ue6NmT0QThhfzfYmeSoYpCG73+tL7Ir2C7P+gshRerI05WuIQuhDkE2Zlg5Wc39hc2DHj+pE23mGJvw==
|
||||||
|
|
||||||
github-slugger@^1.0.0, github-slugger@^1.3.0:
|
github-slugger@^1.0.0, github-slugger@^1.3.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
@@ -15999,9 +16021,9 @@ hast-util-from-parse5@^6.0.0:
|
|||||||
web-namespaces "^1.0.0"
|
web-namespaces "^1.0.0"
|
||||||
|
|
||||||
hast-util-from-parse5@^7.0.0:
|
hast-util-from-parse5@^7.0.0:
|
||||||
version "7.1.1"
|
version "7.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-7.1.1.tgz#1887b4dd4e19f29d9c48c2e1c8bfeaac13a1f3a0"
|
resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz#aecfef73e3ceafdfa4550716443e4eb7b02e22b0"
|
||||||
integrity sha512-R6PoNcUs89ZxLJmMWsVbwSWuz95/9OriyQZ3e2ybwqGsRXzhA6gv49rgGmQvLbZuSNDv9fCg7vV7gXUsvtUFaA==
|
integrity sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/hast" "^2.0.0"
|
"@types/hast" "^2.0.0"
|
||||||
"@types/unist" "^2.0.0"
|
"@types/unist" "^2.0.0"
|
||||||
@@ -17951,14 +17973,14 @@ jimp@^0.16.1:
|
|||||||
regenerator-runtime "^0.13.3"
|
regenerator-runtime "^0.13.3"
|
||||||
|
|
||||||
joi@^17.7.0:
|
joi@^17.7.0:
|
||||||
version "17.7.0"
|
version "17.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3"
|
resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.1.tgz#854fc85c7fa3cfc47c91124d30bffdbb58e06cec"
|
||||||
integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==
|
integrity sha512-teoLhIvWE298R6AeJywcjR4sX2hHjB3/xJX4qPjg+gTg+c0mzUDsziYlqPmLomq9gVsfaMcgPaGc7VxtD/9StA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@hapi/hoek" "^9.0.0"
|
"@hapi/hoek" "^9.0.0"
|
||||||
"@hapi/topo" "^5.0.0"
|
"@hapi/topo" "^5.0.0"
|
||||||
"@sideway/address" "^4.1.3"
|
"@sideway/address" "^4.1.3"
|
||||||
"@sideway/formula" "^3.0.0"
|
"@sideway/formula" "^3.0.1"
|
||||||
"@sideway/pinpoint" "^2.0.0"
|
"@sideway/pinpoint" "^2.0.0"
|
||||||
|
|
||||||
jose@4.11.1, jose@^4.9.3:
|
jose@4.11.1, jose@^4.9.3:
|
||||||
@@ -19939,11 +19961,16 @@ minimist-options@4.1.0:
|
|||||||
is-plain-obj "^1.1.0"
|
is-plain-obj "^1.1.0"
|
||||||
kind-of "^6.0.3"
|
kind-of "^6.0.3"
|
||||||
|
|
||||||
minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.7:
|
minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
|
||||||
version "1.2.7"
|
version "1.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
|
||||||
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
|
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
|
||||||
|
|
||||||
|
minimist@^1.2.7:
|
||||||
|
version "1.2.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||||
|
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||||
|
|
||||||
minipass-collect@^1.0.2:
|
minipass-collect@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
|
resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617"
|
||||||
@@ -22121,9 +22148,9 @@ prism-react-renderer@^1.3.5:
|
|||||||
integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==
|
integrity sha512-IJ+MSwBWKG+SM3b2SUfdrhC+gu01QkV2KmRQgREThBfSQRoufqRfxfHUxpG1WcaFjP+kojcFyO9Qqtpgt3qLCg==
|
||||||
|
|
||||||
prisma-field-encryption@^1.4.0:
|
prisma-field-encryption@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/prisma-field-encryption/-/prisma-field-encryption-1.4.0.tgz#fb644cf98b782c81e443f022e27b0108fe31c7ed"
|
resolved "https://registry.yarnpkg.com/prisma-field-encryption/-/prisma-field-encryption-1.4.1.tgz#caec1712d0d1ac1525f64eaae3d76501f0e33a7f"
|
||||||
integrity sha512-ZJLIZL4IzdkZSmeVnpvW7E4/P74txf2OLZWzzfHQlkLcDJOddfIcXPytCxwHiPZX12aXfsVt0bA2T11mdA/lMw==
|
integrity sha512-J9uOdp/biLw4Gb3Z2dmXpDYXWG6jmqn0Wnp9/ga0Adg53jP0jVeIfO6+iIIY+H07xDUZa635seYs0pTMqYzyhA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@47ng/cloak" "^1.1.0"
|
"@47ng/cloak" "^1.1.0"
|
||||||
"@prisma/generator-helper" "^4.0.0"
|
"@prisma/generator-helper" "^4.0.0"
|
||||||
@@ -22613,11 +22640,11 @@ react-debounce-input@=3.2.4:
|
|||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
|
|
||||||
react-device-detect@^2.2.2:
|
react-device-detect@^2.2.2:
|
||||||
version "2.2.2"
|
version "2.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-2.2.2.tgz#dbabbce798ec359c83f574c3edb24cf1cca641a5"
|
resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-2.2.3.tgz#97a7ae767cdd004e7c3578260f48cf70c036e7ca"
|
||||||
integrity sha512-zSN1gIAztUekp5qUT/ybHwQ9fmOqVT1psxpSlTn1pe0CO+fnJHKRLOWWac5nKxOxvOpD/w84hk1I+EydrJp7SA==
|
integrity sha512-buYY3qrCnQVlIFHrC5UcUoAj7iANs/+srdkwsnNjI7anr3Tt7UY6MqNxtMLlr0tMBied0O49UZVK8XKs3ZIiPw==
|
||||||
dependencies:
|
dependencies:
|
||||||
ua-parser-js "^1.0.2"
|
ua-parser-js "^1.0.33"
|
||||||
|
|
||||||
react-devtools-core@^4.19.1:
|
react-devtools-core@^4.19.1:
|
||||||
version "4.24.6"
|
version "4.24.6"
|
||||||
@@ -26318,7 +26345,7 @@ tzdata@^1.0.30:
|
|||||||
resolved "https://registry.yarnpkg.com/tzdata/-/tzdata-1.0.36.tgz#e5f3998d5e95e0e65dee417ae917a16314df69e9"
|
resolved "https://registry.yarnpkg.com/tzdata/-/tzdata-1.0.36.tgz#e5f3998d5e95e0e65dee417ae917a16314df69e9"
|
||||||
integrity sha512-QxRODDsXS8UVxlPazCZXplqVuD6mCe7tSyDE5SCSHXv1nmrfflXxNH4pD+fjU8KJwePhWqhp6n+FpygQeGLv7w==
|
integrity sha512-QxRODDsXS8UVxlPazCZXplqVuD6mCe7tSyDE5SCSHXv1nmrfflXxNH4pD+fjU8KJwePhWqhp6n+FpygQeGLv7w==
|
||||||
|
|
||||||
ua-parser-js@^1.0.2:
|
ua-parser-js@^1.0.33:
|
||||||
version "1.0.33"
|
version "1.0.33"
|
||||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.33.tgz#f21f01233e90e7ed0f059ceab46eb190ff17f8f4"
|
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.33.tgz#f21f01233e90e7ed0f059ceab46eb190ff17f8f4"
|
||||||
integrity sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==
|
integrity sha512-RqshF7TPTE0XLYAqmjlu5cLLuGdKrNu9O1KLA/qp39QtbZwuzwv1dT46DZSopoUMsYgXpB3Cv8a03FI8b74oFQ==
|
||||||
@@ -27389,6 +27416,21 @@ webpack-bundle-analyzer@4.3.0:
|
|||||||
sirv "^1.0.7"
|
sirv "^1.0.7"
|
||||||
ws "^7.3.1"
|
ws "^7.3.1"
|
||||||
|
|
||||||
|
webpack-bundle-analyzer@4.7.0:
|
||||||
|
version "4.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz#33c1c485a7fcae8627c547b5c3328b46de733c66"
|
||||||
|
integrity sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg==
|
||||||
|
dependencies:
|
||||||
|
acorn "^8.0.4"
|
||||||
|
acorn-walk "^8.0.0"
|
||||||
|
chalk "^4.1.0"
|
||||||
|
commander "^7.2.0"
|
||||||
|
gzip-size "^6.0.0"
|
||||||
|
lodash "^4.17.20"
|
||||||
|
opener "^1.5.2"
|
||||||
|
sirv "^1.0.7"
|
||||||
|
ws "^7.3.1"
|
||||||
|
|
||||||
webpack-dev-middleware@^3.7.3:
|
webpack-dev-middleware@^3.7.3:
|
||||||
version "3.7.3"
|
version "3.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5"
|
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5"
|
||||||
@@ -28118,7 +28160,12 @@ zod-prisma@^0.5.4:
|
|||||||
parenthesis "^3.1.8"
|
parenthesis "^3.1.8"
|
||||||
ts-morph "^13.0.2"
|
ts-morph "^13.0.2"
|
||||||
|
|
||||||
zod@^3.17.3, zod@^3.20.2:
|
zod@^3.17.3:
|
||||||
|
version "3.20.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.6.tgz#2f2f08ff81291d47d99e86140fedb4e0db08361a"
|
||||||
|
integrity sha512-oyu0m54SGCtzh6EClBVqDDlAYRz4jrVtKwQ7ZnsEmMI9HnzuZFj8QFwAY1M5uniIYACdGvv0PBWPF2kO0aNofA==
|
||||||
|
|
||||||
|
zod@^3.20.2:
|
||||||
version "3.20.2"
|
version "3.20.2"
|
||||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.2.tgz#068606642c8f51b3333981f91c0a8ab37dfc2807"
|
resolved "https://registry.yarnpkg.com/zod/-/zod-3.20.2.tgz#068606642c8f51b3333981f91c0a8ab37dfc2807"
|
||||||
integrity sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ==
|
integrity sha512-1MzNQdAvO+54H+EaK5YpyEy0T+Ejo/7YLHS93G3RnYWh5gaotGHwGeN/ZO687qEDU2y4CdStQYXVHIgrUl5UVQ==
|
||||||
|
|||||||
Reference in new issue
Block a user