Add RSVP end date and disable responses
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
<option value="-1" disabled>Will you be attending?</option>
|
||||
<option value="true">
|
||||
@@ -164,6 +171,7 @@ class RsvpCard extends React.Component {
|
||||
defaultValue={this.props.person.attending === true ? 'true' : 'false'}
|
||||
onChange={this.handleAttendingChange}
|
||||
onFocus={this.props.focusCallback}
|
||||
disabled={!this.props.isEditable}
|
||||
>
|
||||
<option value="true">
|
||||
Yes
|
||||
@@ -194,6 +202,7 @@ class RsvpCard extends React.Component {
|
||||
defaultValue={this.props.person.starter}
|
||||
onChange={this.handleStarterChange}
|
||||
onFocus={this.props.focusCallback}
|
||||
disabled={!this.props.isEditable}
|
||||
>
|
||||
{this.props.person.starter === '-1' ? <option value="-1" disabled>Please select a starter</option> : 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' ? <option value="-1" disabled>Please select a main course</option> : 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"
|
||||
/>
|
||||
<div className={s.iconContainer}>
|
||||
@@ -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}
|
||||
|
||||
@@ -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 {
|
||||
<h1 className={s.heading}>Odpovezte prosím</h1>
|
||||
<div className={s.flexContainer}>
|
||||
{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 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>
|
||||
|
||||
@@ -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: <Layout><Rsvp title={title} people={people} /></Layout>,
|
||||
component: (
|
||||
<Layout>
|
||||
<Rsvp
|
||||
title={title}
|
||||
people={people}
|
||||
rsvpEnd={this.getRsvpEnd()}
|
||||
isEditable={this.isEditable()}
|
||||
/>
|
||||
</Layout>
|
||||
),
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user