Skip to content

Stage/6 #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env-sample
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
PORT=1234
APP_NAME=remind.ist
DATABASE_URL=postgres://
NODE_ENV=development
MAILGUN_API_KEY=
MAILGUN_DOMAIN=remind.ist
MAILGUN_SEND_EMAIL_FROM=reminder@remind.ist
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,8 @@ typings/

.DS_Store
.env

# Because I am sick of messing with the merge conflicts of this file
package-lock.json
public/app.js
public/style.css
15 changes: 15 additions & 0 deletions actions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// actions/index.js

const glob = require('glob')

module.exports = glob.sync(
'./actions/**/*.js',
{ ignore: [ './actions/index.js' ] }
).reduce((actions, filename) => {
const pathTokens = filename.split('/')
const key = pathTokens[pathTokens.length - 1].slice(0, -3)

actions[key] = require(`.${filename}`)

return actions
}, {})
10 changes: 10 additions & 0 deletions actions/newReminder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// actions/newReminder.js

module.exports = async (db, { email, message, send_at }) => {
const { rows: [ reminder ] } = await db.query(
'INSERT INTO reminders (email, message, send_at) VALUES ($1, $2, $3) RETURNING *',
[ email, message, send_at ]
)

return reminder
}
9 changes: 8 additions & 1 deletion app.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
// app.js

const server = require('./initialisers/http')
const db = require('./initialisers/postgres')

const run = () => {
server({ db })
}

server()
run()
200 changes: 200 additions & 0 deletions assets/css/base.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
html, body {
height: 100%;
}

* {
margin: 0;
padding: 0;
box-sizing: border-box;
}

body {
line-height: $font-spacing;
font-weight: $font-weight;
font-size: $font-size;
color: $text;
background: $background;
font-family: $font-family;
}

a, a:visited {
color: $primary;
text-decoration: none;
font-weight: bold;
}

a:hover {
text-decoration: underline;
}

main {
background: white;
}

section {
width: 90%;
margin: 0 auto;
}

h1, h2, h3, h4, h5, h6 {
color: $primary;
padding: 15px 0;
}

h1 { font-size: 200%; }
h2 { font-size: 180%; }
h3 { font-size: 160%; }
h4 { font-size: 140%; }
h5 { font-size: 120%; }
h6 { font-size: 100%; }

p {
margin: 10px 0;
}

ul {
width: 100%;

li {
margin-left: 20px;
}
}

strong { font-weight: bold; }

blockquote {
background: lighten($subtle-shade, 10%);
width: 100%;
padding: 5px;
margin: 30px 0;
}

.flex {
display: flex;
width: 100%;
}

.flex.vertical {
flex-direction: column;
height: 100%;
}

.flex.vertical.vcentered {
justify-content: center;
}

.flex.vertical.vcentered > * {
flex-grow: 0;
}

.flex.no-gutters {
justify-content: space-between;
}

.flex.no-gutters > * {
margin: 0;
}

.flex > * {
flex: 1;
margin: 1%;
}

.flex > .wide {
flex: 2;
}

figure img {
max-width: 100%;
max-height: 100%;
}

.center {
text-align: center;
}

.padded {
padding: 25px 0;
}

.vpadded {
padding: 0 25px;
}

header {
border: 1px solid $subtle-shade;
padding: 7px;
background-color: white;
border-top-left-radius: 12px;
border-top-right-radius: 12px;
width: 100%;
background-position: 20px center;
background-size: auto 50%;
background-repeat: no-repeat;
}

.main {
border-right: 1px solid $subtle-shade;
border-left: 1px solid $subtle-shade;
width: 100%;
padding: 7px;
background: white;
}

footer {
width: 100%;
padding: 40px 0;
text-align: center;
border-top: 1px solid $subtle-shade;
margin-top: 100px;
}

.masthead {
background: $primary;
margin-bottom: -30px;
background-image: linear-gradient(
132deg,
darken($primary, 15%) 0,
$primary 71%,
lighten($primary, 15%) 100%
);

h1 {
color: white;
padding: 60px 0;
text-align: center;
}
}

.leadin {
font-size: 120%;
padding: 25px 0;
}

.box {
background: white;
border-left: 4px solid darken($primary, 15%);
padding: 25px;
box-shadow: 0 .5em 1em rgba(0, 0, 0, .2);
width: 50%;
margin: 0 auto;
}

.primary-text {
color: $primary;
}

@media (max-width: 768px) {
section {
width: 98%;
box-shadow: none;
margin: 1%;
}

.box {
width: 90%;
}

.flex:not(.no-stack) { display: block; }
.flex > * { margin: 1%; }
}
32 changes: 32 additions & 0 deletions assets/css/form.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
form > div {
margin: 15px 0;

input, textarea {
font-size: $font-size;
font-family: $font-family;
border-width: 0;
border-bottom: 2px solid $primary;
padding: 8px;
background: lighten($subtle-shade, 10%);
width: 100%;

&:focus {
border-bottom: 2px solid $accent;
}
}
}

button, .button {
font-size: $font-size;
font-family: $font-family;
border-width: 0;
padding: 10px 14px 8px 14px;
background: $primary;
cursor: pointer;
color: white !important;
box-shadow: 0 .5em 1em rgba(0, 0, 0, .2);
font-weight: bold;
text-decoration: none !important;

&:hover { background: lighten($primary, 5%); }
}
5 changes: 5 additions & 0 deletions assets/css/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import './variables.scss';
@import './base.scss';
@import './form.scss';

@import '../../node_modules/flatpickr/dist/flatpickr.css';
12 changes: 12 additions & 0 deletions assets/css/variables.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@charset 'utf-8';

$font-family: 'Muli', sans-serif;
$font-size: 1em;
$font-weight: 300;
$font-spacing: 1.625;

$subtle-shade: #dedede;
$primary: #dd04b5;
$text: #383838;
$background: #fefefe;
$accent: #b5dd04;
14 changes: 14 additions & 0 deletions assets/js/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// assets/js/app.js
import flatpickr from 'flatpickr'

const init = () => {
const pickers = document.querySelectorAll('.flatpickr')

for (const el of pickers) {
flatpickr(el, {
minDate: new Date()
})
}
}

init()
23 changes: 23 additions & 0 deletions bin/db/init
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env node

if (!process.env.DATABASE_URL) require('dotenv').config()

const { readFileSync } = require('fs')

const db = require('../../initialisers/postgres')

const run = () => new Promise(async resolve => {
const schema = readFileSync('./sql/schema.sql', { encoding: 'utf8' })

try {
console.info(`=> Running db query:\n`, schema)
await db.query(schema)
} catch (error) {
console.error(`=> Error running query:\n`, schema, error)
return resolve(1)
}

return resolve(0)
})

run().then(process.exit)
44 changes: 44 additions & 0 deletions bin/reminders/send
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env node

if (!process.env.DATABASE_URL) require('dotenv').config()

const db = require('../../initialisers/postgres')
const { compile, send } = require('../../emails')

const run = () => new Promise(async resolve => {
const now = new Date()
now.setUTCHours(0)
now.setUTCMinutes(0)
now.setUTCSeconds(0)
now.setUTCMilliseconds(0)

console.log(`=> Running mailer job for ${now.toISOString()}`)

const { rows: reminders } = await db.query(
'SELECT * FROM reminders WHERE send_at = $1',
[ now ]
)

console.log(` ${reminders.length} outstanding emails found.`)

if (reminders.length < 1) {
console.log(` Zoinks – no emails need sending, let's get out of here!`)
return resolve()
}

for (const reminder of reminders) {
const message = await compile('reminder', reminder)

await send(message)
await db.query('DELETE FROM reminders WHERE reminder_id = $1', [ reminder.reminder_id ])
}

return resolve(0)
})

run()
.then(process.exit)
.catch(error => {
console.error(error)
process.exit(2)
})
Loading