Merge pull request #292 from emrysal/feature/decrease-conflicts-booking-page-by-component-splitting
Added <AvailableTimes /> and <TimeOptions /> components
This commit is contained in:
commit
0c8d2c74de
4 changed files with 407 additions and 347 deletions
87
components/booking/AvailableTimes.tsx
Normal file
87
components/booking/AvailableTimes.tsx
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import dayjs, {Dayjs} from "dayjs";
|
||||||
|
import isBetween from 'dayjs/plugin/isBetween';
|
||||||
|
dayjs.extend(isBetween);
|
||||||
|
import {useEffect, useMemo, useState} from "react";
|
||||||
|
import getSlots from "../../lib/slots";
|
||||||
|
import Link from "next/link";
|
||||||
|
import {timeZone} from "../../lib/clock";
|
||||||
|
import {useRouter} from "next/router";
|
||||||
|
|
||||||
|
const AvailableTimes = (props) => {
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const { user, rescheduleUid } = router.query;
|
||||||
|
const [loaded, setLoaded] = useState(false);
|
||||||
|
|
||||||
|
const times = getSlots({
|
||||||
|
calendarTimeZone: props.user.timeZone,
|
||||||
|
selectedTimeZone: timeZone(),
|
||||||
|
eventLength: props.eventType.length,
|
||||||
|
selectedDate: props.date,
|
||||||
|
dayStartTime: props.user.startTime,
|
||||||
|
dayEndTime: props.user.endTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleAvailableSlots = (busyTimes: []) => {
|
||||||
|
// Check for conflicts
|
||||||
|
for (let i = times.length - 1; i >= 0; i -= 1) {
|
||||||
|
busyTimes.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
|
||||||
|
setLoaded(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Re-render only when invitee changes date
|
||||||
|
useEffect(() => {
|
||||||
|
setLoaded(false);
|
||||||
|
fetch(`/api/availability/${user}?dateFrom=${props.date.startOf('day').utc().format()}&dateTo=${props.date.endOf('day').utc().format()}`)
|
||||||
|
.then( res => res.json())
|
||||||
|
.then(handleAvailableSlots);
|
||||||
|
}, [props.date]);
|
||||||
|
|
||||||
|
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="text-gray-600 font-light text-xl mb-4 text-left">
|
||||||
|
<span className="w-1/2">
|
||||||
|
{props.date.format("dddd DD MMMM YYYY")}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
loaded ? times.map((time) =>
|
||||||
|
<div key={dayjs(time).utc().format()}>
|
||||||
|
<Link
|
||||||
|
href={`/${props.user.username}/book?date=${dayjs(time).utc().format()}&type=${props.eventType.id}` + (rescheduleUid ? "&rescheduleUid=" + rescheduleUid : "")}>
|
||||||
|
<a key={dayjs(time).format("hh:mma")}
|
||||||
|
className="block font-medium mb-4 text-blue-600 border border-blue-600 rounded hover:text-white hover:bg-blue-600 py-4">{dayjs(time).tz(timeZone()).format(props.timeFormat)}</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
) : <div className="loader"></div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AvailableTimes;
|
73
components/booking/TimeOptions.tsx
Normal file
73
components/booking/TimeOptions.tsx
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import {Switch} from "@headlessui/react";
|
||||||
|
import TimezoneSelect from "react-timezone-select";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {timeZone, is24h} from '../../lib/clock';
|
||||||
|
|
||||||
|
function classNames(...classes) {
|
||||||
|
return classes.filter(Boolean).join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
const TimeOptions = (props) => {
|
||||||
|
|
||||||
|
const [selectedTimeZone, setSelectedTimeZone] = useState('');
|
||||||
|
const [is24hClock, setIs24hClock] = useState(false);
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
setIs24hClock(is24h());
|
||||||
|
setSelectedTimeZone(timeZone());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
props.onSelectTimeZone(timeZone(selectedTimeZone));
|
||||||
|
}, [selectedTimeZone]);
|
||||||
|
|
||||||
|
useEffect( () => {
|
||||||
|
props.onToggle24hClock(is24h(is24hClock));
|
||||||
|
}, [is24hClock]);
|
||||||
|
|
||||||
|
return selectedTimeZone !== "" && (
|
||||||
|
<div className="w-full rounded shadow border bg-white px-4 py-2">
|
||||||
|
<div className="flex mb-4">
|
||||||
|
<div className="w-1/2 font-medium">Time Options</div>
|
||||||
|
<div className="w-1/2">
|
||||||
|
<Switch.Group
|
||||||
|
as="div"
|
||||||
|
className="flex items-center justify-end"
|
||||||
|
>
|
||||||
|
<Switch.Label as="span" className="mr-3">
|
||||||
|
<span className="text-sm text-gray-500">am/pm</span>
|
||||||
|
</Switch.Label>
|
||||||
|
<Switch
|
||||||
|
checked={is24hClock}
|
||||||
|
onChange={setIs24hClock}
|
||||||
|
className={classNames(
|
||||||
|
is24hClock ? "bg-blue-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-blue-500"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="sr-only">Use setting</span>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className={classNames(
|
||||||
|
is24hClock ? "translate-x-3" : "translate-x-0",
|
||||||
|
"pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Switch>
|
||||||
|
<Switch.Label as="span" className="ml-3">
|
||||||
|
<span className="text-sm text-gray-500">24h</span>
|
||||||
|
</Switch.Label>
|
||||||
|
</Switch.Group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<TimezoneSelect
|
||||||
|
id="timeZone"
|
||||||
|
value={selectedTimeZone}
|
||||||
|
onChange={(tz) => setSelectedTimeZone(tz.value)}
|
||||||
|
className="mb-2 shadow-sm focus:ring-blue-500 focus:border-blue-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-md"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TimeOptions;
|
48
lib/clock.ts
Normal file
48
lib/clock.ts
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// handles logic related to user clock display using 24h display / timeZone options.
|
||||||
|
import dayjs, {Dayjs} from 'dayjs';
|
||||||
|
import utc from 'dayjs/plugin/utc';
|
||||||
|
import timezone from 'dayjs/plugin/timezone';
|
||||||
|
|
||||||
|
dayjs.extend(utc)
|
||||||
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
|
interface TimeOptions { is24hClock: boolean, inviteeTimeZone: string };
|
||||||
|
|
||||||
|
const timeOptions: TimeOptions = {
|
||||||
|
is24hClock: false,
|
||||||
|
inviteeTimeZone: '',
|
||||||
|
}
|
||||||
|
|
||||||
|
const isInitialized: boolean = false;
|
||||||
|
|
||||||
|
const initClock = () => {
|
||||||
|
if (typeof localStorage === "undefined" || isInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timeOptions.is24hClock = localStorage.getItem('timeOption.is24hClock') === "true";
|
||||||
|
timeOptions.inviteeTimeZone = localStorage.getItem('timeOption.preferredTimeZone') || dayjs.tz.guess();
|
||||||
|
}
|
||||||
|
|
||||||
|
const is24h = (is24hClock?: boolean) => {
|
||||||
|
initClock();
|
||||||
|
if(typeof is24hClock !== "undefined") set24hClock(is24hClock);
|
||||||
|
return timeOptions.is24hClock;
|
||||||
|
}
|
||||||
|
|
||||||
|
const set24hClock = (is24hClock: boolean) => {
|
||||||
|
localStorage.setItem('timeOption.is24hClock', is24hClock.toString());
|
||||||
|
timeOptions.is24hClock = is24hClock;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTimeZone(selectedTimeZone: string) {
|
||||||
|
localStorage.setItem('timeOption.preferredTimeZone', selectedTimeZone);
|
||||||
|
timeOptions.inviteeTimeZone = selectedTimeZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeZone = (selectedTimeZone?: string) => {
|
||||||
|
initClock();
|
||||||
|
if (selectedTimeZone) setTimeZone(selectedTimeZone)
|
||||||
|
return timeOptions.inviteeTimeZone;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {is24h, timeZone};
|
|
@ -4,86 +4,37 @@ import Link from 'next/link';
|
||||||
import prisma from '../../lib/prisma';
|
import prisma from '../../lib/prisma';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import {Switch} from '@headlessui/react';
|
import { ClockIcon, GlobeIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/solid';
|
||||||
import TimezoneSelect from 'react-timezone-select';
|
|
||||||
import {ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ClockIcon, GlobeIcon} from '@heroicons/react/solid';
|
|
||||||
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
||||||
import isBetween from 'dayjs/plugin/isBetween';
|
|
||||||
import utc from 'dayjs/plugin/utc';
|
import utc from 'dayjs/plugin/utc';
|
||||||
import timezone from 'dayjs/plugin/timezone';
|
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(isSameOrBefore);
|
||||||
dayjs.extend(isBetween);
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(timezone);
|
||||||
|
|
||||||
function classNames(...classes) {
|
import {collectPageParameters, telemetryEventTypes, useTelemetry} from "../../lib/telemetry";
|
||||||
return classes.filter(Boolean).join(' ')
|
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) {
|
export default function Type(props) {
|
||||||
|
|
||||||
|
// Get router variables
|
||||||
|
const router = useRouter();
|
||||||
|
const { rescheduleUid } = router.query;
|
||||||
|
|
||||||
// Initialise state
|
// Initialise state
|
||||||
const [selectedDate, setSelectedDate] = useState<Dayjs>();
|
const [selectedDate, setSelectedDate] = useState<Dayjs>();
|
||||||
const [selectedMonth, setSelectedMonth] = useState(dayjs().month());
|
const [selectedMonth, setSelectedMonth] = useState(dayjs().month());
|
||||||
const [loading, setLoading] = useState(false);
|
|
||||||
const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);
|
const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);
|
||||||
const [is24h, setIs24h] = useState(false);
|
const [timeFormat, setTimeFormat] = useState('hh:mm');
|
||||||
const [busy, setBusy] = useState([]);
|
|
||||||
const telemetry = useTelemetry();
|
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(() => {
|
useEffect(() => {
|
||||||
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.pageView, collectPageParameters()))
|
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
|
// Handle month changes
|
||||||
const incrementMonth = () => {
|
const incrementMonth = () => {
|
||||||
setSelectedMonth(selectedMonth + 1);
|
setSelectedMonth(selectedMonth + 1);
|
||||||
|
@ -93,21 +44,6 @@ export default function Type(props) {
|
||||||
setSelectedMonth(selectedMonth - 1);
|
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
|
// Set up calendar
|
||||||
var daysInMonth = dayjs().month(selectedMonth).daysInMonth();
|
var daysInMonth = dayjs().month(selectedMonth).daysInMonth();
|
||||||
var days = [];
|
var days = [];
|
||||||
|
@ -128,64 +64,30 @@ export default function Type(props) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const changeDate = (day) => {
|
||||||
|
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.dateSelected, collectPageParameters()))
|
||||||
|
setSelectedDate(dayjs().month(selectedMonth).date(day))
|
||||||
|
};
|
||||||
|
|
||||||
// Combine placeholder days with actual days
|
// Combine placeholder days with actual days
|
||||||
const calendar = [...emptyDays, ...days.map((day) =>
|
const calendar = [...emptyDays, ...days.map((day) =>
|
||||||
<button key={day} onClick={(e) => {
|
<button key={day} onClick={() => changeDate(day)} disabled={selectedMonth < parseInt(dayjs().format('MM')) && dayjs().month(selectedMonth).format("D") > day} className={"text-center w-10 h-10 rounded-full mx-auto " + (dayjs().isSameOrBefore(dayjs().date(day).month(selectedMonth)) ? 'bg-blue-50 text-blue-600 font-medium' : 'text-gray-400 font-light') + (dayjs(selectedDate).month(selectedMonth).format("D") == day ? ' bg-blue-600 text-white-important' : '')}>
|
||||||
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.dateSelected, collectPageParameters()))
|
|
||||||
setSelectedDate(dayjs().tz(selectedTimeZone).month(selectedMonth).date(day))
|
|
||||||
}} disabled={selectedMonth < parseInt(dayjs().format('MM')) && dayjs().month(selectedMonth).format("D") > day} className={"text-center w-10 h-10 rounded-full mx-auto " + (dayjs().isSameOrBefore(dayjs().date(day).month(selectedMonth)) ? 'bg-blue-50 text-blue-600 font-medium' : 'text-gray-400 font-light') + (dayjs(selectedDate).month(selectedMonth).format("D") == day ? ' bg-blue-600 text-white-important' : '')}>
|
|
||||||
{day}
|
{day}
|
||||||
</button>
|
</button>
|
||||||
)];
|
)];
|
||||||
|
|
||||||
const times = useMemo(() =>
|
const handleSelectTimeZone = (selectedTimeZone: string) => {
|
||||||
getSlots({
|
if (selectedDate) {
|
||||||
calendarTimeZone: props.user.timeZone,
|
setSelectedDate(selectedDate.tz(selectedTimeZone))
|
||||||
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
|
const handleToggle24hClock = (is24hClock: boolean) => {
|
||||||
if (dayjs(times[i]).isBetween(startTime, endTime)) {
|
if (selectedDate) {
|
||||||
times.splice(i, 1);
|
setTimeFormat(is24hClock ? 'HH:mm' : 'h:mma');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) =>
|
|
||||||
<div key={dayjs(time).utc().format()}>
|
|
||||||
<Link href={`/${props.user.username}/book?date=${dayjs(time).utc().format()}&type=${props.eventType.id}` + (rescheduleUid ? "&rescheduleUid=" + rescheduleUid : "")}>
|
|
||||||
<a key={dayjs(time).format("hh:mma")} className="block font-medium mb-4 text-blue-600 border border-blue-600 rounded hover:text-white hover:bg-blue-600 py-4">{dayjs(time).tz(selectedTimeZone).format(is24h ? "HH:mm" : "hh:mma")}</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Head>
|
<Head>
|
||||||
|
@ -218,56 +120,15 @@ export default function Type(props) {
|
||||||
{props.eventType.length} minutes
|
{props.eventType.length} minutes
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={toggleTimeOptions}
|
onClick={() => setIsTimeOptionsOpen(!isTimeOptionsOpen)}
|
||||||
className="text-gray-500 mb-1 px-2 py-1 -ml-2"
|
className="text-gray-500 mb-1 px-2 py-1 -ml-2"
|
||||||
>
|
>
|
||||||
<GlobeIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
<GlobeIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||||
{selectedTimeZone}
|
{timeZone()}
|
||||||
<ChevronDownIcon className="inline-block w-4 h-4 ml-1 -mt-1" />
|
<ChevronDownIcon className="inline-block w-4 h-4 ml-1 -mt-1" />
|
||||||
</button>
|
</button>
|
||||||
{isTimeOptionsOpen && (
|
{ isTimeOptionsOpen && <TimeOptions onSelectTimeZone={handleSelectTimeZone}
|
||||||
<div className="w-full rounded shadow border bg-white px-4 py-2">
|
onToggle24hClock={handleToggle24hClock} />}
|
||||||
<div className="flex mb-4">
|
|
||||||
<div className="w-1/2 font-medium">Time Options</div>
|
|
||||||
<div className="w-1/2">
|
|
||||||
<Switch.Group
|
|
||||||
as="div"
|
|
||||||
className="flex items-center justify-end"
|
|
||||||
>
|
|
||||||
<Switch.Label as="span" className="mr-3">
|
|
||||||
<span className="text-sm text-gray-500">am/pm</span>
|
|
||||||
</Switch.Label>
|
|
||||||
<Switch
|
|
||||||
checked={is24h}
|
|
||||||
onChange={toggleClockSticky}
|
|
||||||
className={classNames(
|
|
||||||
is24h ? "bg-blue-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-blue-500"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Use setting</span>
|
|
||||||
<span
|
|
||||||
aria-hidden="true"
|
|
||||||
className={classNames(
|
|
||||||
is24h ? "translate-x-3" : "translate-x-0",
|
|
||||||
"pointer-events-none inline-block h-4 w-4 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Switch>
|
|
||||||
<Switch.Label as="span" className="ml-3">
|
|
||||||
<span className="text-sm text-gray-500">24h</span>
|
|
||||||
</Switch.Label>
|
|
||||||
</Switch.Group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<TimezoneSelect
|
|
||||||
id="timeZone"
|
|
||||||
value={selectedTimeZone}
|
|
||||||
onChange={setPreferredTimeZoneSticky}
|
|
||||||
className="mb-2 shadow-sm focus:ring-blue-500 focus:border-blue-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-md"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<p className="text-gray-600 mt-3 mb-8">
|
<p className="text-gray-600 mt-3 mb-8">
|
||||||
{props.eventType.description}
|
{props.eventType.description}
|
||||||
</p>
|
</p>
|
||||||
|
@ -333,16 +194,7 @@ export default function Type(props) {
|
||||||
{calendar}
|
{calendar}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{selectedDate && (
|
{selectedDate && <AvailableTimes timeFormat={timeFormat} user={props.user} eventType={props.eventType} date={selectedDate} />}
|
||||||
<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">
|
|
||||||
{dayjs(selectedDate).format("dddd DD MMMM YYYY")}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{!loading ? availableTimes : <div className="loader"></div>}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* note(peer):
|
{/* note(peer):
|
||||||
|
@ -412,7 +264,7 @@ export async function getServerSideProps(context) {
|
||||||
|
|
||||||
if (!eventType) {
|
if (!eventType) {
|
||||||
return {
|
return {
|
||||||
notFound: true
|
notFound: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue