Merge pull request #163 from 50bbx/feature/add-timezone-select-in-calendar-view
Add timezone select in calendar viewpull/166/head
commit
60d7351eeb
|
@ -23,7 +23,7 @@ const getSlots = ({
|
||||||
|
|
||||||
if(!selectedDate) return []
|
if(!selectedDate) return []
|
||||||
|
|
||||||
const lowerBound = selectedDate.startOf("day");
|
const lowerBound = selectedDate.tz(selectedTimeZone).startOf("day");
|
||||||
|
|
||||||
// Simple case, same timezone
|
// Simple case, same timezone
|
||||||
if (calendarTimeZone === selectedTimeZone) {
|
if (calendarTimeZone === selectedTimeZone) {
|
||||||
|
@ -42,7 +42,7 @@ const getSlots = ({
|
||||||
return slots;
|
return slots;
|
||||||
}
|
}
|
||||||
|
|
||||||
const upperBound = selectedDate.endOf("day");
|
const upperBound = selectedDate.tz(selectedTimeZone).endOf("day");
|
||||||
|
|
||||||
// We need to start generating slots from the start of the calendarTimeZone day
|
// We need to start generating slots from the start of the calendarTimeZone day
|
||||||
const startDateTime = lowerBound
|
const startDateTime = lowerBound
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
"next-transpile-modules": "^7.0.0",
|
"next-transpile-modules": "^7.0.0",
|
||||||
"react": "17.0.1",
|
"react": "17.0.1",
|
||||||
"react-dom": "17.0.1",
|
"react-dom": "17.0.1",
|
||||||
"react-timezone-select": "^0.10.10"
|
"react-timezone-select": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^14.14.33",
|
"@types/node": "^14.14.33",
|
||||||
|
|
|
@ -5,6 +5,7 @@ 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 { Switch } from '@headlessui/react';
|
||||||
|
import TimezoneSelect from 'react-timezone-select';
|
||||||
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';
|
||||||
import isBetween from 'dayjs/plugin/isBetween';
|
import isBetween from 'dayjs/plugin/isBetween';
|
||||||
|
@ -32,6 +33,14 @@ export default function Type(props) {
|
||||||
const [busy, setBusy] = useState([]);
|
const [busy, setBusy] = useState([]);
|
||||||
const telemetry = useTelemetry();
|
const telemetry = useTelemetry();
|
||||||
|
|
||||||
|
const [selectedTimeZone, setSelectedTimeZone] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Setting timezone only client-side
|
||||||
|
setSelectedTimeZone(dayjs.tz.guess())
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
|
||||||
// Get router variables
|
// Get router variables
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { user } = router.query;
|
const { user } = router.query;
|
||||||
|
@ -81,13 +90,13 @@ export default function Type(props) {
|
||||||
const calendar = [...emptyDays, ...days.map((day) =>
|
const calendar = [...emptyDays, ...days.map((day) =>
|
||||||
<button key={day} onClick={(e) => {
|
<button key={day} onClick={(e) => {
|
||||||
telemetry.withJitsu((jitsu) => jitsu.track('date_selected', {page_title: "", source_ip: ""}))
|
telemetry.withJitsu((jitsu) => jitsu.track('date_selected', {page_title: "", source_ip: ""}))
|
||||||
setSelectedDate(dayjs().tz(dayjs.tz.guess()).month(selectedMonth).date(day))
|
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' : '')}>
|
}} 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>
|
||||||
)];
|
)];
|
||||||
|
|
||||||
// Handle date change
|
// Handle date change and timezone change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const changeDate = async () => {
|
const changeDate = async () => {
|
||||||
if (!selectedDate) {
|
if (!selectedDate) {
|
||||||
|
@ -101,17 +110,18 @@ export default function Type(props) {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
changeDate();
|
changeDate();
|
||||||
}, [selectedDate]);
|
}, [selectedDate, selectedTimeZone]);
|
||||||
|
|
||||||
|
const times = useMemo(() =>
|
||||||
const times = getSlots({
|
getSlots({
|
||||||
calendarTimeZone: props.user.timeZone,
|
calendarTimeZone: props.user.timeZone,
|
||||||
selectedTimeZone: dayjs.tz.guess(),
|
selectedTimeZone: selectedTimeZone,
|
||||||
eventLength: props.eventType.length,
|
eventLength: props.eventType.length,
|
||||||
selectedDate: selectedDate,
|
selectedDate: selectedDate,
|
||||||
dayStartTime: props.user.startTime,
|
dayStartTime: props.user.startTime,
|
||||||
dayEndTime: props.user.endTime,
|
dayEndTime: props.user.endTime,
|
||||||
})
|
})
|
||||||
|
, [selectedDate, selectedTimeZone])
|
||||||
|
|
||||||
// Check for conflicts
|
// Check for conflicts
|
||||||
for(let i = times.length - 1; i >= 0; i -= 1) {
|
for(let i = times.length - 1; i >= 0; i -= 1) {
|
||||||
|
@ -135,7 +145,7 @@ export default function Type(props) {
|
||||||
const availableTimes = times.map((time) =>
|
const availableTimes = times.map((time) =>
|
||||||
<div key={dayjs(time).utc().format()}>
|
<div key={dayjs(time).utc().format()}>
|
||||||
<Link href={`/${props.user.username}/book?date=${dayjs(time).utc().format()}&type=${props.eventType.id}`}>
|
<Link href={`/${props.user.username}/book?date=${dayjs(time).utc().format()}&type=${props.eventType.id}`}>
|
||||||
<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(dayjs.tz.guess()).format(is24h ? "HH:mm" : "hh:mma")}</a>
|
<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>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -158,13 +168,9 @@ export default function Type(props) {
|
||||||
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
<ClockIcon className="inline-block w-4 h-4 mr-1 -mt-1" />
|
||||||
{props.eventType.length} minutes
|
{props.eventType.length} minutes
|
||||||
</p>
|
</p>
|
||||||
<button onClick={toggleTimeOptions} className="text-gray-500 mb-1 hover:bg-gray-100 rounded-full px-2 -ml-2 cursor-pointer">
|
<p 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"/>
|
||||||
{dayjs.tz.guess()} <ChevronDownIcon className="inline-block w-4 h-4 mb-1" />
|
<Switch.Group as="span" className="">
|
||||||
</button>
|
|
||||||
{ isTimeOptionsOpen &&
|
|
||||||
<div className="bg-white rounded shadow p-4 absolute w-72">
|
|
||||||
<Switch.Group as="div" className="flex items-center">
|
|
||||||
<Switch.Label as="span" className="mr-3">
|
<Switch.Label as="span" className="mr-3">
|
||||||
<span className="text-sm text-gray-500">am/pm</span>
|
<span className="text-sm text-gray-500">am/pm</span>
|
||||||
</Switch.Label>
|
</Switch.Label>
|
||||||
|
@ -189,8 +195,10 @@ export default function Type(props) {
|
||||||
<span className="text-sm text-gray-500">24h</span>
|
<span className="text-sm text-gray-500">24h</span>
|
||||||
</Switch.Label>
|
</Switch.Label>
|
||||||
</Switch.Group>
|
</Switch.Group>
|
||||||
</div>
|
</p>
|
||||||
}
|
<p className="mt-1 text-gray-500">
|
||||||
|
<TimezoneSelect id="timeZone" value={selectedTimeZone} onChange={({ value }) =>setSelectedTimeZone(value)} className="shadow-sm focus:ring-blue-500 focus:border-blue-500 mt-1 block w-full sm:text-sm border-gray-300 rounded-md" />
|
||||||
|
</p>
|
||||||
<p className="text-gray-600 mt-3 mb-8">{props.eventType.description}</p>
|
<p className="text-gray-600 mt-3 mb-8">{props.eventType.description}</p>
|
||||||
</div>
|
</div>
|
||||||
<div 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={"mt-8 sm:mt-0 " + (selectedDate ? 'sm:w-1/3 border-r sm:px-4' : 'sm:w-1/2 sm:pl-4')}>
|
||||||
|
|
|
@ -2625,10 +2625,10 @@ react-select@^4.2.1:
|
||||||
react-input-autosize "^3.0.0"
|
react-input-autosize "^3.0.0"
|
||||||
react-transition-group "^4.3.0"
|
react-transition-group "^4.3.0"
|
||||||
|
|
||||||
react-timezone-select@^0.10.10:
|
react-timezone-select@^1.0.2:
|
||||||
version "0.10.10"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/react-timezone-select/-/react-timezone-select-0.10.10.tgz#853aeb73e84fcf00bd01eb57c35f2df1b84e1cc0"
|
resolved "https://registry.yarnpkg.com/react-timezone-select/-/react-timezone-select-1.0.2.tgz#37e6d99bc15372bd3f9ebc7541bda6f3a10c852b"
|
||||||
integrity sha512-PEEQQkiL+fFW3940MmhrX6xNf2VMz16BW2UyF6Mu7jzCv89McwJ93Bp5mqE6ouhLPZSsyTnhjILifsEFUUMuFg==
|
integrity sha512-MDv4rmDkop3nZcIH27tLwIuIVovJE6ushmr9r0kR1SSzVErdydV01vI1ch8u4JAAFpnR5lYDt5PBqQW/lUS+Jg==
|
||||||
dependencies:
|
dependencies:
|
||||||
react-select "^4.2.1"
|
react-select "^4.2.1"
|
||||||
spacetime "^6.14.0"
|
spacetime "^6.14.0"
|
||||||
|
|
Loading…
Reference in New Issue