2021-06-22 17:42:32 +00:00
|
|
|
import { useEffect, useState } from "react";
|
2021-07-13 16:10:22 +00:00
|
|
|
import { GetServerSideProps, GetServerSidePropsContext } from "next";
|
2021-06-22 17:42:32 +00:00
|
|
|
import Head from "next/head";
|
2021-08-08 15:13:31 +00:00
|
|
|
import { ChevronDownIcon, ChevronUpIcon, ClockIcon, GlobeIcon } from "@heroicons/react/solid";
|
2021-06-22 17:42:32 +00:00
|
|
|
import { useRouter } from "next/router";
|
2021-07-13 16:10:22 +00:00
|
|
|
import dayjs, { Dayjs } from "dayjs";
|
2021-07-22 12:16:54 +00:00
|
|
|
import * as Collapsible from "@radix-ui/react-collapsible";
|
2021-03-22 13:48:48 +00:00
|
|
|
|
2021-07-08 20:44:40 +00:00
|
|
|
import prisma, { whereAndSelect } from "@lib/prisma";
|
2021-06-22 17:42:32 +00:00
|
|
|
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "../../lib/telemetry";
|
2021-06-20 14:19:41 +00:00
|
|
|
import AvailableTimes from "../../components/booking/AvailableTimes";
|
2021-06-22 17:42:32 +00:00
|
|
|
import TimeOptions from "../../components/booking/TimeOptions";
|
|
|
|
import Avatar from "../../components/Avatar";
|
|
|
|
import { timeZone } from "../../lib/clock";
|
2021-06-21 20:26:04 +00:00
|
|
|
import DatePicker from "../../components/booking/DatePicker";
|
|
|
|
import PoweredByCalendso from "../../components/ui/PoweredByCalendso";
|
2021-07-09 22:59:21 +00:00
|
|
|
import Theme from "@components/Theme";
|
2021-05-26 18:40:22 +00:00
|
|
|
|
2021-06-22 17:42:32 +00:00
|
|
|
export default function Type(props): Type {
|
2021-06-20 14:19:41 +00:00
|
|
|
// Get router variables
|
|
|
|
const router = useRouter();
|
|
|
|
const { rescheduleUid } = router.query;
|
2021-05-26 18:40:22 +00:00
|
|
|
|
2021-07-09 22:59:21 +00:00
|
|
|
const { isReady } = Theme(props.user.theme);
|
|
|
|
|
2021-07-13 16:10:22 +00:00
|
|
|
const [selectedDate, setSelectedDate] = useState<Dayjs>(() => {
|
|
|
|
return props.date && dayjs(props.date).isValid() ? dayjs(props.date) : null;
|
|
|
|
});
|
2021-06-20 14:19:41 +00:00
|
|
|
const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);
|
2021-06-22 17:42:32 +00:00
|
|
|
const [timeFormat, setTimeFormat] = useState("h:mma");
|
2021-06-20 14:19:41 +00:00
|
|
|
const telemetry = useTelemetry();
|
2021-05-06 17:36:57 +00:00
|
|
|
|
2021-06-19 22:50:47 +00:00
|
|
|
useEffect(() => {
|
2021-07-11 16:05:49 +00:00
|
|
|
handleToggle24hClock(localStorage.getItem("timeOption.is24hClock") === "true");
|
2021-06-22 17:42:32 +00:00
|
|
|
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.pageView, collectPageParameters()));
|
|
|
|
}, [telemetry]);
|
2021-05-26 18:40:22 +00:00
|
|
|
|
2021-06-21 20:26:04 +00:00
|
|
|
const changeDate = (date: Dayjs) => {
|
2021-06-22 17:42:32 +00:00
|
|
|
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.dateSelected, collectPageParameters()));
|
2021-06-30 01:35:08 +00:00
|
|
|
setSelectedDate(date);
|
2021-06-20 14:19:41 +00:00
|
|
|
};
|
|
|
|
|
2021-07-13 16:10:22 +00:00
|
|
|
useEffect(() => {
|
|
|
|
if (!selectedDate) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const formattedDate = selectedDate.utc().format("YYYY-MM-DD");
|
|
|
|
|
|
|
|
router.replace(
|
|
|
|
{
|
2021-08-05 17:35:08 +00:00
|
|
|
query: Object.assign(
|
|
|
|
{},
|
|
|
|
{
|
|
|
|
...router.query,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
date: formattedDate,
|
|
|
|
}
|
|
|
|
),
|
2021-07-13 16:10:22 +00:00
|
|
|
},
|
|
|
|
undefined,
|
|
|
|
{
|
|
|
|
shallow: true,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}, [selectedDate]);
|
|
|
|
|
2021-06-22 17:42:32 +00:00
|
|
|
const handleSelectTimeZone = (selectedTimeZone: string): void => {
|
2021-06-20 14:19:41 +00:00
|
|
|
if (selectedDate) {
|
2021-06-22 17:42:32 +00:00
|
|
|
setSelectedDate(selectedDate.tz(selectedTimeZone));
|
2021-03-22 13:48:48 +00:00
|
|
|
}
|
2021-06-30 01:35:08 +00:00
|
|
|
setIsTimeOptionsOpen(false);
|
2021-06-20 14:19:41 +00:00
|
|
|
};
|
2021-03-22 13:48:48 +00:00
|
|
|
|
2021-06-20 00:10:08 +00:00
|
|
|
const handleToggle24hClock = (is24hClock: boolean) => {
|
2021-06-24 22:15:18 +00:00
|
|
|
setTimeFormat(is24hClock ? "HH:mm" : "h:mma");
|
2021-06-22 17:42:32 +00:00
|
|
|
};
|
2021-03-22 13:48:48 +00:00
|
|
|
|
2021-06-20 14:19:41 +00:00
|
|
|
return (
|
2021-07-11 19:35:56 +00:00
|
|
|
isReady && (
|
2021-08-08 15:13:31 +00:00
|
|
|
<div>
|
2021-07-11 19:35:56 +00:00
|
|
|
<Head>
|
|
|
|
<title>
|
|
|
|
{rescheduleUid && "Reschedule"} {props.eventType.title} | {props.user.name || props.user.username}{" "}
|
|
|
|
| Calendso
|
|
|
|
</title>
|
|
|
|
<meta name="title" content={"Meet " + (props.user.name || props.user.username) + " via Calendso"} />
|
|
|
|
<meta name="description" content={props.eventType.description} />
|
2021-06-23 11:14:19 +00:00
|
|
|
|
2021-07-11 19:35:56 +00:00
|
|
|
<meta property="og:type" content="website" />
|
|
|
|
<meta property="og:url" content="https://calendso/" />
|
|
|
|
<meta
|
|
|
|
property="og:title"
|
|
|
|
content={"Meet " + (props.user.name || props.user.username) + " via Calendso"}
|
|
|
|
/>
|
|
|
|
<meta property="og:description" content={props.eventType.description} />
|
|
|
|
<meta
|
|
|
|
property="og:image"
|
|
|
|
content={
|
|
|
|
"https://og-image-one-pi.vercel.app/" +
|
|
|
|
encodeURIComponent(
|
|
|
|
"Meet **" + (props.user.name || props.user.username) + "** <br>" + props.eventType.description
|
|
|
|
).replace(/'/g, "%27") +
|
|
|
|
".png?md=1&images=https%3A%2F%2Fcalendso.com%2Fcalendso-logo-white.svg&images=" +
|
|
|
|
encodeURIComponent(props.user.avatar)
|
|
|
|
}
|
|
|
|
/>
|
2021-06-23 11:14:19 +00:00
|
|
|
|
2021-07-11 19:35:56 +00:00
|
|
|
<meta property="twitter:card" content="summary_large_image" />
|
|
|
|
<meta property="twitter:url" content="https://calendso/" />
|
|
|
|
<meta
|
|
|
|
property="twitter:title"
|
|
|
|
content={"Meet " + (props.user.name || props.user.username) + " via Calendso"}
|
|
|
|
/>
|
|
|
|
<meta property="twitter:description" content={props.eventType.description} />
|
|
|
|
<meta
|
|
|
|
property="twitter:image"
|
|
|
|
content={
|
|
|
|
"https://og-image-one-pi.vercel.app/" +
|
|
|
|
encodeURIComponent(
|
|
|
|
"Meet **" + (props.user.name || props.user.username) + "** <br>" + props.eventType.description
|
|
|
|
).replace(/'/g, "%27") +
|
|
|
|
".png?md=1&images=https%3A%2F%2Fcalendso.com%2Fcalendso-logo-white.svg&images=" +
|
|
|
|
encodeURIComponent(props.user.avatar)
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</Head>
|
2021-08-08 15:13:31 +00:00
|
|
|
<main
|
|
|
|
className={
|
2021-08-12 13:51:40 +00:00
|
|
|
"mx-auto my-0 md:my-24 transition-max-width ease-in-out duration-500 " +
|
2021-08-08 15:46:21 +00:00
|
|
|
(selectedDate ? "max-w-5xl" : "max-w-3xl")
|
2021-08-08 15:13:31 +00:00
|
|
|
}>
|
2021-08-12 13:51:40 +00:00
|
|
|
<div className="sm:dark:border-gray-600 dark:bg-gray-900 bg-white md:border border-gray-200 rounded-sm">
|
|
|
|
{/* mobile: details */}
|
|
|
|
<div className="p-4 sm:p-8 block md:hidden">
|
|
|
|
<div className="flex items-center">
|
|
|
|
<Avatar user={props.user} className="inline-block h-9 w-9 rounded-full" />
|
|
|
|
<div className="ml-3">
|
|
|
|
<p className="text-sm font-medium dark:text-gray-300 text-black">{props.user.name}</p>
|
|
|
|
<div className="flex gap-2 text-xs font-medium text-gray-600">
|
|
|
|
{props.eventType.title}
|
|
|
|
<div>
|
|
|
|
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
|
|
|
{props.eventType.length} minutes
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<p className="dark:text-gray-200 text-gray-600 mt-3">{props.eventType.description}</p>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div className="sm:flex px-4 sm:py-5 sm:p-4">
|
2021-08-08 15:13:31 +00:00
|
|
|
<div
|
|
|
|
className={
|
2021-08-12 13:51:40 +00:00
|
|
|
"hidden md:block pr-8 sm:border-r sm:dark:border-gray-800 " +
|
|
|
|
(selectedDate ? "sm:w-1/3" : "sm:w-1/2")
|
2021-08-08 15:13:31 +00:00
|
|
|
}>
|
|
|
|
<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">
|
|
|
|
{props.eventType.title}
|
|
|
|
</h1>
|
|
|
|
<p className="text-gray-500 mb-1 px-2 py-1 -ml-2">
|
|
|
|
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
|
|
|
{props.eventType.length} minutes
|
|
|
|
</p>
|
|
|
|
|
2021-08-12 13:51:40 +00:00
|
|
|
<TimezoneDropdown />
|
2021-08-08 15:13:31 +00:00
|
|
|
|
|
|
|
<p className="dark:text-gray-200 text-gray-600 mt-3 mb-8">{props.eventType.description}</p>
|
|
|
|
</div>
|
|
|
|
<DatePicker
|
|
|
|
date={selectedDate}
|
|
|
|
periodType={props.eventType?.periodType}
|
|
|
|
periodStartDate={props.eventType?.periodStartDate}
|
|
|
|
periodEndDate={props.eventType?.periodEndDate}
|
|
|
|
periodDays={props.eventType?.periodDays}
|
|
|
|
periodCountCalendarDays={props.eventType?.periodCountCalendarDays}
|
|
|
|
weekStart={props.user.weekStart}
|
|
|
|
onDatePicked={changeDate}
|
2021-06-24 22:15:18 +00:00
|
|
|
workingHours={props.workingHours}
|
2021-07-08 21:14:29 +00:00
|
|
|
organizerTimeZone={props.eventType.timeZone || props.user.timeZone}
|
2021-08-08 15:13:31 +00:00
|
|
|
inviteeTimeZone={timeZone()}
|
2021-08-06 15:29:09 +00:00
|
|
|
eventLength={props.eventType.length}
|
2021-08-08 15:13:31 +00:00
|
|
|
minimumBookingNotice={props.eventType.minimumBookingNotice}
|
2021-06-22 17:42:32 +00:00
|
|
|
/>
|
2021-08-12 13:51:40 +00:00
|
|
|
|
|
|
|
<div className="ml-1 mt-4 block sm:hidden">
|
|
|
|
<TimezoneDropdown />
|
|
|
|
</div>
|
|
|
|
|
2021-08-08 15:13:31 +00:00
|
|
|
{selectedDate && (
|
|
|
|
<AvailableTimes
|
|
|
|
workingHours={props.workingHours}
|
|
|
|
timeFormat={timeFormat}
|
|
|
|
organizerTimeZone={props.eventType.timeZone || props.user.timeZone}
|
|
|
|
minimumBookingNotice={props.eventType.minimumBookingNotice}
|
|
|
|
eventTypeId={props.eventType.id}
|
|
|
|
eventLength={props.eventType.length}
|
|
|
|
date={selectedDate}
|
|
|
|
user={props.user}
|
|
|
|
/>
|
|
|
|
)}
|
2021-08-06 15:29:09 +00:00
|
|
|
</div>
|
2021-08-08 15:13:31 +00:00
|
|
|
</div>
|
|
|
|
{!props.user.hideBranding && <PoweredByCalendso />}
|
2021-07-11 19:35:56 +00:00
|
|
|
</main>
|
|
|
|
</div>
|
|
|
|
)
|
2021-06-20 14:19:41 +00:00
|
|
|
);
|
2021-08-12 13:51:40 +00:00
|
|
|
|
|
|
|
function TimezoneDropdown() {
|
|
|
|
return (
|
|
|
|
<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>
|
|
|
|
);
|
|
|
|
}
|
2021-03-22 13:48:48 +00:00
|
|
|
}
|
|
|
|
|
2021-07-13 16:10:22 +00:00
|
|
|
export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => {
|
|
|
|
const dateQuery = context.query?.date ?? null;
|
|
|
|
const date = Array.isArray(dateQuery) && dateQuery.length > 0 ? dateQuery.pop() : dateQuery;
|
|
|
|
|
2021-07-08 20:44:40 +00:00
|
|
|
const user = await whereAndSelect(
|
|
|
|
prisma.user.findFirst,
|
|
|
|
{
|
2021-06-22 17:42:32 +00:00
|
|
|
username: context.query.user.toLowerCase(),
|
2021-06-18 20:41:12 +00:00
|
|
|
},
|
2021-07-08 20:44:40 +00:00
|
|
|
[
|
|
|
|
"id",
|
|
|
|
"username",
|
|
|
|
"name",
|
|
|
|
"email",
|
|
|
|
"bio",
|
|
|
|
"avatar",
|
|
|
|
"startTime",
|
|
|
|
"endTime",
|
|
|
|
"timeZone",
|
|
|
|
"weekStart",
|
|
|
|
"availability",
|
|
|
|
"hideBranding",
|
2021-07-09 22:59:21 +00:00
|
|
|
"theme",
|
2021-07-08 20:44:40 +00:00
|
|
|
]
|
|
|
|
);
|
2021-05-07 15:17:06 +00:00
|
|
|
|
2021-06-20 14:19:41 +00:00
|
|
|
if (!user) {
|
2021-06-19 15:17:23 +00:00
|
|
|
return {
|
|
|
|
notFound: true,
|
2021-06-22 17:42:32 +00:00
|
|
|
};
|
2021-06-18 20:41:12 +00:00
|
|
|
}
|
2021-03-22 13:48:48 +00:00
|
|
|
|
2021-07-08 20:44:40 +00:00
|
|
|
const eventType = await whereAndSelect(
|
|
|
|
prisma.eventType.findFirst,
|
|
|
|
{
|
2021-06-19 15:17:23 +00:00
|
|
|
userId: user.id,
|
2021-07-08 20:44:40 +00:00
|
|
|
slug: context.query.type,
|
2021-06-22 17:42:32 +00:00
|
|
|
},
|
2021-07-15 14:10:26 +00:00
|
|
|
[
|
|
|
|
"id",
|
|
|
|
"title",
|
|
|
|
"description",
|
|
|
|
"length",
|
|
|
|
"availability",
|
|
|
|
"timeZone",
|
|
|
|
"periodType",
|
|
|
|
"periodDays",
|
|
|
|
"periodStartDate",
|
|
|
|
"periodEndDate",
|
|
|
|
"periodCountCalendarDays",
|
2021-07-22 22:52:27 +00:00
|
|
|
"minimumBookingNotice",
|
2021-07-15 14:10:26 +00:00
|
|
|
]
|
2021-07-08 20:44:40 +00:00
|
|
|
);
|
2021-06-19 15:17:23 +00:00
|
|
|
|
|
|
|
if (!eventType) {
|
2021-03-22 13:48:48 +00:00
|
|
|
return {
|
2021-06-20 14:19:41 +00:00
|
|
|
notFound: true,
|
2021-06-22 17:42:32 +00:00
|
|
|
};
|
2021-06-18 20:41:12 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 22:15:18 +00:00
|
|
|
const getWorkingHours = (providesAvailability) =>
|
|
|
|
providesAvailability.availability && providesAvailability.availability.length
|
|
|
|
? providesAvailability.availability
|
|
|
|
: null;
|
|
|
|
|
2021-06-30 15:56:06 +00:00
|
|
|
const workingHours: [] =
|
2021-06-24 22:15:18 +00:00
|
|
|
getWorkingHours(eventType) ||
|
|
|
|
getWorkingHours(user) ||
|
|
|
|
[
|
|
|
|
{
|
2021-06-27 21:28:35 +00:00
|
|
|
days: [0, 1, 2, 3, 4, 5, 6],
|
2021-06-24 22:15:18 +00:00
|
|
|
startTime: user.startTime,
|
2021-06-30 15:56:06 +00:00
|
|
|
endTime: user.endTime,
|
2021-06-24 22:15:18 +00:00
|
|
|
},
|
2021-06-30 15:56:06 +00:00
|
|
|
].filter((availability): boolean => typeof availability["days"] !== "undefined");
|
2021-06-24 22:15:18 +00:00
|
|
|
|
2021-07-03 14:22:31 +00:00
|
|
|
workingHours.sort((a, b) => a.startTime - b.startTime);
|
|
|
|
|
2021-07-15 14:10:26 +00:00
|
|
|
const eventTypeObject = Object.assign({}, eventType, {
|
|
|
|
periodStartDate: eventType.periodStartDate?.toString() ?? null,
|
|
|
|
periodEndDate: eventType.periodEndDate?.toString() ?? null,
|
|
|
|
});
|
|
|
|
|
2021-06-18 20:41:12 +00:00
|
|
|
return {
|
|
|
|
props: {
|
|
|
|
user,
|
2021-07-13 16:10:22 +00:00
|
|
|
date,
|
2021-07-15 14:10:26 +00:00
|
|
|
eventType: eventTypeObject,
|
2021-06-24 22:15:18 +00:00
|
|
|
workingHours,
|
2021-06-18 20:41:12 +00:00
|
|
|
},
|
2021-06-22 17:42:32 +00:00
|
|
|
};
|
|
|
|
};
|