├── .nvmrc ├── .gitignore ├── README.md ├── package.json ├── app ├── assets │ ├── js │ │ └── script.js │ └── css │ │ └── styles.css └── index.html ├── api └── index.js └── gulpfile.js /.nvmrc: -------------------------------------------------------------------------------- 1 | iojs 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | app/assets/build 2 | node_modules 3 | *.log -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [callum.reviews](http://callum.reviews) 2 | 3 | Has Callum been a dick today? Press the button! 4 | 5 | Note: This website is mostly a joke. If I genuinely have been a dick to you, 6 | I'm sorry! Talk to me about it (or get someone else to talk to me about it); 7 | I never intentionally hurt people's feelings. 8 | 9 | ## License 10 | 11 | Released under the WTFPL license. 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "callum.reviews", 3 | "version": "1.0.0", 4 | "scripts": { 5 | "build": "gulp build", 6 | "start": "node ./api", 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/callumacrae/callum.reviews" 12 | }, 13 | "author": "Callum Macrae ", 14 | "license": "WTFPL", 15 | "bugs": { 16 | "url": "https://github.com/callumacrae/callum.reviews/issues" 17 | }, 18 | "homepage": "http://callum.reviews/", 19 | "private": true, 20 | "dependencies": { 21 | "bluebird": "^2.9.24", 22 | "browser-sync": "^2.5.3", 23 | "express": "^4.12.3", 24 | "gulp": "^3.8.11", 25 | "gulp-babel": "^5.1.0", 26 | "gulp-myth": "^1.0.2", 27 | "minimist": "^1.1.1", 28 | "moment": "^2.10.2", 29 | "mongodb": "^2.0.27" 30 | }, 31 | "devDependencies": { 32 | "chalk": "^1.0.0", 33 | "nodemon": "^1.3.7" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/assets/js/script.js: -------------------------------------------------------------------------------- 1 | /* global Chartist */ 2 | 3 | 'use strict'; 4 | 5 | const angryEmoji = '😠'; 6 | 7 | let button = document.querySelector('button'); 8 | button.addEventListener('click', function () { 9 | this.innerHTML = `${angryEmoji} Thanks for your feedback ${angryEmoji}`; 10 | 11 | let lastDate = localStorage.getItem('last-dick'); 12 | if (lastDate < Date.now() - 9e5) { 13 | localStorage.setItem('last-dick', Date.now()); 14 | 15 | fetch('/api', { method: 'post' }) 16 | .then(res => res.json()) 17 | .then(drawGraph); 18 | } 19 | }); 20 | 21 | fetch('/api') 22 | .then(res => res.json()) 23 | .then(drawGraph); 24 | 25 | function drawGraph(data) { 26 | let labels = data.map((day) => day.date); 27 | let series = data.map((day) => day.dicks); 28 | 29 | new Chartist.Line('.ct-chart', { 30 | labels: labels, 31 | series: [ series ] 32 | }, { 33 | low: 0, 34 | height: 400, 35 | fullWidth: true, 36 | chartPadding: { 37 | right: 40 38 | } 39 | }); 40 | } 41 | -------------------------------------------------------------------------------- /app/assets/css/styles.css: -------------------------------------------------------------------------------- 1 | /* http://www.colourlovers.com/palette/3726935/Is..that_a_flower */ 2 | 3 | :root { 4 | --pink: #EB0E82; 5 | --light-blue: #2863BD; 6 | --dark-blue: #1B1B70; 7 | } 8 | 9 | body { 10 | margin-top: 4em; 11 | 12 | font-family: 'Open Sans', sans-serif; 13 | text-align: center; 14 | 15 | background-color: var(--light-blue); 16 | color: var(--dark-blue); 17 | } 18 | 19 | h1 { 20 | font-weight: 400; 21 | font-size: 4em; 22 | } 23 | 24 | button { 25 | padding: 10px 25px; 26 | font-size: 2em; 27 | 28 | background-color: color(var(--light-blue) blend(var(--dark-blue) 20%)); 29 | border: 2px var(--dark-blue) solid; 30 | border-radius: 15px; 31 | color: var(--dark-blue); 32 | 33 | cursor: pointer; 34 | } 35 | 36 | .ct-chart { 37 | margin-top: 50px; 38 | } 39 | 40 | .ct-line, .ct-point { 41 | stroke: var(--pink) !important; 42 | } 43 | 44 | @media screen and (max-width: 500px) { 45 | body { 46 | font-size: 8pt; 47 | } 48 | 49 | .github-banner { 50 | display: none; 51 | } 52 | } -------------------------------------------------------------------------------- /api/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const argv = require('minimist')(process.argv.slice(2)); 4 | const express = require('express'); 5 | const app = express(); 6 | const moment = require('moment'); 7 | const MongoClient = require('mongodb').MongoClient; 8 | const Promise = require('bluebird'); 9 | 10 | app.use(express.static('app')); 11 | 12 | let collection; 13 | 14 | let mongoConnect = Promise.promisify(MongoClient.connect); 15 | let mongoPromise = mongoConnect('mongodb://localhost:27017/reviews') 16 | .then(function (db) { 17 | collection = db.collection('reviews'); 18 | }); 19 | 20 | app.get('/api', sendData); 21 | function sendData(req, res) { 22 | collection.find().toArray(function (err, days) { 23 | if (err) { 24 | return res.status(500).send(err); 25 | } 26 | 27 | res.send(days); 28 | }); 29 | } 30 | 31 | app.post('/api', function (req, res) { 32 | console.log('%s says you are a dick', req.ip); 33 | 34 | let date = moment().format('Do MMMM YYYY'); 35 | collection.update({ date: date }, { $inc: { dicks: 1 }}, { upsert: true }, function () { 36 | sendData(req, res); 37 | }); 38 | }); 39 | 40 | let serverPromise = Promise.promisify(app.listen.bind(app))(argv.port || 3000); 41 | 42 | Promise.join(mongoPromise, serverPromise) 43 | .then(function () { 44 | console.log('Server started'); 45 | }) 46 | .catch(function () { 47 | console.log('Failed to start :('); 48 | process.exit(1); 49 | }); 50 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const gulp = require('gulp'); 4 | const myth = require('gulp-myth'); 5 | const babel = require('gulp-babel'); 6 | 7 | gulp.task('css', function () { 8 | gulp.src('./app/assets/css/styles.css') 9 | .pipe(myth()) 10 | .pipe(gulp.dest('./app/assets/build')); 11 | }); 12 | 13 | gulp.task('js', function () { 14 | gulp.src('./app/assets/js/script.js') 15 | .pipe(babel()) 16 | .pipe(gulp.dest('./app/assets/build')); 17 | }); 18 | 19 | // This task is for development only 20 | gulp.task('run', function (done) { 21 | // devDependencies required in tasks 22 | const nodemon = require('nodemon'); 23 | const chalk = require('chalk'); 24 | 25 | function doneOnce() { 26 | done(); 27 | doneOnce = function () {}; 28 | } 29 | 30 | nodemon('--watch api ./api --port 3025') 31 | .on('start', function () { 32 | console.log(chalk.grey('App has started')); 33 | doneOnce(); 34 | }) 35 | .on('restart', function () { 36 | console.log(chalk.grey('App restarting')); 37 | }) 38 | .on('crash', function () { 39 | console.log(chalk.grey('App has crashed')); 40 | }); 41 | }); 42 | 43 | gulp.task('build', ['css', 'js']); 44 | 45 | gulp.task('default', ['build', 'run'], function () { 46 | const browserSync = require('browser-sync'); 47 | 48 | let files = [ 49 | './app/index.html', 50 | './app/assets/build/*.js', 51 | './app/assets/build/*.css' 52 | ]; 53 | 54 | browserSync.init(files, { 55 | proxy: 'http://localhost:3025/' 56 | }); 57 | 58 | gulp.watch('./app/assets/css/*.css', ['css']); 59 | gulp.watch('./app/assets/js/*.js', ['js']); 60 | }); 61 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | callum.reviews 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

callum.reviews

15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | Fork me on GitHub 24 | 25 | 26 | 36 | 37 | 38 | --------------------------------------------------------------------------------