diff --git a/src/components/RsvpCard/RsvpCard.css b/src/components/RsvpCard/RsvpCard.css index bb542d8..4487307 100644 --- a/src/components/RsvpCard/RsvpCard.css +++ b/src/components/RsvpCard/RsvpCard.css @@ -44,10 +44,11 @@ color: #616161; font-size: 18px; line-height: 1.3333333; - transition: - border-color ease-in-out 0.3s, - box-shadow ease-in-out 0.3s, - background-color ease-in-out 0.3s; + transition: border-color ease-in-out 0.3s, box-shadow ease-in-out 0.3s, background-color ease-in-out 0.3s; +} + +.input:disabled { + opacity: 0.5; } .input:focus { @@ -87,3 +88,7 @@ .rsvpSummarySuccess { opacity: 1; } + +.rsvpSummaryReadOnly { + opacity: 1; +} diff --git a/src/components/RsvpCard/RsvpCard.js b/src/components/RsvpCard/RsvpCard.js index f43ba91..309f329 100644 --- a/src/components/RsvpCard/RsvpCard.js +++ b/src/components/RsvpCard/RsvpCard.js @@ -21,6 +21,7 @@ class RsvpCard extends React.Component { starter: PropTypes.string, main: PropTypes.string, }).isRequired, + isEditable: PropTypes.bool.isRequired, }; constructor(props) { @@ -32,6 +33,11 @@ class RsvpCard extends React.Component { saveMain: this.props.person.main !== '-1' ? 'saved' : null, saveDietary: this.props.person.dietary ? 'saved' : null, }; + + if (!this.props.isEditable) { + this.state.summaryState = 'readonly'; + this.state.summary = 'Sorry, you can no longer change your RSVP.'; + } } isRSVPComplete(prop, value) { @@ -143,6 +149,7 @@ class RsvpCard extends React.Component { defaultValue="-1" onChange={this.handleAttendingChange} onFocus={this.props.focusCallback} + disabled={!this.props.isEditable} > : null} {starters.map((starter, index) => ( @@ -220,6 +229,7 @@ class RsvpCard extends React.Component { defaultValue={this.props.person.main} onChange={this.handleMainChange} onFocus={this.props.focusCallback} + disabled={!this.props.isEditable} > {this.props.person.main === '-1' ? : null} {mains.map((main, index) => ( @@ -249,6 +259,7 @@ class RsvpCard extends React.Component { onChange={this.handleDietaryChange} onBlur={this.handleDietaryBlur} onFocus={this.props.focusCallback} + disabled={!this.props.isEditable} placeholder="None" />
@@ -262,6 +273,7 @@ class RsvpCard extends React.Component { className={[ s.rsvpSummary, this.state.summaryState === 'success' ? s.rsvpSummarySuccess : null, + this.state.summaryState === 'readonly' ? s.rsvpSummaryReadOnly : null, ].join(' ')} > {this.state.summary} diff --git a/src/routes/rsvp/Rsvp.js b/src/routes/rsvp/Rsvp.js index e3f1474..cb983a4 100644 --- a/src/routes/rsvp/Rsvp.js +++ b/src/routes/rsvp/Rsvp.js @@ -14,6 +14,8 @@ class Rsvp extends React.Component { starter: PropTypes.string, main: PropTypes.string, })).isRequired, + rsvpEnd: PropTypes.string.isRequired, + isEditable: PropTypes.bool.isRequired, }; constructor(props) { @@ -38,11 +40,16 @@ class Rsvp extends React.Component {

Odpovezte prosím

{this.props.people.map(person => ( - + ))}
-

You can modify the above details up until XX/XX/XXXX

+

You can modify the above details up until {this.props.rsvpEnd}

diff --git a/src/routes/rsvp/index.js b/src/routes/rsvp/index.js index 4b9d3ca..b7decf9 100644 --- a/src/routes/rsvp/index.js +++ b/src/routes/rsvp/index.js @@ -3,13 +3,44 @@ import Layout from '../../components/Layout'; import fetchQL from '../../core/fetchQL'; import Rsvp from './Rsvp'; import NotFound from '../notFound/NotFound'; +import { rsvpEndDate } from '../../config'; let title = 'RSVP'; +const months = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', +]; + export default { path: '/rsvp/:password', + getRsvpEnd() { + const date = rsvpEndDate.getDate(); + let dateSuffix = 'th'; + if (date === 1 || date === 21 || date === 31) { dateSuffix = 'st'; } + if (date === 2 || date === 22) { dateSuffix = 'nd'; } + if (date === 3 || date === 23) { dateSuffix = 'rd'; } + const month = months[rsvpEndDate.getMonth()]; + const year = rsvpEndDate.getFullYear(); + return `${date}${dateSuffix} ${month} ${year}`; + }, + + isEditable() { + return new Date() < rsvpEndDate; + }, + async action({ params }) { if (!params.password) { return { redirect: '/' }; @@ -32,7 +63,16 @@ export default { return { title, - component: , + component: ( + + + + ), }; }, diff --git a/src/server.js b/src/server.js index 609cc08..f333440 100644 --- a/src/server.js +++ b/src/server.js @@ -21,7 +21,7 @@ import models from './data/models'; import schema from './data/schema'; import routes from './routes'; import assets from './assets'; // eslint-disable-line import/no-unresolved -import { port, auth, starters, mains } from './config'; +import { port, auth, starters, mains, rsvpEndDate } from './config'; import Person from './data/models/Person'; import Rollbar from './core/rollbar'; import { sendSlackMsgWithDebounce } from './core/slack'; @@ -143,27 +143,40 @@ app.get('/rsvp/:code', bouncer.block, (req, res, next) => { app.post('/rsvp/save', (req, res) => { try { - const update = {}; - update[req.body.prop] = req.body.value; - Person.update(update, { - where: { - key: req.body.key, - }, - }).then(() => { - Person.findOne({ - where: { key: req.body.key }, - }).then(data => { - if (data.completed) { - const attending = `\nAttending: ${data.attending ? 'Yes' : 'No'}`; - const starter = (data.attending && data.starter !== -1) ? `\nStarter: ${starters[data.starter]}` : ''; - const main = (data.attending && data.main !== -1) ? `\nMain: ${mains[data.main]}` : ''; - const dietary = (data.attending && data.dietary) ? `\nDietary requirements: ${data.dietary}` : ''; + if (new Date() > rsvpEndDate) { + res.json({ + success: false, + err: 'RSVP end date has elapsed', + }); + } else { + const update = {}; + update[req.body.prop] = req.body.value; + Person.update(update, { + where: { + key: req.body.key, + }, + }).then(() => { + Person.findOne({ + where: { key: req.body.key }, + }).then(data => { + if (data.completed) { + const attending = `\nAttending: ${data.attending ? 'Yes' : 'No'}`; + const starter = (data.attending && data.starter !== -1) ? `\nStarter: ${starters[data.starter]}` : ''; + const main = (data.attending && data.main !== -1) ? `\nMain: ${mains[data.main]}` : ''; + const dietary = (data.attending && data.dietary) ? `\nDietary requirements: ${data.dietary}` : ''; - sendSlackMsgWithDebounce(`${data.firstname} ${data.lastname} has saved their RSVP:${attending}${starter}${main}${dietary}`, req.body.key); - } - res.json({ - success: true, - prop: req.body.prop, + sendSlackMsgWithDebounce(`${data.firstname} ${data.lastname} has saved their RSVP:${attending}${starter}${main}${dietary}`, req.body.key); + } + res.json({ + success: true, + prop: req.body.prop, + }); + }).catch(err => { + Rollbar.handleError(err); + res.json({ + success: false, + err, + }); }); }).catch(err => { Rollbar.handleError(err); @@ -172,13 +185,7 @@ app.post('/rsvp/save', (req, res) => { err, }); }); - }).catch(err => { - Rollbar.handleError(err); - res.json({ - success: false, - err, - }); - }); + } } catch (err) { Rollbar.handleError(err); res.json({