frontend/src/screens/Schedule.js

341 lines
8.4 KiB
JavaScript
Raw Normal View History

import React, { useState, useEffect } from 'react';
import {
Panel,
Form,
FormControl,
ControlLabel,
HelpBlock,
Button,
ButtonGroup,
Message,
FormGroup,
} from 'rsuite';
import { DateTime } from 'luxon';
2020-08-22 20:49:03 +00:00
import { useHistory } from 'react-router-dom';
2020-08-21 23:08:18 +00:00
import {
NavBar,
DaySelector,
DurationSelector,
SelectedDates,
} from '../components';
2020-08-22 20:49:03 +00:00
import { backend } from '../helpers/http-common';
import { durations } from '../assets/data/durations';
2020-08-21 23:08:18 +00:00
import './styles/Schedule.less';
2020-08-22 20:49:03 +00:00
export default function Schedule({
possibleDates,
setPossibleDates,
currentMeeting,
setCurrentMeeting,
currentUser,
2020-08-22 20:49:03 +00:00
}) {
const [eventsList, setEventsList] = useState([]);
const [datesList, setDatesList] = useState(eventsToDates(eventsList));
2020-08-22 20:49:03 +00:00
const [durationIdx, setDurationIdx] = useState(0);
const [error, setError] = useState(false);
const history = useHistory();
2020-08-22 20:49:03 +00:00
useEffect(() => {
// Push the selected dates up the state tree
// but keep all related logic here
setPossibleDates(eventsList);
}, [eventsList, setPossibleDates]);
2020-08-22 20:49:03 +00:00
useEffect(() => {
2020-06-02 14:40:08 +00:00
let updatedDates = eventsToDates(eventsList).sort();
setDatesList(updatedDates);
}, [eventsList]);
// EVENTS & DATES
2020-08-22 20:49:03 +00:00
const handleSelectDates = (info) => {
let updatedEvents = [];
let datesList = new Set();
/// Add each date selected to datesList
let start = DateTime.fromISO(info.startStr);
let end = DateTime.fromISO(info.endStr);
// Check if selection contains only one day
let singleDay = end.toISODate() === start.plus({ days: 1 }).toISODate();
if (singleDay) {
// When a single date is selected
let selectedDate = info.startStr;
let selectedEvent = { start: selectedDate, display: 'background' };
// If selectedEvent exists in the list, find and set its index
let selectedEventIndex;
for (let index = 0; index < eventsList.length; index++) {
const eventDate = eventsList[index].start;
if (selectedDate === eventDate) {
// If it's the case, set current index to selectedEventIndex
selectedEventIndex = index;
break;
}
}
if (selectedEventIndex !== undefined) {
// If selectedEventIndex exists, remove the corresponding event from the list
eventsList.splice(selectedEventIndex, 1);
updatedEvents = [...eventsList];
} else {
// Add selected event to the list
updatedEvents = [...eventsList, selectedEvent];
}
} else {
// When a range of dates is selected
let currentDate = start;
while (!(+end === +currentDate)) {
// Add currentDate to the datesList
datesList.add(currentDate.toISODate());
let newDate = currentDate.plus({ days: 1 });
currentDate = newDate;
}
// Add each date from the eventsList to datesList
eventsList.forEach((event) => {
datesList.add(event.start);
});
// Create events from datesList and add them to updatedEvents
datesList.forEach((date) => {
updatedEvents.push({ start: date, display: 'background' });
});
}
setEventsList(updatedEvents);
};
const handleDelete = (date) => {
let currentEvent = {
start: date.toFormat('yyyy-MM-dd'),
display: 'background',
};
// Find the event corresponding to the date
let selectedEventIndex;
for (let index = 0; index < eventsList.length; index++) {
const eventDate = eventsList[index].start;
if (currentEvent.start === eventDate) {
// When it's the case, set current index to selectedEventIndex
selectedEventIndex = index;
break;
}
}
// Create updated eventsList
eventsList.splice(selectedEventIndex, 1);
let updatedEvents = [...eventsList];
// Update the eventsList
setEventsList(updatedEvents);
};
2020-08-22 20:49:03 +00:00
const handleClear = () => {
setEventsList([]);
};
// MEETING
const handleSchedule = () => {
// ADD THE MEETING
2020-08-22 20:49:03 +00:00
backend
.post('/meetings', currentMeeting)
.then((response) => {
setCurrentMeeting({
...currentMeeting,
id: response.data.id,
});
addParticipant({
account_id: currentUser.id,
meeting_id: response.data.id,
quorum: 0, // 'false' while functionality not implemented
mandatory: 0, // 'false' while functionality not implemented
host: 1,
answered: 0,
});
2020-08-22 20:49:03 +00:00
history.push('/availability');
})
.catch((error) => {
setError('Failed to add new meeting.');
2020-08-22 20:49:03 +00:00
});
const addParticipant = (data) => {
backend
.post('/participants', data)
.then((response) => {
addPossibleDates({ meeting_id: response.data.meeting_id });
})
.catch((error) => {
setError(
"Current user couldn't be set as participant to the meeting.",
);
});
};
const addPossibleDates = ({ meeting_id }) => {
// Post the possible dates and set add their ID to state
const postPossibleDate = (data) => {
return backend.post('/possible-dates', data);
};
const createRequest = ({ start }) => {
let data = {
meeting_id,
possible_date: start,
};
return postPossibleDate(data);
};
const requests = possibleDates.map((possibleDate) =>
createRequest(possibleDate),
);
// Perform concurrent requests and update possible dates with id
Promise.all(requests)
.then(function (results) {
const addID = ({ data }) => {
let possibleDate = {
id: data.id,
start: data.possible_date.substring(0, 10),
display: 'background',
};
return possibleDate;
};
const possibleDatesWithID = results.map((result) =>
addID(result),
);
setPossibleDates(possibleDatesWithID);
})
.catch((error) => {
setError("Couldn't add possible dates to the meeting.");
});
};
2020-08-22 20:49:03 +00:00
};
const handleChangeMeeting = (value, evt) => {
setCurrentMeeting({
...currentMeeting,
[evt.target.name]: value,
});
};
const handleIncrement = () => {
if (durationIdx <= durations.length - 2) {
setDurationIdx(durationIdx + 1);
}
setCurrentMeeting({
...currentMeeting,
duration: durations[durationIdx].duration,
});
};
const handleDecrement = () => {
if (durationIdx > 0) {
setDurationIdx(durationIdx - 1);
}
setCurrentMeeting({
...currentMeeting,
duration: durations[durationIdx].duration,
});
};
return (
<>
2020-08-19 18:04:52 +00:00
<NavBar title='Schedule a meeting' />
2020-08-21 23:08:18 +00:00
<Panel className={'app-container'}>
<Form className={'meeting-container'}>
<div className={'meeting-info'}>
<FormGroup>
<ControlLabel>Title</ControlLabel>
2020-08-22 20:49:03 +00:00
<FormControl
name='title'
type='text'
formValue={currentMeeting.title}
onChange={handleChangeMeeting}
/>
<HelpBlock>This field is required</HelpBlock>
</FormGroup>
<FormGroup>
<ControlLabel>Description</ControlLabel>
2020-08-22 20:49:03 +00:00
<FormControl
name='description'
componentClass='textarea'
type='text'
rows={3}
placeholder='(optional)'
2020-08-22 20:49:03 +00:00
formValue={currentMeeting.description}
onChange={handleChangeMeeting}
/>
</FormGroup>
<div className='meeting-options-inline'>
<FormGroup className='meeting-duration'>
<ControlLabel>Duration</ControlLabel>
2020-08-22 20:49:03 +00:00
<DurationSelector
durationIdx={durationIdx}
handleDecrement={handleDecrement}
handleIncrement={handleIncrement}
/>
</FormGroup>
</div>
<ButtonGroup justified>
<Button
appearance='ghost'
block
size='lg'
disabled={datesList.length === 0}
onClick={() => handleClear()}
>
Clear selection
</Button>
<Button
appearance='primary'
size='lg'
block
disabled={datesList.length === 0}
2020-08-22 20:49:03 +00:00
onClick={handleSchedule}
>
Confirm dates
</Button>
</ButtonGroup>
{error && <Message type='error' description={error} />}
<div className={'selected-dates'}></div>
{datesList.length > 0 && (
<>
<SelectedDates
datesList={datesList}
handleDelete={handleDelete}
/>
</>
)}
</div>
<div className={'day-selector'}>
<Message
showIcon
type='info'
description='Select possible meetings dates on the calendar.'
/>
<DaySelector
eventsList={eventsList}
2020-08-22 20:49:03 +00:00
handleSelect={handleSelectDates}
handleClear={handleClear}
/>
</div>
</Form>
</Panel>
</>
);
}
// 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;
};