Create settings for slack/email
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import Nodemailer from 'nodemailer';
|
import Nodemailer from 'nodemailer';
|
||||||
import { email, starters, mains, getRsvpEnd } from '../config';
|
import { email, starters, mains, getRsvpEnd } from '../config';
|
||||||
import { handleError } from '../core/rollbar';
|
import { handleError } from '../core/rollbar';
|
||||||
|
import Settings from '../data/models/Settings';
|
||||||
|
|
||||||
const transporter = Nodemailer.createTransport({
|
const transporter = Nodemailer.createTransport({
|
||||||
service: 'gmail',
|
service: 'gmail',
|
||||||
@@ -36,7 +37,8 @@ ${person.attending && person.dietary.length > 0 ? `<p>Your dietary requirements
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const sendRSVPEmail = function sendRSVPEmail(person) {
|
export const sendRSVPEmail = function sendRSVPEmail(person) {
|
||||||
if (email.enabled) {
|
Settings.findAll().then(data => {
|
||||||
|
if (data[0].email) {
|
||||||
const mailOptions = {
|
const mailOptions = {
|
||||||
from: `"${email.senderName}" <${email.senderAddress}>`,
|
from: `"${email.senderName}" <${email.senderAddress}>`,
|
||||||
to: person.email,
|
to: person.email,
|
||||||
@@ -51,6 +53,9 @@ export const sendRSVPEmail = function sendRSVPEmail(person) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
handleError(err);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sendRSVPEmailWithDebounce = function sendRSVPEmailWithDebounce(person) {
|
export const sendRSVPEmailWithDebounce = function sendRSVPEmailWithDebounce(person) {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Slack from 'slack-node';
|
import Slack from 'slack-node';
|
||||||
import { slackWebhook } from '../config';
|
import { slackWebhook } from '../config';
|
||||||
import { handleError } from '../core/rollbar';
|
import { handleError } from '../core/rollbar';
|
||||||
|
import Settings from '../data/models/Settings';
|
||||||
|
|
||||||
const slack = new Slack();
|
const slack = new Slack();
|
||||||
slack.setWebhook(slackWebhook);
|
slack.setWebhook(slackWebhook);
|
||||||
@@ -9,6 +10,8 @@ slack.setWebhook(slackWebhook);
|
|||||||
const slackTimers = {};
|
const slackTimers = {};
|
||||||
|
|
||||||
export const sendSlackMsg = function sendSlackMsg(text, channel) {
|
export const sendSlackMsg = function sendSlackMsg(text, channel) {
|
||||||
|
Settings.findAll().then(data => {
|
||||||
|
if (data[0].slack) {
|
||||||
slack.webhook({
|
slack.webhook({
|
||||||
channel,
|
channel,
|
||||||
username: 'weddingbot',
|
username: 'weddingbot',
|
||||||
@@ -18,6 +21,10 @@ export const sendSlackMsg = function sendSlackMsg(text, channel) {
|
|||||||
handleError(err);
|
handleError(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
handleError(err);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sendSlackMsgWithDebounce = function sendSlackMsgWithDebounce(text, channel, key) {
|
export const sendSlackMsgWithDebounce = function sendSlackMsgWithDebounce(text, channel, key) {
|
||||||
|
|||||||
23
src/data/models/Settings.js
Normal file
23
src/data/models/Settings.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import DataType from 'sequelize';
|
||||||
|
import Model from '../sequelize';
|
||||||
|
|
||||||
|
const Settings = Model.define('Settings', {
|
||||||
|
key: {
|
||||||
|
type: DataType.UUID,
|
||||||
|
defaultValue: DataType.UUIDV1,
|
||||||
|
primaryKey: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
slack: {
|
||||||
|
type: DataType.BOOLEAN,
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
email: {
|
||||||
|
type: DataType.BOOLEAN,
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Settings;
|
||||||
12
src/data/queries/settings.js
Normal file
12
src/data/queries/settings.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { GraphQLList as List } from 'graphql';
|
||||||
|
import SettingsItemType from '../types/SettingsItemType';
|
||||||
|
import Settings from '../models/Settings';
|
||||||
|
|
||||||
|
const settings = {
|
||||||
|
type: new List(SettingsItemType),
|
||||||
|
resolve() {
|
||||||
|
return Settings.findAll();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default settings;
|
||||||
@@ -7,6 +7,7 @@ import me from './queries/me';
|
|||||||
import getAllPersons from './queries/getAllPersons';
|
import getAllPersons from './queries/getAllPersons';
|
||||||
import getPersonByKey from './queries/getPersonByKey';
|
import getPersonByKey from './queries/getPersonByKey';
|
||||||
import getPersonsByPassword from './queries/getPersonsByPassword';
|
import getPersonsByPassword from './queries/getPersonsByPassword';
|
||||||
|
import settings from './queries/settings';
|
||||||
|
|
||||||
const schema = new Schema({
|
const schema = new Schema({
|
||||||
query: new ObjectType({
|
query: new ObjectType({
|
||||||
@@ -16,6 +17,7 @@ const schema = new Schema({
|
|||||||
getAllPersons,
|
getAllPersons,
|
||||||
getPersonByKey,
|
getPersonByKey,
|
||||||
getPersonsByPassword,
|
getPersonsByPassword,
|
||||||
|
settings,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
17
src/data/types/SettingsItemType.js
Normal file
17
src/data/types/SettingsItemType.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import {
|
||||||
|
GraphQLObjectType as ObjectType,
|
||||||
|
GraphQLID as ID,
|
||||||
|
GraphQLBoolean as BooleanType,
|
||||||
|
GraphQLNonNull as NonNull,
|
||||||
|
} from 'graphql';
|
||||||
|
|
||||||
|
const SettingsItemType = new ObjectType({
|
||||||
|
name: 'SettingsItem',
|
||||||
|
fields: {
|
||||||
|
key: { type: new NonNull(ID) },
|
||||||
|
slack: { type: BooleanType },
|
||||||
|
email: { type: BooleanType },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default SettingsItemType;
|
||||||
@@ -11,3 +11,18 @@
|
|||||||
max-width: var(--max-content-width);
|
max-width: var(--max-content-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.formGroup {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
max-width: 100%;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { PropTypes } from 'react';
|
|||||||
import withStyles from 'isomorphic-style-loader/lib/withStyles';
|
import withStyles from 'isomorphic-style-loader/lib/withStyles';
|
||||||
import s from './Admin.css';
|
import s from './Admin.css';
|
||||||
import Link from '../../components/Link';
|
import Link from '../../components/Link';
|
||||||
|
import Button from '../../components/Button';
|
||||||
|
|
||||||
class Admin extends React.Component {
|
class Admin extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
@@ -26,6 +27,10 @@ class Admin extends React.Component {
|
|||||||
hash: PropTypes.string,
|
hash: PropTypes.string,
|
||||||
time: PropTypes.string,
|
time: PropTypes.string,
|
||||||
}),
|
}),
|
||||||
|
settings: PropTypes.shape({
|
||||||
|
slack: PropTypes.boolean,
|
||||||
|
email: PropTypes.boolean,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -54,6 +59,38 @@ class Admin extends React.Component {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3>Settings</h3>
|
||||||
|
<form method="post" action="/admin/settings">
|
||||||
|
<div className={s.formGroup}>
|
||||||
|
<label className={s.label} htmlFor="slack">
|
||||||
|
Send slack messages on RSVP:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className={s.input}
|
||||||
|
type="checkbox"
|
||||||
|
name="slack"
|
||||||
|
defaultChecked={this.props.settings.slack}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={s.formGroup}>
|
||||||
|
<label className={s.label} htmlFor="email">
|
||||||
|
Send confirmation emails on RSVP:
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className={s.input}
|
||||||
|
type="checkbox"
|
||||||
|
name="email"
|
||||||
|
defaultChecked={this.props.settings.email}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={s.formGroup}>
|
||||||
|
<Button type="submit">
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Build hash: <a href={`https://bitbucket.org/dan_head/wedding/commits/${this.props.build.hash}`}>{this.props.build.hash}</a>
|
Build hash: <a href={`https://bitbucket.org/dan_head/wedding/commits/${this.props.build.hash}`}>{this.props.build.hash}</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -19,8 +19,11 @@ export default {
|
|||||||
},
|
},
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const settingsResp = await fetchQL('{settings{slack,email}}');
|
||||||
const { data } = await resp.json();
|
const { data } = await resp.json();
|
||||||
const config = await configResp.json();
|
const config = await configResp.json();
|
||||||
|
const settings = await settingsResp.json();
|
||||||
const people = data.getAllPersons || [];
|
const people = data.getAllPersons || [];
|
||||||
|
|
||||||
const food = {
|
const food = {
|
||||||
@@ -47,6 +50,7 @@ export default {
|
|||||||
starters={food.starters}
|
starters={food.starters}
|
||||||
mains={food.mains}
|
mains={food.mains}
|
||||||
build={config.build}
|
build={config.build}
|
||||||
|
settings={settings.data.settings[0]}
|
||||||
/></Layout>,
|
/></Layout>,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -112,7 +112,6 @@ class AdminPerson extends React.Component {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ 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, rsvpEndDate } from './config';
|
import { port, auth, starters, mains, rsvpEndDate } from './config';
|
||||||
import Person from './data/models/Person';
|
import Person from './data/models/Person';
|
||||||
|
import Settings from './data/models/Settings';
|
||||||
import Rollbar from './core/rollbar';
|
import Rollbar from './core/rollbar';
|
||||||
import { sendSlackMsg, sendSlackMsgWithDebounce } from './core/slack';
|
import { sendSlackMsg, sendSlackMsgWithDebounce } from './core/slack';
|
||||||
import sequelize from './data/sequelize';
|
import sequelize from './data/sequelize';
|
||||||
@@ -318,6 +319,29 @@ app.post('/admin/people/import', (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
app.post('/admin/settings', (req, res, next) => {
|
||||||
|
try {
|
||||||
|
Settings.destroy({ where: {} }).then(() => {
|
||||||
|
const settings = {
|
||||||
|
slack: !!req.body.slack,
|
||||||
|
email: !!req.body.email,
|
||||||
|
};
|
||||||
|
Settings.create(settings).then(() => {
|
||||||
|
res.redirect(201, '/admin');
|
||||||
|
}).catch(err => {
|
||||||
|
Rollbar.handleError(err);
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
}).catch(err => {
|
||||||
|
Rollbar.handleError(err);
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
Rollbar.handleError(err);
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Register server-side rendering middleware
|
// Register server-side rendering middleware
|
||||||
|
|||||||
Reference in New Issue
Block a user