Last active
September 10, 2017 23:53
-
-
Save sheminusminus/ec9cd1461f611a26ee8b09b9dac72997 to your computer and use it in GitHub Desktop.
Events: 1. Create an event 2. Make a form that allows others to update that event
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* eventRouter.js | |
* @file Handles any requests to route /api/events. Server file. | |
*/ | |
const axios = require('axios'); | |
const router = require('express').Router(); | |
// events the user can alter, seeded with event 2 | |
// of course this would normally come from a database | |
const allowedEvents = [2]; | |
// api key | |
const apiKey = 'MY_API_KEY'; | |
// nb url parts | |
const eventBase = 'https://emilykolarsandbox.nationbuilder.com/api/v1/sites/emilykolarsandbox/pages/events'; | |
const tokenQuery = `access_token=${apiKey}`; | |
// content-type and accept headers for requests to nb | |
const headers = { | |
'Content-Type': 'application/json', | |
Accept: 'application/json', | |
}; | |
// parse the nb response for info relevant to the front-end | |
function parseEvent(data) { | |
const name = data.name || ''; | |
const start = data.start_time || new Date(); | |
const end = data.end_time || new Date(); | |
const status = data.status || ''; | |
const venue = data.venue ? data.venue.name : ''; | |
const capacity = data.capacity || 0; | |
const id = data.id || ''; | |
return { | |
id, | |
name, | |
start, | |
end, | |
status, | |
venue, | |
capacity, | |
}; | |
} | |
// get a specific event's data | |
router.get('/:id', (req, res) => { | |
const id = parseInt(req.params.id, 10); | |
if (allowedEvents.includes(id)) { | |
const url = `${eventBase}/${req.params.id}?${tokenQuery}`; | |
axios.get(url, { | |
headers, | |
}).then((resp) => { | |
const event = parseEvent(resp.data.event); | |
res.json(event); | |
}).catch(() => { | |
res.json({ message: 'An error occurred' }); | |
}); | |
} | |
}); | |
// get all event data | |
router.get('/', (req, res) => { | |
const url = `${eventBase}?limit=20&${tokenQuery}`; | |
axios.get(url, { headers }).then((resp) => { | |
const events = []; | |
const results = resp.data.results; | |
for (let i = 0; i < results.length; i++) { | |
const event = parseEvent(results[i]); | |
const eventId = parseInt(event.id, 10); | |
// if the user can access this event, add it to the response data | |
if (allowedEvents.includes(eventId)) { | |
events.push(event); | |
} | |
} | |
res.json(events); | |
}).catch(() => { | |
res.json({ message: 'An error occurred' }); | |
}); | |
}); | |
// create new event { | |
router.post('/', (req, res) => { | |
const url = `${eventBase}?${tokenQuery}`; | |
axios.post(url, { | |
event: { | |
venue: { | |
name: req.body.venue, | |
}, | |
name: req.body.event, | |
capacity: req.body.capacity, | |
start_time: req.body.start, | |
end_time: req.body.end, | |
status: req.body.status, | |
}, | |
}, { headers }).then((resp) => { | |
const event = parseEvent(resp.data.event); | |
const eventId = parseInt(event.id, 10); | |
// allow editor-access to the new event | |
allowedEvents.push(eventId); | |
res.json(event); | |
}).catch(() => { | |
res.json({ message: 'An error occurred' }); | |
}); | |
}); | |
// update event data | |
router.put('/:id', (req, res) => { | |
const id = parseInt(req.params.id, 10); | |
if (allowedEvents.includes(id)) { | |
const url = `${eventBase}/${id}?${tokenQuery}`; | |
axios.put(url, { | |
event: { | |
venue: { | |
name: req.body.venue, | |
}, | |
name: req.body.event, | |
capacity: req.body.capacity, | |
start_time: req.body.start, | |
end_time: req.body.end, | |
status: req.body.status, | |
}, | |
}, { headers }).then((resp) => { | |
const event = parseEvent(resp.data.event); | |
res.json(event); | |
}).catch((error) => { | |
res.json({ message: error.response.data.message }); | |
}); | |
} | |
}); | |
module.exports = router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" | |
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Events NB Exercise</title> | |
<script src="https://unpkg.com/axios/dist/axios.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script> | |
</head> | |
<body> | |
<h3>Super-Event-Admin 9000</h3> | |
New Event <input type="radio" name="eventType" value="CREATE" checked /><br /> | |
Edit Existing <input type="radio" name="eventType" value="UPDATE" /><br /> | |
<select id="selection" disabled> | |
<option disabled selected>Select an event</option> | |
</select> | |
<hr/> | |
<form id="edit"> | |
<label>Venue Name</label><br /> | |
<input type="text" id="venue" placeholder="where is it?" /><br /> | |
<label>Event Name</label><br /> | |
<input type="text" id="event" placeholder="name your event" /><br /> | |
<label>Happening On</label><br /> | |
<input type="date" id="start" /><br /> | |
<label>Capacity</label><br /> | |
<input type="number" id="capacity" /><br /> | |
<input type="submit" id="submitEdits" value="Save Changes" /> | |
</form> | |
<script src="./script.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* index.js | |
* @file Launches a basic express server and sends the index.html over all unmatched routes. Server main. | |
*/ | |
const path = require('path'); | |
const express = require('express'); | |
const bodyParser = require('body-parser'); | |
const app = express(); | |
const eventRouter = require('./server/eventRouter'); | |
app.use(bodyParser.urlencoded({ extended: false })); | |
app.use(bodyParser.json()); | |
app.use(express.static('./pub')); | |
app.use('/api/events', eventRouter); | |
app.get('*', (req, res) => { | |
res.sendFile(path.resolve(__dirname, 'pub/index.html')); | |
}); | |
app.listen(3000, () => { | |
console.log('the magic happens on port 3000'); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* script.js | |
* @file Client script for creating new events and editing existing events. Loaded by index.html. | |
*/ | |
window.addEventListener('load', function() { | |
// global values | |
const baseUrl = '/api/events'; | |
const axios = window.axios; | |
const moment = window.moment; | |
let mode = 'CREATE'; | |
// ui | |
let edit = document.querySelector('#edit'); | |
let venueEL = document.querySelector('#venue'); | |
let nameEl = document.querySelector('#event'); | |
let capacityEl = document.querySelector('#capacity'); | |
let startEl = document.querySelector('#start'); | |
let selectEl = document.getElementById('selection'); | |
let modeEls = document.getElementsByName('eventType'); | |
// data references | |
let start = moment(); | |
let end = moment(); | |
let status = 'unlisted'; | |
// clear the form | |
function clearFields() { | |
edit.reset(); | |
} | |
// inject event info into the form | |
// save start, end, and status data | |
function fillEventFields(data) { | |
start = moment(data.start) || moment(); | |
end = moment(data.end) || moment().add(3, 'hours'); | |
status = data.status || 'unlisted'; | |
venueEL.value = data.venue || ''; | |
nameEl.value = data.name || ''; | |
capacityEl.value = data.capacity || 0; | |
startEl.value = start.format('YYYY-MM-DD');; | |
} | |
// request the event data from our api | |
function getEventsData(callback) { | |
axios.get(baseUrl).then((resp) => { | |
callback(resp.data); | |
}).catch((error) => { | |
console.error(error); | |
}); | |
} | |
// fetch selected event's info and fill fields | |
function setEvent(id) { | |
const url = baseUrl + '/' + id; | |
axios.get(url).then((resp) => { | |
fillEventFields(resp.data); | |
}).catch((err) => { | |
console.log(err); | |
}); | |
} | |
// create option for each event | |
function makeOption(data) { | |
let opt = document.createElement('option'); | |
opt.value = data.id; | |
opt.textContent = data.name; | |
return opt; | |
} | |
// rebuild event select element | |
function listEvents(events) { | |
while (selectEl.firstChild) { | |
selectEl.removeChild(selectEl.firstChild); | |
} | |
const firstOpt = document.createElement('option'); | |
firstOpt.setAttribute('disabled', 'disabled'); | |
firstOpt.textContent = 'Select an event'; | |
firstOpt.setAttribute('selected', 'selected'); | |
selectEl.appendChild(firstOpt); | |
for (let i = 0; i < events.length; i++) { | |
const opt = makeOption(events[i]); | |
selectEl.appendChild(opt); | |
} | |
} | |
// set whether we're in edit mode or create mode | |
function updateMode() { | |
if (mode === 'CREATE') { | |
clearFields(); | |
selectEl.selectedIndex = 0; | |
selectEl.setAttribute('disabled', 'disabled'); | |
} else { | |
selectEl.removeAttribute('disabled'); | |
} | |
} | |
// send the new or updated event data to our api | |
function saveEventData(method, url, callback) { | |
const venue = venueEL.value; | |
const event = nameEl.value; | |
const cap = capacityEl.value; | |
const newStart = moment(startEl.value).hours(start.hours()); | |
const newEnd = moment(startEl.value).hours(end.hours()); | |
axios({ | |
method: method, | |
url: url, | |
data: { | |
venue: venue, | |
event: event, | |
start: newStart.format('YYYY-MM-DDTHH:mmZZ'), | |
end: newEnd.format('YYYY-MM-DDTHH:mmZZ'), | |
capacity: cap, | |
status: status, | |
} | |
}).then(() => { | |
clearFields(); | |
getEventsData(callback); | |
}).catch((error) => { | |
console.error(error); | |
}); | |
} | |
function init() { | |
// listen for create/edit toggle | |
modeEls.forEach((el) => { | |
el.addEventListener('change', (evt) => { | |
mode = evt.target.value; | |
updateMode(); | |
}); | |
}); | |
// listen for form submission | |
edit.addEventListener('submit', (evt) => { | |
evt.preventDefault(); | |
if (mode === 'UPDATE') { | |
const url = baseUrl + '/' + selectEl.value; | |
saveEventData('PUT', url, listEvents); | |
} else { | |
saveEventData('POST', baseUrl, listEvents); | |
} | |
}); | |
// listen for event selection | |
selectEl.addEventListener('change', (evt) => { | |
setEvent(evt.target.value); | |
}); | |
// query for our initial data | |
getEventsData(listEvents); | |
} | |
// start the thing moving | |
init(); | |
}); |
Author
sheminusminus
commented
Sep 10, 2017
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment