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 {ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ClockIcon, GlobeIcon} 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"; 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) { // Initialise state const [selectedDate, setSelectedDate] = useState(); const [selectedMonth, setSelectedMonth] = useState(dayjs().month()); const [loading, setLoading] = useState(false); const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false); const [is24h, setIs24h] = useState(false); const [busy, setBusy] = useState([]); const telemetry = useTelemetry(); const [selectedTimeZone, setSelectedTimeZone] = useState(''); function toggleTimeOptions() { setIsTimeOptionsOpen(!isTimeOptionsOpen); } function toggleClockSticky() { localStorage.setItem('timeOption.is24hClock', (!is24h).toString()); setIs24h(!is24h); } function setPreferredTimeZoneSticky({ value }: string) { localStorage.setItem('timeOption.preferredTimeZone', value); setSelectedTimeZone(value); } function initializeTimeOptions() { setSelectedTimeZone(localStorage.getItem('timeOption.preferredTimeZone') || dayjs.tz.guess()); setIs24h(!!localStorage.getItem('timeOption.is24hClock')); } 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); const res = await fetch(`/api/availability/${user}?dateFrom=${lowerBound.utc().format()}&dateTo=${upperBound.utc().format()}`); const busyTimes = await res.json(); if (busyTimes.length > 0) setBusy(busyTimes); 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) =>
{dayjs(time).tz(selectedTimeZone).format(is24h ? "HH:mm" : "hh:mma")}
); return (
{rescheduleUid && "Reschedule"} {props.eventType.title} | {props.user.name || props.user.username} | Calendso

{props.user.name}

{props.eventType.title}

{props.eventType.length} minutes

{isTimeOptionsOpen && (
Time Options
am/pm Use setting 24h
)}

{props.eventType.description}

{dayjs().month(selectedMonth).format("MMMM YYYY")}
{props.user.weekStart !== 'Monday' ? (
Sun
) : null}
Mon
Tue
Wed
Thu
Fri
Sat
{props.user.weekStart === 'Monday' ? (
Sun
) : null} {calendar}
{selectedDate && (
{dayjs(selectedDate).format("dddd DD MMMM YYYY")}
{!loading ? availableTimes :
}
)}
{/* note(peer): you can remove calendso branding here, but we'd also appreciate it, if you don't <3 */}
powered by{" "} Calendso Logo
); } export async function getServerSideProps(context) { const user = await prisma.user.findFirst({ where: { username: context.query.user, }, select: { id: true, username: true, name: true, email: true, bio: true, avatar: true, eventTypes: true, startTime: true, timeZone: true, endTime: true, weekStart: true, } }); if (!user ) { return { notFound: true, } } const eventType = await prisma.eventType.findFirst({ where: { userId: user.id, slug: { equals: context.query.type, }, }, select: { id: true, title: true, description: true, length: true } }); if (!eventType) { return { notFound: true } } return { props: { user, eventType, }, } }