Add Availability component
This commit is contained in:
parent
fe1dafae3c
commit
036bbcb396
@ -8,6 +8,8 @@
|
||||
"@fullcalendar/interaction": "^5.0.0-beta.4",
|
||||
"@fullcalendar/luxon": "^4.4.2",
|
||||
"@fullcalendar/react": "^5.0.0-beta.4",
|
||||
"@fullcalendar/scrollgrid": "5.0.0-beta.4",
|
||||
"@fullcalendar/timegrid": "5.0.0-beta.4",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
|
@ -1,9 +0,0 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function IntervalSelector() {
|
||||
return (
|
||||
<div>
|
||||
It's empty for now
|
||||
</div>
|
||||
)
|
||||
}
|
153
src/components/Interval/IntervalSelector.js
Normal file
153
src/components/Interval/IntervalSelector.js
Normal file
@ -0,0 +1,153 @@
|
||||
import React from 'react';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { Icon, IconButton } from 'rsuite';
|
||||
|
||||
import FullCalendar from '@fullcalendar/react';
|
||||
import dayGridPlugin from '@fullcalendar/daygrid';
|
||||
import timeGridPlugin from '@fullcalendar/timegrid';
|
||||
import interaction from '@fullcalendar/interaction';
|
||||
import scrollGrid from '@fullcalendar/scrollgrid';
|
||||
|
||||
// FullCalendar props
|
||||
const dayMinWidth = 110;
|
||||
const slotMinTime = '08:00:00';
|
||||
const slotMaxTime = '18:00:00';
|
||||
|
||||
export default function IntervalSelector({
|
||||
selectedDates,
|
||||
handleSelect,
|
||||
availability,
|
||||
setAvailability,
|
||||
}) {
|
||||
/// DATES DISPLAY
|
||||
let startDate = selectedDates[0].start;
|
||||
// create a Datetime object from the last event date,
|
||||
// add one day, and change it to a date string
|
||||
let lastEventDate = selectedDates[selectedDates.length - 1].start;
|
||||
let endDate = DateTime.fromFormat(lastEventDate, 'yyyy-MM-dd')
|
||||
.plus({ days: 1 })
|
||||
.toFormat('yyyy-MM-dd');
|
||||
|
||||
// Create an event dates list used to create the days columns
|
||||
const daysList = [];
|
||||
selectedDates.forEach((event) => {
|
||||
daysList.push(event.start);
|
||||
});
|
||||
|
||||
const handleDayDidMount = (info) => {
|
||||
let currentDate = DateTime.fromJSDate(info.date).toFormat('yyyy-MM-dd');
|
||||
if (!daysList.includes(currentDate)) {
|
||||
info.el.remove();
|
||||
}
|
||||
};
|
||||
const handleViewMount = ({ el }) => {
|
||||
// Adapt column width to number of dates present
|
||||
const timegridBody = el.querySelectorAll('div.fc-timegrid-body');
|
||||
const timegridSlots = el.querySelectorAll(
|
||||
'div.fc-timegrid-slots table',
|
||||
);
|
||||
// Update width based on rendered dates number
|
||||
let newWidth = daysList.length * dayMinWidth;
|
||||
timegridBody[0].style['min-width'] = `${newWidth}px`;
|
||||
timegridSlots[0].style['min-width'] = `${newWidth}px`;
|
||||
};
|
||||
|
||||
/// AVAILABILITY INTERACTION
|
||||
// Create an array of stringified availability
|
||||
let avListJson = [];
|
||||
availability.forEach((event) => {
|
||||
avListJson.push(JSON.stringify(event));
|
||||
});
|
||||
|
||||
const handleClick = (eventStart, eventEnd) => {
|
||||
let updatedAvailibility = [];
|
||||
// Create an event object to compare
|
||||
let currentAvailability = {
|
||||
start: eventStart,
|
||||
end: eventEnd,
|
||||
};
|
||||
// Make it to JSON
|
||||
let currentAvJson = JSON.stringify(currentAvailability);
|
||||
|
||||
// Check if there's a similar event obj in the availability
|
||||
let index = avListJson.findIndex(
|
||||
(availability) => currentAvJson === availability,
|
||||
);
|
||||
if (index > -1) {
|
||||
updatedAvailibility = [...availability];
|
||||
updatedAvailibility.splice(index, 1);
|
||||
}
|
||||
|
||||
setAvailability([...updatedAvailibility]);
|
||||
};
|
||||
|
||||
const handleContent = ({ event }) => {
|
||||
let eventStart = event.start;
|
||||
let eventEnd = event.end;
|
||||
|
||||
let evStartDT = DateTime.fromJSDate(eventStart);
|
||||
let evEndDT = DateTime.fromJSDate(eventEnd);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
}}
|
||||
>
|
||||
<p>
|
||||
{evStartDT.toFormat('HH:mm')} -{' '}
|
||||
{evEndDT.toFormat('HH:mm')}
|
||||
</p>
|
||||
<IconButton
|
||||
className={'event-close'}
|
||||
icon={<Icon icon='close' />}
|
||||
appearance="default"
|
||||
size="xs"
|
||||
circle
|
||||
onClick={() => handleClick(eventStart, eventEnd)}
|
||||
></IconButton>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<FullCalendar
|
||||
plugins={[dayGridPlugin, interaction, timeGridPlugin, scrollGrid]}
|
||||
// timeZone={'UTC'}
|
||||
events={availability}
|
||||
// View props
|
||||
headerToolbar={{ right: '' }}
|
||||
initialView='timeGrid'
|
||||
visibleRange={{
|
||||
start: startDate,
|
||||
end: endDate,
|
||||
}}
|
||||
allDaySlot={false}
|
||||
dayMinWidth={dayMinWidth}
|
||||
dayHeaderFormat={{
|
||||
weekday: 'short',
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
omitCommas: true,
|
||||
}}
|
||||
slotMinTime={slotMinTime}
|
||||
slotMaxTime={slotMaxTime}
|
||||
dayCellDidMount={(arg) => handleDayDidMount(arg)}
|
||||
dayHeaderDidMount={(arg) => handleDayDidMount(arg)}
|
||||
viewDidMount={(arg) => handleViewMount(arg)}
|
||||
// Interaction props
|
||||
selectable={true}
|
||||
unselectAuto={true}
|
||||
selectOverlap={false}
|
||||
select={(info) => handleSelect(info)}
|
||||
longPressDelay={150}
|
||||
eventContent={(arg) => handleContent(arg)}
|
||||
/>
|
||||
);
|
||||
}
|
@ -18,7 +18,7 @@ export default function MenuDropdown() {
|
||||
<Dropdown.Item eventKey={'login'}>Login</Dropdown.Item>
|
||||
<Dropdown.Item eventKey={'register'}>Register</Dropdown.Item>
|
||||
<Dropdown.Item eventKey={'/'}>Dashboard</Dropdown.Item>
|
||||
<Dropdown.Item eventKey={3}>Schedule a Meeting</Dropdown.Item>
|
||||
<Dropdown.Item eventKey={'schedule'}>Schedule a Meeting</Dropdown.Item>
|
||||
<Dropdown.Item eventKey={4}>Account Settings</Dropdown.Item>
|
||||
<Dropdown.Item eventKey={5}>Log Out</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
|
@ -9,18 +9,18 @@ export default function DaySelector({ eventsList, handleSelect, handleClear }) {
|
||||
return (
|
||||
<FullCalendar
|
||||
plugins={[dayGridPlugin, interaction]}
|
||||
initialView='dayGridMonth'
|
||||
// showNonCurrentDates={false}
|
||||
selectable={true}
|
||||
// unselectAuto={false}
|
||||
longPressDelay={150}
|
||||
select={(info) => handleSelect(info)}
|
||||
defaultAllDay={true}
|
||||
events={eventsList}
|
||||
// view props
|
||||
initialView='dayGridMonth'
|
||||
defaultAllDay={true}
|
||||
customButtons={{
|
||||
resetSelection: { text: 'clear selection', click: handleClear },
|
||||
}}
|
||||
headerToolbar={{ right: 'resetSelection today prev,next' }}
|
||||
// interaction props
|
||||
selectable={true}
|
||||
longPressDelay={150}
|
||||
select={(info) => handleSelect(info)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import {
|
||||
Panel,
|
||||
Form,
|
||||
@ -9,43 +10,32 @@ import {
|
||||
Message,
|
||||
FormGroup,
|
||||
} from 'rsuite';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import NavBar from '../../components/Navbar/NavBar';
|
||||
import TimezonePicker from '../../components/General/TimezonePicker';
|
||||
import IntervalSelector from '../../components/Availability/IntervalSelector';
|
||||
import IntervalSelector from '../../components/Interval/IntervalSelector';
|
||||
|
||||
import './Availability.less';
|
||||
|
||||
const eventsList = [
|
||||
{
|
||||
start: '2020-06-10',
|
||||
display: 'background',
|
||||
},
|
||||
{
|
||||
start: '2020-06-11',
|
||||
display: 'background',
|
||||
},
|
||||
{
|
||||
start: '2020-06-12',
|
||||
display: 'background',
|
||||
},
|
||||
{
|
||||
start: '2020-06-13',
|
||||
display: 'background',
|
||||
},
|
||||
];
|
||||
|
||||
export default function Availability({ title }) {
|
||||
// const [eventsList, setEventsList] = useState([]);
|
||||
const [availability, setAvailability] = useState([]);
|
||||
const [availability, setAvailability] = useState([...availabilityList]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(availability);
|
||||
}, [availability]);
|
||||
|
||||
const handleClear = () => {
|
||||
setAvailability([]);
|
||||
};
|
||||
|
||||
const handleSelect = () => {
|
||||
console.log('Congrats, you have selected!');
|
||||
const handleSelect = ({ start, end }) => {
|
||||
let updatedEvents = [];
|
||||
let newAvailability = {
|
||||
start: start,
|
||||
end: end,
|
||||
};
|
||||
updatedEvents.push(newAvailability);
|
||||
setAvailability([...availability, ...updatedEvents]);
|
||||
};
|
||||
|
||||
return (
|
||||
@ -95,7 +85,9 @@ export default function Availability({ title }) {
|
||||
description='Select your availibility on the calendar.'
|
||||
/>
|
||||
<IntervalSelector
|
||||
eventsList={eventsList}
|
||||
selectedDates={eventsList}
|
||||
availability={availability}
|
||||
setAvailability={setAvailability}
|
||||
handleSelect={handleSelect}
|
||||
handleClear={handleClear}
|
||||
/>
|
||||
@ -106,18 +98,76 @@ export default function Availability({ title }) {
|
||||
);
|
||||
}
|
||||
|
||||
// Convert events to Luxon Datetime objects
|
||||
const eventsToDates = (events) => {
|
||||
let dates = [];
|
||||
events.forEach((event) => {
|
||||
dates.push(DateTime.fromFormat(event.start, 'yyyy-MM-dd'));
|
||||
});
|
||||
return dates;
|
||||
};
|
||||
|
||||
const containerStyle = {
|
||||
// TODO Move to a .less file
|
||||
maxWidth: '1200px',
|
||||
margin: '30px auto',
|
||||
backgroundColor: 'rgba(255,255,255,0.6)',
|
||||
};
|
||||
|
||||
// TODO Remove fake data
|
||||
const eventsList = [
|
||||
{
|
||||
start: '2020-06-05',
|
||||
display: 'background',
|
||||
},
|
||||
{
|
||||
start: '2020-06-06',
|
||||
display: 'background',
|
||||
},
|
||||
{
|
||||
start: '2020-06-08',
|
||||
display: 'background',
|
||||
},
|
||||
{
|
||||
start: '2020-06-10',
|
||||
display: 'background',
|
||||
},
|
||||
{
|
||||
start: '2020-06-11',
|
||||
display: 'background',
|
||||
},
|
||||
{
|
||||
start: '2020-06-21',
|
||||
display: 'background',
|
||||
},
|
||||
{
|
||||
start: '2020-06-29',
|
||||
display: 'background',
|
||||
},
|
||||
];
|
||||
|
||||
const availabilityList = [
|
||||
{
|
||||
start: '2020-06-08T08:00:00.000Z',
|
||||
end: '2020-06-08T12:00:00.000Z',
|
||||
},
|
||||
{
|
||||
start: '2020-06-10T09:00:00.000Z',
|
||||
end: '2020-06-10T13:00:00.000Z',
|
||||
},
|
||||
{
|
||||
start: '2020-06-11T09:00:00.000Z',
|
||||
end: '2020-06-11T13:00:00.000Z',
|
||||
},
|
||||
{
|
||||
start: '2020-06-11T06:30:00.000Z',
|
||||
end: '2020-06-11T08:30:00.000Z',
|
||||
},
|
||||
{
|
||||
start: '2020-06-21T07:00:00.000Z',
|
||||
end: '2020-06-21T11:30:00.000Z',
|
||||
},
|
||||
{
|
||||
start: '2020-06-21T12:00:00.000Z',
|
||||
end: '2020-06-21T15:30:00.000Z',
|
||||
},
|
||||
{
|
||||
start: '2020-06-06T08:00:00.000Z',
|
||||
end: '2020-06-06T14:00:00.000Z',
|
||||
},
|
||||
{
|
||||
start: '2020-06-05T10:30:00.000Z',
|
||||
end: '2020-06-05T14:30:00.000Z',
|
||||
},
|
||||
];
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import { Panel, Form, FormGroup, FormControl, ControlLabel, HelpBlock, Button } from 'rsuite';
|
||||
import { Panel, Form, FormGroup, FormControl, ControlLabel, Button } from 'rsuite';
|
||||
|
||||
import NavBar from './../components/Navbar/NavBar';
|
||||
|
||||
|
28
yarn.lock
28
yarn.lock
@ -1061,7 +1061,7 @@
|
||||
preact "^10.0.5"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@fullcalendar/daygrid@^5.0.0-beta.4":
|
||||
"@fullcalendar/daygrid@5.0.0-beta.4", "@fullcalendar/daygrid@^5.0.0-beta.4":
|
||||
version "5.0.0-beta.4"
|
||||
resolved "https://registry.yarnpkg.com/@fullcalendar/daygrid/-/daygrid-5.0.0-beta.4.tgz#b5616fddfe9a5496e5e98f20d6fafdcb6393a42e"
|
||||
integrity sha512-ZWCF4dUCDngPqOJ3iUGl/ZiSOah8mgo9fayPR3WLazGMoQTsSAqMBGSuhFaNNdMDWQ+dhcT2JBa8e9DNoe2S+g==
|
||||
@ -1082,6 +1082,14 @@
|
||||
resolved "https://registry.yarnpkg.com/@fullcalendar/luxon/-/luxon-4.4.2.tgz#27b4913abd6fb323da8fda63e1bd03df96b21815"
|
||||
integrity sha512-NQFPsdDAyk152JzsyNMrfZWpnZUTxCrP3to/0es8Eoa97i/UxjxB5+N0SVwJqcNO0rQpaLHmeg5piHhraABShQ==
|
||||
|
||||
"@fullcalendar/premium-common@5.0.0-beta.4":
|
||||
version "5.0.0-beta.4"
|
||||
resolved "https://registry.yarnpkg.com/@fullcalendar/premium-common/-/premium-common-5.0.0-beta.4.tgz#fd6571f2cba1026a9a42da8385c6e8febfdc7065"
|
||||
integrity sha512-ZxmlzzjKAVy/xPP/HKkgxit0utPmHyjLw8Zj83KhmWHM7DRo9Eko0rNrSm/gDvsk0YkWpoiJRKcRRu022ulzLQ==
|
||||
dependencies:
|
||||
"@fullcalendar/common" "5.0.0-beta.4"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@fullcalendar/react@^5.0.0-beta.4":
|
||||
version "5.0.0-beta.4"
|
||||
resolved "https://registry.yarnpkg.com/@fullcalendar/react/-/react-5.0.0-beta.4.tgz#9c721093bfb49f938469f93960a10894623e152c"
|
||||
@ -1090,6 +1098,24 @@
|
||||
"@fullcalendar/common" "5.0.0-beta.4"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@fullcalendar/scrollgrid@5.0.0-beta.4":
|
||||
version "5.0.0-beta.4"
|
||||
resolved "https://registry.yarnpkg.com/@fullcalendar/scrollgrid/-/scrollgrid-5.0.0-beta.4.tgz#e4651af5f44b0156aa838b8a292358da9897d41c"
|
||||
integrity sha512-E9pkjqW5+BDWXo07SBaHei7VMNR5OozzRXFGDRMU1W/u+BzMP4IypBp0M8wDtL78IoJK3fNsv49vOWsHi4x7Mg==
|
||||
dependencies:
|
||||
"@fullcalendar/common" "5.0.0-beta.4"
|
||||
"@fullcalendar/premium-common" "5.0.0-beta.4"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@fullcalendar/timegrid@5.0.0-beta.4":
|
||||
version "5.0.0-beta.4"
|
||||
resolved "https://registry.yarnpkg.com/@fullcalendar/timegrid/-/timegrid-5.0.0-beta.4.tgz#e31b92772705738191617c9f1af4b91d27fcf8c6"
|
||||
integrity sha512-kdI3NQ0Yt7bPxn0v6d01AyQeW5gKVPgGA8J2hsTpITkN/eaQJUxrmmOGEFhSVwvvJU0m+ji71Z30eKESNs/leA==
|
||||
dependencies:
|
||||
"@fullcalendar/common" "5.0.0-beta.4"
|
||||
"@fullcalendar/daygrid" "5.0.0-beta.4"
|
||||
tslib "^1.9.3"
|
||||
|
||||
"@hapi/address@2.x.x":
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
|
||||
|
Loading…
Reference in New Issue
Block a user