Merge branch 'main' into bugfix/unify-email-sending
38
components/Dialog.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import React from "react";
|
||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||
|
||||
export function Dialog({ children, ...props }) {
|
||||
return (
|
||||
<DialogPrimitive.Root {...props}>
|
||||
<DialogPrimitive.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||
{children}
|
||||
</DialogPrimitive.Root>
|
||||
);
|
||||
}
|
||||
|
||||
export const DialogContent = React.forwardRef(({ children, ...props }, forwardedRef) => (
|
||||
<DialogPrimitive.Content
|
||||
{...props}
|
||||
className="fixed bg-white min-w-[360px] rounded top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-left overflow-hidden shadow-xl sm:align-middle sm:max-w-lg sm:w-full p-6"
|
||||
ref={forwardedRef}>
|
||||
{children}
|
||||
</DialogPrimitive.Content>
|
||||
));
|
||||
|
||||
export function DialogHeader({ title, subtitle }: { title: string; subtitle: string }) {
|
||||
return (
|
||||
<div className="mb-8">
|
||||
<h3 className="text-lg leading-6 font-bold text-gray-900" id="modal-title">
|
||||
{title}
|
||||
</h3>
|
||||
<div>
|
||||
<p className="text-sm text-gray-400">{subtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
DialogContent.displayName = "DialogContent";
|
||||
|
||||
export const DialogTrigger = DialogPrimitive.Trigger;
|
||||
export const DialogClose = DialogPrimitive.Close;
|
7
components/Loader.tsx
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default function Loader() {
|
||||
return (
|
||||
<div className="loader border-black dark:border-white">
|
||||
<span className="loader-inner bg-black dark:bg-white"></span>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -8,7 +8,7 @@ export default function Modal(props) {
|
|||
<Dialog
|
||||
as="div"
|
||||
static
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
open={props.open}
|
||||
onClose={props.handleClose}>
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
|
@ -20,7 +20,7 @@ export default function Modal(props) {
|
|||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0">
|
||||
<Dialog.Overlay className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
|
||||
<Dialog.Overlay className="fixed inset-0 bg-gray-500 z-0 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
{/* This element is to trick the browser into centering the modal contents. */}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import Link from "next/link";
|
||||
import { CodeIcon, CreditCardIcon, KeyIcon, UserGroupIcon, UserIcon } from "@heroicons/react/solid";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
import classNames from "@lib/classNames";
|
||||
|
||||
export default function SettingsShell(props) {
|
||||
const router = useRouter();
|
||||
|
@ -38,9 +35,9 @@ export default function SettingsShell(props) {
|
|||
];
|
||||
|
||||
return (
|
||||
<div className="max-w-6xl">
|
||||
<div className="min-w-full overflow-scroll sm:mx-auto -mx-4 min-h-16">
|
||||
<nav className="-mb-px flex space-x-8 px-4 sm:px-0 " aria-label="Tabs">
|
||||
<div>
|
||||
<div className="sm:mx-auto">
|
||||
<nav className="-mb-px flex space-x-2 sm:space-x-8" aria-label="Tabs">
|
||||
{tabs.map((tab) => (
|
||||
<Link key={tab.name} href={tab.href}>
|
||||
<a
|
||||
|
@ -54,7 +51,7 @@ export default function SettingsShell(props) {
|
|||
<tab.icon
|
||||
className={classNames(
|
||||
tab.current ? "text-neutral-900" : "text-gray-400 group-hover:text-gray-500",
|
||||
"-ml-0.5 mr-2 h-5 w-5"
|
||||
"-ml-0.5 mr-2 h-5 w-5 hidden sm:inline-block"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
@ -63,8 +60,9 @@ export default function SettingsShell(props) {
|
|||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
<hr />
|
||||
</div>
|
||||
<main>{props.children}</main>
|
||||
<main className="max-w-4xl">{props.children}</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,13 +16,11 @@ import {
|
|||
PuzzleIcon,
|
||||
} from "@heroicons/react/solid";
|
||||
import Logo from "./Logo";
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
import classNames from "@lib/classNames";
|
||||
|
||||
export default function Shell(props) {
|
||||
const router = useRouter();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [session, loading] = useSession();
|
||||
const telemetry = useTelemetry();
|
||||
|
||||
|
@ -46,7 +44,7 @@ export default function Shell(props) {
|
|||
current: router.pathname.startsWith("/availability"),
|
||||
},
|
||||
{
|
||||
name: "Integrations",
|
||||
name: "App Store",
|
||||
href: "/integrations",
|
||||
icon: PuzzleIcon,
|
||||
current: router.pathname.startsWith("/integrations"),
|
||||
|
@ -70,117 +68,121 @@ export default function Shell(props) {
|
|||
}
|
||||
|
||||
return session ? (
|
||||
<div className="h-screen flex overflow-hidden bg-gray-100">
|
||||
{/* Static sidebar for desktop */}
|
||||
<div className="hidden md:flex md:flex-shrink-0">
|
||||
<div className="flex flex-col w-64">
|
||||
{/* Sidebar component, swap this element with another sidebar if you like */}
|
||||
<div className="flex flex-col h-0 flex-1 border-r border-gray-200 bg-white">
|
||||
<div className="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
|
||||
<Link href="/">
|
||||
<a className="px-4">
|
||||
<Logo small />
|
||||
</a>
|
||||
</Link>
|
||||
<nav className="mt-5 flex-1 px-2 bg-white space-y-1">
|
||||
{navigation.map((item) => (
|
||||
<Link key={item.name} href={item.href}>
|
||||
<a
|
||||
className={classNames(
|
||||
item.current
|
||||
? "bg-neutral-100 text-neutral-900"
|
||||
: "text-neutral-500 hover:bg-gray-50 hover:text-neutral-900",
|
||||
"group flex items-center px-2 py-2 text-sm font-medium rounded-sm"
|
||||
)}>
|
||||
<item.icon
|
||||
<>
|
||||
<div className="h-screen flex overflow-hidden bg-gray-100">
|
||||
{/* Static sidebar for desktop */}
|
||||
<div className="hidden md:flex md:flex-shrink-0">
|
||||
<div className="flex flex-col w-56">
|
||||
{/* Sidebar component, swap this element with another sidebar if you like */}
|
||||
<div className="flex flex-col h-0 flex-1 border-r border-gray-200 bg-white">
|
||||
<div className="flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
|
||||
<Link href="/event-types">
|
||||
<a className="px-4">
|
||||
<Logo small />
|
||||
</a>
|
||||
</Link>
|
||||
<nav className="mt-5 flex-1 px-2 bg-white space-y-1">
|
||||
{navigation.map((item) => (
|
||||
<Link key={item.name} href={item.href}>
|
||||
<a
|
||||
className={classNames(
|
||||
item.current ? "text-neutral-500" : "text-neutral-400 group-hover:text-neutral-500",
|
||||
"mr-3 flex-shrink-0 h-5 w-5"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{item.name}
|
||||
</a>
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
<div className="flex-shrink-0 flex border-t border-gray-200 p-4">
|
||||
<UserDropdown session={session} />
|
||||
item.current
|
||||
? "bg-neutral-100 text-neutral-900"
|
||||
: "text-neutral-500 hover:bg-gray-50 hover:text-neutral-900",
|
||||
"group flex items-center px-2 py-2 text-sm font-medium rounded-sm"
|
||||
)}>
|
||||
<item.icon
|
||||
className={classNames(
|
||||
item.current
|
||||
? "text-neutral-500"
|
||||
: "text-neutral-400 group-hover:text-neutral-500",
|
||||
"mr-3 flex-shrink-0 h-5 w-5"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{item.name}
|
||||
</a>
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
<div className="flex-shrink-0 flex p-4">
|
||||
<UserDropdown session={session} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col w-0 flex-1 overflow-hidden">
|
||||
{/* show top navigation for md and smaller (tablet and phones) */}
|
||||
<nav className="md:hidden bg-white shadow p-4 flex justify-between items-center">
|
||||
<Link href="/">
|
||||
<a>
|
||||
<Logo />
|
||||
</a>
|
||||
</Link>
|
||||
<div className="flex gap-3 items-center self-center">
|
||||
<button className="bg-white p-2 rounded-full text-gray-400 hover:text-gray-500 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black">
|
||||
<span className="sr-only">View notifications</span>
|
||||
<Link href="/settings/profile">
|
||||
|
||||
<div className="flex flex-col w-0 flex-1 overflow-hidden">
|
||||
<main className="flex-1 relative z-0 overflow-y-auto focus:outline-none">
|
||||
{/* show top navigation for md and smaller (tablet and phones) */}
|
||||
<nav className="md:hidden bg-white shadow p-4 flex justify-between items-center">
|
||||
<Link href="/event-types">
|
||||
<a>
|
||||
<CogIcon className="h-6 w-6" aria-hidden="true" />
|
||||
<Logo />
|
||||
</a>
|
||||
</Link>
|
||||
</button>
|
||||
<div className="mt-1">
|
||||
<UserDropdown small bottom session={session} />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main className="flex-1 relative z-0 overflow-y-auto focus:outline-none">
|
||||
<div className="py-6">
|
||||
<div className="block sm:flex justify-between px-4 sm:px-6 md:px-8">
|
||||
<div className="mb-6">
|
||||
<h1 className="text-2xl font-semibold text-gray-900">{props.heading}</h1>
|
||||
<p className="text-sm text-neutral-500 mr-4">{props.subtitle}</p>
|
||||
</div>
|
||||
<div className="mb-4 flex-shrink-0">{props.CTA}</div>
|
||||
</div>
|
||||
<div className="px-4 sm:px-6 md:px-8">{props.children}</div>
|
||||
|
||||
{/* show bottom navigation for md and smaller (tablet and phones) */}
|
||||
<nav className="md:hidden flex fixed bottom-0 bg-white w-full rounded-lg shadow">
|
||||
{/* note(PeerRich): using flatMap instead of map to remove settings from bottom nav */}
|
||||
{navigation.flatMap((item, itemIdx) =>
|
||||
item.name === "Settings" ? (
|
||||
[]
|
||||
) : (
|
||||
<Link key={item.name} href={item.href}>
|
||||
<a
|
||||
className={classNames(
|
||||
item.current ? "text-gray-900" : "text-neutral-400 hover:text-gray-700",
|
||||
itemIdx === 0 ? "rounded-l-lg" : "",
|
||||
itemIdx === navigation.length - 1 ? "rounded-r-lg" : "",
|
||||
"group relative min-w-0 flex-1 overflow-hidden bg-white py-2 px-2 text-xs sm:text-sm font-medium text-center hover:bg-gray-50 focus:z-10"
|
||||
)}
|
||||
aria-current={item.current ? "page" : undefined}>
|
||||
<item.icon
|
||||
className={classNames(
|
||||
item.current ? "text-gray-900" : "text-gray-400 group-hover:text-gray-500",
|
||||
"block mx-auto flex-shrink-0 h-5 w-5 mb-1 text-center"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span>{item.name}</span>
|
||||
<div className="flex gap-3 items-center self-center">
|
||||
<button className="bg-white p-2 rounded-full text-gray-400 hover:text-gray-500 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black">
|
||||
<span className="sr-only">View notifications</span>
|
||||
<Link href="/settings/profile">
|
||||
<a>
|
||||
<CogIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
)}
|
||||
</button>
|
||||
<div className="mt-1">
|
||||
<UserDropdown small bottom session={session} />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div className="py-8">
|
||||
<div className="block sm:flex justify-between px-4 sm:px-6 md:px-8">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-xl font-bold text-gray-900">{props.heading}</h1>
|
||||
<p className="text-sm text-neutral-500 mr-4">{props.subtitle}</p>
|
||||
</div>
|
||||
<div className="mb-4 flex-shrink-0">{props.CTA}</div>
|
||||
</div>
|
||||
<div className="px-4 sm:px-6 md:px-8">{props.children}</div>
|
||||
|
||||
{/* add padding to content for mobile navigation*/}
|
||||
<div className="block md:hidden pt-12" />
|
||||
</div>
|
||||
</main>
|
||||
{/* show bottom navigation for md and smaller (tablet and phones) */}
|
||||
<nav className="bottom-nav md:hidden flex fixed bottom-0 bg-white w-full shadow">
|
||||
{/* note(PeerRich): using flatMap instead of map to remove settings from bottom nav */}
|
||||
{navigation.flatMap((item, itemIdx) =>
|
||||
item.name === "Settings" ? (
|
||||
[]
|
||||
) : (
|
||||
<Link key={item.name} href={item.href}>
|
||||
<a
|
||||
className={classNames(
|
||||
item.current ? "text-gray-900" : "text-neutral-400 hover:text-gray-700",
|
||||
itemIdx === 0 ? "rounded-l-lg" : "",
|
||||
itemIdx === navigation.length - 1 ? "rounded-r-lg" : "",
|
||||
"group relative min-w-0 flex-1 overflow-hidden bg-white py-2 px-2 text-xs sm:text-sm font-medium text-center hover:bg-gray-50 focus:z-10"
|
||||
)}
|
||||
aria-current={item.current ? "page" : undefined}>
|
||||
<item.icon
|
||||
className={classNames(
|
||||
item.current ? "text-gray-900" : "text-gray-400 group-hover:text-gray-500",
|
||||
"block mx-auto flex-shrink-0 h-5 w-5 mb-1 text-center"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
</Link>
|
||||
)
|
||||
)}
|
||||
</nav>
|
||||
|
||||
{/* add padding to content for mobile navigation*/}
|
||||
<div className="block md:hidden pt-12" />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : null;
|
||||
}
|
||||
|
||||
|
@ -210,7 +212,7 @@ function UserDropdown({ session, small, bottom }: { session: any; small?: boolea
|
|||
<span className="flex-1 flex flex-col min-w-0">
|
||||
<span className="text-gray-900 text-sm font-medium truncate">{session.user.name}</span>
|
||||
<span className="text-neutral-500 font-normal text-sm truncate">
|
||||
{session.user.username}
|
||||
/{session.user.username}
|
||||
</span>
|
||||
</span>
|
||||
)}
|
||||
|
|
36
components/Tooltip.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import React from "react";
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
|
||||
export function Tooltip({
|
||||
children,
|
||||
content,
|
||||
open,
|
||||
defaultOpen,
|
||||
onOpenChange,
|
||||
...props
|
||||
}: {
|
||||
[x: string]: any;
|
||||
children: React.ReactNode;
|
||||
content: React.ReactNode;
|
||||
open: boolean;
|
||||
defaultOpen: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}) {
|
||||
return (
|
||||
<TooltipPrimitive.Root
|
||||
delayDuration={150}
|
||||
open={open}
|
||||
defaultOpen={defaultOpen}
|
||||
onOpenChange={onOpenChange}>
|
||||
<TooltipPrimitive.Trigger as={Slot}>{children}</TooltipPrimitive.Trigger>
|
||||
<TooltipPrimitive.Content
|
||||
className="bg-black text-xs -mt-2 text-white px-1 py-0.5 shadow-lg rounded-sm"
|
||||
side="top"
|
||||
align="center"
|
||||
{...props}>
|
||||
{content}
|
||||
</TooltipPrimitive.Content>
|
||||
</TooltipPrimitive.Root>
|
||||
);
|
||||
}
|
|
@ -2,6 +2,8 @@ import Link from "next/link";
|
|||
import { useRouter } from "next/router";
|
||||
import Slots from "./Slots";
|
||||
import { ExclamationIcon } from "@heroicons/react/solid";
|
||||
import React from "react";
|
||||
import Loader from "@components/Loader";
|
||||
|
||||
const AvailableTimes = ({
|
||||
date,
|
||||
|
@ -25,7 +27,7 @@ const AvailableTimes = ({
|
|||
});
|
||||
|
||||
return (
|
||||
<div className="sm:pl-4 mt-8 sm:mt-0 text-center sm:w-1/3 md:max-h-97 overflow-y-auto">
|
||||
<div className="sm:pl-4 mt-8 sm:mt-0 text-center sm:w-1/3 md:max-h-97 overflow-y-auto">
|
||||
<div className="text-gray-600 font-light text-xl mb-4 text-left">
|
||||
<span className="w-1/2 dark:text-white text-gray-600">{date.format("dddd DD MMMM YYYY")}</span>
|
||||
</div>
|
||||
|
@ -37,7 +39,7 @@ const AvailableTimes = ({
|
|||
`/${user.username}/book?date=${slot.utc().format()}&type=${eventTypeId}` +
|
||||
(rescheduleUid ? "&rescheduleUid=" + rescheduleUid : "")
|
||||
}>
|
||||
<a className="block font-medium mb-4 text-blue-600 border border-blue-600 rounded hover:text-white hover:bg-blue-600 py-4">
|
||||
<a className="block font-medium mb-4 bg-white dark:bg-neutral-900 text-primary-500 dark:text-neutral-200 border border-primary-500 dark:border-black rounded-sm hover:text-white hover:bg-primary-500 dark:hover:border-black py-4 dark:hover:bg-black">
|
||||
{slot.format(timeFormat)}
|
||||
</a>
|
||||
</Link>
|
||||
|
@ -49,7 +51,7 @@ const AvailableTimes = ({
|
|||
</div>
|
||||
)}
|
||||
|
||||
{!isFullyBooked && slots.length === 0 && !hasErrors && <div className="loader" />}
|
||||
{!isFullyBooked && slots.length === 0 && !hasErrors && <Loader />}
|
||||
|
||||
{hasErrors && (
|
||||
<div className="bg-yellow-50 border-l-4 border-yellow-400 p-4">
|
||||
|
|
|
@ -147,12 +147,14 @@ const DatePicker = ({
|
|||
onClick={() => setSelectedDate(inviteeDate.date(day))}
|
||||
disabled={isDisabled(day)}
|
||||
className={
|
||||
"text-center w-10 h-10 rounded-full mx-auto" +
|
||||
(isDisabled(day) ? " text-gray-400 font-light" : " text-blue-600 font-medium") +
|
||||
"text-center w-10 h-10 mx-auto hover:border hover:border-black dark:hover:border-white" +
|
||||
(isDisabled(day)
|
||||
? " text-gray-400 font-light hover:border-0 cursor-default"
|
||||
: " dark:text-white text-primary-500 font-medium") +
|
||||
(selectedDate && selectedDate.isSame(inviteeDate.date(day), "day")
|
||||
? " bg-blue-600 text-white-important"
|
||||
? " bg-black text-white-important"
|
||||
: !isDisabled(day)
|
||||
? " bg-blue-50 dark:bg-gray-900 dark:bg-opacity-30"
|
||||
? " bg-gray-100 dark:bg-black dark:bg-opacity-30"
|
||||
: "")
|
||||
}>
|
||||
{day}
|
||||
|
@ -164,26 +166,34 @@ const DatePicker = ({
|
|||
return selectedMonth ? (
|
||||
<div
|
||||
className={
|
||||
"mt-8 sm:mt-0 " +
|
||||
(selectedDate ? "sm:w-1/3 sm:border-r sm:dark:border-gray-900 sm:px-4" : "sm:w-1/2 sm:pl-4")
|
||||
"mt-8 sm:mt-0 min-w-[350px] " +
|
||||
(selectedDate
|
||||
? "w-full sm:w-1/2 md:w-1/3 sm:border-r sm:dark:border-black sm:pl-4 sm:pr-6"
|
||||
: "sm:w-1/2 sm:pl-4")
|
||||
}>
|
||||
<div className="flex text-gray-600 font-light text-xl mb-4 ml-2">
|
||||
<span className="w-1/2 text-gray-600 dark:text-white">
|
||||
{dayjs().month(selectedMonth).format("MMMM YYYY")}
|
||||
<strong className="text-gray-900 dark:text-white">
|
||||
{dayjs().month(selectedMonth).format("MMMM")}
|
||||
</strong>
|
||||
<span className="text-gray-500"> {dayjs().month(selectedMonth).format("YYYY")}</span>
|
||||
</span>
|
||||
<div className="w-1/2 text-right">
|
||||
<div className="w-1/2 text-right text-gray-600 dark:text-gray-400">
|
||||
<button
|
||||
onClick={decrementMonth}
|
||||
className={"mr-4 " + (selectedMonth <= dayjs().tz(inviteeTimeZone).month() && "text-gray-400")}
|
||||
className={
|
||||
"group mr-2 p-1" +
|
||||
(selectedMonth <= dayjs().tz(inviteeTimeZone).month() && "text-gray-400 dark:text-gray-600")
|
||||
}
|
||||
disabled={selectedMonth <= dayjs().tz(inviteeTimeZone).month()}>
|
||||
<ChevronLeftIcon className="w-5 h-5" />
|
||||
<ChevronLeftIcon className="group-hover:text-black dark:group-hover:text-white w-5 h-5" />
|
||||
</button>
|
||||
<button onClick={incrementMonth}>
|
||||
<ChevronRightIcon className="w-5 h-5" />
|
||||
<button className="group p-1" onClick={incrementMonth}>
|
||||
<ChevronRightIcon className="group-hover:text-black dark:group-hover:text-white w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-7 gap-y-4 text-center">
|
||||
<div className="grid grid-cols-7 gap-4 text-center">
|
||||
{["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
|
||||
.sort((a, b) => (weekStart.startsWith(a) ? -1 : weekStart.startsWith(b) ? 1 : 0))
|
||||
.map((weekDay) => (
|
||||
|
|
|
@ -2,10 +2,7 @@ import { Switch } from "@headlessui/react";
|
|||
import TimezoneSelect from "react-timezone-select";
|
||||
import { useEffect, useState } from "react";
|
||||
import { is24h, timeZone } from "../../lib/clock";
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
import classNames from "@lib/classNames";
|
||||
|
||||
const TimeOptions = (props) => {
|
||||
const [selectedTimeZone, setSelectedTimeZone] = useState("");
|
||||
|
@ -28,7 +25,7 @@ const TimeOptions = (props) => {
|
|||
|
||||
return (
|
||||
selectedTimeZone !== "" && (
|
||||
<div className="w-full rounded shadow border dark:bg-gray-700 dark:border-0 bg-white px-4 py-2">
|
||||
<div className="absolute w-full max-w-80 rounded-sm border border-gray-200 dark:bg-gray-700 dark:border-0 bg-white px-4 py-2">
|
||||
<div className="flex mb-4">
|
||||
<div className="w-1/2 dark:text-white text-gray-600 font-medium">Time Options</div>
|
||||
<div className="w-1/2">
|
||||
|
@ -40,7 +37,7 @@ const TimeOptions = (props) => {
|
|||
checked={is24hClock}
|
||||
onChange={setIs24hClock}
|
||||
className={classNames(
|
||||
is24hClock ? "bg-blue-600" : "dark:bg-gray-600 bg-gray-200",
|
||||
is24hClock ? "bg-black" : "dark:bg-gray-600 bg-gray-200",
|
||||
"relative inline-flex flex-shrink-0 h-5 w-8 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black"
|
||||
)}>
|
||||
<span className="sr-only">Use setting</span>
|
||||
|
|
|
@ -35,12 +35,14 @@ export default function EditTeamModal(props) {
|
|||
|
||||
return (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 z-0 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"></div>
|
||||
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
|
|
|
@ -40,12 +40,14 @@ export default function MemberInvitationModal(props) {
|
|||
|
||||
return (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 z-0 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"></div>
|
||||
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
|
|
|
@ -1,15 +1,26 @@
|
|||
import { useState } from 'react';
|
||||
|
||||
export default function Button(props) {
|
||||
return(
|
||||
<button type="submit" className="btn btn-primary">
|
||||
return (
|
||||
<button type="submit" className="btn btn-primary dark:btn-white">
|
||||
{!props.loading && props.children}
|
||||
{props.loading &&
|
||||
<svg className="animate-spin mx-4 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
{props.loading && (
|
||||
<svg
|
||||
className="animate-spin mx-4 h-5 w-5 text-white"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24">
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
}
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Link from "next/link";
|
||||
|
||||
const PoweredByCalendso = () => (
|
||||
<div className="text-xs text-center sm:text-right pt-1">
|
||||
<div className="text-xs text-center sm:text-right p-1">
|
||||
<Link href={`https://calendso.com?utm_source=embed&utm_medium=powered-by-button`}>
|
||||
<a target="_blank" className="dark:text-white text-gray-500 opacity-50 hover:opacity-100">
|
||||
powered by{" "}
|
||||
|
|
|
@ -91,7 +91,7 @@ export const Scheduler = ({
|
|||
type="button"
|
||||
onClick={() => removeScheduleAt(idx)}
|
||||
className="btn-sm bg-transparent px-2 py-1 ml-1">
|
||||
<TrashIcon className="h-6 w-6 inline text-gray-400 -mt-1" />
|
||||
<TrashIcon className="h-5 w-5 inline text-gray-400 -mt-1" />
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
|
|
|
@ -28,12 +28,14 @@ export default function SetTimesModal(props) {
|
|||
|
||||
return (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" aria-hidden="true"></div>
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 z-0 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"></div>
|
||||
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
|
|
|
@ -39,7 +39,7 @@ export default class CalEventParser {
|
|||
* Returns a footer section with links to change the event (as HTML).
|
||||
*/
|
||||
public getChangeEventFooterHtml(): string {
|
||||
return `<p style="color: #4b5563; margin-top: 20px;">Need to make a change? <a href="${this.getCancelLink()}" style="color: #161e2e;">Cancel</a> or <a href="${this.getRescheduleLink()}" style="color: #161e2e;">reschedule</a>.</p>`;
|
||||
return `<p style="color: #4b5563; margin-top: 20px;">Need to make a change? <a href="${this.getCancelLink()}" style="color: #161e2e;">Cancel</a> or <a href="${this.getRescheduleLink()}" style="color: #161e2e;">reschedule</a></p>`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -210,7 +210,7 @@ const MicrosoftOffice365Calendar = (credential): CalendarApiAdapter => {
|
|||
|
||||
return {
|
||||
getAvailability: (dateFrom, dateTo, selectedCalendars) => {
|
||||
const filter = "?$filter=start/dateTime ge '" + dateFrom + "' and end/dateTime le '" + dateTo + "'";
|
||||
const filter = "?startdatetime=" + dateFrom + "&enddatetime=" + dateTo;
|
||||
return auth
|
||||
.getToken()
|
||||
.then((accessToken) => {
|
||||
|
@ -233,7 +233,7 @@ const MicrosoftOffice365Calendar = (credential): CalendarApiAdapter => {
|
|||
headers: {
|
||||
Prefer: 'outlook.timezone="Etc/GMT"',
|
||||
},
|
||||
url: `/me/calendars/${calendarId}/events${filter}`,
|
||||
url: `/me/calendars/${calendarId}/calendarView${filter}`,
|
||||
}));
|
||||
|
||||
return fetch("https://graph.microsoft.com/v1.0/$batch", {
|
||||
|
@ -313,7 +313,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
|
|||
getAvailability: (dateFrom, dateTo, selectedCalendars) =>
|
||||
new Promise((resolve, reject) =>
|
||||
auth.getToken().then((myGoogleAuth) => {
|
||||
const calendar = google.calendar({ version: "v3", auth: myGoogleAuth });
|
||||
const calendar = google.calendar({
|
||||
version: "v3",
|
||||
auth: myGoogleAuth,
|
||||
});
|
||||
const selectedCalendarIds = selectedCalendars
|
||||
.filter((e) => e.integration === integrationType)
|
||||
.map((e) => e.externalId);
|
||||
|
@ -379,7 +382,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
|
|||
payload["conferenceData"] = event.conferenceData;
|
||||
}
|
||||
|
||||
const calendar = google.calendar({ version: "v3", auth: myGoogleAuth });
|
||||
const calendar = google.calendar({
|
||||
version: "v3",
|
||||
auth: myGoogleAuth,
|
||||
});
|
||||
calendar.events.insert(
|
||||
{
|
||||
auth: myGoogleAuth,
|
||||
|
@ -422,7 +428,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
|
|||
payload["location"] = event.location;
|
||||
}
|
||||
|
||||
const calendar = google.calendar({ version: "v3", auth: myGoogleAuth });
|
||||
const calendar = google.calendar({
|
||||
version: "v3",
|
||||
auth: myGoogleAuth,
|
||||
});
|
||||
calendar.events.update(
|
||||
{
|
||||
auth: myGoogleAuth,
|
||||
|
@ -445,7 +454,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
|
|||
deleteEvent: (uid: string) =>
|
||||
new Promise((resolve, reject) =>
|
||||
auth.getToken().then((myGoogleAuth) => {
|
||||
const calendar = google.calendar({ version: "v3", auth: myGoogleAuth });
|
||||
const calendar = google.calendar({
|
||||
version: "v3",
|
||||
auth: myGoogleAuth,
|
||||
});
|
||||
calendar.events.delete(
|
||||
{
|
||||
auth: myGoogleAuth,
|
||||
|
@ -467,7 +479,10 @@ const GoogleCalendar = (credential): CalendarApiAdapter => {
|
|||
listCalendars: () =>
|
||||
new Promise((resolve, reject) =>
|
||||
auth.getToken().then((myGoogleAuth) => {
|
||||
const calendar = google.calendar({ version: "v3", auth: myGoogleAuth });
|
||||
const calendar = google.calendar({
|
||||
version: "v3",
|
||||
auth: myGoogleAuth,
|
||||
});
|
||||
calendar.calendarList
|
||||
.list()
|
||||
.then((cals) => {
|
||||
|
|
3
lib/classNames.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
|
@ -41,6 +41,10 @@ module.exports = withTM({
|
|||
future: {
|
||||
webpack5: true,
|
||||
},
|
||||
i18n: {
|
||||
locales: ["en"],
|
||||
defaultLocale: "en",
|
||||
},
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
"@heroicons/react": "^1.0.1",
|
||||
"@jitsu/sdk-js": "^2.0.1",
|
||||
"@prisma/client": "^2.23.0",
|
||||
"@radix-ui/react-collapsible": "^0.0.16",
|
||||
"@radix-ui/react-dialog": "^0.0.19",
|
||||
"@radix-ui/react-slot": "^0.0.12",
|
||||
"@radix-ui/react-tooltip": "^0.0.21",
|
||||
"@tailwindcss/forms": "^0.2.1",
|
||||
"async": "^3.2.0",
|
||||
"bcryptjs": "^2.4.3",
|
||||
|
@ -36,6 +40,7 @@
|
|||
"react": "17.0.1",
|
||||
"react-dates": "^21.8.0",
|
||||
"react-dom": "17.0.1",
|
||||
"react-multi-email": "^0.5.3",
|
||||
"react-phone-number-input": "^3.1.21",
|
||||
"react-select": "^4.3.0",
|
||||
"react-timezone-select": "^1.0.2",
|
||||
|
|
154
pages/404.tsx
Normal file
|
@ -0,0 +1,154 @@
|
|||
import { ChevronRightIcon } from "@heroicons/react/solid";
|
||||
import { BookOpenIcon, CheckIcon, CodeIcon, DocumentTextIcon } from "@heroicons/react/outline";
|
||||
import { useRouter } from "next/router";
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import Head from "next/head";
|
||||
|
||||
const links = [
|
||||
{
|
||||
title: "Documentation",
|
||||
description: "Learn how to integrate our tools with your app",
|
||||
icon: DocumentTextIcon,
|
||||
href: "https://docs.calendso.com",
|
||||
},
|
||||
{
|
||||
title: "API Reference",
|
||||
description: "A complete API reference for our libraries",
|
||||
icon: CodeIcon,
|
||||
href: "https://api.docs.calendso.com",
|
||||
},
|
||||
{
|
||||
title: "Blog",
|
||||
description: "Read our latest news and articles",
|
||||
icon: BookOpenIcon,
|
||||
href: "https://calendso.com/blog",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Custom404() {
|
||||
const router = useRouter();
|
||||
const username = router.asPath.replace("%20", "-");
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>404: This page could not be found.</title>
|
||||
</Head>
|
||||
<div className="bg-white min-h-screen px-4">
|
||||
<main className="max-w-xl mx-auto pb-6 pt-16 sm:pt-24">
|
||||
<div className="text-center">
|
||||
<p className="text-sm font-semibold text-black uppercase tracking-wide">404 error</p>
|
||||
<h1 className="mt-2 text-4xl font-extrabold text-gray-900 tracking-tight sm:text-5xl">
|
||||
This page does not exist.
|
||||
</h1>
|
||||
<a href="https://checkout.calendso.com" className="inline-block mt-2 text-lg ">
|
||||
The username <strong className="text-blue-500">calendso.com{username}</strong> is still
|
||||
available. <span className="text-blue-500">Register now</span>.
|
||||
</a>
|
||||
</div>
|
||||
<div className="mt-12">
|
||||
<h2 className="text-sm font-semibold text-gray-500 tracking-wide uppercase">Popular pages</h2>
|
||||
|
||||
<ul role="list" className="mt-4">
|
||||
<li className="border-2 border-green-500 px-4 py-2">
|
||||
<a href="https://checkout.calendso.com" className="relative py-6 flex items-start space-x-4">
|
||||
<div className="flex-shrink-0">
|
||||
<span className="flex items-center justify-center h-12 w-12 rounded-lg bg-green-50">
|
||||
<CheckIcon className="h-6 w-6 text-green-500" aria-hidden="true" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h3 className="text-base font-medium text-gray-900">
|
||||
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500">
|
||||
<span className="focus:outline-none">
|
||||
<span className="absolute inset-0" aria-hidden="true" />
|
||||
Register <strong className="text-green-500">{username}</strong>
|
||||
</span>
|
||||
</span>
|
||||
</h3>
|
||||
<p className="text-base text-gray-500">Claim your username and schedule events</p>
|
||||
</div>
|
||||
<div className="flex-shrink-0 self-center">
|
||||
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul role="list" className="mt-4 border-gray-200 divide-y divide-gray-200">
|
||||
{links.map((link, linkIdx) => (
|
||||
<li key={linkIdx} className="px-4 py-2">
|
||||
<Link href={link.href}>
|
||||
<a className="relative py-6 flex items-start space-x-4">
|
||||
<div className="flex-shrink-0">
|
||||
<span className="flex items-center justify-center h-12 w-12 rounded-lg bg-gray-50">
|
||||
<link.icon className="h-6 w-6 text-gray-700" aria-hidden="true" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h3 className="text-base font-medium text-gray-900">
|
||||
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500">
|
||||
<span className="absolute inset-0" aria-hidden="true" />
|
||||
{link.title}
|
||||
</span>
|
||||
</h3>
|
||||
<p className="text-base text-gray-500">{link.description}</p>
|
||||
</div>
|
||||
<div className="flex-shrink-0 self-center">
|
||||
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
<li className="px-4 py-2">
|
||||
<a href="https://calendso.com/slack" className="relative py-6 flex items-start space-x-4">
|
||||
<div className="flex-shrink-0">
|
||||
<span className="flex items-center justify-center h-12 w-12 rounded-lg bg-gray-50">
|
||||
<svg viewBox="0 0 2447.6 2452.5" className="h-6 w-6" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clipRule="evenodd" fillRule="evenodd">
|
||||
<path
|
||||
d="m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z"
|
||||
fill="rgba(55, 65, 81)"></path>
|
||||
<path
|
||||
d="m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z"
|
||||
fill="rgba(55, 65, 81)"></path>
|
||||
<path
|
||||
d="m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z"
|
||||
fill="rgba(55, 65, 81)"></path>
|
||||
<path
|
||||
d="m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0"
|
||||
fill="rgba(55, 65, 81)"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h3 className="text-base font-medium text-gray-900">
|
||||
<span className="rounded-sm focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-gray-500">
|
||||
<span className="absolute inset-0" aria-hidden="true" />
|
||||
Slack
|
||||
</span>
|
||||
</h3>
|
||||
<p className="text-base text-gray-500">Join our community</p>
|
||||
</div>
|
||||
<div className="flex-shrink-0 self-center">
|
||||
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="mt-8">
|
||||
<Link href="/">
|
||||
<a className="text-base font-medium text-black hover:text-gray-500">
|
||||
Or go back home<span aria-hidden="true"> →</span>
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -4,50 +4,73 @@ import Link from "next/link";
|
|||
import prisma, { whereAndSelect } from "@lib/prisma";
|
||||
import Avatar from "../components/Avatar";
|
||||
import Theme from "@components/Theme";
|
||||
import { ClockIcon, InformationCircleIcon, UserIcon } from "@heroicons/react/solid";
|
||||
import React from "react";
|
||||
import { ArrowRightIcon } from "@heroicons/react/outline";
|
||||
|
||||
export default function User(props): User {
|
||||
const { isReady } = Theme(props.user.theme);
|
||||
|
||||
const eventTypes = props.eventTypes.map((type) => (
|
||||
<li
|
||||
<div
|
||||
key={type.id}
|
||||
className="dark:bg-gray-800 dark:opacity-90 dark:hover:opacity-100 dark:hover:bg-gray-800 bg-white hover:bg-gray-50 ">
|
||||
className="group relative dark:bg-neutral-900 dark:border-0 dark:hover:border-neutral-600 bg-white hover:bg-gray-50 border border-neutral-200 hover:border-black rounded-sm">
|
||||
<ArrowRightIcon className="absolute transition-opacity h-4 w-4 right-3 top-3 text-black dark:text-white opacity-0 group-hover:opacity-100" />
|
||||
<Link href={`/${props.user.username}/${type.slug}`}>
|
||||
<a className="block px-6 py-4">
|
||||
<div
|
||||
className="inline-block w-3 h-3 rounded-full mr-2"
|
||||
style={{ backgroundColor: getRandomColorCode() }}></div>
|
||||
<h2 className="inline-block font-medium dark:text-white">{type.title}</h2>
|
||||
<p className="inline-block text-gray-400 dark:text-gray-100 ml-2">{type.description}</p>
|
||||
<h2 className="font-semibold text-neutral-900 dark:text-white">{type.title}</h2>
|
||||
<div className="mt-2 flex space-x-4">
|
||||
<div className="flex text-sm text-neutral-500">
|
||||
<ClockIcon
|
||||
className="flex-shrink-0 mt-0.5 mr-1.5 h-4 w-4 text-neutral-400 dark:text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p className="dark:text-white">{type.length}m</p>
|
||||
</div>
|
||||
<div className="flex text-sm min-w-16 text-neutral-500">
|
||||
<UserIcon
|
||||
className="flex-shrink-0 mt-0.5 mr-1.5 h-4 w-4 text-neutral-400 dark:text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p className="dark:text-white">1-on-1</p>
|
||||
</div>
|
||||
<div className="flex text-sm text-neutral-500">
|
||||
<InformationCircleIcon
|
||||
className="flex-shrink-0 mt-0.5 mr-1.5 h-4 w-4 text-neutral-400 dark:text-white"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p className="dark:text-white">{type.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
</div>
|
||||
));
|
||||
return (
|
||||
isReady && (
|
||||
<div>
|
||||
<div className="bg-neutral-50 dark:bg-black h-screen">
|
||||
<Head>
|
||||
<title>{props.user.name || props.user.username} | Calendso</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<main className="max-w-2xl mx-auto my-24">
|
||||
<main className="max-w-3xl mx-auto py-24 px-4">
|
||||
<div className="mb-8 text-center">
|
||||
<Avatar user={props.user} className="mx-auto w-24 h-24 rounded-full mb-4" />
|
||||
<h1 className="text-3xl font-semibold text-gray-800 dark:text-white mb-1">
|
||||
<h1 className="text-3xl font-bold text-neutral-900 dark:text-white mb-1">
|
||||
{props.user.name || props.user.username}
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-white">{props.user.bio}</p>
|
||||
<p className="text-neutral-500 dark:text-white">{props.user.bio}</p>
|
||||
</div>
|
||||
<div className="shadow overflow-hidden rounded-md">
|
||||
<ul className="divide-y divide-gray-200 dark:divide-gray-900">{eventTypes}</ul>
|
||||
{eventTypes.length == 0 && (
|
||||
<div className="space-y-6">{eventTypes}</div>
|
||||
{eventTypes.length == 0 && (
|
||||
<div className="shadow overflow-hidden rounded-sm">
|
||||
<div className="p-8 text-center text-gray-400 dark:text-white">
|
||||
<h2 className="font-semibold text-3xl text-gray-600">Uh oh!</h2>
|
||||
<p className="max-w-md mx-auto">This user hasn't set up any event types yet.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
|
@ -76,6 +99,7 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
|
|||
select: {
|
||||
slug: true,
|
||||
title: true,
|
||||
length: true,
|
||||
description: true,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { GetServerSideProps, GetServerSidePropsContext } from "next";
|
||||
import Head from "next/head";
|
||||
import { ChevronDownIcon, ClockIcon, GlobeIcon } from "@heroicons/react/solid";
|
||||
import { ChevronDownIcon, ChevronUpIcon, ClockIcon, GlobeIcon } from "@heroicons/react/solid";
|
||||
import { useRouter } from "next/router";
|
||||
import dayjs, { Dayjs } from "dayjs";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
|
||||
import prisma, { whereAndSelect } from "@lib/prisma";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "../../lib/telemetry";
|
||||
|
@ -51,13 +52,11 @@ export default function Type(props): Type {
|
|||
query: Object.assign(
|
||||
{},
|
||||
{
|
||||
date: formattedDate,
|
||||
...router.query,
|
||||
},
|
||||
rescheduleUid
|
||||
? {
|
||||
rescheduleUid: rescheduleUid,
|
||||
}
|
||||
: {}
|
||||
{
|
||||
date: formattedDate,
|
||||
}
|
||||
),
|
||||
},
|
||||
undefined,
|
||||
|
@ -130,13 +129,13 @@ export default function Type(props): Type {
|
|||
<main
|
||||
className={
|
||||
"mx-auto my-0 sm:my-24 transition-max-width ease-in-out duration-500 " +
|
||||
(selectedDate ? "max-w-6xl" : "max-w-3xl")
|
||||
(selectedDate ? "max-w-5xl" : "max-w-3xl")
|
||||
}>
|
||||
<div className="dark:bg-gray-800 bg-white sm:shadow sm:rounded-lg">
|
||||
<div className="dark:bg-neutral-900 bg-white border border-gray-200 rounded-sm dark:border-0">
|
||||
<div className="sm:flex px-4 py-5 sm:p-4">
|
||||
<div
|
||||
className={
|
||||
"pr-8 sm:border-r sm:dark:border-gray-900 " + (selectedDate ? "sm:w-1/3" : "sm:w-1/2")
|
||||
"pr-8 sm:border-r sm:dark:border-black " + (selectedDate ? "sm:w-1/3" : "sm:w-1/2")
|
||||
}>
|
||||
<Avatar user={props.user} className="w-16 h-16 rounded-full mb-4" />
|
||||
<h2 className="font-medium dark:text-gray-300 text-gray-500">{props.user.name}</h2>
|
||||
|
@ -147,19 +146,25 @@ export default function Type(props): Type {
|
|||
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||
{props.eventType.length} minutes
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setIsTimeOptionsOpen(!isTimeOptionsOpen)}
|
||||
className="text-gray-500 mb-1 px-2 py-1 -ml-2">
|
||||
<GlobeIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||
{timeZone()}
|
||||
<ChevronDownIcon className="inline-block w-4 h-4 ml-1 -mt-1" />
|
||||
</button>
|
||||
{isTimeOptionsOpen && (
|
||||
<TimeOptions
|
||||
onSelectTimeZone={handleSelectTimeZone}
|
||||
onToggle24hClock={handleToggle24hClock}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Collapsible.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen}>
|
||||
<Collapsible.Trigger className="text-gray-500 mb-1 px-2 py-1 -ml-2 text-left min-w-32 ">
|
||||
<GlobeIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||
{timeZone()}
|
||||
{isTimeOptionsOpen ? (
|
||||
<ChevronUpIcon className="inline-block w-4 h-4 ml-1 -mt-1" />
|
||||
) : (
|
||||
<ChevronDownIcon className="inline-block w-4 h-4 ml-1 -mt-1" />
|
||||
)}
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
<TimeOptions
|
||||
onSelectTimeZone={handleSelectTimeZone}
|
||||
onToggle24hClock={handleToggle24hClock}
|
||||
/>
|
||||
</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
|
||||
<p className="dark:text-gray-200 text-gray-600 mt-3 mb-8">{props.eventType.description}</p>
|
||||
</div>
|
||||
<DatePicker
|
||||
|
|
|
@ -15,6 +15,8 @@ import Avatar from "../../components/Avatar";
|
|||
import Button from "../../components/ui/Button";
|
||||
import { EventTypeCustomInputType } from "../../lib/eventTypeInput";
|
||||
import Theme from "@components/Theme";
|
||||
import { ReactMultiEmail } from "react-multi-email";
|
||||
import "react-multi-email/style.css";
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
@ -27,7 +29,8 @@ export default function Book(props: any): JSX.Element {
|
|||
const [preferredTimeZone, setPreferredTimeZone] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const [guestToggle, setGuestToggle] = useState(false);
|
||||
const [guestEmails, setGuestEmails] = useState([]);
|
||||
const locations = props.eventType.locations || [];
|
||||
|
||||
const [selectedLocation, setSelectedLocation] = useState<LocationType>(
|
||||
|
@ -44,6 +47,10 @@ export default function Book(props: any): JSX.Element {
|
|||
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.timeSelected, collectPageParameters()));
|
||||
});
|
||||
|
||||
function toggleGuestEmailInput() {
|
||||
setGuestToggle(!guestToggle);
|
||||
}
|
||||
|
||||
const locationInfo = (type: LocationType) => locations.find((location) => location.type === type);
|
||||
|
||||
// TODO: Move to translations
|
||||
|
@ -65,7 +72,7 @@ export default function Book(props: any): JSX.Element {
|
|||
const data = event.target["custom_" + input.id];
|
||||
if (data) {
|
||||
if (input.type === EventTypeCustomInputType.Bool) {
|
||||
return input.label + "\n" + (data.value ? "Yes" : "No");
|
||||
return input.label + "\n" + (data.checked ? "Yes" : "No");
|
||||
} else {
|
||||
return input.label + "\n" + data.value;
|
||||
}
|
||||
|
@ -85,6 +92,7 @@ export default function Book(props: any): JSX.Element {
|
|||
name: event.target.name.value,
|
||||
email: event.target.email.value,
|
||||
notes: notes,
|
||||
guests: guestEmails,
|
||||
timeZone: preferredTimeZone,
|
||||
eventTypeId: props.eventType.id,
|
||||
rescheduleUid: rescheduleUid,
|
||||
|
@ -153,9 +161,9 @@ export default function Book(props: any): JSX.Element {
|
|||
</Head>
|
||||
|
||||
<main className="max-w-3xl mx-auto my-0 sm:my-24">
|
||||
<div className="dark:bg-gray-800 bg-white overflow-hidden sm:shadow sm:rounded-lg">
|
||||
<div className="dark:bg-neutral-900 bg-white overflow-hidden border border-gray-200 dark:border-0 sm:rounded-sm">
|
||||
<div className="sm:flex px-4 py-5 sm:p-4">
|
||||
<div className="sm:w-1/2 sm:border-r sm:dark:border-gray-900">
|
||||
<div className="sm:w-1/2 sm:border-r sm:dark:border-black">
|
||||
<Avatar user={props.user} className="w-16 h-16 rounded-full mb-4" />
|
||||
<h2 className="font-medium dark:text-gray-300 text-gray-500">{props.user.name}</h2>
|
||||
<h1 className="text-3xl font-semibold dark:text-white text-gray-800 mb-4">
|
||||
|
@ -171,7 +179,7 @@ export default function Book(props: any): JSX.Element {
|
|||
{locationInfo(selectedLocation).address}
|
||||
</p>
|
||||
)}
|
||||
<p className="text-blue-600 mb-4">
|
||||
<p className="text-green-500 mb-4">
|
||||
<CalendarIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||
{preferredTimeZone &&
|
||||
dayjs(date)
|
||||
|
@ -192,7 +200,7 @@ export default function Book(props: any): JSX.Element {
|
|||
name="name"
|
||||
id="name"
|
||||
required
|
||||
className="shadow-sm dark:bg-gray-700 dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
className="shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
placeholder="John Doe"
|
||||
defaultValue={props.booking ? props.booking.attendees[0].name : ""}
|
||||
/>
|
||||
|
@ -210,7 +218,7 @@ export default function Book(props: any): JSX.Element {
|
|||
name="email"
|
||||
id="email"
|
||||
required
|
||||
className="shadow-sm dark:bg-gray-700 dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
className="shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
placeholder="you@example.com"
|
||||
defaultValue={props.booking ? props.booking.attendees[0].email : ""}
|
||||
/>
|
||||
|
@ -218,7 +226,9 @@ export default function Book(props: any): JSX.Element {
|
|||
</div>
|
||||
{locations.length > 1 && (
|
||||
<div className="mb-4">
|
||||
<span className="block text-sm font-medium text-gray-700">Location</span>
|
||||
<span className="block text-sm font-medium dark:text-white text-gray-700">
|
||||
Location
|
||||
</span>
|
||||
{locations.map((location) => (
|
||||
<label key={location.type} className="block">
|
||||
<input
|
||||
|
@ -230,7 +240,9 @@ export default function Book(props: any): JSX.Element {
|
|||
value={location.type}
|
||||
checked={selectedLocation === location.type}
|
||||
/>
|
||||
<span className="text-sm ml-2">{locationLabels[location.type]}</span>
|
||||
<span className="text-sm ml-2 dark:text-gray-500">
|
||||
{locationLabels[location.type]}
|
||||
</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
|
@ -248,7 +260,7 @@ export default function Book(props: any): JSX.Element {
|
|||
placeholder="Enter phone number"
|
||||
id="phone"
|
||||
required
|
||||
className="shadow-sm dark:bg-gray-700 dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
className="shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
onChange={() => {
|
||||
/* DO NOT REMOVE: Callback required by PhoneInput, comment added to satisfy eslint:no-empty-function */
|
||||
}}
|
||||
|
@ -274,7 +286,7 @@ export default function Book(props: any): JSX.Element {
|
|||
id={"custom_" + input.id}
|
||||
required={input.required}
|
||||
rows={3}
|
||||
className="shadow-sm dark:bg-gray-700 dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
className="shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
placeholder=""
|
||||
/>
|
||||
)}
|
||||
|
@ -284,7 +296,7 @@ export default function Book(props: any): JSX.Element {
|
|||
name={"custom_" + input.id}
|
||||
id={"custom_" + input.id}
|
||||
required={input.required}
|
||||
className="shadow-sm dark:bg-gray-700 dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
className="shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
placeholder=""
|
||||
/>
|
||||
)}
|
||||
|
@ -294,7 +306,7 @@ export default function Book(props: any): JSX.Element {
|
|||
name={"custom_" + input.id}
|
||||
id={"custom_" + input.id}
|
||||
required={input.required}
|
||||
className="shadow-sm dark:bg-gray-700 dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
className="shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
placeholder=""
|
||||
/>
|
||||
)}
|
||||
|
@ -304,7 +316,7 @@ export default function Book(props: any): JSX.Element {
|
|||
type="checkbox"
|
||||
name={"custom_" + input.id}
|
||||
id={"custom_" + input.id}
|
||||
className="focus:ring-black h-4 w-4 text-blue-600 border-gray-300 rounded mr-2"
|
||||
className="focus:ring-black h-4 w-4 text-black border-gray-300 rounded mr-2"
|
||||
placeholder=""
|
||||
/>
|
||||
<label
|
||||
|
@ -316,6 +328,35 @@ export default function Book(props: any): JSX.Element {
|
|||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="mb-4">
|
||||
{!guestToggle && (
|
||||
<label
|
||||
onClick={toggleGuestEmailInput}
|
||||
htmlFor="guests"
|
||||
className="block text-sm font-medium text-blue-500 mb-1 hover:cursor-pointer">
|
||||
+ Additional Guests
|
||||
</label>
|
||||
)}
|
||||
{guestToggle && (
|
||||
<ReactMultiEmail
|
||||
placeholder="Input your Email Address"
|
||||
emails={guestEmails}
|
||||
onChange={(_emails: string[]) => {
|
||||
setGuestEmails(_emails);
|
||||
}}
|
||||
getLabel={(email: string, index: number, removeEmail: (index: number) => void) => {
|
||||
return (
|
||||
<div data-tag key={index}>
|
||||
{email}
|
||||
<span data-tag-handle onClick={() => removeEmail(index)}>
|
||||
×
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label
|
||||
htmlFor="notes"
|
||||
|
@ -326,13 +367,14 @@ export default function Book(props: any): JSX.Element {
|
|||
name="notes"
|
||||
id="notes"
|
||||
rows={3}
|
||||
className="shadow-sm dark:bg-gray-700 dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
className="shadow-sm dark:bg-black dark:text-white dark:border-gray-900 focus:ring-black focus:border-black block w-full sm:text-sm border-gray-300 rounded-md"
|
||||
placeholder="Please share anything that will help prepare for our meeting."
|
||||
defaultValue={props.booking ? props.booking.description : ""}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-start">
|
||||
<Button type="submit" loading={loading} className="btn btn-primary">
|
||||
{/* TODO: add styling props to <Button variant="" color="" /> and get rid of btn-primary */}
|
||||
<Button type="submit" loading={loading}>
|
||||
{rescheduleUid ? "Reschedule" : "Confirm"}
|
||||
</Button>
|
||||
<Link
|
||||
|
@ -343,7 +385,7 @@ export default function Book(props: any): JSX.Element {
|
|||
props.eventType.slug +
|
||||
(rescheduleUid ? "?rescheduleUid=" + rescheduleUid : "")
|
||||
}>
|
||||
<a className="ml-2 btn btn-white">Cancel</a>
|
||||
<a className="ml-2 text-sm dark:text-white p-2">Cancel</a>
|
||||
</Link>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -404,10 +446,13 @@ export async function getServerSideProps(context) {
|
|||
},
|
||||
});
|
||||
|
||||
const eventTypeObject = Object.assign({}, eventType, {
|
||||
periodStartDate: eventType.periodStartDate?.toString() ?? null,
|
||||
periodEndDate: eventType.periodEndDate?.toString() ?? null,
|
||||
});
|
||||
const eventTypeObject = [eventType].map((e) => {
|
||||
return {
|
||||
...e,
|
||||
periodStartDate: e.periodStartDate?.toString() ?? null,
|
||||
periodEndDate: e.periodEndDate?.toString() ?? null,
|
||||
};
|
||||
})[0];
|
||||
|
||||
let booking = null;
|
||||
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import "../styles/globals.css";
|
||||
import { createTelemetryClient, TelemetryProvider } from "../lib/telemetry";
|
||||
import { createTelemetryClient, TelemetryProvider } from "@lib/telemetry";
|
||||
import { Provider } from "next-auth/client";
|
||||
import type { AppProps } from "next/app";
|
||||
import Head from "next/head";
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<TelemetryProvider value={createTelemetryClient()}>
|
||||
<Provider session={pageProps.session}>
|
||||
<Head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
</Head>
|
||||
<Component {...pageProps} />
|
||||
</Provider>
|
||||
</TelemetryProvider>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import Document, { Html, Head, Main, NextScript } from "next/document";
|
||||
import Document, { Head, Html, Main, NextScript } from "next/document";
|
||||
|
||||
class MyDocument extends Document {
|
||||
static async getInitialProps(ctx) {
|
||||
|
@ -9,8 +9,16 @@ class MyDocument extends Document {
|
|||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<body className="dark:bg-gray-900 bg-white">
|
||||
<Head>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/site.webmanifest" />
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#000000" />
|
||||
<meta name="msapplication-TileColor" content="#ff0000" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
</Head>
|
||||
<body className="dark:bg-black bg-gray-100">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
|
|
|
@ -13,12 +13,14 @@ import { User } from "@prisma/client";
|
|||
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import isBetween from "dayjs/plugin/isBetween";
|
||||
import dayjsBusinessDays from "dayjs-business-days";
|
||||
import { Exception } from "handlebars";
|
||||
import EventOrganizerRequestMail from "@lib/emails/EventOrganizerRequestMail";
|
||||
|
||||
dayjs.extend(dayjsBusinessDays);
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(isBetween);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
const translator = short();
|
||||
|
@ -27,7 +29,6 @@ const log = logger.getChildLogger({ prefix: ["[api] book:user"] });
|
|||
function isAvailable(busyTimes, time, length) {
|
||||
// Check for conflicts
|
||||
let t = true;
|
||||
|
||||
if (Array.isArray(busyTimes) && busyTimes.length > 0) {
|
||||
busyTimes.forEach((busyTime) => {
|
||||
const startTime = dayjs(busyTime.start);
|
||||
|
@ -148,8 +149,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
|
||||
const calendarAvailability = await getBusyCalendarTimes(
|
||||
currentUser.credentials,
|
||||
dayjs(req.body.start).startOf("day").utc().format(),
|
||||
dayjs(req.body.end).endOf("day").utc().format(),
|
||||
dayjs(req.body.start).startOf("day").utc().format("YYYY-MM-DDTHH:mm:ss[Z]"),
|
||||
dayjs(req.body.end).endOf("day").utc().format("YYYY-MM-DDTHH:mm:ss[Z]"),
|
||||
selectedCalendars
|
||||
);
|
||||
const videoAvailability = await getBusyVideoTimes(currentUser.credentials);
|
||||
|
@ -201,6 +202,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
},
|
||||
});
|
||||
|
||||
const invitee = [{ email: req.body.email, name: req.body.name, timeZone: req.body.timeZone }];
|
||||
const guests = req.body.guests.map((guest) => {
|
||||
const g = {
|
||||
email: guest,
|
||||
name: "",
|
||||
timeZone: req.body.timeZone,
|
||||
};
|
||||
return g;
|
||||
});
|
||||
const attendeesList = [...invitee, ...guests];
|
||||
|
||||
const evt: CalendarEvent = {
|
||||
type: selectedEventType.title,
|
||||
title: getEventName(req.body.name, selectedEventType.title, selectedEventType.eventName),
|
||||
|
@ -208,7 +220,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
startTime: req.body.start,
|
||||
endTime: req.body.end,
|
||||
organizer: { email: currentUser.email, name: currentUser.name, timeZone: currentUser.timeZone },
|
||||
attendees: [{ email: req.body.email, name: req.body.name, timeZone: req.body.timeZone }],
|
||||
attendees: attendeesList,
|
||||
location: req.body.location, // Will be processed by the EventManager later.
|
||||
};
|
||||
|
||||
|
@ -226,7 +238,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
|
||||
try {
|
||||
isAvailableToBeBooked = isAvailable(commonAvailability, req.body.start, selectedEventType.length);
|
||||
} catch {
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
log.debug({
|
||||
message: "Unable set isAvailableToBeBooked. Using true. ",
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ export default function Error() {
|
|||
|
||||
return (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
|
|
|
@ -43,7 +43,7 @@ export default function Login({ csrfToken }) {
|
|||
</div>
|
||||
<div className="w-1/2 text-right">
|
||||
<Link href="/auth/forgot-password">
|
||||
<a className="font-medium text-secondary-600 text-sm">Forgot?</a>
|
||||
<a className="font-medium text-primary-600 text-sm">Forgot?</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,7 @@ import { CheckIcon } from "@heroicons/react/outline";
|
|||
export default function Logout() {
|
||||
return (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
|
@ -33,7 +33,7 @@ export default function Logout() {
|
|||
</div>
|
||||
<div className="mt-5 sm:mt-6">
|
||||
<Link href="/auth/login">
|
||||
<a className="inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-blue-600 text-base font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black sm:text-sm">
|
||||
<a className="inline-flex justify-center w-full rounded-md border border-transparent shadow-sm px-4 py-2 bg-black text-base font-medium text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black sm:text-sm">
|
||||
Go back to the login page
|
||||
</a>
|
||||
</Link>
|
||||
|
|
|
@ -848,13 +848,13 @@ export default function EventTypePage({
|
|||
</div>
|
||||
{showLocationModal && (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
className="fixed inset-0 bg-gray-500 z-0 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"></div>
|
||||
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
|
@ -897,13 +897,13 @@ export default function EventTypePage({
|
|||
)}
|
||||
{showAddCustomModal && (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
className="fixed inset-0 bg-gray-500 z-0 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
|
|
|
@ -7,18 +7,20 @@ import { useRouter } from "next/router";
|
|||
import { useRef, useState } from "react";
|
||||
import { getSession, useSession } from "next-auth/client";
|
||||
import { ClockIcon } from "@heroicons/react/outline";
|
||||
import Loader from "@components/Loader";
|
||||
|
||||
export default function Availability(props) {
|
||||
const [, loading] = useSession();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [session, loading] = useSession();
|
||||
const router = useRouter();
|
||||
// const [showAddModal, setShowAddModal] = useState(false);
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
const [successModalOpen, setSuccessModalOpen] = useState(false);
|
||||
const [showChangeTimesModal, setShowChangeTimesModal] = useState(false);
|
||||
// const titleRef = useRef<HTMLInputElement>(); Everything is unused
|
||||
// const slugRef = useRef<HTMLInputElement>();
|
||||
// const descriptionRef = useRef<HTMLTextAreaElement>();
|
||||
// const lengthRef = useRef<HTMLInputElement>();
|
||||
// const isHiddenRef = useRef<HTMLInputElement>();
|
||||
const titleRef = useRef<HTMLInputElement>();
|
||||
const slugRef = useRef<HTMLInputElement>();
|
||||
const descriptionRef = useRef<HTMLTextAreaElement>();
|
||||
const lengthRef = useRef<HTMLInputElement>();
|
||||
const isHiddenRef = useRef<HTMLInputElement>();
|
||||
|
||||
const startHoursRef = useRef<HTMLInputElement>();
|
||||
const startMinsRef = useRef<HTMLInputElement>();
|
||||
|
@ -28,16 +30,12 @@ export default function Availability(props) {
|
|||
const bufferMinsRef = useRef<HTMLInputElement>();
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="loader">
|
||||
<span className="loader-inner"></span>
|
||||
</div>
|
||||
);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
// function toggleAddModal() { Unused as well
|
||||
// setShowAddModal(!showAddModal);
|
||||
// }
|
||||
function toggleAddModal() {
|
||||
setShowAddModal(!showAddModal);
|
||||
}
|
||||
|
||||
function toggleChangeTimesModal() {
|
||||
setShowChangeTimesModal(!showChangeTimesModal);
|
||||
|
@ -55,10 +53,7 @@ export default function Availability(props) {
|
|||
m = m < 10 ? "0" + m : m;
|
||||
return `${h}:${m}`;
|
||||
}
|
||||
|
||||
/*
|
||||
Currently unused, so it's commented out.
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async function createEventTypeHandler(event) {
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -69,7 +64,7 @@ export default function Availability(props) {
|
|||
const enteredIsHidden = isHiddenRef.current.checked;
|
||||
|
||||
// TODO: Add validation
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const response = await fetch("/api/availability/eventtype", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
|
@ -89,7 +84,7 @@ export default function Availability(props) {
|
|||
toggleAddModal();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
async function updateStartEndTimesHandler(event) {
|
||||
event.preventDefault();
|
||||
|
||||
|
@ -105,8 +100,8 @@ export default function Availability(props) {
|
|||
const bufferMins = enteredBufferHours * 60 + enteredBufferMins;
|
||||
|
||||
// TODO: Add validation
|
||||
|
||||
await fetch("/api/availability/day", {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const response = await fetch("/api/availability/day", {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify({ start: startMins, end: endMins, buffer: bufferMins }),
|
||||
headers: {
|
||||
|
@ -130,7 +125,7 @@ export default function Availability(props) {
|
|||
|
||||
">
|
||||
<div className="flex">
|
||||
<div className="w-1/2 mr-2 bg-white shadow rounded-sm">
|
||||
<div className="w-1/2 mr-2 bg-white border border-gray-200 rounded-sm">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
||||
Change the start and end times of your day
|
||||
|
@ -149,7 +144,7 @@ export default function Availability(props) {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-1/2 ml-2 bg-white shadow rounded-sm">
|
||||
<div className="w-1/2 ml-2 border border-gray-200 rounded-sm">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
||||
Something doesn't look right?
|
||||
|
@ -159,7 +154,7 @@ export default function Availability(props) {
|
|||
</div>
|
||||
<div className="mt-5">
|
||||
<Link href="/availability/troubleshoot">
|
||||
<a className="btn btn-primary">Launch troubleshooter</a>
|
||||
<a className="btn btn-white">Launch troubleshooter</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -167,13 +162,13 @@ export default function Availability(props) {
|
|||
</div>
|
||||
{showChangeTimesModal && (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
className="fixed inset-0 bg-gray-500 z-0 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"></div>
|
||||
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
|
|
|
@ -6,20 +6,19 @@ import dayjs from "dayjs";
|
|||
import utc from "dayjs/plugin/utc";
|
||||
import { GetServerSideProps } from "next";
|
||||
import prisma from "@lib/prisma";
|
||||
import Loader from "@components/Loader";
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
export default function Troubleshoot({ user }) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [session, loading] = useSession();
|
||||
const [availability, setAvailability] = useState([]);
|
||||
const [selectedDate] = useState(dayjs());
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [selectedDate, setSelectedDate] = useState(dayjs());
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="loader">
|
||||
<span className="loader-inner"></span>
|
||||
</div>
|
||||
);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
function convertMinsToHrsMins(mins) {
|
||||
|
@ -85,11 +84,7 @@ export default function Troubleshoot({ user }) {
|
|||
</div>
|
||||
</div>
|
||||
))}
|
||||
{availability.length === 0 && (
|
||||
<div className="loader">
|
||||
<span className="loader-inner"></span>
|
||||
</div>
|
||||
)}
|
||||
{availability.length === 0 && <Loader />}
|
||||
<div className="bg-black overflow-hidden rounded-sm">
|
||||
<div className="px-4 sm:px-6 py-2 text-white">
|
||||
Your day ends at {convertMinsToHrsMins(user.endTime)}
|
||||
|
|
|
@ -3,9 +3,17 @@ import prisma from "../../lib/prisma";
|
|||
import { getSession, useSession } from "next-auth/client";
|
||||
import Shell from "../../components/Shell";
|
||||
import { useRouter } from "next/router";
|
||||
import dayjs from "dayjs";
|
||||
import { Fragment } from "react";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
import { DotsHorizontalIcon } from "@heroicons/react/solid";
|
||||
import classNames from "@lib/classNames";
|
||||
import { ClockIcon, XIcon } from "@heroicons/react/outline";
|
||||
|
||||
export default function Bookings({ bookings }) {
|
||||
const [, loading] = useSession();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [session, loading] = useSession();
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
if (loading) {
|
||||
|
@ -35,7 +43,7 @@ export default function Bookings({ bookings }) {
|
|||
<div className="-mx-4 sm:mx-auto flex flex-col">
|
||||
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
|
||||
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
|
||||
<div className="shadow overflow-hidden border-b border-gray-200 sm:rounded-sm">
|
||||
<div className="border border-gray-200 overflow-hidden border-b rounded-sm">
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
{bookings
|
||||
|
@ -49,32 +57,33 @@ export default function Bookings({ bookings }) {
|
|||
Unconfirmed
|
||||
</span>
|
||||
)}
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{booking.attendees[0].name}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">{booking.attendees[0].email}</div>
|
||||
<div
|
||||
style={{ maxWidth: 150 }}
|
||||
className="block lg:hidden font-medium text-xs text-gray-900 truncate">
|
||||
<div className="text-sm text-neutral-900 font-medium truncate max-w-60 md:max-w-96">
|
||||
{booking.title}
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
className={
|
||||
"px-6 py-4 max-w-20 w-full" + (booking.rejected ? " line-through" : "")
|
||||
}>
|
||||
<div className="hidden lg:block text-sm text-neutral-900 font-medium">
|
||||
{booking.title}
|
||||
<div className="sm:hidden">
|
||||
<div className="text-sm text-gray-900">
|
||||
{dayjs(booking.startTime).format("D MMMM YYYY")}:{" "}
|
||||
<small className="text-sm text-gray-500">
|
||||
{dayjs(booking.startTime).format("HH:mm")} -{" "}
|
||||
{dayjs(booking.endTime).format("HH:mm")}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden lg:block text-sm text-neutral-500">
|
||||
You and {booking.attendees[0].name}
|
||||
<div className="text-sm text-blue-500">
|
||||
<a href={"mailto:" + booking.attendees[0].email}>
|
||||
{booking.attendees[0].email}
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<td className="hidden sm:table-cell px-6 py-4 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-900">
|
||||
{dayjs(booking.startTime).format("D MMMM YYYY")}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{dayjs(booking.startTime).format("HH:mm")} -{" "}
|
||||
{dayjs(booking.endTime).format("HH:mm")}
|
||||
</div>
|
||||
</td>
|
||||
{/* <td className="px-6 py-4 whitespace-nowrap">
|
||||
<div className="text-sm text-gray-500">
|
||||
{dayjs(booking.startTime).format("D MMMM YYYY HH:mm")}
|
||||
</div>
|
||||
</td> */}
|
||||
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||
{!booking.confirmed && !booking.rejected && (
|
||||
<>
|
||||
|
@ -85,7 +94,7 @@ export default function Bookings({ bookings }) {
|
|||
</button>
|
||||
<button
|
||||
onClick={() => confirmBookingHandler(booking, false)}
|
||||
className="text-xs sm:text-sm ml-4 inline-flex items-center px-4 py-2 border-transparent font-medium rounded-sm shadow-sm text-neutral-700 bg-white hover:bg-neutral-100 border border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black ml-2">
|
||||
className="text-xs sm:text-sm inline-flex items-center px-4 py-2 border-transparent font-medium rounded-sm shadow-sm text-neutral-700 bg-white hover:bg-neutral-100 border border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black ml-2">
|
||||
Reject
|
||||
</button>
|
||||
</>
|
||||
|
@ -94,14 +103,89 @@ export default function Bookings({ bookings }) {
|
|||
<>
|
||||
<a
|
||||
href={window.location.href + "/../cancel/" + booking.uid}
|
||||
className="text-xs sm:text-sm inline-flex items-center px-4 py-2 border-transparent font-medium rounded-sm shadow-sm text-neutral-700 bg-white hover:bg-neutral-100 border border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black ml-2">
|
||||
className="hidden text-xs sm:text-sm lg:inline-flex items-center px-4 py-2 border-transparent font-medium rounded-sm shadow-sm text-neutral-700 bg-white hover:bg-neutral-100 border border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black ml-2">
|
||||
<XIcon
|
||||
className="mr-3 h-5 w-5 text-neutral-400 group-hover:text-neutral-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Cancel
|
||||
</a>
|
||||
<a
|
||||
href={window.location.href + "/../reschedule/" + booking.uid}
|
||||
className="text-xs sm:text-sm inline-flex items-center px-4 py-2 border-transparent font-medium rounded-sm shadow-sm text-neutral-700 bg-white hover:bg-neutral-100 border border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black ml-2">
|
||||
className="hidden text-xs sm:text-sm lg:inline-flex items-center px-4 py-2 border-transparent font-medium rounded-sm shadow-sm text-neutral-700 bg-white hover:bg-neutral-100 border border-neutral-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black ml-2">
|
||||
<ClockIcon
|
||||
className="mr-3 h-5 w-5 text-neutral-400 group-hover:text-neutral-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Reschedule
|
||||
</a>
|
||||
<Menu as="div" className="inline-block lg:hidden text-left ">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div>
|
||||
<Menu.Button className="text-neutral-400 mt-1 p-2 border border-transparent hover:border-gray-200">
|
||||
<span className="sr-only">Open options</span>
|
||||
<DotsHorizontalIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</Menu.Button>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
show={open}
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95">
|
||||
<Menu.Items
|
||||
static
|
||||
className="origin-top-right absolute right-0 mt-2 w-56 rounded-sm shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none divide-y divide-neutral-100">
|
||||
<div className="py-1">
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
href={window.location.href + "/../cancel/" + booking.uid}
|
||||
className={classNames(
|
||||
active
|
||||
? "bg-neutral-100 text-neutral-900"
|
||||
: "text-neutral-700",
|
||||
"group flex items-center px-4 py-2 text-sm font-medium"
|
||||
)}>
|
||||
<XIcon
|
||||
className="mr-3 h-5 w-5 text-neutral-400 group-hover:text-neutral-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Cancel
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
href={
|
||||
window.location.href + "/../reschedule/" + booking.uid
|
||||
}
|
||||
className={classNames(
|
||||
active
|
||||
? "bg-neutral-100 text-neutral-900"
|
||||
: "text-neutral-700",
|
||||
"group flex items-center px-4 py-2 text-sm w-full font-medium"
|
||||
)}>
|
||||
<ClockIcon
|
||||
className="mr-3 h-5 w-5 text-neutral-400 group-hover:text-neutral-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Reschedule
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
</>
|
||||
)}
|
||||
{!booking.confirmed && booking.rejected && (
|
||||
|
@ -137,7 +221,7 @@ export async function getServerSideProps(context) {
|
|||
},
|
||||
});
|
||||
|
||||
const bookings = await prisma.booking.findMany({
|
||||
const b = await prisma.booking.findMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
|
@ -149,11 +233,17 @@ export async function getServerSideProps(context) {
|
|||
confirmed: true,
|
||||
rejected: true,
|
||||
id: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
},
|
||||
orderBy: {
|
||||
startTime: "desc",
|
||||
startTime: "asc",
|
||||
},
|
||||
});
|
||||
|
||||
const bookings = b.reverse().map((booking) => {
|
||||
return { ...booking, startTime: booking.startTime.toISOString(), endTime: booking.endTime.toISOString() };
|
||||
});
|
||||
|
||||
return { props: { bookings } };
|
||||
}
|
||||
|
|
|
@ -1,172 +1,179 @@
|
|||
import {useState} from 'react';
|
||||
import Head from 'next/head';
|
||||
import prisma from '../../lib/prisma';
|
||||
import {useRouter} from 'next/router';
|
||||
import dayjs from 'dayjs';
|
||||
import {CalendarIcon, ClockIcon, XIcon} from '@heroicons/react/solid';
|
||||
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
||||
import isBetween from 'dayjs/plugin/isBetween';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import {collectPageParameters, telemetryEventTypes, useTelemetry} from "../../lib/telemetry";
|
||||
import { useState } from "react";
|
||||
import Head from "next/head";
|
||||
import prisma from "../../lib/prisma";
|
||||
import { useRouter } from "next/router";
|
||||
import dayjs from "dayjs";
|
||||
import { CalendarIcon, ClockIcon, XIcon } from "@heroicons/react/solid";
|
||||
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
|
||||
import isBetween from "dayjs/plugin/isBetween";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "../../lib/telemetry";
|
||||
|
||||
dayjs.extend(isSameOrBefore);
|
||||
dayjs.extend(isBetween);
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default function Type(props) {
|
||||
// Get router variables
|
||||
const router = useRouter();
|
||||
const { uid } = router.query;
|
||||
// Get router variables
|
||||
const router = useRouter();
|
||||
const { uid } = router.query;
|
||||
|
||||
const [is24h, setIs24h] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const telemetry = useTelemetry();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [is24h, setIs24h] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const telemetry = useTelemetry();
|
||||
|
||||
const cancellationHandler = async (event) => {
|
||||
setLoading(true);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const cancellationHandler = async (event) => {
|
||||
setLoading(true);
|
||||
|
||||
let payload = {
|
||||
uid: uid
|
||||
};
|
||||
const payload = {
|
||||
uid: uid,
|
||||
};
|
||||
|
||||
telemetry.withJitsu(jitsu => jitsu.track(telemetryEventTypes.bookingCancelled, collectPageParameters()));
|
||||
const res = await fetch(
|
||||
'/api/cancel',
|
||||
{
|
||||
body: JSON.stringify(payload),
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
method: 'POST'
|
||||
}
|
||||
);
|
||||
|
||||
if(res.status >= 200 && res.status < 300) {
|
||||
router.push('/cancel/success?user=' + props.user.username + '&title=' + props.eventType.title);
|
||||
} else {
|
||||
setLoading(false);
|
||||
setError("An error with status code " + res.status + " occurred. Please try again later.");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<title>
|
||||
Cancel {props.booking.title} | {props.user.name || props.user.username} |
|
||||
Calendso
|
||||
</title>
|
||||
<link rel="icon" href="/favicon.ico"/>
|
||||
</Head>
|
||||
<main className="max-w-3xl mx-auto my-24">
|
||||
<div className="fixed z-10 inset-0 overflow-y-auto">
|
||||
<div
|
||||
className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div className="fixed inset-0 my-4 sm:my-0 transition-opacity" aria-hidden="true">
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true">​</span>
|
||||
<div
|
||||
className="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6"
|
||||
role="dialog" aria-modal="true" aria-labelledby="modal-headline">
|
||||
{error && <div>
|
||||
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100">
|
||||
<XIcon className="h-6 w-6 text-red-600" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-5">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
||||
{error}
|
||||
</h3>
|
||||
</div>
|
||||
</div>}
|
||||
{!error && <div>
|
||||
<div
|
||||
className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100">
|
||||
<XIcon className="h-6 w-6 text-red-600"/>
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-5">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-headline">
|
||||
Really cancel your booking?
|
||||
</h3>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">
|
||||
Instead, you could also reschedule it.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-4 border-t border-b py-4">
|
||||
<h2 className="text-lg font-medium text-gray-600 mb-2">{props.booking.title}</h2>
|
||||
<p className="text-gray-500 mb-1">
|
||||
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1"/>
|
||||
{props.eventType.length} minutes
|
||||
</p>
|
||||
<p className="text-gray-500">
|
||||
<CalendarIcon className="inline-block w-4 h-4 mr-1 -mt-1"/>
|
||||
{dayjs.utc(props.booking.startTime).format((is24h ? 'H:mm' : 'h:mma') + ", dddd DD MMMM YYYY")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>}
|
||||
<div className="mt-5 sm:mt-6 text-center">
|
||||
<div className="mt-5">
|
||||
<button onClick={cancellationHandler} disabled={loading} type="button"
|
||||
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm mx-2 btn-white">
|
||||
Cancel
|
||||
</button>
|
||||
<button onClick={() => router.push('/reschedule/' + uid)} disabled={loading} type="button"
|
||||
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-gray-700 bg-gray-100 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:text-sm mx-2 btn-white">
|
||||
Reschedule
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
telemetry.withJitsu((jitsu) =>
|
||||
jitsu.track(telemetryEventTypes.bookingCancelled, collectPageParameters())
|
||||
);
|
||||
const res = await fetch("/api/cancel", {
|
||||
body: JSON.stringify(payload),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
if (res.status >= 200 && res.status < 300) {
|
||||
router.push("/cancel/success?user=" + props.user.username + "&title=" + props.eventType.title);
|
||||
} else {
|
||||
setLoading(false);
|
||||
setError("An error with status code " + res.status + " occurred. Please try again later.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<title>
|
||||
Cancel {props.booking.title} | {props.user.name || props.user.username} | Calendso
|
||||
</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<main className="max-w-3xl mx-auto my-24">
|
||||
<div className="fixed z-50 inset-0 overflow-y-auto">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div className="fixed inset-0 my-4 sm:my-0 transition-opacity" aria-hidden="true">
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
</span>
|
||||
<div
|
||||
className="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-headline">
|
||||
{error && (
|
||||
<div>
|
||||
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100">
|
||||
<XIcon className="h-6 w-6 text-red-600" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-5">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
||||
{error}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!error && (
|
||||
<div>
|
||||
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100">
|
||||
<XIcon className="h-6 w-6 text-red-600" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-5">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-headline">
|
||||
Really cancel your booking?
|
||||
</h3>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">Instead, you could also reschedule it.</p>
|
||||
</div>
|
||||
<div className="mt-4 border-t border-b py-4">
|
||||
<h2 className="text-lg font-medium text-gray-600 mb-2">{props.booking.title}</h2>
|
||||
<p className="text-gray-500 mb-1">
|
||||
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||
{props.eventType.length} minutes
|
||||
</p>
|
||||
<p className="text-gray-500">
|
||||
<CalendarIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||
{dayjs
|
||||
.utc(props.booking.startTime)
|
||||
.format((is24h ? "H:mm" : "h:mma") + ", dddd DD MMMM YYYY")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-5 sm:mt-6 text-center">
|
||||
<div className="mt-5">
|
||||
<button
|
||||
onClick={cancellationHandler}
|
||||
disabled={loading}
|
||||
type="button"
|
||||
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm mx-2 btn-white">
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={() => router.push("/reschedule/" + uid)}
|
||||
disabled={loading}
|
||||
type="button"
|
||||
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-gray-700 bg-gray-100 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:text-sm mx-2 btn-white">
|
||||
Reschedule
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context) {
|
||||
const booking = await prisma.booking.findFirst({
|
||||
where: {
|
||||
uid: context.query.uid,
|
||||
},
|
||||
const booking = await prisma.booking.findFirst({
|
||||
where: {
|
||||
uid: context.query.uid,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
attendees: true,
|
||||
eventType: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
description: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
attendees: true,
|
||||
eventType: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
username: true,
|
||||
name: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Workaround since Next.js has problems serializing date objects (see https://github.com/vercel/next.js/issues/11993)
|
||||
const bookingObj = Object.assign({}, booking, {
|
||||
startTime: booking.startTime.toString(),
|
||||
endTime: booking.endTime.toString()
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
user: booking.user,
|
||||
eventType: booking.eventType,
|
||||
booking: bookingObj
|
||||
id: true,
|
||||
username: true,
|
||||
name: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Workaround since Next.js has problems serializing date objects (see https://github.com/vercel/next.js/issues/11993)
|
||||
const bookingObj = Object.assign({}, booking, {
|
||||
startTime: booking.startTime.toString(),
|
||||
endTime: booking.endTime.toString(),
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
user: booking.user,
|
||||
eventType: booking.eventType,
|
||||
booking: bookingObj,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,98 +1,92 @@
|
|||
import {useState} from 'react';
|
||||
import Head from 'next/head';
|
||||
import prisma from '../../lib/prisma';
|
||||
import {useRouter} from 'next/router';
|
||||
import dayjs from 'dayjs';
|
||||
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
||||
import isBetween from 'dayjs/plugin/isBetween';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import {CheckIcon} from "@heroicons/react/outline";
|
||||
import Head from "next/head";
|
||||
import prisma from "../../lib/prisma";
|
||||
import { useRouter } from "next/router";
|
||||
import dayjs from "dayjs";
|
||||
import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
|
||||
import isBetween from "dayjs/plugin/isBetween";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import timezone from "dayjs/plugin/timezone";
|
||||
import { CheckIcon } from "@heroicons/react/outline";
|
||||
|
||||
dayjs.extend(isSameOrBefore);
|
||||
dayjs.extend(isBetween);
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(' ')
|
||||
}
|
||||
|
||||
export default function Type(props) {
|
||||
// Get router variables
|
||||
const router = useRouter();
|
||||
// Get router variables
|
||||
const router = useRouter();
|
||||
|
||||
const [is24h, setIs24h] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<title>
|
||||
Cancelled {props.title} | {props.user.name || props.user.username} |
|
||||
Calendso
|
||||
</title>
|
||||
<link rel="icon" href="/favicon.ico"/>
|
||||
</Head>
|
||||
<main className="max-w-3xl mx-auto my-24">
|
||||
<div className="fixed z-10 inset-0 overflow-y-auto">
|
||||
<div
|
||||
className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div className="fixed inset-0 my-4 sm:my-0 transition-opacity" aria-hidden="true">
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen"
|
||||
aria-hidden="true">​</span>
|
||||
<div
|
||||
className="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6"
|
||||
role="dialog" aria-modal="true" aria-labelledby="modal-headline">
|
||||
<div>
|
||||
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100">
|
||||
<CheckIcon className="h-6 w-6 text-green-600" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-5">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-headline">
|
||||
Cancellation successful
|
||||
</h3>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">
|
||||
Feel free to pick another event anytime.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-6 text-center">
|
||||
<div className="mt-5">
|
||||
<button onClick={() => router.push('/' + props.user.username)} type="button"
|
||||
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-gray-700 bg-gray-100 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:text-sm mx-2 btn-white">
|
||||
Pick another
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<title>
|
||||
Cancelled {props.title} | {props.user.name || props.user.username} | Calendso
|
||||
</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<main className="max-w-3xl mx-auto my-24">
|
||||
<div className="fixed z-50 inset-0 overflow-y-auto">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div className="fixed inset-0 my-4 sm:my-0 transition-opacity" aria-hidden="true">
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
</span>
|
||||
<div
|
||||
className="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-headline">
|
||||
<div>
|
||||
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100">
|
||||
<CheckIcon className="h-6 w-6 text-green-600" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-5">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-headline">
|
||||
Cancellation successful
|
||||
</h3>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500">Feel free to pick another event anytime.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<div className="mt-5 sm:mt-6 text-center">
|
||||
<div className="mt-5">
|
||||
<button
|
||||
onClick={() => router.push("/" + props.user.username)}
|
||||
type="button"
|
||||
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-md text-gray-700 bg-gray-100 hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 sm:text-sm mx-2 btn-white">
|
||||
Pick another
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export async function getServerSideProps(context) {
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
username: context.query.user,
|
||||
},
|
||||
select: {
|
||||
username: true,
|
||||
name: true,
|
||||
bio: true,
|
||||
avatar: true,
|
||||
eventTypes: true
|
||||
}
|
||||
});
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
username: context.query.user,
|
||||
},
|
||||
select: {
|
||||
username: true,
|
||||
name: true,
|
||||
bio: true,
|
||||
avatar: true,
|
||||
eventTypes: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
user,
|
||||
title: context.query.title
|
||||
},
|
||||
}
|
||||
return {
|
||||
props: {
|
||||
user,
|
||||
title: context.query.title,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import {
|
|||
ExternalLinkIcon,
|
||||
LinkIcon,
|
||||
LocationMarkerIcon,
|
||||
PencilIcon,
|
||||
PlusIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/solid";
|
||||
|
@ -100,7 +99,6 @@ export default function EventTypePage({
|
|||
availability,
|
||||
}: Props): JSX.Element {
|
||||
const router = useRouter();
|
||||
//const [session, loading] = useSession(); unused, so it's commented out
|
||||
|
||||
console.log(eventType);
|
||||
const inputOptions: OptionBase[] = [
|
||||
|
@ -265,7 +263,7 @@ export default function EventTypePage({
|
|||
},
|
||||
});
|
||||
|
||||
router.push("/availability");
|
||||
router.push("/event-types");
|
||||
}
|
||||
|
||||
const openLocationModal = (type: LocationType) => {
|
||||
|
@ -388,39 +386,32 @@ export default function EventTypePage({
|
|||
<title>{eventType.title} | Event Type | Calendso</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<Shell heading={"Event Type: " + eventType.title} subtitle={eventType.description}>
|
||||
<div className="flex">
|
||||
<div className="w-10/12 mr-2">
|
||||
<div className="bg-white rounded-sm border border-neutral-200 p-8">
|
||||
<Shell
|
||||
heading={
|
||||
<input
|
||||
ref={titleRef}
|
||||
type="text"
|
||||
name="title"
|
||||
id="title"
|
||||
required
|
||||
className="pl-0 text-xl font-bold text-gray-900 cursor-pointer border-none focus:ring-0 bg-transparent focus:outline-none"
|
||||
placeholder="Quick Chat"
|
||||
defaultValue={eventType.title}
|
||||
/>
|
||||
}
|
||||
subtitle={eventType.description}>
|
||||
<div className="block sm:flex">
|
||||
<div className="w-full sm:w-10/12 mr-2">
|
||||
<div className="bg-white rounded-sm border border-neutral-200 -mx-4 sm:mx-0 p-4 sm:p-8">
|
||||
<form onSubmit={updateEventTypeHandler} className="space-y-4">
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
<label htmlFor="title" className="flex font-medium text-neutral-700 mt-1">
|
||||
<PencilIcon className="w-4 h-4 mr-2 mt-1 text-neutral-500" />
|
||||
Title
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<input
|
||||
ref={titleRef}
|
||||
type="text"
|
||||
name="title"
|
||||
id="title"
|
||||
required
|
||||
className="shadow-sm focus:ring-primary-500 focus:border-primary-500 block w-full sm:text-sm border-neutral-300 rounded-sm"
|
||||
placeholder="Quick Chat"
|
||||
defaultValue={eventType.title}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
<label htmlFor="slug" className="flex font-medium text-neutral-700 mt-1">
|
||||
<LinkIcon className="w-4 h-4 mr-2 mt-1 text-neutral-500" />
|
||||
<div className="block sm:flex items-center">
|
||||
<div className="min-w-44 mb-4 sm:mb-0">
|
||||
<label htmlFor="slug" className="text-sm flex font-medium text-neutral-700 mt-0">
|
||||
<LinkIcon className="w-4 h-4 mr-2 mt-0.5 text-neutral-500" />
|
||||
URL
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<div className="w-full">
|
||||
<div className="flex rounded-sm shadow-sm">
|
||||
<span className="inline-flex items-center px-3 rounded-l-sm border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
|
||||
{typeof location !== "undefined" ? location.hostname : ""}/{user.username}/
|
||||
|
@ -437,14 +428,45 @@ export default function EventTypePage({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
<label htmlFor="location" className="flex font-medium text-neutral-700 mt-1">
|
||||
<LocationMarkerIcon className="w-4 h-4 mr-2 mt-1 text-neutral-500" />
|
||||
|
||||
<div className="block sm:flex items-center">
|
||||
<div className="min-w-44 mb-4 sm:mb-0">
|
||||
<label htmlFor="length" className="text-sm flex font-medium text-neutral-700 mt-0">
|
||||
<ClockIcon className="w-4 h-4 mr-2 mt-0.5 text-neutral-500" />
|
||||
Duration
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<div className="mt-1 relative rounded-sm shadow-sm">
|
||||
<input
|
||||
ref={lengthRef}
|
||||
type="number"
|
||||
name="length"
|
||||
id="length"
|
||||
required
|
||||
className="focus:ring-primary-500 focus:border-primary-500 block w-full pl-2 pr-12 sm:text-sm border-gray-300 rounded-sm"
|
||||
placeholder="15"
|
||||
defaultValue={eventType.length}
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
||||
<span className="text-gray-500 sm:text-sm" id="duration">
|
||||
mins
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div className="block sm:flex items-center">
|
||||
<div className="min-w-44 mb-4 sm:mb-0">
|
||||
<label htmlFor="location" className="text-sm flex font-medium text-neutral-700 mt-0">
|
||||
<LocationMarkerIcon className="w-4 h-4 mr-2 mt-0.5 text-neutral-500" />
|
||||
Location
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<div className="w-full">
|
||||
{locations.length === 0 && (
|
||||
<div className="mt-1 mb-2">
|
||||
<div className="flex">
|
||||
|
@ -468,19 +490,19 @@ export default function EventTypePage({
|
|||
className="mb-2 p-2 border border-neutral-300 rounded-sm shadow-sm">
|
||||
<div className="flex justify-between">
|
||||
{location.type === LocationType.InPerson && (
|
||||
<div className="flex-grow flex">
|
||||
<div className="flex-grow flex items-center">
|
||||
<LocationMarkerIcon className="h-6 w-6" />
|
||||
<span className="ml-2 text-sm">{location.address}</span>
|
||||
</div>
|
||||
)}
|
||||
{location.type === LocationType.Phone && (
|
||||
<div className="flex-grow flex">
|
||||
<div className="flex-grow flex items-center">
|
||||
<PhoneIcon className="h-6 w-6" />
|
||||
<span className="ml-2 text-sm">Phone call</span>
|
||||
</div>
|
||||
)}
|
||||
{location.type === LocationType.GoogleMeet && (
|
||||
<div className="flex-grow flex">
|
||||
<div className="flex-grow flex items-center">
|
||||
<svg
|
||||
className="h-6 w-6"
|
||||
viewBox="0 0 64 54"
|
||||
|
@ -511,7 +533,7 @@ export default function EventTypePage({
|
|||
</div>
|
||||
)}
|
||||
{location.type === LocationType.Zoom && (
|
||||
<div className="flex-grow flex">
|
||||
<div className="flex-grow flex items-center">
|
||||
<svg
|
||||
className="h-6 w-6"
|
||||
viewBox="0 0 64 64"
|
||||
|
@ -555,10 +577,12 @@ export default function EventTypePage({
|
|||
<li>
|
||||
<button
|
||||
type="button"
|
||||
className="sm:flex sm:items-start text-sm text-primary-600"
|
||||
className="bg-neutral-100 rounded-sm py-2 px-3 flex"
|
||||
onClick={() => setShowLocationModal(true)}>
|
||||
<PlusIcon className="h-5 w-5" />
|
||||
<span className="font-medium">Add another location option</span>
|
||||
<PlusIcon className="h-4 w-4 mt-0.5 text-neutral-900" />
|
||||
<span className="ml-1 text-neutral-700 text-sm font-medium">
|
||||
Add another location
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
)}
|
||||
|
@ -566,41 +590,17 @@ export default function EventTypePage({
|
|||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
<label htmlFor="length" className="flex font-medium text-neutral-700 mt-1">
|
||||
<ClockIcon className="w-4 h-4 mr-2 mt-1 text-neutral-500" />
|
||||
Duration
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<div className="mt-1 relative rounded-sm shadow-sm">
|
||||
<input
|
||||
ref={lengthRef}
|
||||
type="number"
|
||||
name="length"
|
||||
id="length"
|
||||
required
|
||||
className="focus:ring-primary-500 focus:border-primary-500 block w-full pl-2 pr-12 sm:text-sm border-gray-300 rounded-sm"
|
||||
placeholder="15"
|
||||
defaultValue={eventType.length}
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
||||
<span className="text-gray-500 sm:text-sm" id="duration">
|
||||
mins
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
<label htmlFor="description" className="flex font-medium text-neutral-700 mt-1">
|
||||
<DocumentIcon className="w-4 h-4 mr-2 mt-1 text-neutral-500" />
|
||||
|
||||
<hr className="border-neutral-200" />
|
||||
|
||||
<div className="block sm:flex items-center">
|
||||
<div className="min-w-44 mb-4 sm:mb-0">
|
||||
<label htmlFor="description" className="text-sm flex font-medium text-neutral-700 mt-0">
|
||||
<DocumentIcon className="w-4 h-4 mr-2 mt-0.5 text-neutral-500" />
|
||||
Description
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<div className="w-full">
|
||||
<textarea
|
||||
ref={descriptionRef}
|
||||
name="description"
|
||||
|
@ -620,13 +620,15 @@ export default function EventTypePage({
|
|||
<span className="text-neutral-700 text-sm font-medium">Show advanced settings</span>
|
||||
</Disclosure.Button>
|
||||
<Disclosure.Panel className="space-y-4">
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
<label htmlFor="eventName" className="flex font-medium text-neutral-700 mt-2">
|
||||
<div className="block sm:flex items-center">
|
||||
<div className="min-w-44 mb-4 sm:mb-0">
|
||||
<label
|
||||
htmlFor="eventName"
|
||||
className="text-sm flex font-medium text-neutral-700 mt-2">
|
||||
Event name
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<div className="w-full">
|
||||
<div className="mt-1 relative rounded-sm shadow-sm">
|
||||
<input
|
||||
ref={eventNameRef}
|
||||
|
@ -640,15 +642,15 @@ export default function EventTypePage({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
<div className="block sm:flex items-center">
|
||||
<div className="min-w-44 mb-4 sm:mb-0">
|
||||
<label
|
||||
htmlFor="additionalFields"
|
||||
className="flex font-medium text-neutral-700 mt-2">
|
||||
className="text-sm flex font-medium text-neutral-700 mt-2">
|
||||
Additional inputs
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<div className="w-full">
|
||||
<ul className="w-96 mt-1">
|
||||
{customInputs.map((customInput) => (
|
||||
<li key={customInput.label} className="bg-secondary-50 mb-2 p-2 border">
|
||||
|
@ -694,13 +696,13 @@ export default function EventTypePage({
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
<label htmlFor="hidden" className="flex font-medium text-neutral-700">
|
||||
<div className="block sm:flex items-center">
|
||||
<div className="min-w-44 mb-4 sm:mb-0">
|
||||
<label htmlFor="hidden" className="text-sm flex font-medium text-neutral-700">
|
||||
Hide event type
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<div className="w-full">
|
||||
<div className="relative flex items-start">
|
||||
<div className="flex items-center h-5">
|
||||
<input
|
||||
|
@ -721,15 +723,15 @@ export default function EventTypePage({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
<div className="block sm:flex items-center">
|
||||
<div className="min-w-44 mb-4 sm:mb-0">
|
||||
<label
|
||||
htmlFor="requiresConfirmation"
|
||||
className="flex font-medium text-neutral-700">
|
||||
className="text-sm flex font-medium text-neutral-700">
|
||||
Opt-in booking
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<div className="w-full">
|
||||
<div className="relative flex items-start">
|
||||
<div className="flex items-center h-5">
|
||||
<input
|
||||
|
@ -750,15 +752,18 @@ export default function EventTypePage({
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
|
||||
<hr className="border-neutral-200" />
|
||||
|
||||
<div className="block sm:flex">
|
||||
<div className="min-w-44 mb-4 sm:mb-0">
|
||||
<label
|
||||
htmlFor="inviteesCanSchedule"
|
||||
className="flex font-medium text-neutral-700 mt-2">
|
||||
className="text-sm flex font-medium text-neutral-700 mt-2">
|
||||
Invitees can schedule
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<div className="w-full">
|
||||
<RadioGroup value={periodType} onChange={setPeriodType}>
|
||||
<RadioGroup.Label className="sr-only">Date Range</RadioGroup.Label>
|
||||
<div>
|
||||
|
@ -769,22 +774,22 @@ export default function EventTypePage({
|
|||
className={({ checked }) =>
|
||||
classnames(
|
||||
checked ? "border-secondary-200 z-10" : "border-gray-200",
|
||||
"relative min-h-14 lg:flex items-center cursor-pointer focus:outline-none"
|
||||
"relative min-h-14 flex items-center cursor-pointer focus:outline-none"
|
||||
)
|
||||
}>
|
||||
{({ active, checked }) => (
|
||||
<>
|
||||
<span
|
||||
<div
|
||||
className={classnames(
|
||||
checked
|
||||
? "bg-primary-600 border-transparent"
|
||||
: "bg-white border-gray-300",
|
||||
active ? "ring-2 ring-offset-2 ring-primary-500" : "",
|
||||
"h-4 w-4 mt-0.5 cursor-pointer rounded-full border flex items-center justify-center"
|
||||
"h-4 w-4 mt-0.5 mr-2 cursor-pointer rounded-full border items-center justify-center"
|
||||
)}
|
||||
aria-hidden="true">
|
||||
<span className="rounded-full bg-white w-1.5 h-1.5" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="lg:ml-3 flex flex-col">
|
||||
<RadioGroup.Label
|
||||
as="span"
|
||||
|
@ -849,13 +854,18 @@ export default function EventTypePage({
|
|||
</RadioGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="w-1/4">
|
||||
<label htmlFor="availability" className="flex font-medium text-neutral-700 mt-2">
|
||||
|
||||
<hr className="border-neutral-200" />
|
||||
|
||||
<div className="block sm:flex">
|
||||
<div className="min-w-44 mb-4 sm:mb-0">
|
||||
<label
|
||||
htmlFor="availability"
|
||||
className="text-sm flex font-medium text-neutral-700 mt-2">
|
||||
Availability
|
||||
</label>
|
||||
</div>
|
||||
<div className="w-3/4">
|
||||
<div className="w-full">
|
||||
<Scheduler
|
||||
setAvailability={setEnteredAvailability}
|
||||
setTimeZone={setSelectedTimeZone}
|
||||
|
@ -883,7 +893,7 @@ export default function EventTypePage({
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-2/12 ml-2 px-4">
|
||||
<div className="w-full sm:w-2/12 ml-2 px-4 mt-8 sm:mt-0 min-w-32">
|
||||
<div className="space-y-4">
|
||||
<a
|
||||
href={"/" + user.username + "/" + eventType.slug}
|
||||
|
@ -902,7 +912,7 @@ export default function EventTypePage({
|
|||
type="button"
|
||||
className="flex text-md font-medium text-neutral-700">
|
||||
<LinkIcon className="w-4 h-4 mt-1 mr-2 text-neutral-500" />
|
||||
Copy link to event
|
||||
Copy link
|
||||
</button>
|
||||
<button
|
||||
onClick={deleteEventTypeHandler}
|
||||
|
@ -916,13 +926,13 @@ export default function EventTypePage({
|
|||
</div>
|
||||
{showLocationModal && (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
className="fixed inset-0 bg-gray-500 z-0 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"></div>
|
||||
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
|
@ -966,13 +976,13 @@ export default function EventTypePage({
|
|||
)}
|
||||
{showAddCustomModal && (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
className="fixed inset-0 bg-gray-500 z-0 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import prisma from "../../lib/prisma";
|
||||
import Shell from "../../components/Shell";
|
||||
import { useRouter } from "next/router";
|
||||
import { getSession, useSession } from "next-auth/client";
|
||||
import { Fragment, useRef, useState } from "react";
|
||||
import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@components/Dialog";
|
||||
import { Tooltip } from "@components/Tooltip";
|
||||
import Loader from "@components/Loader";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
import {
|
||||
ClockIcon,
|
||||
|
@ -15,15 +11,18 @@ import {
|
|||
PlusIcon,
|
||||
UserIcon,
|
||||
} from "@heroicons/react/solid";
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
import classNames from "@lib/classNames";
|
||||
import { getSession, useSession } from "next-auth/client";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { Fragment, useRef } from "react";
|
||||
import Shell from "../../components/Shell";
|
||||
import prisma from "../../lib/prisma";
|
||||
|
||||
export default function Availability({ user, types }) {
|
||||
const [session, loading] = useSession();
|
||||
const router = useRouter();
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
|
||||
const titleRef = useRef<HTMLInputElement>();
|
||||
const slugRef = useRef<HTMLInputElement>();
|
||||
|
@ -39,7 +38,6 @@ export default function Availability({ user, types }) {
|
|||
const enteredLength = lengthRef.current.value;
|
||||
|
||||
// TODO: Add validation
|
||||
|
||||
await fetch("/api/availability/eventtype", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
|
@ -54,23 +52,120 @@ export default function Availability({ user, types }) {
|
|||
});
|
||||
|
||||
if (enteredTitle && enteredLength) {
|
||||
router.replace(router.asPath);
|
||||
toggleAddModal();
|
||||
await router.replace(router.asPath);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAddModal() {
|
||||
setShowAddModal(!showAddModal);
|
||||
function autoPopulateSlug() {
|
||||
let t = titleRef.current.value;
|
||||
t = t.replace(/\s+/g, "-").toLowerCase();
|
||||
slugRef.current.value = t;
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="loader">
|
||||
<span className="loader-inner"></span>
|
||||
</div>
|
||||
);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
const CreateNewEventDialog = () => (
|
||||
<Dialog>
|
||||
<DialogTrigger className="py-2 px-4 mt-6 border border-transparent rounded-sm shadow-sm text-sm font-medium text-white bg-neutral-900 hover:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900">
|
||||
<PlusIcon className="w-5 h-5 mr-1 inline" />
|
||||
New event type
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<div className="mb-8">
|
||||
<h3 className="text-lg leading-6 font-bold text-gray-900" id="modal-title">
|
||||
Add a new event type
|
||||
</h3>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Create a new event type for people to book times with.</p>
|
||||
</div>
|
||||
</div>
|
||||
<form onSubmit={createEventTypeHandler}>
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="title" className="block text-sm font-medium text-gray-700">
|
||||
Title
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<input
|
||||
onChange={autoPopulateSlug}
|
||||
ref={titleRef}
|
||||
type="text"
|
||||
name="title"
|
||||
id="title"
|
||||
required
|
||||
className="shadow-sm focus:ring-neutral-900 focus:border-neutral-900 block w-full sm:text-sm border-gray-300 rounded-sm"
|
||||
placeholder="Quick Chat"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="slug" className="block text-sm font-medium text-gray-700">
|
||||
URL
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<div className="flex rounded-sm shadow-sm">
|
||||
<span className="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
|
||||
{location.hostname}/{user.username}/
|
||||
</span>
|
||||
<input
|
||||
ref={slugRef}
|
||||
type="text"
|
||||
name="slug"
|
||||
id="slug"
|
||||
required
|
||||
className="flex-1 block w-full focus:ring-neutral-900 focus:border-neutral-900 min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="description" className="block text-sm font-medium text-gray-700">
|
||||
Description
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<textarea
|
||||
ref={descriptionRef}
|
||||
name="description"
|
||||
id="description"
|
||||
className="shadow-sm focus:ring-neutral-900 focus:border-neutral-900 block w-full sm:text-sm border-gray-300 rounded-sm"
|
||||
placeholder="A quick video meeting."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="length" className="block text-sm font-medium text-gray-700">
|
||||
Length
|
||||
</label>
|
||||
<div className="mt-1 relative rounded-sm shadow-sm">
|
||||
<input
|
||||
ref={lengthRef}
|
||||
type="number"
|
||||
name="length"
|
||||
id="length"
|
||||
required
|
||||
className="focus:ring-neutral-900 focus:border-neutral-900 block w-full pr-20 sm:text-sm border-gray-300 rounded-sm"
|
||||
placeholder="15"
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 text-sm">
|
||||
minutes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-8 sm:flex sm:flex-row-reverse">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Continue
|
||||
</button>
|
||||
<DialogClose as="button" className="btn btn-white mx-2">
|
||||
Cancel
|
||||
</DialogClose>
|
||||
</div>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
|
@ -80,23 +175,16 @@ export default function Availability({ user, types }) {
|
|||
<Shell
|
||||
heading="Event Types"
|
||||
subtitle="Create events to share for people to book on your calendar."
|
||||
CTA={
|
||||
<button
|
||||
onClick={toggleAddModal}
|
||||
className="flex justify-center py-2 px-4 border border-transparent rounded-sm shadow-sm text-sm font-medium text-white bg-neutral-900 hover:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900">
|
||||
<PlusIcon className="w-5 h-5 mr-1" />
|
||||
New event type
|
||||
</button>
|
||||
}>
|
||||
<div className="bg-white shadow overflow-hidden sm:rounded-sm">
|
||||
CTA={types.length !== 0 && <CreateNewEventDialog />}>
|
||||
<div className="bg-white border border-gray-200 rounded-sm overflow-hidden -mx-4 sm:mx-0">
|
||||
<ul className="divide-y divide-neutral-200">
|
||||
{types.map((type) => (
|
||||
<li key={type.id}>
|
||||
<Link href={"/event-types/" + type.id}>
|
||||
<a className="block hover:bg-neutral-50">
|
||||
<div className="px-4 py-4 flex items-center sm:px-6">
|
||||
<div className="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between">
|
||||
<div className="truncate">
|
||||
<div className="hover:bg-neutral-50">
|
||||
<div className="px-4 py-4 flex items-center sm:px-6">
|
||||
<Link href={"/event-types/" + type.id}>
|
||||
<a className="min-w-0 flex-1 sm:flex sm:items-center sm:justify-between">
|
||||
<span className="truncate ">
|
||||
<div className="flex text-sm">
|
||||
<p className="font-medium text-neutral-900 truncate">{type.title}</p>
|
||||
{type.hidden && (
|
||||
|
@ -108,164 +196,174 @@ export default function Availability({ user, types }) {
|
|||
<div className="mt-2 flex space-x-4">
|
||||
<div className="flex items-center text-sm text-neutral-500">
|
||||
<ClockIcon
|
||||
className="flex-shrink-0 mr-1.5 h-5 w-5 text-neutral-400"
|
||||
className="flex-shrink-0 mr-1.5 h-4 w-4 text-neutral-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p>{type.length}m</p>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-neutral-500">
|
||||
<UserIcon
|
||||
className="flex-shrink-0 mr-1.5 h-5 w-5 text-neutral-400"
|
||||
className="flex-shrink-0 mr-1.5 h-4 w-4 text-neutral-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p>1-on-1</p>
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-neutral-500">
|
||||
<InformationCircleIcon
|
||||
className="flex-shrink-0 mr-1.5 h-5 w-5 text-neutral-400"
|
||||
className="flex-shrink-0 mr-1.5 h-4 w-4 text-neutral-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<p>{type.description.substring(0, 100)}</p>
|
||||
<div className="max-w-32 sm:max-w-full truncate">
|
||||
{type.description.substring(0, 100)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4 flex-shrink-0 sm:mt-0 sm:ml-5">
|
||||
<div className="flex overflow-hidden space-x-5">
|
||||
<Link href={"/" + session.user.username + "/" + type.slug}>
|
||||
<a className="text-neutral-400">
|
||||
<ExternalLinkIcon className="w-5 h-5" />
|
||||
</a>
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
window.location.hostname + "/" + session.user.username + "/" + type.slug
|
||||
);
|
||||
}}
|
||||
className="text-neutral-400">
|
||||
<LinkIcon className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-5 flex-shrink-0">
|
||||
<Menu as="div" className="inline-block text-left">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div>
|
||||
<Menu.Button className="text-neutral-400 mt-1">
|
||||
<span className="sr-only">Open options</span>
|
||||
<DotsHorizontalIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</Menu.Button>
|
||||
</div>
|
||||
</span>
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<Transition
|
||||
show={open}
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95">
|
||||
<Menu.Items
|
||||
static
|
||||
className="origin-top-right absolute right-0 mt-2 w-56 rounded-sm shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none divide-y divide-neutral-100">
|
||||
<div className="py-1">
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
href={"/" + session.user.username + "/" + type.slug}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={classNames(
|
||||
active ? "bg-neutral-100 text-neutral-900" : "text-neutral-700",
|
||||
"group flex items-center px-4 py-2 text-sm font-medium"
|
||||
)}>
|
||||
<ExternalLinkIcon
|
||||
className="mr-3 h-5 w-5 text-neutral-400 group-hover:text-neutral-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Preview
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
window.location.hostname +
|
||||
"/" +
|
||||
session.user.username +
|
||||
"/" +
|
||||
type.slug
|
||||
);
|
||||
}}
|
||||
className={classNames(
|
||||
active ? "bg-neutral-100 text-neutral-900" : "text-neutral-700",
|
||||
"group flex items-center px-4 py-2 text-sm w-full font-medium"
|
||||
)}>
|
||||
<LinkIcon
|
||||
className="mr-3 h-5 w-5 text-neutral-400 group-hover:text-neutral-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Copy link to event
|
||||
</button>
|
||||
)}
|
||||
</Menu.Item>
|
||||
{/*<Menu.Item>*/}
|
||||
{/* {({ active }) => (*/}
|
||||
{/* <a*/}
|
||||
{/* href="#"*/}
|
||||
{/* className={classNames(*/}
|
||||
{/* active ? "bg-neutral-100 text-neutral-900" : "text-neutral-700",*/}
|
||||
{/* "group flex items-center px-4 py-2 text-sm font-medium"*/}
|
||||
{/* )}>*/}
|
||||
{/* <DuplicateIcon*/}
|
||||
{/* className="mr-3 h-5 w-5 text-neutral-400 group-hover:text-neutral-500"*/}
|
||||
{/* aria-hidden="true"*/}
|
||||
{/* />*/}
|
||||
{/* Duplicate*/}
|
||||
{/* </a>*/}
|
||||
{/* )}*/}
|
||||
{/*</Menu.Item>*/}
|
||||
</div>
|
||||
{/*<div className="py-1">*/}
|
||||
{/* <Menu.Item>*/}
|
||||
{/* {({ active }) => (*/}
|
||||
{/* <a*/}
|
||||
{/* href="#"*/}
|
||||
{/* className={classNames(*/}
|
||||
{/* active ? "bg-red-100 text-red-900" : "text-red-700",*/}
|
||||
{/* "group flex items-center px-4 py-2 text-sm font-medium"*/}
|
||||
{/* )}>*/}
|
||||
{/* <TrashIcon*/}
|
||||
{/* className="mr-3 h-5 w-5 text-red-400 group-hover:text-red-700"*/}
|
||||
{/* aria-hidden="true"*/}
|
||||
{/* />*/}
|
||||
{/* Delete*/}
|
||||
{/* </a>*/}
|
||||
{/* )}*/}
|
||||
{/* </Menu.Item>*/}
|
||||
{/*</div>*/}
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
<div className="hidden sm:flex mt-4 flex-shrink-0 sm:mt-0 sm:ml-5">
|
||||
<div className="flex overflow-hidden space-x-5">
|
||||
<Tooltip content="Preview">
|
||||
<a
|
||||
href={"/" + session.user.username + "/" + type.slug}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="group cursor-pointer text-neutral-400 p-2 border border-transparent hover:border-gray-200">
|
||||
<ExternalLinkIcon className="group-hover:text-black w-5 h-5" />
|
||||
</a>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="Copy link">
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
window.location.hostname + "/" + session.user.username + "/" + type.slug
|
||||
);
|
||||
}}
|
||||
className="group text-neutral-400 p-2 border border-transparent hover:border-gray-200">
|
||||
<LinkIcon className="group-hover:text-black w-5 h-5" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
<div className="flex sm:hidden ml-5 flex-shrink-0">
|
||||
<Menu as="div" className="inline-block text-left">
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div>
|
||||
<Menu.Button className="text-neutral-400 mt-1 p-2 border border-transparent hover:border-gray-200">
|
||||
<span className="sr-only">Open options</span>
|
||||
<DotsHorizontalIcon className="h-5 w-5" aria-hidden="true" />
|
||||
</Menu.Button>
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
show={open}
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95">
|
||||
<Menu.Items
|
||||
static
|
||||
className="origin-top-right absolute right-0 mt-2 w-56 rounded-sm shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none divide-y divide-neutral-100">
|
||||
<div className="py-1">
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<a
|
||||
href={"/" + session.user.username + "/" + type.slug}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={classNames(
|
||||
active ? "bg-neutral-100 text-neutral-900" : "text-neutral-700",
|
||||
"group flex items-center px-4 py-2 text-sm font-medium"
|
||||
)}>
|
||||
<ExternalLinkIcon
|
||||
className="mr-3 h-4 w-4 text-neutral-400 group-hover:text-neutral-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Preview
|
||||
</a>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
window.location.hostname +
|
||||
"/" +
|
||||
session.user.username +
|
||||
"/" +
|
||||
type.slug
|
||||
);
|
||||
}}
|
||||
className={classNames(
|
||||
active ? "bg-neutral-100 text-neutral-900" : "text-neutral-700",
|
||||
"group flex items-center px-4 py-2 text-sm w-full font-medium"
|
||||
)}>
|
||||
<LinkIcon
|
||||
className="mr-3 h-4 w-4 text-neutral-400 group-hover:text-neutral-500"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
Copy link to event
|
||||
</button>
|
||||
)}
|
||||
</Menu.Item>
|
||||
{/*<Menu.Item>*/}
|
||||
{/* {({ active }) => (*/}
|
||||
{/* <a*/}
|
||||
{/* href="#"*/}
|
||||
{/* className={classNames(*/}
|
||||
{/* active ? "bg-neutral-100 text-neutral-900" : "text-neutral-700",*/}
|
||||
{/* "group flex items-center px-4 py-2 text-sm font-medium"*/}
|
||||
{/* )}>*/}
|
||||
{/* <DuplicateIcon*/}
|
||||
{/* className="mr-3 h-4 w-4 text-neutral-400 group-hover:text-neutral-500"*/}
|
||||
{/* aria-hidden="true"*/}
|
||||
{/* />*/}
|
||||
{/* Duplicate*/}
|
||||
{/* </a>*/}
|
||||
{/* )}*/}
|
||||
{/*</Menu.Item>*/}
|
||||
</div>
|
||||
{/*<div className="py-1">*/}
|
||||
{/* <Menu.Item>*/}
|
||||
{/* {({ active }) => (*/}
|
||||
{/* <a*/}
|
||||
{/* href="#"*/}
|
||||
{/* className={classNames(*/}
|
||||
{/* active ? "bg-red-100 text-red-900" : "text-red-700",*/}
|
||||
{/* "group flex items-center px-4 py-2 text-sm font-medium"*/}
|
||||
{/* )}>*/}
|
||||
{/* <TrashIcon*/}
|
||||
{/* className="mr-3 h-5 w-5 text-red-400 group-hover:text-red-700"*/}
|
||||
{/* aria-hidden="true"*/}
|
||||
{/* />*/}
|
||||
{/* Delete*/}
|
||||
{/* </a>*/}
|
||||
{/* )}*/}
|
||||
{/* </Menu.Item>*/}
|
||||
{/*</div>*/}
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</>
|
||||
)}
|
||||
</Menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
{types.length === 0 && (
|
||||
<div className="text-center max-w-lg mx-auto">
|
||||
<div className="md:py-20">
|
||||
<svg
|
||||
className="mx-auto mb-4 w-32 h-32"
|
||||
className="w-1/2 md:w-32 mx-auto block mb-4"
|
||||
viewBox="0 0 132 132"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
|
@ -502,122 +600,13 @@ export default function Availability({ user, types }) {
|
|||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<h3 className="mt-2 text-xl font-bold text-neutral-900">Create your first event type</h3>
|
||||
<p className="mt-1 text-md text-neutral-600">
|
||||
Event types enable you to share links that show available times on your calendar and allow
|
||||
people to make bookings with you.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{showAddModal && (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"></div>
|
||||
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
</span>
|
||||
|
||||
<div className="inline-block align-bottom bg-white rounded-sm px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6">
|
||||
<div className="mb-4">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
||||
Add a new event type
|
||||
</h3>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">
|
||||
Create a new event type for people to book times with.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<form onSubmit={createEventTypeHandler}>
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="title" className="block text-sm font-medium text-gray-700">
|
||||
Title
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<input
|
||||
ref={titleRef}
|
||||
type="text"
|
||||
name="title"
|
||||
id="title"
|
||||
required
|
||||
className="shadow-sm focus:ring-neutral-900 focus:border-neutral-900 block w-full sm:text-sm border-gray-300 rounded-sm"
|
||||
placeholder="Quick Chat"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="slug" className="block text-sm font-medium text-gray-700">
|
||||
URL
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<div className="flex rounded-sm shadow-sm">
|
||||
<span className="inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm">
|
||||
{location.hostname}/{user.username}/
|
||||
</span>
|
||||
<input
|
||||
ref={slugRef}
|
||||
type="text"
|
||||
name="slug"
|
||||
id="slug"
|
||||
required
|
||||
className="flex-1 block w-full focus:ring-neutral-900 focus:border-neutral-900 min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="description" className="block text-sm font-medium text-gray-700">
|
||||
Description
|
||||
</label>
|
||||
<div className="mt-1">
|
||||
<textarea
|
||||
ref={descriptionRef}
|
||||
name="description"
|
||||
id="description"
|
||||
className="shadow-sm focus:ring-neutral-900 focus:border-neutral-900 block w-full sm:text-sm border-gray-300 rounded-sm"
|
||||
placeholder="A quick video meeting."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="length" className="block text-sm font-medium text-gray-700">
|
||||
Length
|
||||
</label>
|
||||
<div className="mt-1 relative rounded-sm shadow-sm">
|
||||
<input
|
||||
ref={lengthRef}
|
||||
type="number"
|
||||
name="length"
|
||||
id="length"
|
||||
required
|
||||
className="focus:ring-neutral-900 focus:border-neutral-900 block w-full pr-20 sm:text-sm border-gray-300 rounded-sm"
|
||||
placeholder="15"
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 text-sm">
|
||||
minutes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* TODO: Add an error message when required input fields empty*/}
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<button type="submit" className="btn btn-primary">
|
||||
Create
|
||||
</button>
|
||||
<button onClick={toggleAddModal} type="button" className="btn btn-white mr-2">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div className="text-center block md:max-w-screen-sm mx-auto">
|
||||
<h3 className="mt-2 text-xl font-bold text-neutral-900">Create your first event type</h3>
|
||||
<p className="mt-1 text-md text-neutral-600">
|
||||
Event types enable you to share links that show available times on your calendar and allow
|
||||
people to make bookings with you.
|
||||
</p>
|
||||
<CreateNewEventDialog />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import Loader from "@components/Loader";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
function RedirectPage() {
|
||||
|
@ -6,6 +7,7 @@ function RedirectPage() {
|
|||
router.push("/event-types");
|
||||
return;
|
||||
}
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
RedirectPage.getInitialProps = (ctx) => {
|
||||
|
|
|
@ -4,19 +4,18 @@ import { getIntegrationName, getIntegrationType } from "../../lib/integrations";
|
|||
import Shell from "../../components/Shell";
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useSession } from "next-auth/client";
|
||||
import { getSession, useSession } from "next-auth/client";
|
||||
import Loader from "@components/Loader";
|
||||
|
||||
export default function Integration(props) {
|
||||
const router = useRouter();
|
||||
const [, loading] = useSession();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [session, loading] = useSession();
|
||||
|
||||
const [showAPIKey, setShowAPIKey] = useState(false);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="loader">
|
||||
<span className="loader-inner"></span>
|
||||
</div>
|
||||
);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
function toggleShowAPIKey() {
|
||||
|
@ -26,13 +25,15 @@ export default function Integration(props) {
|
|||
async function deleteIntegrationHandler(event) {
|
||||
event.preventDefault();
|
||||
|
||||
await fetch("/api/integrations", {
|
||||
/*eslint-disable */
|
||||
const response = await fetch("/api/integrations", {
|
||||
method: "DELETE",
|
||||
body: JSON.stringify({ id: props.integration.id }),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
/*eslint-enable */
|
||||
|
||||
router.push("/integrations");
|
||||
}
|
||||
|
@ -40,27 +41,27 @@ export default function Integration(props) {
|
|||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<title>{getIntegrationName(props.integration.type)} | Integrations | Calendso</title>
|
||||
<title>{getIntegrationName(props.integration.type)} App | Calendso</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<Shell heading={getIntegrationName(props.integration.type)} subtitle="Manage and delete integrations.">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="col-span-2 bg-white shadow overflow-hidden rounded-sm">
|
||||
<Shell heading={getIntegrationName(props.integration.type)} subtitle="Manage and delete this app.">
|
||||
<div className="block sm:grid grid-cols-3 gap-4">
|
||||
<div className="col-span-2 bg-white border border-gray-200 mb-6 overflow-hidden rounded-sm">
|
||||
<div className="px-4 py-5 sm:px-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Integration Details</h3>
|
||||
<p className="mt-1 max-w-2xl text-sm text-gray-500">
|
||||
Information about your {getIntegrationName(props.integration.type)} integration.
|
||||
Information about your {getIntegrationName(props.integration.type)} App.
|
||||
</p>
|
||||
</div>
|
||||
<div className="border-t border-gray-200 px-4 py-5 sm:px-6">
|
||||
<dl className="grid gap-y-8">
|
||||
<div>
|
||||
<dt className="text-sm font-medium text-gray-500">Integration name</dt>
|
||||
<dt className="text-sm font-medium text-gray-500">App name</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900">{getIntegrationName(props.integration.type)}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt className="text-sm font-medium text-gray-500">Integration type</dt>
|
||||
<dt className="text-sm font-medium text-gray-500">App Category</dt>
|
||||
<dd className="mt-1 text-sm text-gray-900">{getIntegrationType(props.integration.type)}</dd>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -90,18 +91,18 @@ export default function Integration(props) {
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="bg-white shadow rounded-sm">
|
||||
<div className="bg-white border border-gray-200 mb-6 rounded-sm">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Delete this integration</h3>
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Delete this app</h3>
|
||||
<div className="mt-2 max-w-xl text-sm text-gray-500">
|
||||
<p>Once you delete this integration, it will be permanently removed.</p>
|
||||
<p>Once you delete this app, it will be permanently removed.</p>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<button
|
||||
onClick={deleteIntegrationHandler}
|
||||
type="button"
|
||||
className="inline-flex items-center justify-center px-4 py-2 border border-transparent font-medium rounded-sm text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:text-sm">
|
||||
Delete integration
|
||||
Delete App
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -114,7 +115,8 @@ export default function Integration(props) {
|
|||
}
|
||||
|
||||
export async function getServerSideProps(context) {
|
||||
//const session = await getSession(context); Session is never used right now
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const session = await getSession(context);
|
||||
|
||||
const integration = await prisma.credential.findFirst({
|
||||
where: {
|
||||
|
|
|
@ -4,31 +4,18 @@ import prisma from "../../lib/prisma";
|
|||
import Shell from "../../components/Shell";
|
||||
import { useEffect, useState } from "react";
|
||||
import { getSession, useSession } from "next-auth/client";
|
||||
import {
|
||||
CalendarIcon,
|
||||
CheckCircleIcon,
|
||||
ChevronRightIcon,
|
||||
PlusIcon,
|
||||
XCircleIcon,
|
||||
} from "@heroicons/react/solid";
|
||||
import { CheckCircleIcon, ChevronRightIcon, PlusIcon, XCircleIcon } from "@heroicons/react/solid";
|
||||
import { InformationCircleIcon } from "@heroicons/react/outline";
|
||||
import { Switch } from "@headlessui/react";
|
||||
import Loader from "@components/Loader";
|
||||
import classNames from "@lib/classNames";
|
||||
import { Dialog, DialogClose, DialogContent, DialogHeader, DialogTrigger } from "@components/Dialog";
|
||||
|
||||
export default function Home({ integrations }) {
|
||||
const [, loading] = useSession();
|
||||
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
const [showSelectCalendarModal, setShowSelectCalendarModal] = useState(false);
|
||||
export default function IntegrationHome({ integrations }) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [session, loading] = useSession();
|
||||
const [selectableCalendars, setSelectableCalendars] = useState([]);
|
||||
|
||||
function toggleAddModal() {
|
||||
setShowAddModal(!showAddModal);
|
||||
}
|
||||
|
||||
function toggleShowCalendarModal() {
|
||||
setShowSelectCalendarModal(!showSelectCalendarModal);
|
||||
}
|
||||
|
||||
function loadCalendars() {
|
||||
fetch("api/availability/calendar")
|
||||
.then((response) => response.json())
|
||||
|
@ -80,40 +67,119 @@ export default function Home({ integrations }) {
|
|||
}
|
||||
}
|
||||
|
||||
function classNames(...classes) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
useEffect(loadCalendars, [integrations]);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="loader">
|
||||
<span className="loader-inner"></span>
|
||||
</div>
|
||||
);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
const ConnectNewAppDialog = () => (
|
||||
<Dialog>
|
||||
<DialogTrigger className="py-2 px-4 mt-6 border border-transparent rounded-sm shadow-sm text-sm font-medium text-white bg-neutral-900 hover:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900">
|
||||
<PlusIcon className="w-5 h-5 mr-1 inline" />
|
||||
Connect a new App
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader title="Connect a new App" subtitle="Connect a new app to your account." />
|
||||
<div className="my-4">
|
||||
<ul className="divide-y divide-gray-200">
|
||||
{integrations
|
||||
.filter((integration) => integration.installed)
|
||||
.map((integration) => (
|
||||
<li key={integration.type} className="flex py-4">
|
||||
<div className="w-1/12 mr-4 pt-2">
|
||||
<img className="h-8 w-8 mr-2" src={integration.imageSrc} alt={integration.title} />
|
||||
</div>
|
||||
<div className="w-10/12">
|
||||
<h2 className="text-gray-800 font-medium">{integration.title}</h2>
|
||||
<p className="text-gray-400 text-sm">{integration.description}</p>
|
||||
</div>
|
||||
<div className="w-2/12 text-right pt-2">
|
||||
<button
|
||||
onClick={() => integrationHandler(integration.type)}
|
||||
className="font-medium text-neutral-900 hover:text-neutral-500">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<DialogClose as="button" className="btn btn-white mx-2">
|
||||
Cancel
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
const SelectCalendarDialog = () => (
|
||||
<Dialog>
|
||||
<DialogTrigger className="py-2 px-4 mt-6 border border-transparent rounded-sm shadow-sm text-sm font-medium text-white bg-neutral-900 hover:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900">
|
||||
Select calendars
|
||||
</DialogTrigger>
|
||||
|
||||
<DialogContent>
|
||||
<DialogHeader
|
||||
title="Select calendars"
|
||||
subtitle="If no entry is selected, all calendars will be checked"
|
||||
/>
|
||||
<div className="my-4">
|
||||
<ul className="divide-y divide-gray-200">
|
||||
{selectableCalendars.map((calendar) => (
|
||||
<li key={calendar.name} className="flex py-4">
|
||||
<div className="w-1/12 mr-4 pt-2">
|
||||
<img
|
||||
className="h-8 w-8 mr-2"
|
||||
src={getCalendarIntegrationImage(calendar.integration)}
|
||||
alt={calendar.integration}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-10/12 pt-3">
|
||||
<h2 className="text-gray-800 font-medium">{calendar.name}</h2>
|
||||
</div>
|
||||
<div className="w-2/12 text-right pt-3">
|
||||
<Switch
|
||||
checked={calendar.selected}
|
||||
onChange={calendarSelectionHandler(calendar)}
|
||||
className={classNames(
|
||||
calendar.selected ? "bg-neutral-900" : "bg-gray-200",
|
||||
"relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-500"
|
||||
)}>
|
||||
<span className="sr-only">Select calendar</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={classNames(
|
||||
calendar.selected ? "translate-x-5" : "translate-x-0",
|
||||
"pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<DialogClose as="button" className="btn btn-white mx-2">
|
||||
Cancel
|
||||
</DialogClose>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Head>
|
||||
<title>Integrations | Calendso</title>
|
||||
<title>App Store | Calendso</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<Shell
|
||||
heading="Integrations"
|
||||
subtitle="Connect your favourite apps."
|
||||
CTA={
|
||||
<button
|
||||
onClick={toggleAddModal}
|
||||
type="button"
|
||||
className="flex justify-center py-2 px-4 border border-transparent rounded-sm shadow-sm text-sm font-medium text-white bg-neutral-900 hover:bg-neutral-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-900">
|
||||
<PlusIcon className="w-5 h-5 mr-1" />
|
||||
Add new integration
|
||||
</button>
|
||||
}>
|
||||
<div className="bg-white shadow overflow-hidden rounded-sm mb-8">
|
||||
<Shell heading="App Store" subtitle="Connect your favourite apps." CTA={<ConnectNewAppDialog />}>
|
||||
<div className="bg-white border border-gray-200 overflow-hidden rounded-sm mb-8">
|
||||
{integrations.filter((ig) => ig.credential).length !== 0 ? (
|
||||
<ul className="divide-y divide-gray-200">
|
||||
{integrations
|
||||
|
@ -172,224 +238,41 @@ export default function Home({ integrations }) {
|
|||
</div>
|
||||
<div className="py-5 sm:p-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
||||
You don't have any integrations added.
|
||||
You don't have any apps connected.
|
||||
</h3>
|
||||
<div className="mt-2 text-sm text-gray-500">
|
||||
<p>
|
||||
You currently do not have any integrations set up. Add your first integration to get
|
||||
started.
|
||||
You currently do not have any apps connected. Connect your first app to get started.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-3 text-sm">
|
||||
<button
|
||||
onClick={toggleAddModal}
|
||||
className="font-medium text-neutral-900 hover:text-neutral-500">
|
||||
{" "}
|
||||
Add your first integration <span aria-hidden="true">→</span>
|
||||
</button>
|
||||
</div>
|
||||
<ConnectNewAppDialog />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{showAddModal && (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
{/* <!--
|
||||
Background overlay, show/hide based on modal state.
|
||||
|
||||
Entering: "ease-out duration-300"
|
||||
From: "opacity-0"
|
||||
To: "opacity-100"
|
||||
Leaving: "ease-in duration-200"
|
||||
From: "opacity-100"
|
||||
To: "opacity-0"
|
||||
--> */}
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"></div>
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
</span>
|
||||
{/* <!--
|
||||
Modal panel, show/hide based on modal state.
|
||||
|
||||
Entering: "ease-out duration-300"
|
||||
From: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
To: "opacity-100 translate-y-0 sm:scale-100"
|
||||
Leaving: "ease-in duration-200"
|
||||
From: "opacity-100 translate-y-0 sm:scale-100"
|
||||
To: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
--> */}
|
||||
<div className="inline-block align-bottom bg-white rounded-sm px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-neutral-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<PlusIcon className="h-6 w-6 text-neutral-900" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
||||
Add a new integration
|
||||
</h3>
|
||||
<div>
|
||||
<p className="text-sm text-gray-400">Link a new integration to your account.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<ul className="divide-y divide-gray-200">
|
||||
{integrations
|
||||
.filter((integration) => integration.installed)
|
||||
.map((integration) => (
|
||||
<li key={integration.type} className="flex py-4">
|
||||
<div className="w-1/12 mr-4 pt-2">
|
||||
<img
|
||||
className="h-8 w-8 mr-2"
|
||||
src={integration.imageSrc}
|
||||
alt={integration.title}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-10/12">
|
||||
<h2 className="text-gray-800 font-medium">{integration.title}</h2>
|
||||
<p className="text-gray-400 text-sm">{integration.description}</p>
|
||||
</div>
|
||||
<div className="w-2/12 text-right pt-2">
|
||||
<button
|
||||
onClick={() => integrationHandler(integration.type)}
|
||||
className="font-medium text-neutral-900 hover:text-neutral-500">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
onClick={toggleAddModal}
|
||||
type="button"
|
||||
className="mt-3 w-full inline-flex justify-center rounded-sm border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-500 sm:mt-0 sm:w-auto sm:text-sm">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="bg-white shadow rounded-sm">
|
||||
<div className="bg-white border border-gray-200 rounded-sm mb-8">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Select calendars</h3>
|
||||
<div className="mt-2 max-w-xl text-sm text-gray-500">
|
||||
<p>Select which calendars are checked for availability to prevent double bookings.</p>
|
||||
</div>
|
||||
<SelectCalendarDialog />
|
||||
</div>
|
||||
</div>
|
||||
<div className="border border-gray-200 rounded-sm">
|
||||
<div className="px-4 py-5 sm:p-6">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">Launch your own App</h3>
|
||||
<div className="mt-2 max-w-xl text-sm text-gray-500">
|
||||
<p>If you want to add your own App here, get in touch with us.</p>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<button type="button" onClick={toggleShowCalendarModal} className="btn btn-primary">
|
||||
Select calendars
|
||||
</button>
|
||||
<a href="apps@calendso.com" className="btn btn-white">
|
||||
Contact us
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{showSelectCalendarModal && (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
{/* <!--
|
||||
Background overlay, show/hide based on modal state.
|
||||
|
||||
Entering: "ease-out duration-300"
|
||||
From: "opacity-0"
|
||||
To: "opacity-100"
|
||||
Leaving: "ease-in duration-200"
|
||||
From: "opacity-100"
|
||||
To: "opacity-0"
|
||||
--> */}
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"></div>
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
</span>
|
||||
{/* <!--
|
||||
Modal panel, show/hide based on modal state.
|
||||
|
||||
Entering: "ease-out duration-300"
|
||||
From: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
To: "opacity-100 translate-y-0 sm:scale-100"
|
||||
Leaving: "ease-in duration-200"
|
||||
From: "opacity-100 translate-y-0 sm:scale-100"
|
||||
To: "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
--> */}
|
||||
<div className="inline-block align-bottom bg-white rounded-sm px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:p-6">
|
||||
<div className="sm:flex sm:items-start">
|
||||
<div className="mx-auto flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-neutral-100 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<CalendarIcon className="h-6 w-6 text-neutral-900" />
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900" id="modal-title">
|
||||
Select calendars
|
||||
</h3>
|
||||
<div>
|
||||
<p className="text-sm text-gray-400">
|
||||
If no entry is selected, all calendars will be checked
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<ul className="divide-y divide-gray-200">
|
||||
{selectableCalendars.map((calendar) => (
|
||||
<li key={calendar.name} className="flex py-4">
|
||||
<div className="w-1/12 mr-4 pt-2">
|
||||
<img
|
||||
className="h-8 w-8 mr-2"
|
||||
src={getCalendarIntegrationImage(calendar.integration)}
|
||||
alt={calendar.integration}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-10/12 pt-3">
|
||||
<h2 className="text-gray-800 font-medium">{calendar.name}</h2>
|
||||
</div>
|
||||
<div className="w-2/12 text-right pt-3">
|
||||
<Switch
|
||||
checked={calendar.selected}
|
||||
onChange={calendarSelectionHandler(calendar)}
|
||||
className={classNames(
|
||||
calendar.selected ? "bg-neutral-900" : "bg-gray-200",
|
||||
"relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-500"
|
||||
)}>
|
||||
<span className="sr-only">Select calendar</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={classNames(
|
||||
calendar.selected ? "translate-x-5" : "translate-x-0",
|
||||
"pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
|
||||
<button
|
||||
onClick={toggleShowCalendarModal}
|
||||
type="button"
|
||||
className="mt-3 w-full inline-flex justify-center rounded-sm border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-neutral-500 sm:mt-0 sm:w-auto sm:text-sm">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Shell>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -3,17 +3,14 @@ import prisma from "../../lib/prisma";
|
|||
import Shell from "../../components/Shell";
|
||||
import SettingsShell from "../../components/Settings";
|
||||
import { getSession, useSession } from "next-auth/client";
|
||||
import Loader from "@components/Loader";
|
||||
|
||||
export default function Embed(props) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [session, loading] = useSession();
|
||||
//const router = useRouter(); Unused
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="loader">
|
||||
<span className="loader-inner"></span>
|
||||
</div>
|
||||
);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -5,19 +5,18 @@ import Modal from "../../components/Modal";
|
|||
import Shell from "../../components/Shell";
|
||||
import SettingsShell from "../../components/Settings";
|
||||
import { getSession, useSession } from "next-auth/client";
|
||||
import Loader from "@components/Loader";
|
||||
|
||||
export default function Settings() {
|
||||
const [, loading] = useSession();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [session, loading] = useSession();
|
||||
|
||||
const [successModalOpen, setSuccessModalOpen] = useState(false);
|
||||
const oldPasswordRef = useRef<HTMLInputElement>();
|
||||
const newPasswordRef = useRef<HTMLInputElement>();
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="loader">
|
||||
<span className="loader-inner"></span>
|
||||
</div>
|
||||
);
|
||||
return <Loader />;
|
||||
}
|
||||
|
||||
const closeSuccessModal = () => {
|
||||
|
@ -32,19 +31,21 @@ export default function Settings() {
|
|||
|
||||
// TODO: Add validation
|
||||
|
||||
await fetch("/api/auth/changepw", {
|
||||
/*eslint-disable */
|
||||
const response = await fetch("/api/auth/changepw", {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify({ oldPassword: enteredOldPassword, newPassword: enteredNewPassword }),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
/*eslint-enable */
|
||||
|
||||
setSuccessModalOpen(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<Shell heading="Password" subtitle="Change the password that you use to sign in.">
|
||||
<Shell heading="Password" subtitle="Change the password that you use to sign in to your account.">
|
||||
<Head>
|
||||
<title>Change Password | Calendso</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
|
|
|
@ -12,6 +12,11 @@ import TimezoneSelect from "react-timezone-select";
|
|||
import { UsernameInput } from "../../components/ui/UsernameInput";
|
||||
import ErrorAlert from "../../components/ui/alerts/Error";
|
||||
|
||||
const themeOptions = [
|
||||
{ value: "light", label: "Light" },
|
||||
{ value: "dark", label: "Dark" },
|
||||
];
|
||||
|
||||
export default function Settings(props) {
|
||||
const [successModalOpen, setSuccessModalOpen] = useState(false);
|
||||
const usernameRef = useRef<HTMLInputElement>();
|
||||
|
@ -19,18 +24,13 @@ export default function Settings(props) {
|
|||
const descriptionRef = useRef<HTMLTextAreaElement>();
|
||||
const avatarRef = useRef<HTMLInputElement>();
|
||||
const hideBrandingRef = useRef<HTMLInputElement>();
|
||||
const [selectedTheme, setSelectedTheme] = useState({ value: "" });
|
||||
const [selectedTheme, setSelectedTheme] = useState({ value: props.user.theme });
|
||||
const [selectedTimeZone, setSelectedTimeZone] = useState({ value: props.user.timeZone });
|
||||
const [selectedWeekStartDay, setSelectedWeekStartDay] = useState({ value: "" });
|
||||
const [selectedWeekStartDay, setSelectedWeekStartDay] = useState({ value: props.user.weekStart });
|
||||
|
||||
const [hasErrors, setHasErrors] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
const themeOptions = [
|
||||
{ value: "light", label: "Light" },
|
||||
{ value: "dark", label: "Dark" },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedTheme(
|
||||
props.user.theme ? themeOptions.find((theme) => theme.value === props.user.theme) : null
|
||||
|
@ -179,6 +179,7 @@ export default function Settings(props) {
|
|||
id="theme"
|
||||
isDisabled={!selectedTheme}
|
||||
defaultValue={selectedTheme || themeOptions[0]}
|
||||
value={selectedTheme || themeOptions[0]}
|
||||
onChange={setSelectedTheme}
|
||||
className="shadow-sm focus:ring-neutral-500 focus:border-neutral-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-sm"
|
||||
options={themeOptions}
|
||||
|
|
|
@ -126,13 +126,13 @@ export default function Teams() {
|
|||
</div>
|
||||
{showCreateTeamModal && (
|
||||
<div
|
||||
className="fixed z-10 inset-0 overflow-y-auto"
|
||||
className="fixed z-50 inset-0 overflow-y-auto"
|
||||
aria-labelledby="modal-title"
|
||||
role="dialog"
|
||||
aria-modal="true">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div
|
||||
className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
|
||||
className="fixed inset-0 bg-gray-500 z-0 bg-opacity-75 transition-opacity"
|
||||
aria-hidden="true"></div>
|
||||
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
|
|
|
@ -4,7 +4,7 @@ import prisma, { whereAndSelect } from "../lib/prisma";
|
|||
import { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { CheckIcon } from "@heroicons/react/outline";
|
||||
import { CalendarIcon, ClockIcon, LocationMarkerIcon } from "@heroicons/react/solid";
|
||||
import { ClockIcon } from "@heroicons/react/solid";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import toArray from "dayjs/plugin/toArray";
|
||||
|
@ -60,7 +60,7 @@ export default function Success(props) {
|
|||
|
||||
return (
|
||||
isReady && (
|
||||
<div>
|
||||
<div className="bg-neutral-50 dark:bg-neutral-900 h-screen">
|
||||
<Head>
|
||||
<title>
|
||||
Booking {props.eventType.requiresConfirmation ? "Submitted" : "Confirmed"} | {eventName} |
|
||||
|
@ -68,69 +68,69 @@ export default function Success(props) {
|
|||
</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
<main className="max-w-3xl mx-auto my-24">
|
||||
<div className="fixed z-10 inset-0 overflow-y-auto">
|
||||
<main className="max-w-3xl mx-auto py-24">
|
||||
<div className="fixed z-50 inset-0 overflow-y-auto">
|
||||
<div className="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
||||
<div className="fixed inset-0 my-4 sm:my-0 transition-opacity" aria-hidden="true">
|
||||
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||
​
|
||||
</span>
|
||||
<div
|
||||
className="inline-block align-bottom dark:bg-gray-800 bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6"
|
||||
className="inline-block align-bottom dark:bg-gray-800 bg-white rounded-sm px-8 pt-5 pb-4 text-left overflow-hidden border border-neutral-200 dark:border-neutral-700 transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full sm:py-6"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="modal-headline">
|
||||
<div>
|
||||
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100">
|
||||
{!props.eventType.requiresConfirmation && (
|
||||
<CheckIcon className="h-6 w-6 text-green-600" />
|
||||
<CheckIcon className="h-8 w-8 text-green-600" />
|
||||
)}
|
||||
{props.eventType.requiresConfirmation && (
|
||||
<ClockIcon className="h-6 w-6 text-green-600" />
|
||||
<ClockIcon className="h-8 w-8 text-green-600" />
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-3 text-center sm:mt-5">
|
||||
<h3
|
||||
className="text-lg leading-6 font-medium dark:text-white text-gray-900"
|
||||
className="text-2xl leading-6 font-semibold dark:text-white text-neutral-900"
|
||||
id="modal-headline">
|
||||
Booking {props.eventType.requiresConfirmation ? "Submitted" : "Confirmed"}
|
||||
{props.eventType.requiresConfirmation ? "Submitted" : "This meeting is scheduled"}
|
||||
</h3>
|
||||
<div className="mt-2">
|
||||
<p className="text-sm text-gray-500 dark:text-gray-300">
|
||||
<div className="mt-3">
|
||||
<p className="text-sm text-neutral-600 dark:text-gray-300">
|
||||
{props.eventType.requiresConfirmation
|
||||
? `${
|
||||
props.user.name || props.user.username
|
||||
} still needs to confirm or reject the booking.`
|
||||
: `You are scheduled in with ${props.user.name || props.user.username}.`}
|
||||
: `We emailed you and the other attendees a calendar invitation with all the details.`}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-4 border-t border-b dark:border-gray-900 py-4">
|
||||
<h2 className="text-lg font-medium text-gray-600 dark:text-gray-100 mb-2">
|
||||
{eventName}
|
||||
</h2>
|
||||
<p className="text-gray-500 dark:text-gray-50 mb-1">
|
||||
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||
{props.eventType.length} minutes
|
||||
</p>
|
||||
<div className="mt-4 text-gray-700 dark:text-gray-300 border-t border-b dark:border-gray-900 py-4 grid grid-cols-3 text-left">
|
||||
<div className="font-medium">What</div>
|
||||
<div className="mb-6 col-span-2">{eventName}</div>
|
||||
<div className="font-medium">When</div>
|
||||
<div className="mb-6 col-span-2">
|
||||
{date.format("dddd, DD MMMM YYYY")}
|
||||
<br />
|
||||
{date.format(is24h ? "H:mm" : "h:mma")} - {props.eventType.length} mins{" "}
|
||||
<span className="text-gray-500">
|
||||
({localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()})
|
||||
</span>
|
||||
</div>
|
||||
{location && (
|
||||
<p className="text-gray-500 mb-1">
|
||||
<LocationMarkerIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||
{location}
|
||||
</p>
|
||||
<>
|
||||
<div className="font-medium">Where</div>
|
||||
<div className="col-span-2">{location}</div>
|
||||
</>
|
||||
)}
|
||||
<p className="text-gray-500 dark:text-gray-50">
|
||||
<CalendarIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||
{date.format((is24h ? "H:mm" : "h:mma") + ", dddd DD MMMM YYYY")}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!props.eventType.requiresConfirmation && (
|
||||
<div className="mt-5 sm:mt-0 pt-2 text-center">
|
||||
<span className="font-medium text-gray-500 dark:text-gray-50">
|
||||
Add to your calendar
|
||||
<div className="mt-5 sm:mt-0 sm:pt-4 pt-2 text-center flex">
|
||||
<span className="font-medium text-gray-700 dark:text-gray-50 flex self-center mr-6">
|
||||
Add to calendar
|
||||
</span>
|
||||
<div className="flex mt-2">
|
||||
<div className="flex">
|
||||
<Link
|
||||
href={
|
||||
`https://calendar.google.com/calendar/r/eventedit?dates=${date
|
||||
|
@ -142,9 +142,9 @@ export default function Success(props) {
|
|||
props.eventType.description
|
||||
}` + (location ? "&location=" + encodeURIComponent(location) : "")
|
||||
}>
|
||||
<a className="mx-2 btn-wide btn-white">
|
||||
<a className="mx-2 rounded-sm border border-neutral-200 dark:border-neutral-700 py-2 px-3">
|
||||
<svg
|
||||
className="inline-block w-4 h-4 mr-1 -mt-1"
|
||||
className="inline-block w-4 h-4 -mt-1"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24">
|
||||
|
@ -166,7 +166,9 @@ export default function Success(props) {
|
|||
eventName
|
||||
) + (location ? "&location=" + location : "")
|
||||
}>
|
||||
<a className="mx-2 btn-wide btn-white" target="_blank">
|
||||
<a
|
||||
className="mx-2 rounded-sm border border-neutral-200 dark:border-neutral-700 py-2 px-3"
|
||||
target="_blank">
|
||||
<svg
|
||||
className="inline-block w-4 h-4 mr-1 -mt-1"
|
||||
fill="currentColor"
|
||||
|
@ -190,7 +192,9 @@ export default function Success(props) {
|
|||
eventName
|
||||
) + (location ? "&location=" + location : "")
|
||||
}>
|
||||
<a className="mx-2 btn-wide btn-white" target="_blank">
|
||||
<a
|
||||
className="mx-2 rounded-sm border border-neutral-200 dark:border-neutral-700 py-2 px-3"
|
||||
target="_blank">
|
||||
<svg
|
||||
className="inline-block w-4 h-4 mr-1 -mt-1"
|
||||
fill="currentColor"
|
||||
|
@ -202,7 +206,9 @@ export default function Success(props) {
|
|||
</a>
|
||||
</Link>
|
||||
<Link href={"data:text/calendar," + eventLink()}>
|
||||
<a className="mx-2 btn-wide btn-white" download={props.eventType.title + ".ics"}>
|
||||
<a
|
||||
className="mx-2 rounded-sm border border-neutral-200 dark:border-neutral-700 py-2 px-3"
|
||||
download={props.eventType.title + ".ics"}>
|
||||
<svg
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
BIN
public/android-chrome-192x192.png
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
public/android-chrome-256x256.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
public/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
9
public/browserconfig.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#ff0000</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
|
@ -1,11 +1,41 @@
|
|||
<svg width="104" height="20" viewBox="0 0 104 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 1C0 0.447715 0.447715 0 1 0H19C19.5523 0 20 0.447715 20 1V19C20 19.5523 19.5523 20 19 20H10.4142C10.149 20 9.89464 19.8946 9.70711 19.7071L5.21739 15.2174L0.292893 10.2929C0.105357 10.1054 0 9.851 0 9.58579V1Z" fill="black"/>
|
||||
<path d="M32.0349 15.426C34.2609 15.426 35.8783 14.226 36.1914 12.313L33.5827 11.7391C33.4262 12.7826 32.887 13.3565 32.0175 13.3565C30.9218 13.3565 30.2957 12.4173 30.2957 10.7826C30.2957 9.26951 30.9392 8.36516 32.0001 8.36516C32.8522 8.36516 33.4088 8.90429 33.5653 9.93038L36.1914 9.39125C35.8609 7.42603 34.3479 6.31299 32.0696 6.31299C29.2175 6.31299 27.4609 8.03473 27.4609 10.8695C27.4609 13.6347 29.287 15.426 32.0349 15.426Z" fill="black"/>
|
||||
<path d="M40.0978 15.426C41.4543 15.426 42.5673 14.8173 43.0021 13.8434C43.0195 14.3304 43.0543 14.7999 43.1412 15.2173H45.8543C45.6978 14.6782 45.6108 13.826 45.6108 12.7999V9.91299C45.6108 7.40864 44.3586 6.31299 41.5586 6.31299C39.0891 6.31299 37.5238 7.46082 37.3847 9.39125H40.1499C40.2021 8.55647 40.6891 8.10429 41.5412 8.10429C42.4108 8.10429 42.8282 8.53908 42.8282 9.53038V9.82603L40.7238 10.1217C39.4021 10.2956 38.5673 10.5739 37.9934 10.9739C37.4021 11.3912 37.1065 12.0347 37.1065 12.8521C37.1065 14.3999 38.3065 15.426 40.0978 15.426ZM41.1412 13.6521C40.4108 13.6521 39.9238 13.2521 39.9238 12.6434C39.9238 11.9999 40.3934 11.6347 41.4195 11.4434L42.9151 11.1999V11.8956C42.9151 12.9391 42.2021 13.6521 41.1412 13.6521Z" fill="black"/>
|
||||
<path d="M50.6759 2.60864H47.8759V15.2173H50.6759V2.60864Z" fill="black"/>
|
||||
<path d="M61.1052 10.6608C61.1052 7.8956 59.5748 6.31299 56.8444 6.31299C54.1313 6.31299 52.427 8.10429 52.427 10.9565C52.427 13.7739 54.1661 15.426 57.0357 15.426C58.8444 15.426 60.3226 14.5739 61.0183 13.1478L58.9661 12.2086C58.5661 13.0086 57.9226 13.426 57.0704 13.426C55.9052 13.426 55.0878 12.6782 55.0704 11.4956H61.1052V10.6608ZM58.4096 9.93038H55.0704C55.1052 8.6956 55.7139 7.96516 56.7922 7.96516C57.8357 7.96516 58.4096 8.62603 58.4096 9.86082V9.93038Z" fill="black"/>
|
||||
<path d="M62.8535 15.2173H65.6535V10.5217C65.6535 9.18256 66.1926 8.43473 67.1665 8.43473C68.0535 8.43473 68.4709 9.00864 68.4709 10.2086V15.2173H71.2535V9.82603C71.2535 7.4956 70.2969 6.2956 68.4187 6.2956C67.0969 6.2956 66.1404 6.92169 65.5317 8.19125V6.52169H62.8535V15.2173Z" fill="black"/>
|
||||
<path d="M76.3209 15.426C77.5904 15.426 78.6339 14.7652 79.0687 13.7217V15.2173H81.7122V2.60864H78.9295V7.6869C78.4948 6.78256 77.6252 6.31299 76.4774 6.31299C74.3035 6.31299 72.8426 8.13908 72.8426 10.9043C72.8426 13.6173 74.2339 15.426 76.3209 15.426ZM77.4165 13.4956C76.3556 13.4956 75.7122 12.4869 75.7122 10.8521C75.7122 9.18256 76.3209 8.24342 77.3817 8.24342C78.373 8.24342 78.9643 9.06082 78.9643 10.5391V11.2869C78.9643 12.6434 78.373 13.4956 77.4165 13.4956Z" fill="black"/>
|
||||
<path d="M87.5423 15.426C89.9771 15.426 91.3858 14.3304 91.3858 12.5565C91.3858 11.0608 90.6554 10.1391 88.7945 9.87821L87.2467 9.63473C86.4815 9.53038 86.2206 9.30429 86.2206 8.8869C86.2206 8.39995 86.6032 8.17386 87.438 8.17386C88.5162 8.17386 89.3858 8.60864 90.0293 9.33908L91.4728 8.03473C90.7597 6.95647 89.2815 6.31299 87.5249 6.31299C85.1249 6.31299 83.6988 7.37386 83.6988 9.06082C83.6988 10.5391 84.5858 11.4608 86.4641 11.7391L87.7162 11.913C88.6728 12.0521 88.951 12.2434 88.951 12.7652C88.951 13.2521 88.4988 13.5304 87.5945 13.5304C86.3945 13.5304 85.4554 13.0608 84.8467 12.1217L83.2293 13.3391C84.0641 14.713 85.6293 15.426 87.5423 15.426Z" fill="black"/>
|
||||
<path d="M96.9109 15.426C99.4848 15.426 101.433 13.8434 101.433 10.8521C101.433 7.86082 99.4848 6.31299 96.9109 6.31299C94.3544 6.31299 92.4066 7.86082 92.4066 10.8521C92.4066 13.8434 94.3544 15.426 96.9109 15.426ZM96.9109 13.4956C95.8327 13.4956 95.1892 12.626 95.1892 10.8521C95.1892 9.06082 95.8327 8.24342 96.9109 8.24342C98.0066 8.24342 98.6501 9.06082 98.6501 10.8521C98.6501 12.626 98.0066 13.4956 96.9109 13.4956Z" fill="black"/>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 25.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px"
|
||||
viewBox="0 0 427 97.5" style="enable-background:new 0 0 427 97.5;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fillRule:evenodd;clipRule:evenodd;fill:#104D86;}
|
||||
</style>
|
||||
<path class="st0" d="M27.5,88.2c-4.9,0-9.7-1.2-14-3.6c-4.2-2.4-7.6-5.8-9.9-10c-4.8-8.8-4.8-19.4,0-28.2c2.3-4.2,5.8-7.7,10-10
|
||||
c4.3-2.4,9.1-3.7,14-3.6c6-0.1,11.8,1.7,16.5,5.3s8,8.7,9.9,15.4H42.8c-1.3-3-3.4-5.5-6.2-7.2c-2.6-1.6-5.6-2.5-8.7-2.5
|
||||
c-2.8,0-5.6,0.7-8.1,2s-4.7,3.3-6.1,5.8c-3,5.3-3.1,11.9-0.2,17.3c1.4,2.5,3.4,4.5,5.8,6s5.2,2.3,8,2.2c7.2,0,12.4-3.3,15.4-9.9
|
||||
h11.4c-1.1,4.2-3.1,8.1-5.8,11.5c-2.5,3-5.6,5.4-9.2,7C35.5,87.4,31.5,88.1,27.5,88.2L27.5,88.2z M99.6,82.1
|
||||
c-4.6,4-10.6,6.2-16.8,6.1c-4.9,0-9.7-1.2-14-3.6c-4.1-2.4-7.5-5.8-9.8-10c-2.5-4.5-3.7-9.5-3.6-14.6c-0.1-15,11.9-27.2,26.9-27.3
|
||||
c0.1,0,0.2,0,0.3,0c6.4-0.2,12.6,2.1,17.5,6.2v-5.2h11.5V87h-12V82.1z M83.5,43.7c-3,0-5.9,0.7-8.4,2.2c-2.5,1.4-4.6,3.5-6,6
|
||||
c-1.5,2.5-2.2,5.4-2.2,8.4c-0.2,4.5,1.5,8.9,4.7,12.2c3.2,3.2,7.6,4.9,12.1,4.8c2.9,0,5.8-0.7,8.3-2.2c2.5-1.4,4.5-3.5,5.9-6
|
||||
c2.9-5.3,2.9-11.7,0-17c-1.4-2.5-3.5-4.6-6-6C89.3,44.5,86.4,43.8,83.5,43.7L83.5,43.7z M122,14.8h9.7V87H122
|
||||
C122,87.1,122,14.8,122,14.8z M149.8,65.2c0.5,2.3,1.5,4.4,3,6.3c1.4,1.8,3.3,3.2,5.4,4.1c2.2,1,4.6,1.5,7,1.5
|
||||
c2.8,0.1,5.6-0.5,8.1-1.8c2.4-1.4,4.3-3.4,5.5-5.9h11.9c-2.7,6.5-6.2,11.2-10.4,14.2c-4.4,3.1-9.8,4.7-15.2,4.5
|
||||
c-4.8,0.1-9.6-1.2-13.8-3.6c-4.1-2.4-7.5-5.8-9.7-10c-2.4-4.3-3.6-9.2-3.5-14.1c-0.1-4.9,1.1-9.8,3.5-14.1c2.3-4.2,5.7-7.6,9.8-10
|
||||
c8.6-4.8,19.2-4.8,27.7,0.1c4.1,2.4,7.4,6,9.6,10.2c2.2,4.3,3.3,9.1,3.3,14c0,1-0.1,2.5-0.3,4.5h-41.9V65.2z M165.2,43.6
|
||||
c-3.4-0.1-6.8,0.9-9.6,2.9c-2.7,2-4.7,4.8-5.6,8h30.2c-0.9-3.2-2.9-6-5.6-8C171.9,44.5,168.6,43.5,165.2,43.6z M234.8,57.3
|
||||
c0-4.7-1-8.2-3-10.5s-4.9-3.5-8.7-3.5c-2.3,0-4.6,0.7-6.5,2c-2,1.3-3.7,3.2-4.8,5.4c-1.2,2.3-1.8,4.8-1.8,7.4V87h-11.2V33.8h10.5
|
||||
v4.8c3.9-3.9,9.3-6,14.9-5.8c3.9,0,7.7,1,11,3s6,4.8,7.9,8.2c1.9,3.5,2.9,7.5,2.9,11.5v31.6h-11.2L234.8,57.3L234.8,57.3z
|
||||
M296.7,82.2c-2.4,1.9-5,3.4-7.9,4.4c-3,1-6.2,1.5-9.4,1.5c-4.8,0.1-9.6-1.2-13.8-3.7c-4.1-2.4-7.5-5.9-9.8-10.1
|
||||
c-2.4-4.3-3.6-9.1-3.6-14.1c-0.1-5.1,1.1-10.1,3.5-14.7c2.3-4.2,5.7-7.7,9.8-10.1c4.3-2.5,9.2-3.8,14.1-3.7c3.1,0,6.1,0.5,9,1.4
|
||||
c2.8,0.9,5.4,2.3,7.8,4.1V14.9h11.3v72.2h-11.1L296.7,82.2L296.7,82.2z M280.4,43.1c-3,0-5.9,0.7-8.4,2.2c-2.5,1.4-4.6,3.5-6,6
|
||||
c-1.5,2.6-2.2,5.5-2.2,8.5c-0.2,4.6,1.5,9.1,4.7,12.4c3.2,3.2,7.5,5,12.1,4.8c2.9,0,5.8-0.7,8.3-2.3c2.5-1.5,4.5-3.6,5.9-6.1
|
||||
c2.9-5.4,2.9-11.8,0-17.2C291.8,46.2,286.3,43,280.4,43.1z M335.8,88.2c-3.8,0.1-7.5-0.8-10.8-2.4c-3.1-1.6-5.7-4-7.5-7
|
||||
c-1.9-3.3-2.9-7.1-3-10.9h11c0,2.7,1.1,5.3,3.1,7.1c2.1,1.7,4.8,2.6,7.5,2.5c1.8,0,3.6-0.2,5.3-0.8c1.3-0.4,2.4-1.2,3.3-2.2
|
||||
c0.7-0.8,1.1-1.9,1.1-3c0.1-1.2-0.4-2.4-1.2-3.2c-0.5-0.5-1.1-0.8-1.7-1.2c-0.7-0.3-1.4-0.6-2.2-0.8c-1.8-0.4-3.9-0.9-6.1-1.3
|
||||
c-1.8-0.2-3.5-0.6-5.1-1s-3.5-1-5-1.6c-0.8-0.2-1.6-0.6-2.4-1l-1-0.7c-0.4-0.4-0.8-0.7-1.2-1c-0.7-0.5-1.3-1.1-1.8-1.8
|
||||
c-0.6-0.8-1.1-1.7-1.5-2.6c-0.9-2-1.3-4.2-1.2-6.4c0-3,0.9-6,2.7-8.5c1.8-2.5,4.3-4.5,7.2-5.7c3.3-1.4,6.8-2.1,10.3-2
|
||||
c3.6-0.1,7.1,0.7,10.3,2.4c3,1.5,5.4,3.8,7.1,6.7c1.8,3.2,2.7,6.8,2.7,10.4H345c-0.1-3-1-5.2-2.7-6.7s-4-2.3-7-2.3s-5.2,0.5-6.8,1.5
|
||||
c-1.4,0.8-2.3,2.2-2.3,3.8c-0.1,1,0.3,2,1,2.6c0.6,0.5,1.2,0.9,1.8,1.2c0.6,0.3,1.3,0.5,2,0.7c0.8,0.2,1.7,0.4,2.7,0.6l3.1,0.5
|
||||
c3.4,0.5,6.7,1.2,10,2.2c2.6,0.8,5,2.3,6.7,4.5c1.8,2.4,2.9,5.3,3,8.4l0.1,1.8c0.1,3.2-0.9,6.4-2.8,9.1s-4.5,4.8-7.5,6
|
||||
C343.2,87.6,339.5,88.3,335.8,88.2L335.8,88.2z M412.4,74.6c-2.4,4.2-5.9,7.7-10.2,10c-4.5,2.4-9.6,3.7-14.8,3.6
|
||||
c-4.9,0.1-9.7-1.2-14-3.6c-4.2-2.4-7.7-5.8-10.1-9.9c-2.5-4.2-3.8-9-3.7-13.8c-0.1-5.1,1.1-10.1,3.6-14.6c2.4-4.2,6-7.7,10.2-10
|
||||
c4.4-2.4,9.3-3.6,14.3-3.6s9.9,1.2,14.3,3.6c4.2,2.3,7.8,5.7,10.2,9.9c2.4,4.3,3.7,9.2,3.6,14.2C416.1,65.4,414.8,70.3,412.4,74.6
|
||||
L412.4,74.6z M387.8,43.3c-3,0-6,0.8-8.6,2.3s-4.8,3.6-6.3,6.2c-1.5,2.6-2.3,5.5-2.3,8.5c0,4.6,1.8,9,5.2,12.2
|
||||
c3.2,3.4,7.6,5.3,12.3,5.3c3,0,6-0.8,8.5-2.4c2.6-1.5,4.7-3.7,6.2-6.3c1.5-2.6,2.3-5.6,2.3-8.6s-0.8-6-2.3-8.6s-3.7-4.7-6.3-6.2
|
||||
C393.9,44.1,390.9,43.3,387.8,43.3z"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
public/favicon-16x16.png
Normal file
After Width: | Height: | Size: 736 B |
BIN
public/favicon-32x32.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
public/mstile-150x150.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
21
public/safari-pinned-tab.svg
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="363.000000pt" height="363.000000pt" viewBox="0 0 363.000000 363.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,363.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M1738 2645 c-2 -2 -27 -6 -57 -9 -65 -8 -188 -48 -256 -84 -209 -110
|
||||
-337 -273 -407 -517 -30 -107 -32 -313 -4 -425 64 -253 214 -439 439 -545 384
|
||||
-181 845 -49 1053 303 45 76 93 185 97 221 l2 25 -160 1 c-88 0 -165 -1 -172
|
||||
-3 -6 -2 -14 -14 -17 -26 -8 -32 -85 -129 -132 -167 -82 -66 -190 -100 -319
|
||||
-101 -133 0 -247 52 -344 158 -206 225 -155 617 99 766 154 89 325 96 486 18
|
||||
87 -42 170 -119 209 -196 l21 -39 167 0 c92 0 167 0 167 1 0 0 -11 33 -24 72
|
||||
-25 73 -75 174 -88 180 -5 2 -8 8 -8 14 0 14 -67 95 -120 146 -96 92 -272 179
|
||||
-400 197 -45 7 -227 14 -232 10z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
19
public/site.webmanifest
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "Calendso",
|
||||
"short_name": "Calendso",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
|
@ -81,6 +81,27 @@
|
|||
}
|
||||
}
|
||||
|
||||
::-moz-selection {
|
||||
color: white;
|
||||
background: black;
|
||||
}
|
||||
|
||||
::selection {
|
||||
color: white;
|
||||
background: black;
|
||||
}
|
||||
|
||||
/* add padding bottom to bottom nav on standalone mode */
|
||||
@media all and (display-mode: standalone) {
|
||||
.bottom-nav {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.react-multi-email > [type='text'] {
|
||||
--tw-ring-shadow: 0 0 0 0 0;
|
||||
}
|
||||
|
||||
/* !important to override react-select */
|
||||
.react-select__value-container{
|
||||
border: 0 !important;
|
||||
|
@ -140,7 +161,8 @@
|
|||
height: 30px;
|
||||
margin: 60px auto;
|
||||
position: relative;
|
||||
border: 4px solid #000;
|
||||
border-width: 4px;
|
||||
border-style: solid;
|
||||
animation: loader 2s infinite ease;
|
||||
}
|
||||
|
||||
|
@ -148,7 +170,6 @@
|
|||
vertical-align: top;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
background-color: #000;
|
||||
animation: loader-inner 2s infinite ease-in;
|
||||
}
|
||||
|
||||
|
|
376
yarn.lock
|
@ -753,6 +753,306 @@
|
|||
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.26.0-23.9b816b3aa13cc270074f172f30d6eda8a8ce867d.tgz#cfdacfad3acc0f3bf1d7710aa8f3852fd85ac6d9"
|
||||
integrity sha512-a0jIhLvw9rFh6nZTr5Y3uzP28I2xNDu3pqxANvwMNnmIoYr1wYEcO1pMXn/36BGXldDdAWMmAbhfloHA3IB8DA==
|
||||
|
||||
"@radix-ui/popper@0.0.10":
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/popper/-/popper-0.0.10.tgz#9f707d9cec8762423f81acaf8e650e40a554cb73"
|
||||
integrity sha512-YFKuPqQPKscreQid7NuB4it3PMzSwGg03vgrud6sVliHkI43QNAOHyrHyMNo015jg6QK5GVDn+7J2W5uygqSGA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
csstype "^3.0.4"
|
||||
|
||||
"@radix-ui/primitive@0.0.5":
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-0.0.5.tgz#8464fb4db04401bde72d36e27e05714080668d40"
|
||||
integrity sha512-VeL6A5LpKYRJhDDj5tCTnzP3zm+FnvybsAkgBHQ4LUPPBnqRdWLoyKpZhlwFze/z22QHINaTIcE9Z/fTcrUR1g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-arrow@0.0.15":
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-0.0.15.tgz#2fb7e4cab626f87d4f7a403672c57bce74b0a7b4"
|
||||
integrity sha512-lw3/3nPmEeK67IgndT764w/65EMm5psXnr2efCeo0eWOERTnFAswNka2bKJUSKY02FHECkH4qVzhwupFyeYv0g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-polymorphic" "0.0.13"
|
||||
"@radix-ui/react-primitive" "0.0.15"
|
||||
|
||||
"@radix-ui/react-collapsible@^0.0.16":
|
||||
version "0.0.16"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-0.0.16.tgz#6a99068f70bb85a60f8cbd43f093bd3053ab61cc"
|
||||
integrity sha512-kY9wojEbrpTge6sz3BZl1oXer2Szhi+MW60TSDf14mL6l8+e4ugp5y2ItGDjcW5B7AzL00dsMtqlxuAkhFjWxQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "0.0.5"
|
||||
"@radix-ui/react-compose-refs" "0.0.5"
|
||||
"@radix-ui/react-context" "0.0.5"
|
||||
"@radix-ui/react-id" "0.0.6"
|
||||
"@radix-ui/react-polymorphic" "0.0.12"
|
||||
"@radix-ui/react-presence" "0.0.14"
|
||||
"@radix-ui/react-primitive" "0.0.14"
|
||||
"@radix-ui/react-use-controllable-state" "0.0.6"
|
||||
"@radix-ui/react-use-layout-effect" "0.0.5"
|
||||
|
||||
"@radix-ui/react-compose-refs@0.0.5":
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-0.0.5.tgz#0f71f0de1dec341f30cebd420b6bc3d12a3037dd"
|
||||
integrity sha512-O9mH9X/2EwuAEEoZXrU4alcrRbAhhZHGpIJ5bOH6rmRcokhaoWRBY1tOEe2lgHdb/bkKrY+viLi4Zq8Ju6/09Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-context@0.0.5":
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-0.0.5.tgz#7c15f46795d7765dabfaf6f9c53791ad28c521c2"
|
||||
integrity sha512-bwrzAc0qc2EPepSTLBT4+93uCiI9wP78VSmPg2K+k71O/vpx7dPs0VqrewwCBNCHT54NIwaRr2hEsm2uqYi02A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-dialog@^0.0.19":
|
||||
version "0.0.19"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-0.0.19.tgz#5a76fa380142a7a97c15c585ab071f63fba5297d"
|
||||
integrity sha512-7FbWaj/C/TDpfJ+VJ4wNAQIjENDNfwAqNvAfeb+TEtBjgjmsfRDgA1AMenlA5N1QuRtAokRMTHUs3ukW49oQ+g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "0.0.5"
|
||||
"@radix-ui/react-compose-refs" "0.0.5"
|
||||
"@radix-ui/react-context" "0.0.5"
|
||||
"@radix-ui/react-dismissable-layer" "0.0.14"
|
||||
"@radix-ui/react-focus-guards" "0.0.7"
|
||||
"@radix-ui/react-focus-scope" "0.0.14"
|
||||
"@radix-ui/react-id" "0.0.6"
|
||||
"@radix-ui/react-polymorphic" "0.0.12"
|
||||
"@radix-ui/react-portal" "0.0.14"
|
||||
"@radix-ui/react-presence" "0.0.14"
|
||||
"@radix-ui/react-primitive" "0.0.14"
|
||||
"@radix-ui/react-slot" "0.0.12"
|
||||
"@radix-ui/react-use-controllable-state" "0.0.6"
|
||||
aria-hidden "^1.1.1"
|
||||
react-remove-scroll "^2.4.0"
|
||||
|
||||
"@radix-ui/react-dismissable-layer@0.0.14":
|
||||
version "0.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-0.0.14.tgz#9d8a3415a2830688070c6596dec18b43c33df7b2"
|
||||
integrity sha512-0pmRuGYYvWlEaED1igGFLjic0+hD0OqvsnrZaN3n1nDOkoCd7H5CA2geaShSrlBF5riI2Dr9jIZPGLbDRhs4DA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "0.0.5"
|
||||
"@radix-ui/react-polymorphic" "0.0.12"
|
||||
"@radix-ui/react-primitive" "0.0.14"
|
||||
"@radix-ui/react-use-body-pointer-events" "0.0.6"
|
||||
"@radix-ui/react-use-callback-ref" "0.0.5"
|
||||
"@radix-ui/react-use-escape-keydown" "0.0.6"
|
||||
|
||||
"@radix-ui/react-focus-guards@0.0.7":
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-0.0.7.tgz#285ed081c877587acd4ee7e6d8260bdf9044e922"
|
||||
integrity sha512-enAsmrUunptHVzPLTuZqwTP/X3WFBnyJ/jP9W+0g+bRvI3o7V1kxNc+T2Rp1eRTFBW+lUNnt08qkugPytyTRog==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-focus-scope@0.0.14":
|
||||
version "0.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-0.0.14.tgz#778e2a3ea607621d82e0139616d7ea6d517d9533"
|
||||
integrity sha512-D3v6Tw8vzpIBNd2I32Q2G4LCiXMIlmc6Pl2VV9CZjSatDOjkV/ckGbhkQyQ7QxnD/0CmiSxNo5hTeGRmZDjwmA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "0.0.5"
|
||||
"@radix-ui/react-polymorphic" "0.0.12"
|
||||
"@radix-ui/react-primitive" "0.0.14"
|
||||
"@radix-ui/react-use-callback-ref" "0.0.5"
|
||||
|
||||
"@radix-ui/react-id@0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-0.0.6.tgz#c4b27d11861805e91ac296e7758ab47e3947b65c"
|
||||
integrity sha512-PzmraF34fYggsYvTIZVJ5S68WMp3aKUN3HkSmGnz4zn9zpRjkAbbg7Xn3ueQI3FQsLWKgyUfnpsmWFDndpcqYg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-polymorphic@0.0.12":
|
||||
version "0.0.12"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-polymorphic/-/react-polymorphic-0.0.12.tgz#bf4ae516669b68e059549538104d97322f7c876b"
|
||||
integrity sha512-/GYNMicBnGzjD1d2fCAuzql1VeFrp8mqM3xfzT1kxhnV85TKdURO45jBfMgqo17XNXoNhWIAProUsCO4qFAAIg==
|
||||
|
||||
"@radix-ui/react-polymorphic@0.0.13":
|
||||
version "0.0.13"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-polymorphic/-/react-polymorphic-0.0.13.tgz#d010d48281626191c9513f11db5d82b37662418a"
|
||||
integrity sha512-0sGqBp+v9/yrsMhPfAejxcem2MwAFgaSAxF3Sieaklm6ZVYM/hTZxxWI5NVOLGV+482GwW0wIqwpVUzREjmh+w==
|
||||
|
||||
"@radix-ui/react-popper@0.0.18":
|
||||
version "0.0.18"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-0.0.18.tgz#e85ec077c18ffca92ce97cc19586dcc6f022fffb"
|
||||
integrity sha512-j8nPqX5scAmeGuyW9VQv+M4MkKsV/ulR1Yt0eu13LyGLT3L7FM2YBMt3KlbpUxrT3mrNGC0eEQAiVgm/G3/fGQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/popper" "0.0.10"
|
||||
"@radix-ui/react-arrow" "0.0.15"
|
||||
"@radix-ui/react-compose-refs" "0.0.5"
|
||||
"@radix-ui/react-context" "0.0.5"
|
||||
"@radix-ui/react-polymorphic" "0.0.13"
|
||||
"@radix-ui/react-primitive" "0.0.15"
|
||||
"@radix-ui/react-use-rect" "0.0.7"
|
||||
"@radix-ui/react-use-size" "0.0.6"
|
||||
"@radix-ui/rect" "0.0.5"
|
||||
|
||||
"@radix-ui/react-portal@0.0.14":
|
||||
version "0.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-0.0.14.tgz#31513d8777cf5e50a3a30ebc9deb34821e890e9e"
|
||||
integrity sha512-Wi9arVwVenonjZIX6znCBYaasua03Tb+UtrBZShepJkLGtbGxDlzExijiGIaIRNetl46Oc2pw0F6Y6HffDnUww==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-polymorphic" "0.0.12"
|
||||
"@radix-ui/react-primitive" "0.0.14"
|
||||
"@radix-ui/react-use-layout-effect" "0.0.5"
|
||||
|
||||
"@radix-ui/react-portal@0.0.15":
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-0.0.15.tgz#833bccb192aafb9420bd037d5827e88caf429dc4"
|
||||
integrity sha512-qMESsdqph1gbRGzy9oSzUoeZYXnR2egXVcEZDqmesfn8w/o1rC1wadKkyBf7qo/YyjUX4mvXknAA+ftp1aQp+w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-polymorphic" "0.0.13"
|
||||
"@radix-ui/react-primitive" "0.0.15"
|
||||
"@radix-ui/react-use-layout-effect" "0.0.5"
|
||||
|
||||
"@radix-ui/react-presence@0.0.14":
|
||||
version "0.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-0.0.14.tgz#6a86058bbbf46234dd8840dacd620b3ac5797025"
|
||||
integrity sha512-ufof9B76DHXV0sC8H7Lswh2AepdJFG8qEtF32JWrbA9N1bl2Jnf9px76KsagyC0MA8crGEZO5A96wizGuSgGWQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "0.0.5"
|
||||
|
||||
"@radix-ui/react-presence@0.0.15":
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-0.0.15.tgz#4ff12feb436f1499148feb11c3a63a5d8fab568a"
|
||||
integrity sha512-+5+ePKUdTkqN1ze7nYmcoeHSsmKCcREwt0NhvNgDocPaqEUoZSkK9Mq6eMiMXSj02NkXH9P+bK32VCClYFnMBQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "0.0.5"
|
||||
"@radix-ui/react-use-layout-effect" "0.0.5"
|
||||
|
||||
"@radix-ui/react-primitive@0.0.14":
|
||||
version "0.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-0.0.14.tgz#752a967cb05d4c5643634fe20274e7dc905d1cce"
|
||||
integrity sha512-FYOWGCrxFpLdB534aWTwMK4Pjg8cxFb+745qWhPfI+cYi+aYUddJQD3ilRHHXxCBD72ve7/PufqeB7Y/QlKqgg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-polymorphic" "0.0.12"
|
||||
|
||||
"@radix-ui/react-primitive@0.0.15":
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-0.0.15.tgz#c0cf609ee565a32969d20943e2697b42a04fbdf3"
|
||||
integrity sha512-Y7JLnen/G3AT0cQXXkBo3A1OuWaKGerkd2gKs0Fuqxv+kTxEmYoqSp/soo0Mm3Ccw61LKLQAjPiE37GK9/Zqwg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-polymorphic" "0.0.13"
|
||||
|
||||
"@radix-ui/react-slot@0.0.12", "@radix-ui/react-slot@^0.0.12":
|
||||
version "0.0.12"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-0.0.12.tgz#c4d8a75fffca561aeeca2ed9603384d86757f60a"
|
||||
integrity sha512-Em8P/xYyh3O/32IhrmARJNH+J/XCAVnw6h2zGu6oeReliIX7ktU67pMSeyyIZiU2hNXzaXYB/xDdixizQe/DGA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-compose-refs" "0.0.5"
|
||||
|
||||
"@radix-ui/react-tooltip@^0.0.21":
|
||||
version "0.0.21"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-0.0.21.tgz#86160645cf0441fa7f465c8aaa265887cc3ff9b4"
|
||||
integrity sha512-+QLMXclfX0XM3inY5LEAvmKsomQ+S0cqzo1v/oS8CiIcawg01RDLV9mzjDYLnpE4eKokn30d+gk4r1YAtWIbZA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "0.0.5"
|
||||
"@radix-ui/react-compose-refs" "0.0.5"
|
||||
"@radix-ui/react-context" "0.0.5"
|
||||
"@radix-ui/react-id" "0.0.6"
|
||||
"@radix-ui/react-polymorphic" "0.0.13"
|
||||
"@radix-ui/react-popper" "0.0.18"
|
||||
"@radix-ui/react-portal" "0.0.15"
|
||||
"@radix-ui/react-presence" "0.0.15"
|
||||
"@radix-ui/react-primitive" "0.0.15"
|
||||
"@radix-ui/react-slot" "0.0.12"
|
||||
"@radix-ui/react-use-controllable-state" "0.0.6"
|
||||
"@radix-ui/react-use-escape-keydown" "0.0.6"
|
||||
"@radix-ui/react-use-layout-effect" "0.0.5"
|
||||
"@radix-ui/react-use-previous" "0.0.5"
|
||||
"@radix-ui/react-use-rect" "0.0.7"
|
||||
"@radix-ui/react-visually-hidden" "0.0.15"
|
||||
|
||||
"@radix-ui/react-use-body-pointer-events@0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-body-pointer-events/-/react-use-body-pointer-events-0.0.6.tgz#30b21301880417e7dbb345871ff5a83f2abe0d8d"
|
||||
integrity sha512-ouYb7u1+K9TsiEcNs3HceNUBUgB2PV41EyD5O6y6ZPMxl1lW/QAy5dfyfJMRyaRWQ6kxwmGoKlCSb4uPTruzuQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-use-layout-effect" "0.0.5"
|
||||
|
||||
"@radix-ui/react-use-callback-ref@0.0.5":
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-0.0.5.tgz#fa8db050229cda573dfeeae213d74ef06f6130db"
|
||||
integrity sha512-z1AI221vmq9f3vsDyrCsGLCatKagbM1YeCGdRMhMsUBzFFCaJ+Axyoe/ndVqW8wwsraGWr1zYVAhIEdlC0GvPg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-use-controllable-state@0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-0.0.6.tgz#c4b16bc911a25889333388a684a04df937e5fec7"
|
||||
integrity sha512-fBk4hUSKc4N7X/NAaifWYfKKfNuOB9xvj0MBQQYS5oOTNRgg4y8/Ax3jZ0adsplXDm7ix75sxqWm0nrvUoAjcw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-use-callback-ref" "0.0.5"
|
||||
|
||||
"@radix-ui/react-use-escape-keydown@0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-0.0.6.tgz#1ad1c81b99961b7dbe376ef54151ebc8bef627a0"
|
||||
integrity sha512-MJpVj21BYwWllmp2xbXPqpKPssJ1WWrZi+Qx7PY5hVcBhQr5Jo6yKwIX677pH5Yql95ENTTT5LW3q+LVFYIISw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-use-callback-ref" "0.0.5"
|
||||
|
||||
"@radix-ui/react-use-layout-effect@0.0.5":
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-0.0.5.tgz#cbbd059090edc765749da00d9f562a9abd43cbac"
|
||||
integrity sha512-bNPW2JNOr/p2hXr0hfKKqrEy5deNSRF17sw3l9Z7qlEnvIbBtQ7iwY/wrxIz5P7XFyYGoXodIUDH5G8PEucE3A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-use-previous@0.0.5":
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-0.0.5.tgz#75191d1fa0ac24c560fe8cfbaa2f1174858cbb2f"
|
||||
integrity sha512-GjtJlWlDAEMqCm2RDnVdWI6tk4/ZQfRq/VlP05Xy5rFZj6lD37VZWVWUELMBasRPzd2AS/9wPmphOgjH0VnE5A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-use-rect@0.0.7":
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-0.0.7.tgz#e3a55fa7183ef436042198787bf38f8c9befcc14"
|
||||
integrity sha512-OmaeFTgyiGNAchaxzDu+kFLz4Ly8RUcT5nwfoz4Nddd86I8Zdq93iNFnOpVLoVYqBnFEmvR6zexHXNFATrMbbQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/rect" "0.0.5"
|
||||
|
||||
"@radix-ui/react-use-size@0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-0.0.6.tgz#998eaf6e8871b868f81f3b7faac06c3e896c37a0"
|
||||
integrity sha512-kP4RIb2I5oHQzwzXJ21Hu8htNqf+sdaRzywxQpbj+hmqeUhpvIkhoq+ShNWV9wE/3c1T7gPnka8/nKYsKaKdCg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@radix-ui/react-visually-hidden@0.0.15":
|
||||
version "0.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-0.0.15.tgz#7bd18af3fb5da1349f9b04006d22c3d6e9ce0453"
|
||||
integrity sha512-8J13Nzu9MfT2z+mDTGRfBukPi5L9LXLV7w1HvNZPVqxGLK8p7/CoXnt8XdS1HKSFm6akZmWJXMZVNVBUsONOcA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/react-polymorphic" "0.0.13"
|
||||
"@radix-ui/react-primitive" "0.0.15"
|
||||
|
||||
"@radix-ui/rect@0.0.5":
|
||||
version "0.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-0.0.5.tgz#6000d8d800288114af4bbc5863e6b58755d7d978"
|
||||
integrity sha512-gXw171KbjyttA7K1DRIvPguLmKsg8raitB67MIcsdZwcquy+a1O2w3xY21NIKEqGhJwqJkECPUmMJDXgMNYuAg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
|
||||
"@sinonjs/commons@^1.7.0":
|
||||
version "1.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
|
||||
|
@ -1220,6 +1520,13 @@ argparse@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
||||
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
||||
|
||||
aria-hidden@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.1.3.tgz#bb48de18dc84787a3c6eee113709c473c64ec254"
|
||||
integrity sha512-RhVWFtKH5BiGMycI72q2RAFMLQi8JP9bLuQXgR5a8Znp7P5KOIADSJeyfI8PCVxLEp067B2HbP5JIiI/PXIZeA==
|
||||
dependencies:
|
||||
tslib "^1.0.0"
|
||||
|
||||
array-includes@^3.1.2, array-includes@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a"
|
||||
|
@ -2006,7 +2313,7 @@ cssstyle@^2.3.0:
|
|||
dependencies:
|
||||
cssom "~0.3.6"
|
||||
|
||||
csstype@^3.0.2:
|
||||
csstype@^3.0.2, csstype@^3.0.4:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
|
||||
integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
|
||||
|
@ -2109,6 +2416,11 @@ detect-newline@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
|
||||
integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
|
||||
|
||||
detect-node-es@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
|
||||
integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==
|
||||
|
||||
detective@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b"
|
||||
|
@ -2780,6 +3092,11 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
|
|||
has "^1.0.3"
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
get-nonce@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
|
||||
integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==
|
||||
|
||||
get-orientation@1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/get-orientation/-/get-orientation-1.1.2.tgz#20507928951814f8a91ded0a0e67b29dfab98947"
|
||||
|
@ -3186,6 +3503,13 @@ internal-slot@^1.0.3:
|
|||
has "^1.0.3"
|
||||
side-channel "^1.0.4"
|
||||
|
||||
invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
is-arguments@^1.0.4:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9"
|
||||
|
@ -4213,7 +4537,7 @@ log-update@^4.0.0:
|
|||
slice-ansi "^4.0.0"
|
||||
wrap-ansi "^6.2.0"
|
||||
|
||||
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
|
@ -5211,6 +5535,11 @@ react-moment-proptypes@^1.6.0:
|
|||
dependencies:
|
||||
moment ">=1.6.0"
|
||||
|
||||
react-multi-email@^0.5.3:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/react-multi-email/-/react-multi-email-0.5.3.tgz#734a0d4d1af23feef5cb5e635bde23963b0a9e8b"
|
||||
integrity sha512-1AneeJlAwjvzkPV740q2SXes/kW3HKOzR3gs+U7whrHN5nz+yH5Unosf/rvz8kRj/eFwBf6fTzMqlJiupu7S5Q==
|
||||
|
||||
react-outside-click-handler@^1.2.4:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-outside-click-handler/-/react-outside-click-handler-1.3.0.tgz#3831d541ac059deecd38ec5423f81e80ad60e115"
|
||||
|
@ -5245,6 +5574,25 @@ react-refresh@0.8.3:
|
|||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
|
||||
integrity sha512-X8jZHc7nCMjaCqoU+V2I0cOhNW+QMBwSUkeXnTi8IPe6zaRWfn60ZzvFDZqWPfmSJfjub7dDW1SP0jaHWLu/hg==
|
||||
|
||||
react-remove-scroll-bar@^2.1.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.2.0.tgz#d4d545a7df024f75d67e151499a6ab5ac97c8cdd"
|
||||
integrity sha512-UU9ZBP1wdMR8qoUs7owiVcpaPwsQxUDC2lypP6mmixaGlARZa7ZIBx1jcuObLdhMOvCsnZcvetOho0wzPa9PYg==
|
||||
dependencies:
|
||||
react-style-singleton "^2.1.0"
|
||||
tslib "^1.0.0"
|
||||
|
||||
react-remove-scroll@^2.4.0:
|
||||
version "2.4.3"
|
||||
resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.4.3.tgz#83d19b02503b04bd8141ed6e0b9e6691a2e935a6"
|
||||
integrity sha512-lGWYXfV6jykJwbFpsuPdexKKzp96f3RbvGapDSIdcyGvHb7/eqyn46C7/6h+rUzYar1j5mdU+XECITHXCKBk9Q==
|
||||
dependencies:
|
||||
react-remove-scroll-bar "^2.1.0"
|
||||
react-style-singleton "^2.1.0"
|
||||
tslib "^1.0.0"
|
||||
use-callback-ref "^1.2.3"
|
||||
use-sidecar "^1.0.1"
|
||||
|
||||
react-select@^4.3.0, react-select@^4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-select/-/react-select-4.3.1.tgz#389fc07c9bc7cf7d3c377b7a05ea18cd7399cb81"
|
||||
|
@ -5258,6 +5606,15 @@ react-select@^4.3.0, react-select@^4.3.1:
|
|||
react-input-autosize "^3.0.0"
|
||||
react-transition-group "^4.3.0"
|
||||
|
||||
react-style-singleton@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.1.1.tgz#ce7f90b67618be2b6b94902a30aaea152ce52e66"
|
||||
integrity sha512-jNRp07Jza6CBqdRKNgGhT3u9umWvils1xsuMOjZlghBDH2MU0PL2WZor4PGYjXpnRCa9DQSlHMs/xnABWOwYbA==
|
||||
dependencies:
|
||||
get-nonce "^1.0.0"
|
||||
invariant "^2.2.4"
|
||||
tslib "^1.0.0"
|
||||
|
||||
react-timezone-select@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/react-timezone-select/-/react-timezone-select-1.0.4.tgz#66664f508f927e9f9c0f051aea51fd3196534401"
|
||||
|
@ -6103,7 +6460,7 @@ ts-pnp@^1.1.6:
|
|||
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92"
|
||||
integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==
|
||||
|
||||
tslib@^1.8.1, tslib@^1.9.0:
|
||||
tslib@^1.0.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||
|
@ -6256,6 +6613,19 @@ url@^0.11.0:
|
|||
punycode "1.3.2"
|
||||
querystring "0.2.0"
|
||||
|
||||
use-callback-ref@^1.2.3:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.2.5.tgz#6115ed242cfbaed5915499c0a9842ca2912f38a5"
|
||||
integrity sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==
|
||||
|
||||
use-sidecar@^1.0.1:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.0.5.tgz#ffff2a17c1df42e348624b699ba6e5c220527f2b"
|
||||
integrity sha512-k9jnrjYNwN6xYLj1iaGhonDghfvmeTmYjAiGvOr7clwKfPjMXJf4/HOr7oT5tJwYafgp2tG2l3eZEOfoELiMcA==
|
||||
dependencies:
|
||||
detect-node-es "^1.1.0"
|
||||
tslib "^1.9.3"
|
||||
|
||||
use-subscription@1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1"
|
||||
|
|