Add RSVP end date and disable responses

This commit is contained in:
2017-02-14 20:29:37 +00:00
parent c85aafd617
commit d44d86e2ce
5 changed files with 106 additions and 35 deletions

View File

@@ -44,10 +44,11 @@
color: #616161; color: #616161;
font-size: 18px; font-size: 18px;
line-height: 1.3333333; line-height: 1.3333333;
transition: transition: border-color ease-in-out 0.3s, box-shadow ease-in-out 0.3s, background-color ease-in-out 0.3s;
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 { .input:focus {
@@ -87,3 +88,7 @@
.rsvpSummarySuccess { .rsvpSummarySuccess {
opacity: 1; opacity: 1;
} }
.rsvpSummaryReadOnly {
opacity: 1;
}

View File

@@ -21,6 +21,7 @@ class RsvpCard extends React.Component {
starter: PropTypes.string, starter: PropTypes.string,
main: PropTypes.string, main: PropTypes.string,
}).isRequired, }).isRequired,
isEditable: PropTypes.bool.isRequired,
}; };
constructor(props) { constructor(props) {
@@ -32,6 +33,11 @@ class RsvpCard extends React.Component {
saveMain: this.props.person.main !== '-1' ? 'saved' : null, saveMain: this.props.person.main !== '-1' ? 'saved' : null,
saveDietary: this.props.person.dietary ? '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) { isRSVPComplete(prop, value) {
@@ -143,6 +149,7 @@ class RsvpCard extends React.Component {
defaultValue="-1" defaultValue="-1"
onChange={this.handleAttendingChange} onChange={this.handleAttendingChange}
onFocus={this.props.focusCallback} onFocus={this.props.focusCallback}
disabled={!this.props.isEditable}
> >
<option value="-1" disabled>Will you be attending?</option> <option value="-1" disabled>Will you be attending?</option>
<option value="true"> <option value="true">
@@ -164,6 +171,7 @@ class RsvpCard extends React.Component {
defaultValue={this.props.person.attending === true ? 'true' : 'false'} defaultValue={this.props.person.attending === true ? 'true' : 'false'}
onChange={this.handleAttendingChange} onChange={this.handleAttendingChange}
onFocus={this.props.focusCallback} onFocus={this.props.focusCallback}
disabled={!this.props.isEditable}
> >
<option value="true"> <option value="true">
Yes Yes
@@ -194,6 +202,7 @@ class RsvpCard extends React.Component {
defaultValue={this.props.person.starter} defaultValue={this.props.person.starter}
onChange={this.handleStarterChange} onChange={this.handleStarterChange}
onFocus={this.props.focusCallback} onFocus={this.props.focusCallback}
disabled={!this.props.isEditable}
> >
{this.props.person.starter === '-1' ? <option value="-1" disabled>Please select a starter</option> : null} {this.props.person.starter === '-1' ? <option value="-1" disabled>Please select a starter</option> : null}
{starters.map((starter, index) => ( {starters.map((starter, index) => (
@@ -220,6 +229,7 @@ class RsvpCard extends React.Component {
defaultValue={this.props.person.main} defaultValue={this.props.person.main}
onChange={this.handleMainChange} onChange={this.handleMainChange}
onFocus={this.props.focusCallback} onFocus={this.props.focusCallback}
disabled={!this.props.isEditable}
> >
{this.props.person.main === '-1' ? <option value="-1" disabled>Please select a main course</option> : null} {this.props.person.main === '-1' ? <option value="-1" disabled>Please select a main course</option> : null}
{mains.map((main, index) => ( {mains.map((main, index) => (
@@ -249,6 +259,7 @@ class RsvpCard extends React.Component {
onChange={this.handleDietaryChange} onChange={this.handleDietaryChange}
onBlur={this.handleDietaryBlur} onBlur={this.handleDietaryBlur}
onFocus={this.props.focusCallback} onFocus={this.props.focusCallback}
disabled={!this.props.isEditable}
placeholder="None" placeholder="None"
/> />
<div className={s.iconContainer}> <div className={s.iconContainer}>
@@ -262,6 +273,7 @@ class RsvpCard extends React.Component {
className={[ className={[
s.rsvpSummary, s.rsvpSummary,
this.state.summaryState === 'success' ? s.rsvpSummarySuccess : null, this.state.summaryState === 'success' ? s.rsvpSummarySuccess : null,
this.state.summaryState === 'readonly' ? s.rsvpSummaryReadOnly : null,
].join(' ')} ].join(' ')}
> >
{this.state.summary} {this.state.summary}

View File

@@ -14,6 +14,8 @@ class Rsvp extends React.Component {
starter: PropTypes.string, starter: PropTypes.string,
main: PropTypes.string, main: PropTypes.string,
})).isRequired, })).isRequired,
rsvpEnd: PropTypes.string.isRequired,
isEditable: PropTypes.bool.isRequired,
}; };
constructor(props) { constructor(props) {
@@ -38,11 +40,16 @@ class Rsvp extends React.Component {
<h1 className={s.heading}>Odpovezte prosím</h1> <h1 className={s.heading}>Odpovezte prosím</h1>
<div className={s.flexContainer}> <div className={s.flexContainer}>
{this.props.people.map(person => ( {this.props.people.map(person => (
<RsvpCard key={person.key} person={person} focusCallback={this.focusCallback} /> <RsvpCard
key={person.key}
person={person}
focusCallback={this.focusCallback}
isEditable={this.props.isEditable}
/>
))} ))}
</div> </div>
<div className={s.notes}> <div className={s.notes}>
<p>You can modify the above details up until XX/XX/XXXX</p> <p>You can modify the above details up until {this.props.rsvpEnd}</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -3,13 +3,44 @@ import Layout from '../../components/Layout';
import fetchQL from '../../core/fetchQL'; import fetchQL from '../../core/fetchQL';
import Rsvp from './Rsvp'; import Rsvp from './Rsvp';
import NotFound from '../notFound/NotFound'; import NotFound from '../notFound/NotFound';
import { rsvpEndDate } from '../../config';
let title = 'RSVP'; let title = 'RSVP';
const months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
export default { export default {
path: '/rsvp/:password', 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 }) { async action({ params }) {
if (!params.password) { if (!params.password) {
return { redirect: '/' }; return { redirect: '/' };
@@ -32,7 +63,16 @@ export default {
return { return {
title, title,
component: <Layout><Rsvp title={title} people={people} /></Layout>, component: (
<Layout>
<Rsvp
title={title}
people={people}
rsvpEnd={this.getRsvpEnd()}
isEditable={this.isEditable()}
/>
</Layout>
),
}; };
}, },

View File

@@ -21,7 +21,7 @@ import models from './data/models';
import schema from './data/schema'; import schema from './data/schema';
import routes from './routes'; import routes from './routes';
import assets from './assets'; // eslint-disable-line import/no-unresolved 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 Person from './data/models/Person';
import Rollbar from './core/rollbar'; import Rollbar from './core/rollbar';
import { sendSlackMsgWithDebounce } from './core/slack'; import { sendSlackMsgWithDebounce } from './core/slack';
@@ -143,6 +143,12 @@ app.get('/rsvp/:code', bouncer.block, (req, res, next) => {
app.post('/rsvp/save', (req, res) => { app.post('/rsvp/save', (req, res) => {
try { try {
if (new Date() > rsvpEndDate) {
res.json({
success: false,
err: 'RSVP end date has elapsed',
});
} else {
const update = {}; const update = {};
update[req.body.prop] = req.body.value; update[req.body.prop] = req.body.value;
Person.update(update, { Person.update(update, {
@@ -179,6 +185,7 @@ app.post('/rsvp/save', (req, res) => {
err, err,
}); });
}); });
}
} catch (err) { } catch (err) {
Rollbar.handleError(err); Rollbar.handleError(err);
res.json({ res.json({