feat: eventsAPI

This commit is contained in:
cv
2021-04-10 11:39:53 +02:00
parent e2561bcd54
commit e62b79f84e
13 changed files with 384 additions and 193 deletions
+2
View File
@@ -10,11 +10,13 @@
"@testing-library/jest-dom": "^5.11.4",
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"axios": "^0.21.1",
"date-fns": "^2.19.0",
"framer-motion": "^4",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"react-icons": "^4.2.0",
"react-query": "^3.13.5",
"react-router": "^5.2.0",
"react-router-dom": "^5.2.0",
"react-scripts": "4.0.3",
+8
View File
@@ -0,0 +1,8 @@
import { serverURL } from './apiConstants';
export const eventsURL = serverURL + 'events/';
export const fetchAllEvents = async () => {
const res = await fetch(eventsURL + 'all');
// TODO: Safe json convert
return res.json();
};
+5 -12
View File
@@ -2,9 +2,9 @@ import { IconButton } from '@chakra-ui/button';
import { SettingsIcon } from '@chakra-ui/icons';
import { Grid, GridItem, Heading } from '@chakra-ui/layout';
import { Box } from '@chakra-ui/layout';
import { useContext } from 'react';
import { useEffect, useState } from 'react';
import { EventContext } from '../../app/context/eventContext';
import { useContext, useEffect } from 'react';
import { useQuery } from 'react-query';
import { useState } from 'react';
import { EventListContext } from '../../app/context/eventListContext';
import NumberedText from '../../common/components/text/NumberedText';
import PlaybackControl from '../control/PlaybackControl';
@@ -12,26 +12,22 @@ import MessageForm from '../form/MessageForm';
import PreviewContainer from '../viewers/PreviewContainer';
import styles from './Editor.module.css';
import EventList from './list/EventList';
import EventListWrapper from './list/EventListWrapper';
export default function Editor() {
const [formMode, setFormMode] = useState(null);
const [events] = useContext(EventListContext);
const [webEvents, setWebEvents] = useState(null);
const [event, setEvent] = useContext(EventContext);
const [playback, setPlayback] = useState({
current: null,
next: null,
currentTimer: null,
numEvents: 0,
state: 'pause',
prevState: 'pause',
});
const updatePlayback = (vals) => {
setPlayback({ ...playback, ...vals });
};
console.log('events here', webEvents);
return (
<Grid
templateRows='1fr 1fr 1fr'
@@ -46,10 +42,7 @@ export default function Editor() {
</Heading>
<NumberedText number={1} text={'Manage and select event to run'} />
<div className={styles.content}>
<EventList
events={webEvents}
formMode={formMode}
setFormMode={setFormMode}
<EventListWrapper
selected={playback.current}
updatePlayback={updatePlayback}
/>
+42 -147
View File
@@ -1,160 +1,55 @@
import { useContext, useEffect } from 'react';
import { EventContext } from '../../../app/context/eventContext';
import { EventListContext } from '../../../app/context/eventListContext';
import EventListItem from './EventListItem';
import style from './List.module.css';
import DelayBlock from './DelayBlock';
import BlockBlock from './BlockBlock';
import EventListMenu from '../../menu/EventListMenu';
export default function EventList(props) {
const [events, setEvents] = useContext(EventListContext);
const [event] = useContext(EventContext);
const { events, selected, eventsHandler } = props;
// update number of events
useEffect(() => {
const f = events.filter((e) => e.type === 'event');
props.updatePlayback({ numEvents: f.length });
}, [events]);
const insertItemAt = (item, index) => {
// handle insert at beggining of array
if (index === -1) {
// move all items one element down, starting from new position
events.forEach((e) => {
e.order = e.order + 1;
});
return [item, ...events];
} else {
let before = events.slice(0, index + 1);
let after = events.slice(index + 1);
// move all items one element down, starting from new position
after.forEach((e) => {
e.order = e.order + 1;
});
return [...before, item, ...after];
}
};
const createEvent = (itemIndex = -1) => {
// make an event
// TODO: Replace this with global def somewhere
// TODO: handle random ids better
let newEvent = {
id: Math.random(),
order: itemIndex + 1,
title: '',
subtitle: '',
presenter: '',
timeStart: new Date(),
timeEnd: new Date(),
clockStarted: null,
timerDuration: 0,
type: 'event',
};
// set to state
setEvents(insertItemAt(newEvent, itemIndex));
};
const createDelay = (itemIndex = 0) => {
// make an event
// TODO: Replace this with global def somewhere
// TODO: handle random ids better
let newEvent = {
id: Math.random(),
order: itemIndex + 1,
timerDuration: 0,
type: 'delay',
};
// set to state
setEvents(insertItemAt(newEvent, itemIndex));
};
const createBlock = (itemIndex = 0) => {
// make an event
// TODO: Replace this with global def somewhere
// TODO: handle random ids better
let newEvent = {
id: Math.random(),
order: itemIndex + 1,
type: 'block',
};
// set to state
setEvents(insertItemAt(newEvent, itemIndex));
};
const deleteEvent = (index) => {
// TODO: This feels weird?
let e = events.splice(index, 1);
setEvents([...events]);
};
const replaceAt = (array, index, value) => {
const ret = array.slice(0);
ret[index] = value;
return ret;
};
const updateData = (itemIndex, data) => {
const newEvents = replaceAt(events, itemIndex, data);
setEvents(newEvents);
};
console.log('events in event list', events);
console.log('EventList: events in event list', events);
let cumulativeDelay = 0;
let eventCount = -1;
// Torbjorn: is this very dirty code?
// -- map and cumulative delay thing
// -- should i skip the order value and just use the array as order? (checkout dnd)
return (
<>
<EventListMenu createEvent={createEvent} />
<div className={style.eventContainer}>
{events.map((e, index) => {
if (e.type === 'event') {
eventCount = eventCount + 1;
return (
<EventListItem
key={e.id}
index={index}
data={e}
selected={props.selected === eventCount}
createEvent={createEvent}
deleteEvent={deleteEvent}
createDelay={createDelay}
createBlock={createBlock}
updateData={updateData}
delay={cumulativeDelay}
/>
);
} else if (e.type === 'block') {
cumulativeDelay = 0;
return (
<BlockBlock
key={e.id}
index={index}
createEvent={createEvent}
deleteEvent={deleteEvent}
/>
);
} else if (e.type === 'delay') {
cumulativeDelay = cumulativeDelay + e.timerDuration;
return (
<DelayBlock
key={e.id}
index={index}
data={e}
createEvent={createEvent}
deleteEvent={deleteEvent}
updateData={updateData}
/>
);
}
})}
</div>
</>
<div className={style.eventContainer}>
{events.map((e, index) => {
if (e.type === 'event') {
eventCount = eventCount + 1;
return (
<EventListItem
key={e.id}
index={index}
data={e}
selected={selected === eventCount}
eventsHandler={eventsHandler}
delay={cumulativeDelay}
/>
);
} else if (e.type === 'block') {
cumulativeDelay = 0;
return (
<BlockBlock
key={e.id}
index={index}
data={e}
eventsHandler={eventsHandler}
/>
);
} else if (e.type === 'delay') {
cumulativeDelay = cumulativeDelay + e.timerDuration;
return (
<DelayBlock
key={e.id}
index={index}
data={e}
eventsHandler={eventsHandler}
/>
);
}
})}
</div>
);
}
@@ -0,0 +1,81 @@
import { useMutation, useQuery, useQueryClient } from 'react-query';
import { useEffect } from 'react';
import { fetchAllEvents } from '../../../app/api/eventsApi.js';
import EventList from './EventList';
import EventListMenu from '../../menu/EventListMenu.jsx';
import axios from 'axios';
import { showErrorToast } from '../../../common/helpers/toastManager';
import { eventsURL } from '../../../app/api/eventsApi';
import { Skeleton } from '@chakra-ui/skeleton';
import style from './List.module.css';
export default function EventListWrapper() {
const { data, status, isError } = useQuery('events', fetchAllEvents);
const queryClient = useQueryClient();
// TODO: Move to events API?
const addEvent = useMutation((data) => axios.post(eventsURL, data));
const updateEvent = useMutation((data) => axios.put(eventsURL, data));
const deleteEvent = useMutation((eventId) =>
axios.delete(eventsURL + eventId)
);
// Show toasts on errors
useEffect(() => {
if (isError) {
showErrorToast('Error fetching data');
}
}, [isError]);
// Events API
const eventsHandler = async (action, payload) => {
// Torbjorn: is this a good way to do it?
// How do I handle the mutation thing
// https://react-query.tanstack.com/guides/invalidations-from-mutations
// https://react-query.tanstack.com/guides/updates-from-mutation-responses
switch (action) {
case 'add':
try {
const event = await addEvent
.mutateAsync(payload)
.then(queryClient.invalidateQueries('events'));
} catch (error) {
showErrorToast('Error creating event', error.message);
}
break;
case 'update':
try {
const event = await updateEvent
.mutateAsync(payload)
.then(queryClient.invalidateQueries('events'));
} catch (error) {
showErrorToast('Error updating event', error.message);
}
break;
case 'delete':
try {
const event = await deleteEvent
.mutateAsync(payload)
.then(queryClient.invalidateQueries('events'));
} catch (error) {
showErrorToast('Error deleting event', error.message);
}
break;
default:
showErrorToast('Unrecognised request', action);
break;
}
};
return (
<>
<EventListMenu eventsHandler={eventsHandler} />
{status === 'success' ? (
<EventList events={data} eventsHandler={eventsHandler} />
) : (
<div className={style.eventContainer}>
<Skeleton height='50vh' />
</div>
)}
</>
);
}
+81 -9
View File
@@ -1120,7 +1120,7 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.13.10"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d"
integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw==
@@ -3219,6 +3219,13 @@ axe-core@^4.0.2:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.3.tgz#64a4c85509e0991f5168340edc4bedd1ceea6966"
integrity sha512-vwPpH4Aj4122EW38mxO/fxhGKtwWTMLDIJfZ1He0Edbtjcfna/R3YB67yVhezUMzqc3Jr3+Ii50KRntlENL4xQ==
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
follow-redirects "^1.10.0"
axobject-query@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
@@ -3467,6 +3474,11 @@ bfj@^7.0.2:
hoopy "^0.1.4"
tryer "^1.0.1"
big-integer@^1.6.16:
version "1.6.48"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@@ -3568,6 +3580,19 @@ braces@^3.0.1, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
broadcast-channel@^3.4.1:
version "3.5.3"
resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.5.3.tgz#c75c39d923ae8af6284a893bfdc8bd3996d2dd2d"
integrity sha512-OLOXfwReZa2AAAh9yOUyiALB3YxBe0QpThwwuyRHLgpl8bSznSDmV6Mz7LeBJg1VZsMcDcNMy7B53w12qHrIhQ==
dependencies:
"@babel/runtime" "^7.7.2"
detect-node "^2.0.4"
js-sha3 "0.8.0"
microseconds "0.2.0"
nano-time "1.0.0"
rimraf "3.0.2"
unload "2.2.0"
brorand@^1.0.1, brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
@@ -5804,7 +5829,7 @@ focus-lock@^0.8.1:
dependencies:
tslib "^1.9.3"
follow-redirects@^1.0.0:
follow-redirects@^1.0.0, follow-redirects@^1.10.0:
version "1.13.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==
@@ -7482,6 +7507,11 @@ jest@26.6.0:
import-local "^3.0.2"
jest-cli "^26.6.0"
js-sha3@0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -7919,6 +7949,14 @@ map-visit@^1.0.0:
dependencies:
object-visit "^1.0.0"
match-sorter@^6.0.2:
version "6.3.0"
resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.0.tgz#454a1b31ed218cddbce6231a0ecb5fdc549fed01"
integrity sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ==
dependencies:
"@babel/runtime" "^7.12.5"
remove-accents "0.4.2"
md5.js@^1.3.4:
version "1.3.5"
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
@@ -8011,6 +8049,11 @@ micromatch@^4.0.2:
braces "^3.0.1"
picomatch "^2.0.5"
microseconds@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
@@ -8213,6 +8256,13 @@ nan@^2.12.1:
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19"
integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==
nano-time@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=
dependencies:
big-integer "^1.6.16"
nanoid@^3.1.20:
version "3.1.22"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"
@@ -9959,6 +10009,15 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-query@^3.13.5:
version "3.13.5"
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.13.5.tgz#b59e2094014dae0461ed7aab68ae030be41eda8a"
integrity sha512-rL0NO2gVfaeMaVD0Z0yrxZT1oMGejGxGD5t+5d8eC9kpID6oa3r2bFBR3nJeuPhwdGvZtifbB1KJkKdPYaYqVg==
dependencies:
"@babel/runtime" "^7.5.5"
broadcast-channel "^3.4.1"
match-sorter "^6.0.2"
react-refresh@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f"
@@ -10268,6 +10327,11 @@ relateurl@^0.2.7:
resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
remove-accents@0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=
remove-trailing-separator@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
@@ -10473,6 +10537,13 @@ rgba-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
dependencies:
glob "^7.1.3"
rimraf@^2.5.4, rimraf@^2.6.3:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
@@ -10480,13 +10551,6 @@ rimraf@^2.5.4, rimraf@^2.6.3:
dependencies:
glob "^7.1.3"
rimraf@^3.0.0, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
dependencies:
glob "^7.1.3"
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
@@ -11841,6 +11905,14 @@ universalify@^2.0.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unload@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
dependencies:
"@babel/runtime" "^7.6.2"
detect-node "^2.0.4"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+2 -1
View File
@@ -28,7 +28,8 @@ const app = express();
app.use(cors());
// Implement middleware
// ---
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
// Implement route endpoints
app.use('/events', eventsRouter);
+100 -2
View File
@@ -1,5 +1,16 @@
// utils
const { nanoid } = require('nanoid');
// import json with initial data
const events = require('../data/eventsData.json');
let events = require('../data/eventsData.json');
const eventDefs = require('../data/eventsDefinition.js');
// aux
const replaceAt = (array, index, value) => {
const ret = array.slice(0);
ret[index] = value;
return ret;
};
// Create controller for GET request to '/events'
// Returns ACK message
@@ -8,7 +19,94 @@ exports.eventsGet = async (req, res) => {
};
// Create controller for GET request to '/events/all'
// Returns playback state object
// Returns -
exports.eventsGetAll = async (req, res) => {
res.json(events);
};
// Create controller for GET request to '/events/:Id'
// Returns -
exports.eventsGetById = async (req, res) => {
// TODO
};
// Create controller for POST request to '/events/'
// Returns -
exports.eventsPost = async (req, res) => {
// TODO: Validate event
if (req.body) {
// ensure structure
let newEvent = {};
req.body.id = nanoid(10);
switch (req.body.type) {
case 'event':
newEvent = { ...eventDefs.event, ...req.body };
break;
case 'delay':
newEvent = { ...eventDefs.delay, ...req.body };
break;
case 'block':
newEvent = { ...eventDefs.block, ...req.body };
break;
default:
res.sendStatus(400);
break;
}
// Torbjorn: hmmmmmmm, can we look here?
if (newEvent.order <= 0) {
newEvent.order = 0;
// insert at top
events.forEach((e) => {
e.order = e.order + 1;
});
events = [newEvent, ...events];
} else if (newEvent.order >= events.length) {
newEvent.order = events.length;
events = [...events, newEvent];
} else {
let before = events.slice(0, newEvent.order);
let after = events.slice(newEvent.order);
// move all items one element down, starting from new position
after.forEach((e) => {
e.order = e.order + 1;
});
events = [...before, newEvent, ...after];
}
console.log('added', events);
res.sendStatus(201);
} else {
console.log('nothing to add', req.body);
res.sendStatus(400);
}
};
// Create controller for PUT request to '/events/:id'
// Returns -
exports.eventsPut = async (req, res) => {
const itemIndex = events.findIndex((e) => e.id == req.body.id);
if (!itemIndex) res.sendStatus(400);
const newEvents = replaceAt(events, itemIndex, req.body);
events = [...newEvents];
res.sendStatus(200);
};
// Create controller for DELETE request to '/events/'
// Returns -
exports.eventsDelete = async (req, res) => {
if (req.params.id) {
const itemIndex = events.findIndex((e) => e.id == req.params.id);
if (!itemIndex) res.sendStatus(400);
// Torbjorn: this syntax is very bad
const e = events.splice(itemIndex, 1);
events = [...events];
res.sendStatus(200);
} else {
res.sendStatus(400);
}
};
+19 -13
View File
@@ -1,48 +1,54 @@
[
{
"id": "1",
"id":"5ce39a29-a70c-426a-9ce2-f97782049c71",
"order": 0,
"timerDuration": 5,
"type": "delay"
},
{
"id": "5ce39a29-a70c-426a-9ce2-f97782049c72",
"order": 1,
"title": "Is the internet a fad?",
"subtitle": "It is",
"presenter": "Carlos Valente",
"timeStart": "",
"timeEnd":"",
"timeStart": "2020-04-09T11:25:43.511Z",
"timeEnd": "2020-04-09T12:25:43.511Z",
"type": "event"
},
{
"id": 0.4849093424577693,
"id":"5ce39a29-a70c-426a-9ce2-f97782049c73",
"order": 2,
"timerDuration": 25,
"type": "delay"
},
{
"id": "2",
"id": "5ce39a29-a70c-426a-9ce2-f97782049c74",
"order": 3,
"title": "Is reddit a dictatorship?",
"subtitle": "It is",
"presenter": "Carlos Valente",
"timeStart": "",
"timeEnd":"",
"timeStart": "2020-04-09T13:25:43.511Z",
"timeEnd": "2020-04-09T14:25:43.511Z",
"type": "event"
},
{
"id": "3",
"id": "5ce39a29-a70c-426a-9ce2-f97782049c75",
"order": 4,
"title": "Out of words",
"subtitle": "",
"presenter": "Carlos Valente",
"timeStart": "",
"timeEnd":"",
"timeStart": "2020-04-09T15:25:43.511Z",
"timeEnd": "2020-04-09T16:25:43.511Z",
"type": "event"
},
{
"id": "4",
"id": "5ce39a29-a70c-426a-9ce2-f97782049c76",
"order": 5,
"title": "Really",
"subtitle": "",
"presenter": "Carlos Valente",
"timeStart": "",
"timeEnd":"",
"timeStart": "2020-04-09T17:25:43.511Z",
"timeEnd": "2020-04-09T18:25:43.511Z",
"type": "event"
}
]
+23
View File
@@ -0,0 +1,23 @@
const event = {
order: 0,
title: '',
subtitle: '',
presenter: '',
timeStart: new Date(),
timeEnd: new Date(),
clockStarted: null,
type: 'event',
};
const delay = {
order: 0,
timerDuration: 0,
type: 'delay',
};
const block = {
order: 0,
type: 'block',
};
module.exports = { event, delay, block };
+2 -1
View File
@@ -6,6 +6,7 @@
"body-parser": "~1.19.0",
"express": "~4.17.1",
"express-session": "~1.17.1",
"nanoid": "^3.1.22",
"passport": "~0.4.1",
"passport-local": "~1.0.0",
"socket.io": "^4.0.0"
@@ -13,4 +14,4 @@
"scripts": {
"start": "node app.js"
}
}
}
+14 -8
View File
@@ -1,19 +1,25 @@
const express = require('express');
const cors = require('cors');
const router = express.Router();
// Cross origin stuff
const corsOptions = {
origin: 'http://localhost',
}
// import playback controller
const eventsController = require('../controllers/eventsController');
// create route between controller and '/events' endpoint
router.get('/', cors(corsOptions), eventsController.eventsGet);
router.get('/', eventsController.eventsGet);
// create route between controller and '/events/all' endpoint
router.get('/all', cors(corsOptions), eventsController.eventsGetAll);
router.get('/all', eventsController.eventsGetAll);
// create route between controller and '/events/:id' endpoint
router.get('/:id', eventsController.eventsGetById);
// create route between controller and '/events/' endpoint
router.post('/', eventsController.eventsPost);
// create route between controller and '/events/:id' endpoint
router.put('/', eventsController.eventsPut);
// create route between controller and '/events/:id' endpoint
router.delete('/:id', eventsController.eventsDelete);
module.exports = router;
+5
View File
@@ -339,6 +339,11 @@ ms@2.1.2:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nanoid@^3.1.22:
version "3.1.22"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"
integrity sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==
negotiator@0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"