Add RSVP end date and disable responses
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -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,27 +143,40 @@ app.get('/rsvp/:code', bouncer.block, (req, res, next) => {
|
|||||||
|
|
||||||
app.post('/rsvp/save', (req, res) => {
|
app.post('/rsvp/save', (req, res) => {
|
||||||
try {
|
try {
|
||||||
const update = {};
|
if (new Date() > rsvpEndDate) {
|
||||||
update[req.body.prop] = req.body.value;
|
res.json({
|
||||||
Person.update(update, {
|
success: false,
|
||||||
where: {
|
err: 'RSVP end date has elapsed',
|
||||||
key: req.body.key,
|
});
|
||||||
},
|
} else {
|
||||||
}).then(() => {
|
const update = {};
|
||||||
Person.findOne({
|
update[req.body.prop] = req.body.value;
|
||||||
where: { key: req.body.key },
|
Person.update(update, {
|
||||||
}).then(data => {
|
where: {
|
||||||
if (data.completed) {
|
key: req.body.key,
|
||||||
const attending = `\nAttending: ${data.attending ? 'Yes' : 'No'}`;
|
},
|
||||||
const starter = (data.attending && data.starter !== -1) ? `\nStarter: ${starters[data.starter]}` : '';
|
}).then(() => {
|
||||||
const main = (data.attending && data.main !== -1) ? `\nMain: ${mains[data.main]}` : '';
|
Person.findOne({
|
||||||
const dietary = (data.attending && data.dietary) ? `\nDietary requirements: ${data.dietary}` : '';
|
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);
|
sendSlackMsgWithDebounce(`${data.firstname} ${data.lastname} has saved their RSVP:${attending}${starter}${main}${dietary}`, req.body.key);
|
||||||
}
|
}
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
prop: req.body.prop,
|
prop: req.body.prop,
|
||||||
|
});
|
||||||
|
}).catch(err => {
|
||||||
|
Rollbar.handleError(err);
|
||||||
|
res.json({
|
||||||
|
success: false,
|
||||||
|
err,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
Rollbar.handleError(err);
|
Rollbar.handleError(err);
|
||||||
@@ -172,13 +185,7 @@ app.post('/rsvp/save', (req, res) => {
|
|||||||
err,
|
err,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).catch(err => {
|
}
|
||||||
Rollbar.handleError(err);
|
|
||||||
res.json({
|
|
||||||
success: false,
|
|
||||||
err,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Rollbar.handleError(err);
|
Rollbar.handleError(err);
|
||||||
res.json({
|
res.json({
|
||||||
|
|||||||
Reference in New Issue
Block a user