Moved DatePicker and PoweredByCalendso to seperate components
This commit is contained in:
parent
f99729c392
commit
b50fe16566
3 changed files with 149 additions and 130 deletions
109
components/booking/DatePicker.tsx
Normal file
109
components/booking/DatePicker.tsx
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import {ChevronLeftIcon, ChevronRightIcon} from "@heroicons/react/solid";
|
||||||
|
import {useEffect, useState} from "react";
|
||||||
|
|
||||||
|
const DatePicker = ({ weekStart, onDatePicked }) => {
|
||||||
|
|
||||||
|
const [selectedMonth, setSelectedMonth] = useState(dayjs().month());
|
||||||
|
const [selectedDay, setSelectedDay] = useState(dayjs().date());
|
||||||
|
const [hasPickedDate, setHasPickedDate] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (hasPickedDate) {
|
||||||
|
onDatePicked(dayjs().month(selectedMonth).date(selectedDay));
|
||||||
|
}
|
||||||
|
}, [hasPickedDate, selectedDay]);
|
||||||
|
|
||||||
|
// Handle month changes
|
||||||
|
const incrementMonth = () => {
|
||||||
|
setSelectedMonth(selectedMonth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const decrementMonth = () => {
|
||||||
|
setSelectedMonth(selectedMonth - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (weekStart === 'Monday') {
|
||||||
|
weekdayOfFirst -= 1;
|
||||||
|
if (weekdayOfFirst < 0)
|
||||||
|
weekdayOfFirst = 6;
|
||||||
|
}
|
||||||
|
const emptyDays = Array(weekdayOfFirst).fill(null).map((day, i) =>
|
||||||
|
<div key={`e-${i}`} className={"text-center w-10 h-10 rounded-full mx-auto"}>
|
||||||
|
{null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Combine placeholder days with actual days
|
||||||
|
const calendar = [...emptyDays, ...days.map((day) =>
|
||||||
|
<button key={day}
|
||||||
|
onClick={() => { setHasPickedDate(true); setSelectedDay(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().date(selectedDay).month(selectedMonth).format("D") == day ? ' bg-blue-600 text-white-important' : ''
|
||||||
|
)
|
||||||
|
}>
|
||||||
|
{day}
|
||||||
|
</button>
|
||||||
|
)];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
"mt-8 sm:mt-0 " +
|
||||||
|
(hasPickedDate
|
||||||
|
? "sm:w-1/3 border-r sm:px-4"
|
||||||
|
: "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">
|
||||||
|
{dayjs().month(selectedMonth).format("MMMM YYYY")}
|
||||||
|
</span>
|
||||||
|
<div className="w-1/2 text-right">
|
||||||
|
<button
|
||||||
|
onClick={decrementMonth}
|
||||||
|
className={
|
||||||
|
"mr-4 " +
|
||||||
|
(selectedMonth < parseInt(dayjs().format("MM")) &&
|
||||||
|
"text-gray-400")
|
||||||
|
}
|
||||||
|
disabled={selectedMonth < parseInt(dayjs().format("MM"))}
|
||||||
|
>
|
||||||
|
<ChevronLeftIcon className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
<button onClick={incrementMonth}>
|
||||||
|
<ChevronRightIcon className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-7 gap-y-4 text-center">
|
||||||
|
{
|
||||||
|
['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||||
|
.sort( (a, b) => weekStart.startsWith(a) ? -1 : weekStart.startsWith(b) ? 1 : 0 )
|
||||||
|
.map( (weekDay) =>
|
||||||
|
<div key={weekDay} className="uppercase text-gray-400 text-xs tracking-widest">{weekDay}</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{calendar}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DatePicker;
|
22
components/ui/PoweredByCalendso.tsx
Normal file
22
components/ui/PoweredByCalendso.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
const PoweredByCalendso = (props) => (
|
||||||
|
<div className="text-xs text-right pt-1">
|
||||||
|
<Link href="https://calendso.com">
|
||||||
|
<a
|
||||||
|
style={{ color: "#104D86" }}
|
||||||
|
className="opacity-50 hover:opacity-100"
|
||||||
|
>
|
||||||
|
powered by{" "}
|
||||||
|
<img
|
||||||
|
style={{ top: -2 }}
|
||||||
|
className="w-auto inline h-3 relative"
|
||||||
|
src="/calendso-logo-word.svg"
|
||||||
|
alt="Calendso Logo"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default PoweredByCalendso;
|
|
@ -2,7 +2,6 @@ import {useEffect, useState, useMemo} from 'react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import prisma from '../../lib/prisma';
|
import prisma from '../../lib/prisma';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import { ClockIcon, GlobeIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/solid';
|
import { ClockIcon, GlobeIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon } from '@heroicons/react/solid';
|
||||||
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
||||||
|
@ -17,16 +16,16 @@ import AvailableTimes from "../../components/booking/AvailableTimes";
|
||||||
import TimeOptions from "../../components/booking/TimeOptions"
|
import TimeOptions from "../../components/booking/TimeOptions"
|
||||||
import Avatar from '../../components/Avatar';
|
import Avatar from '../../components/Avatar';
|
||||||
import {timeZone} from "../../lib/clock";
|
import {timeZone} from "../../lib/clock";
|
||||||
|
import DatePicker from "../../components/booking/DatePicker";
|
||||||
|
import PoweredByCalendso from "../../components/ui/PoweredByCalendso";
|
||||||
|
import {useRouter} from "next/router";
|
||||||
|
|
||||||
export default function Type(props) {
|
export default function Type(props) {
|
||||||
|
|
||||||
// Get router variables
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { rescheduleUid } = router.query;
|
const { rescheduleUid } = router.query;
|
||||||
|
|
||||||
// Initialise state
|
|
||||||
const [selectedDate, setSelectedDate] = useState<Dayjs>();
|
const [selectedDate, setSelectedDate] = useState<Dayjs>();
|
||||||
const [selectedMonth, setSelectedMonth] = useState(dayjs().month());
|
|
||||||
const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);
|
const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);
|
||||||
const [timeFormat, setTimeFormat] = useState('hh:mm');
|
const [timeFormat, setTimeFormat] = useState('hh:mm');
|
||||||
const telemetry = useTelemetry();
|
const telemetry = useTelemetry();
|
||||||
|
@ -35,47 +34,11 @@ export default function Type(props) {
|
||||||
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.pageView, collectPageParameters()))
|
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.pageView, collectPageParameters()))
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Handle month changes
|
const changeDate = (date: Dayjs) => {
|
||||||
const incrementMonth = () => {
|
|
||||||
setSelectedMonth(selectedMonth + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const decrementMonth = () => {
|
|
||||||
setSelectedMonth(selectedMonth - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) =>
|
|
||||||
<div key={`e-${i}`} className={"text-center w-10 h-10 rounded-full mx-auto"}>
|
|
||||||
{null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
const changeDate = (day) => {
|
|
||||||
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.dateSelected, collectPageParameters()))
|
telemetry.withJitsu((jitsu) => jitsu.track(telemetryEventTypes.dateSelected, collectPageParameters()))
|
||||||
setSelectedDate(dayjs().month(selectedMonth).date(day))
|
setSelectedDate(date);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Combine placeholder days with actual days
|
|
||||||
const calendar = [...emptyDays, ...days.map((day) =>
|
|
||||||
<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' : '')}>
|
|
||||||
{day}
|
|
||||||
</button>
|
|
||||||
)];
|
|
||||||
|
|
||||||
const handleSelectTimeZone = (selectedTimeZone: string) => {
|
const handleSelectTimeZone = (selectedTimeZone: string) => {
|
||||||
if (selectedDate) {
|
if (selectedDate) {
|
||||||
setSelectedDate(selectedDate.tz(selectedTimeZone))
|
setSelectedDate(selectedDate.tz(selectedTimeZone))
|
||||||
|
@ -120,102 +83,27 @@ export default function Type(props) {
|
||||||
{props.eventType.length} minutes
|
{props.eventType.length} minutes
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={() => setIsTimeOptionsOpen(!isTimeOptionsOpen)}
|
onClick={() => setIsTimeOptionsOpen(true)}
|
||||||
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" />
|
||||||
{timeZone()}
|
{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 && <TimeOptions onSelectTimeZone={handleSelectTimeZone}
|
{ isTimeOptionsOpen && <TimeOptions onSelectTimeZone={handleSelectTimeZone}
|
||||||
onToggle24hClock={handleToggle24hClock} />}
|
onToggle24hClock={handleToggle24hClock} />}
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<DatePicker weekStart={props.user.weekStart} onDatePicked={changeDate} />
|
||||||
className={
|
|
||||||
"mt-8 sm:mt-0 " +
|
|
||||||
(selectedDate
|
|
||||||
? "sm:w-1/3 border-r sm:px-4"
|
|
||||||
: "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">
|
|
||||||
{dayjs().month(selectedMonth).format("MMMM YYYY")}
|
|
||||||
</span>
|
|
||||||
<div className="w-1/2 text-right">
|
|
||||||
<button
|
|
||||||
onClick={decrementMonth}
|
|
||||||
className={
|
|
||||||
"mr-4 " +
|
|
||||||
(selectedMonth < parseInt(dayjs().format("MM")) &&
|
|
||||||
"text-gray-400")
|
|
||||||
}
|
|
||||||
disabled={selectedMonth < parseInt(dayjs().format("MM"))}
|
|
||||||
>
|
|
||||||
<ChevronLeftIcon className="w-5 h-5" />
|
|
||||||
</button>
|
|
||||||
<button onClick={incrementMonth}>
|
|
||||||
<ChevronRightIcon className="w-5 h-5" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-7 gap-y-4 text-center">
|
|
||||||
{props.user.weekStart !== 'Monday' ? (
|
|
||||||
<div className="uppercase text-gray-400 text-xs tracking-widest">
|
|
||||||
Sun
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<div className="uppercase text-gray-400 text-xs tracking-widest">
|
|
||||||
Mon
|
|
||||||
</div>
|
|
||||||
<div className="uppercase text-gray-400 text-xs tracking-widest">
|
|
||||||
Tue
|
|
||||||
</div>
|
|
||||||
<div className="uppercase text-gray-400 text-xs tracking-widest">
|
|
||||||
Wed
|
|
||||||
</div>
|
|
||||||
<div className="uppercase text-gray-400 text-xs tracking-widest">
|
|
||||||
Thu
|
|
||||||
</div>
|
|
||||||
<div className="uppercase text-gray-400 text-xs tracking-widest">
|
|
||||||
Fri
|
|
||||||
</div>
|
|
||||||
<div className="uppercase text-gray-400 text-xs tracking-widest">
|
|
||||||
Sat
|
|
||||||
</div>
|
|
||||||
{props.user.weekStart === 'Monday' ? (
|
|
||||||
<div className="uppercase text-gray-400 text-xs tracking-widest">
|
|
||||||
Sun
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{calendar}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{selectedDate && <AvailableTimes timeFormat={timeFormat} user={props.user} eventType={props.eventType} date={selectedDate} />}
|
{selectedDate && <AvailableTimes timeFormat={timeFormat} user={props.user} eventType={props.eventType} date={selectedDate} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* note(peer):
|
{/* note(peer):
|
||||||
you can remove calendso branding here, but we'd also appreciate it, if you don't <3
|
you can remove calendso branding here, but we'd also appreciate it, if you don't <3
|
||||||
*/}
|
*/}
|
||||||
<div className="text-xs text-right pt-1">
|
<PoweredByCalendso />
|
||||||
<Link href="https://calendso.com">
|
|
||||||
<a
|
|
||||||
style={{ color: "#104D86" }}
|
|
||||||
className="opacity-50 hover:opacity-100"
|
|
||||||
>
|
|
||||||
powered by{" "}
|
|
||||||
<img
|
|
||||||
style={{ top: -2 }}
|
|
||||||
className="w-auto inline h-3 relative"
|
|
||||||
src="/calendso-logo-word.svg"
|
|
||||||
alt="Calendso Logo"
|
|
||||||
/>
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue