+ );
}
-export async function getServerSideProps(context) {
- const user = await prisma.user.findFirst({
- where: {
- username: context.query.user,
- },
- select: {
- id: true,
- username: true,
- email:true,
- name: true,
- bio: true,
- avatar: true,
- eventTypes: true
- }
- });
-
- if (!user) {
- return {
- notFound: true,
- }
- }
-
- const eventTypes = await prisma.eventType.findMany({
- where: {
- userId: user.id,
- hidden: false
- }
- });
+export const getServerSideProps: GetServerSideProps = async (context) => {
+ const user = await prisma.user.findFirst({
+ where: {
+ username: context.query.user.toLowerCase(),
+ },
+ select: {
+ id: true,
+ username: true,
+ email: true,
+ name: true,
+ bio: true,
+ avatar: true,
+ eventTypes: true,
+ },
+ });
+ if (!user) {
return {
- props: {
- user,
- eventTypes
- },
- }
-}
+ notFound: true,
+ };
+ }
+
+ const eventTypes = await prisma.eventType.findMany({
+ where: {
+ userId: user.id,
+ hidden: false,
+ },
+ });
+
+ return {
+ props: {
+ user,
+ eventTypes,
+ },
+ };
+};
// Auxiliary methods
-
-export function getRandomColorCode() {
- let color = '#';
- for (let idx = 0; idx < 6; idx++) {
- color += Math.floor(Math.random() * 10);
- }
- return color;
-}
\ No newline at end of file
+export function getRandomColorCode(): string {
+ let color = "#";
+ for (let idx = 0; idx < 6; idx++) {
+ color += Math.floor(Math.random() * 10);
+ }
+ return color;
+}
diff --git a/pages/[user]/[type].tsx b/pages/[user]/[type].tsx
index 941e86b6..04a13d3b 100644
--- a/pages/[user]/[type].tsx
+++ b/pages/[user]/[type].tsx
@@ -1,410 +1,237 @@
-import {useEffect, useMemo, useState} from 'react';
-import Head from 'next/head';
-import Link from 'next/link';
-import prisma from '../../lib/prisma';
-import {useRouter} from 'next/router';
-import dayjs, {Dayjs} from 'dayjs';
-import {Switch} from '@headlessui/react';
-import TimezoneSelect from 'react-timezone-select';
+import { useEffect, useState } from "react";
+import { GetServerSideProps } from "next";
+import Head from "next/head";
+import Link from "next/link";
+import prisma from "../../lib/prisma";
+import { useRouter } from "next/router";
+import dayjs, { Dayjs } from "dayjs";
import {
ClockIcon,
GlobeIcon,
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
- XCircleIcon, ExclamationIcon
-} 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 Avatar from '../../components/Avatar';
-import getSlots from '../../lib/slots';
-import {collectPageParameters, telemetryEventTypes, useTelemetry} from "../../lib/telemetry";
-
+} from "@heroicons/react/solid";
+import isSameOrBefore from "dayjs/plugin/isSameOrBefore";
+import utc from "dayjs/plugin/utc";
+import timezone from "dayjs/plugin/timezone";
dayjs.extend(isSameOrBefore);
-dayjs.extend(isBetween);
dayjs.extend(utc);
dayjs.extend(timezone);
-function classNames(...classes) {
- return classes.filter(Boolean).join(' ')
-}
+import { collectPageParameters, telemetryEventTypes, useTelemetry } from "../../lib/telemetry";
+import AvailableTimes from "../../components/booking/AvailableTimes";
+import TimeOptions from "../../components/booking/TimeOptions";
+import Avatar from "../../components/Avatar";
+import { timeZone } from "../../lib/clock";
-export default function Type(props) {
- // Initialise state
- const [selectedDate, setSelectedDate] = useState();
- const [selectedMonth, setSelectedMonth] = useState(dayjs().month());
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState(false);
- const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);
- const [is24h, setIs24h] = useState(false);
- const [busy, setBusy] = useState([]);
- const telemetry = useTelemetry();
+export default function Type(props): Type {
+ // Get router variables
+ const router = useRouter();
+ const { rescheduleUid } = router.query;
- const [selectedTimeZone, setSelectedTimeZone] = useState('');
+ // Initialise state
+ const [selectedDate, setSelectedDate] = useState();
+ const [selectedMonth, setSelectedMonth] = useState(dayjs().month());
+ const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);
+ const [timeFormat, setTimeFormat] = useState("h:mma");
+ const telemetry = useTelemetry();
- function toggleTimeOptions() {
- setIsTimeOptionsOpen(!isTimeOptionsOpen);
- }
+ useEffect((): void => {
+ telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.pageView, collectPageParameters()));
+ }, [telemetry]);
- function toggleClockSticky() {
- localStorage.setItem('timeOption.is24hClock', (!is24h).toString());
- setIs24h(!is24h);
- }
+ // Handle month changes
+ const incrementMonth = () => {
+ setSelectedMonth(selectedMonth + 1);
+ };
- function setPreferredTimeZoneSticky({ value }: string) {
- localStorage.setItem('timeOption.preferredTimeZone', value);
- setSelectedTimeZone(value);
- }
+ const decrementMonth = () => {
+ setSelectedMonth(selectedMonth - 1);
+ };
- function initializeTimeOptions() {
- setSelectedTimeZone(localStorage.getItem('timeOption.preferredTimeZone') || dayjs.tz.guess());
- setIs24h(!!localStorage.getItem('timeOption.is24hClock'));
- }
+ // Set up calendar
+ const daysInMonth = dayjs().month(selectedMonth).daysInMonth();
+ const days = [];
+ for (let i = 1; i <= daysInMonth; i++) {
+ days.push(i);
+ }
- useEffect(() => {
- telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.pageView, collectPageParameters()))
- }, []);
-
- // Handle date change and timezone change
- useEffect(() => {
-
- if ( ! selectedTimeZone ) {
- initializeTimeOptions();
- }
-
- const changeDate = async () => {
- if (!selectedDate) {
- return
- }
-
- setLoading(true);
- setError(false);
-
- const res = await fetch(`/api/availability/${user}?dateFrom=${lowerBound.utc().format()}&dateTo=${upperBound.utc().format()}`);
- if (res.ok) {
- const busyTimes = await res.json();
- if (busyTimes.length > 0) setBusy(busyTimes);
- } else {
- setError(true);
- }
-
- setLoading(false);
- }
- changeDate();
- }, [selectedDate, selectedTimeZone]);
-
- // Get router variables
- const router = useRouter();
- const { user, rescheduleUid } = router.query;
-
- // Handle month changes
- const incrementMonth = () => {
- setSelectedMonth(selectedMonth + 1);
- }
-
- const decrementMonth = () => {
- setSelectedMonth(selectedMonth - 1);
- }
-
- // Need to define the bounds of the 24-hour window
- const lowerBound = useMemo(() => {
- if(!selectedDate) {
- return
- }
-
- return selectedDate.startOf('day')
- }, [selectedDate])
-
- const upperBound = useMemo(() => {
- if(!selectedDate) return
-
- return selectedDate.endOf('day')
- }, [selectedDate])
-
- // Set up calendar
- var daysInMonth = dayjs().month(selectedMonth).daysInMonth();
- var days = [];
- for (let i = 1; i <= daysInMonth; i++) {
- days.push(i);
- }
-
- // Create placeholder elements for empty days in first week
- let weekdayOfFirst = dayjs().month(selectedMonth).date(1).day();
- if (props.user.weekStart === 'Monday') {
- weekdayOfFirst -= 1;
- if (weekdayOfFirst < 0)
- weekdayOfFirst = 6;
- }
- const emptyDays = Array(weekdayOfFirst).fill(null).map((day, i) =>
-
- {null}
-
- );
-
- // Combine placeholder days with actual days
- const calendar = [...emptyDays, ...days.map((day) =>
-
- )];
-
- const times = useMemo(() =>
- getSlots({
- calendarTimeZone: props.user.timeZone,
- selectedTimeZone: selectedTimeZone,
- eventLength: props.eventType.length,
- selectedDate: selectedDate,
- dayStartTime: props.user.startTime,
- dayEndTime: props.user.endTime,
- })
- , [selectedDate, selectedTimeZone])
-
- // Check for conflicts
- for(let i = times.length - 1; i >= 0; i -= 1) {
- busy.forEach(busyTime => {
- let startTime = dayjs(busyTime.start);
- let endTime = dayjs(busyTime.end);
-
- // Check if start times are the same
- if (dayjs(times[i]).format('HH:mm') == startTime.format('HH:mm')) {
- times.splice(i, 1);
- }
-
- // Check if time is between start and end times
- if (dayjs(times[i]).isBetween(startTime, endTime)) {
- times.splice(i, 1);
- }
-
- // Check if slot end time is between start and end time
- if (dayjs(times[i]).add(props.eventType.length, 'minutes').isBetween(startTime, endTime)) {
- times.splice(i, 1);
- }
-
- // Check if startTime is between slot
- if(startTime.isBetween(dayjs(times[i]), dayjs(times[i]).add(props.eventType.length, 'minutes'))) {
- times.splice(i, 1);
- }
- });
- }
-
- // Display available times
- const availableTimes = times.map((time) =>
-