frontend/src/screens/Schedule.js

336 lines
8.3 KiB
JavaScript

import React, { useState } from 'react';
import { DateTime } from 'luxon';
import { useHistory } from 'react-router-dom';
import {
Panel,
Form,
FormControl,
ControlLabel,
HelpBlock,
Button,
ButtonGroup,
Message,
FormGroup,
} from 'rsuite';
import {
NavBar,
DaySelector,
DurationSelector,
SelectedDates,
} from '../components';
import { backend } from '../helpers/http-common';
import { useAuth } from '../helpers/authContext';
import { durations } from '../assets/data/durations';
import './styles/Schedule.less';
export default function Schedule({
possibleDates,
setPossibleDates,
currentMeeting,
setCurrentMeeting,
currentUser,
setParticipant,
}) {
const [eventsList, setEventsList] = useState([]);
const [durationIdx, setDurationIdx] = useState(0);
const [error, setError] = useState(false);
const history = useHistory();
const { authToken } = useAuth();
// EVENTS & DATES
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' });
});
}
// Sort by date
updatedEvents.sort(
(date1, date2) => Date.parse(date1.start) - Date.parse(date2.start),
);
setEventsList(updatedEvents);
setPossibleDates(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);
};
const handleClear = () => {
setEventsList([]);
};
// MEETING
const handleSchedule = () => {
// Add token to the request
backend.defaults.headers.common['Authorization'] = authToken;
// ADD THE MEETING
backend
.post('/meetings', currentMeeting)
.then((response) => {
setCurrentMeeting({
...currentMeeting,
id: response.data.id,
});
addParticipant({
account_id: currentUser.id,
email: currentUser.email,
meeting_id: response.data.id,
quorum: 0, // 'false' while functionality not implemented
mandatory: 0, // 'false' while functionality not implemented
host: 1,
answered: 0,
});
})
.catch((error) => {
setError('Failed to add new meeting.');
});
const addParticipant = (data) => {
backend
.post('/participants', data)
.then((response) => {
setParticipant(response.data);
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 = {
start: data.possible_date.substring(0, 10),
display: 'background',
id: data.id,
};
return possibleDate;
};
const possibleDatesWithID = results
// Add id
.map((result) => addID(result));
setPossibleDates(possibleDatesWithID);
history.push('/availability');
})
.catch((error) => {
setError("Couldn't add possible dates to the meeting.");
});
};
};
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 (
<>
<NavBar title='Schedule a meeting' />
<Panel className={'app-container'}>
<Form className={'meeting-container'}>
<div className={'meeting-info'}>
<FormGroup>
<ControlLabel>Title</ControlLabel>
<FormControl
name='title'
type='text'
formValue={currentMeeting.title}
onChange={handleChangeMeeting}
/>
<HelpBlock>This field is required</HelpBlock>
</FormGroup>
<FormGroup>
<ControlLabel>Description</ControlLabel>
<FormControl
name='description'
componentClass='textarea'
type='text'
rows={3}
placeholder='(optional)'
formValue={currentMeeting.description}
onChange={handleChangeMeeting}
/>
</FormGroup>
<div className='meeting-options-inline'>
<FormGroup className='meeting-duration'>
<ControlLabel>Duration</ControlLabel>
<DurationSelector
durationIdx={durationIdx}
handleDecrement={handleDecrement}
handleIncrement={handleIncrement}
/>
</FormGroup>
</div>
<ButtonGroup justified>
<Button
appearance='ghost'
block
size='lg'
disabled={possibleDates.length === 0}
onClick={() => handleClear()}
>
Clear selection
</Button>
<Button
appearance='primary'
size='lg'
block
disabled={possibleDates.length === 0}
onClick={handleSchedule}
>
Confirm dates
</Button>
</ButtonGroup>
{error && <Message type='error' description={error} />}
<div className={'selected-dates'}></div>
{possibleDates.length > 0 && (
<>
<SelectedDates
possibleDates={possibleDates}
handleDelete={handleDelete}
/>
</>
)}
</div>
<div className={'day-selector'}>
<Message
showIcon
type='info'
description='Select possible meetings dates on the calendar.'
/>
<DaySelector
eventsList={eventsList}
handleSelect={handleSelectDates}
handleClear={handleClear}
/>
</div>
</Form>
</Panel>
</>
);
}