├── .babelrc ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── README.md ├── backend └── lib │ ├── helpers │ └── server-helpers.js │ ├── index.js │ ├── redis │ ├── client.js │ └── redisFunctions.js │ ├── routes │ ├── Images.js │ ├── ReactUrls.js │ └── Scripts.js │ ├── server.js │ └── tests │ ├── runner.js │ └── server.test.js ├── frontend └── src │ ├── js │ ├── actions │ │ └── actions_index.js │ ├── components │ │ ├── App.js │ │ ├── Footer │ │ │ └── footer_index.js │ │ └── Header │ │ │ └── header_index.js │ ├── index.js │ ├── reducers │ │ ├── reducer_generic.js │ │ └── reducers_index.js │ ├── routes.js │ └── views │ │ ├── About │ │ └── about_index.js │ │ ├── Contact │ │ └── contact_index.js │ │ └── Home │ │ └── home_index.js │ └── scss │ ├── _bootstrap-navbar.scss │ ├── _variables.scss │ └── style.scss ├── package-lock.json ├── package.json ├── public ├── img │ ├── react-logo.png │ └── rhino.png └── index.html └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "es2015", "stage-1"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public/app.js 3 | backend/dist 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "react" 4 | ], 5 | "parserOptions": { 6 | "ecmaVersion": 6, 7 | "sourceType": "module", 8 | "ecmaFeatures": { 9 | "impliedStrict": true, 10 | "jsx": true, 11 | "modules": true, 12 | "experimentalObjectRestSpread": true 13 | } 14 | }, 15 | "env": { 16 | "browser": true, 17 | "node": true, 18 | "es6": true 19 | }, 20 | "rules": { 21 | "comma-dangle": ["error", "only-multiline"], 22 | "no-cond-assign": ["error", "always"], 23 | "no-constant-condition": "warn", 24 | "no-control-regex": "error", 25 | "no-dupe-args": "error", 26 | "no-dupe-keys": "error", 27 | "no-duplicate-case": "error", 28 | "no-empty": "error", 29 | "no-empty-character-class": "error", 30 | "no-ex-assign": "error", 31 | "no-extra-boolean-cast": "error", 32 | "no-extra-parens": ["error", "all"], 33 | "no-extra-semi": "error", 34 | "no-func-assign": "error", 35 | "no-inner-declarations": ["error", "functions"], 36 | "no-invalid-regexp": "error", 37 | "no-irregular-whitespace": "error", 38 | "no-negated-in-lhs": "error", 39 | "no-obj-calls": "error", 40 | "no-regex-spaces": "error", 41 | "no-sparse-arrays": "error", 42 | "no-unexpected-multiline": "error", 43 | "no-unreachable": "error", 44 | "use-isnan": "error", 45 | "valid-typeof": "error", 46 | 47 | "array-callback-return": "error", 48 | "complexity": ["error", { "maximum": 7 }], 49 | "curly": ["error", "multi-line", "consistent"], 50 | "dot-location": ["error", "property"], 51 | "dot-notation": "error", 52 | "eqeqeq": ["error", "smart"], 53 | "no-caller": "error", 54 | "no-empty-pattern": "error", 55 | "no-eval": "error", 56 | "no-extend-native": "error", 57 | "no-fallthrough": "error", 58 | "no-floating-decimal": "error", 59 | "no-implied-eval": "error", 60 | "no-invalid-this": "error", 61 | "no-iterator": "error", 62 | "no-labels": "error", 63 | "no-lone-blocks": "error", 64 | "no-multi-spaces": "error", 65 | "no-native-reassign": "error", 66 | "no-new": "error", 67 | "no-new-func": "error", 68 | "no-new-wrappers": "error", 69 | "no-octal": "error", 70 | "no-octal-escape": "error", 71 | "no-param-reassign": "error", 72 | "no-proto": "error", 73 | "no-redeclare": "error", 74 | "no-return-assign": "error", 75 | "no-script-url": "error", 76 | "no-self-assign": "error", 77 | "no-self-compare": "error", 78 | "no-sequences": "error", 79 | "no-unused-expressions": "error", 80 | "no-useless-call": "error", 81 | "no-useless-concat": "error", 82 | "no-useless-escape": "error", 83 | "no-void": "error", 84 | "no-with": "error", 85 | "radix": ["error", "as-needed"], 86 | "wrap-iife": ["error", "inside"], 87 | "yoda": ["error", "never", { "exceptRange": true }], 88 | 89 | "strict": ["error", "safe"], 90 | 91 | "no-delete-var": "error", 92 | "no-label-var": "error", 93 | "no-shadow": ["error", { "builtinGlobals": true, "allow": ["_", "__", "done", "cb", "resolve", "reject"] }], 94 | "no-shadow-restricted-names": "error", 95 | "no-undef": "error", 96 | "no-undef-init": "error", 97 | "no-undefined": "error", 98 | "no-unused-vars": ["error", { "vars": "local", "args": "after-used" }], 99 | 100 | "handle-callback-err": ["error", "^.(e|E)rr"], 101 | "no-mixed-requires": "error", 102 | "no-new-require": "error", 103 | "no-path-concat": "error", 104 | "no-process-exit": "error", 105 | 106 | "array-bracket-spacing": ["error", "always"], 107 | "block-spacing": ["error", "never"], 108 | "brace-style": ["error", "1tbs", { "allowSingleLine": true }], 109 | "comma-spacing": "error", 110 | "computed-property-spacing": "error", 111 | "consistent-this": ["error", "that"], 112 | "eol-last": "error", 113 | "indent": ["error", 2], 114 | "jsx-quotes": ["error", "prefer-single"], 115 | "key-spacing": ["error", { "beforeColon": false, "afterColon": true, "mode": "strict" }], 116 | "keyword-spacing": "error", 117 | "max-depth": ["error", 4], 118 | "max-len": ["error", 100], 119 | "max-nested-callbacks": ["error", 4], 120 | "max-params": ["error", 6], 121 | "max-statements-per-line": ["error", { "max": 2 }], 122 | "new-cap": "error", 123 | "new-parens": "error", 124 | "no-array-constructor": "error", 125 | "no-lonely-if": "error", 126 | "no-mixed-spaces-and-tabs": "error", 127 | "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1 }], 128 | "no-nested-ternary": "error", 129 | "no-new-object": "error", 130 | "no-spaced-func": "error", 131 | "no-trailing-spaces": "error", 132 | "no-unneeded-ternary": "error", 133 | "no-whitespace-before-property": "error", 134 | "object-curly-spacing": ["error", "always"], 135 | "quote-props": ["error", "consistent-as-needed"], 136 | "quotes": ["error", "single"], 137 | "semi": ["error", "never"], 138 | "space-before-blocks": ["error", "always"], 139 | "space-before-function-paren": ["error", "always"], 140 | "space-in-parens": ["error", "never"], 141 | 142 | "arrow-spacing": "error", 143 | "constructor-super": "error", 144 | "no-class-assign": "error", 145 | "no-useless-constructor": "error", 146 | "prefer-arrow-callback": "error", 147 | "prefer-const": "error", 148 | "require-yield": "error", 149 | 150 | "react/no-danger": 2, 151 | "react/no-direct-mutation-state": 2, 152 | "react/no-multi-comp": [2, { "ignoreStateless": true }], 153 | "react/no-unknown-property": 2, 154 | "react/react-in-jsx-scope": 2, 155 | "react/self-closing-comp": 2, 156 | "react/sort-comp": 2, 157 | "react/wrap-multilines": 2, 158 | "react/jsx-boolean-value": 2, 159 | "react/jsx-closing-bracket-location": 2, 160 | "react/jsx-curly-spacing": 2, 161 | "react/jsx-equals-spacing": 2, 162 | "react/jsx-indent-props": [2, 2], 163 | "react/jsx-indent": [2, 2], 164 | "react/jsx-max-props-per-line": [2, { "maximum": 4 }], 165 | "react/jsx-no-duplicate-props": 2, 166 | "react/jsx-no-undef": 2, 167 | "react/jsx-pascal-case": 2, 168 | "react/jsx-uses-react": 2, 169 | "react/jsx-uses-vars": 2 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | 35 | backend/dist 36 | public/app.js 37 | dump.rdb 38 | config.env 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hackathon boilerplate 2 | 3 | [Founders and Coders'](http://www.foundersandcoders.com/) current cohort are doing Hackathons all through April. This is some boilerplate code to get us off to a good start. 4 | 5 | It's a work in progress so feel free to contribute! 6 | 7 | It's based around the core technologies of FAC7's RHINO stack (React, Redis, Hapi and Node): 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ### What's in the boilerplate? 18 | 19 | The aim of the boilerplate is to have a skeleton of a React single page app up and running with a Hapi server in the background serving static files. 20 | 21 | The server can easily be extended to serve data as an API by communicating with the configured redis server. 22 | 23 | The frontend also has a skeleton redux setup which can be extended 24 | 25 | There are two main branches, one with twitter auth set up (called twitter) and the master branch without twitter auth. If using twitter auth remember sign up for a twitter app and add the consumer key and consumer secret (as `CONSUMER_KEY` and `CONSUMER_SECRET`) to a config.env file 26 | 27 | If you want to get straight up and running skip to the last section! 28 | 29 | # Key Features 30 | 31 | ## Build Tools 32 | 33 | 34 | ### Webpack & Babel 35 | 36 | 37 | 38 | Frontend code is run through Webpack which transpiles all the js and jsx through babel and bundles it into one neat file `app.js` which meets ES5 standards 39 | 40 | ### Sass 41 | 42 | 43 | 44 | Webpack also watches for `.scss` files and turns them into css that can be used in the bundle 45 | 46 | Just `require` or `import` the `.scss` file directly into your React code 47 | 48 | Make sure if you're using sass variables that are in annother file to use the `@import` rule at the top of the .scss file you're using. 49 | 50 | # Backend 51 | 52 | The backend folder holds all the server and db files: 53 | 54 | The root server file is `server.js` and this imports routes (as just plain objects) from the routes folder. The aim was to keep the server file as clean and neat as possible. 55 | 56 | The server is configured to serve js files, images (from the `./public/img` folder) and handles requests from react-router urls (these would usually crash the server if a user were to put them in manually so the `ReactUrls.js` route takes the request and replies with `index.html` (react router is clever and handles the page the user was on!)) 57 | 58 | ### Bluebird.js 59 | 60 | 61 | 62 | Redis is separated into the client and the redisFunctions files: The example redis functions (these should also be deleted) take advantage of the fantastic Bluebird Promise library: 63 | 64 | these let you write code that relies on callbacks in a much cleaner, semantic way: 65 | 66 | So this function: 67 | 68 | ```js 69 | getDummyData((err, data) => { 70 | if (err) throw err; 71 | else { 72 | reply(data) 73 | } 74 | }) 75 | ``` 76 | 77 | could be rewritten using bluebird as: 78 | 79 | ```js 80 | getDummyData() 81 | .then(data => reply(data)) 82 | .catch(error => { throw error }) 83 | ``` 84 | 85 | .then() calls can be chained, making code that calls the database multiple times much cleaner 86 | 87 | Bluebird is optional but highly recommended. 88 | 89 | # Frontend 90 | 91 | Frontend code is split up between the `frontend` folder and the `public` folder (the public folder holds the index.html and img files (and is also the webpack app.js bundle target)) 92 | 93 | Components live in the Components folder (which should be reorganised depending on the project needs) and the current `App.js` holds the Header and Footer wrapper that appear on each page. 94 | 95 | The `routes.js` folder is the highest level component which takes all of the components and orders them as routes 96 | 97 | These then get rendered to the DOM in `index.js` 98 | 99 | ### React Bootstrap 100 | 101 | 102 | 103 | The current boilerplate makes use of React Bootstrap which is a brilliant collection of components based on the Bootstrap css framework 104 | 105 | Take a look at the [documentation here](https://react-bootstrap.github.io/) 106 | 107 | to use Bootstrap components you can import as many as you like directly into your component: 108 | 109 | ```js 110 | import { Button, Nav, Grid } from 'react-bootstrap' 111 | ``` 112 | 113 | and then use them in your components as JSX: 114 | 115 | ```js 116 | render () { 117 | return ( 118 |
119 |
123 | ) 124 | } 125 | ``` 126 | 127 | [the react-bootstrap docs](https://react-bootstrap.github.io/components.html#navs) have some great examples of how to use these components 128 | 129 | ### Header and Footer 130 | 131 | The current header and footer have been configured to take a logo image and some links (that are rendered as react router links) - these options are in the `App.js` file. If you add or remove links in `App.js`, make sure to update them in your router (`routes.js`) 132 | 133 | There are also some default colors set for the header and footer background colors: these can be changed in `_variables.scss` in the `scss` folder 134 | 135 | # Get Up and running 136 | 137 | 1- Clone or fork the repo 138 | (if you want to repurpose it as your own delete the .git folder and copy the files over to your new repo) and change the `repository` and `bugs` fields in the `package.json` 139 | ``` 140 | $ git clone https://github.com/andrewMacmurray/hackathon-boilerplate.git 141 | ``` 142 | 2- Install all the dependencies by `cd`-ing into the folder and running: 143 | ``` 144 | $ npm install 145 | ``` 146 | 3- Start your redis server (in annother terminal window) and then open the redis-cli 147 | ``` 148 | $ redis-server 149 | ``` 150 | ``` 151 | $ redis-cli 152 | ``` 153 | 4- If you just want to make changes to the frontend code (i.e. you don't need any data from the hapi server or database) run: 154 | ``` 155 | $ npm run dev 156 | ``` 157 | This fires up the webpack dev server with hot reloading. Go to `localhost:8080` in your browser to see the build 158 | 159 | 5- If you want to see the whole app running (with api data and all), you need to run two commands: 160 | ```sh 161 | $ npm run nodemon 162 | ``` 163 | The nodemon command runs the backend code through babel and starts the server (every time you make changes to the code this gets run) 164 | ```sh 165 | $ npm run watch 166 | ``` 167 | The watch command runs webpack in watch mode, this watches for changes in your code and adds the changes to the bundled file `app.js` in the public folder 168 | 169 | WARNING: previewing change when running both watch and nodemon is slower than running in just frontend dev mode (so don't freak out when the browser says `page not found`, just give it a moment) 170 | -------------------------------------------------------------------------------- /backend/lib/helpers/server-helpers.js: -------------------------------------------------------------------------------- 1 | module.exports = (err) => { 2 | if (err) { 3 | console.log('plugins error: ', err) 4 | throw err 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /backend/lib/index.js: -------------------------------------------------------------------------------- 1 | require('env2')('./config.env') 2 | const createServer = require('./server.js') 3 | const createClient = require('./redis/client.js') 4 | 5 | // with redis 6 | // const client = createClient() 7 | // const server = createServer(client) 8 | 9 | // without redis 10 | const server = createServer() 11 | 12 | server.start((err) => { 13 | if (err) { 14 | console.log('server error: ', err) 15 | } else { 16 | console.log('server listening on port: ' + server.info.port) 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /backend/lib/redis/client.js: -------------------------------------------------------------------------------- 1 | const redis = require('redis') 2 | const bluebird = require('bluebird') 3 | 4 | bluebird.promisifyAll(redis.RedisClient.prototype) 5 | bluebird.promisifyAll(redis.Multi.prototype) 6 | 7 | module.exports = (opts) => { 8 | 9 | const config = { 10 | url: process.env.REDIS_URL || 'redis://localhost:6379', 11 | db: process.env.REDIS_DB || 0 12 | } 13 | 14 | if (opts && opts.env === 'TEST') config.db = 5 15 | 16 | return redis.createClient(config) 17 | } 18 | -------------------------------------------------------------------------------- /backend/lib/redis/redisFunctions.js: -------------------------------------------------------------------------------- 1 | // All database helper functions should take the redis client object as the first argument 2 | 3 | module.exports.genericDbHelperFunction = (client, args) => { 4 | // ... body 5 | } 6 | -------------------------------------------------------------------------------- /backend/lib/routes/Images.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | path: '/img/{imageUrl*}', 3 | method: 'GET', 4 | handler: { 5 | directory: { path: './public/img' } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/routes/ReactUrls.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | path: '/{param*}', 3 | method: 'GET', 4 | handler: (response, reply) => { 5 | reply.file('./public/index.html') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /backend/lib/routes/Scripts.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | path: '/{filename}.js', 3 | method: 'GET', 4 | handler: (response, reply) => { 5 | const js = './public' + response.path 6 | reply.file(js) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /backend/lib/server.js: -------------------------------------------------------------------------------- 1 | require('env2')('./config.env') 2 | 3 | const Hapi = require('hapi') 4 | 5 | // helper methods 6 | const handlePlugins = require('./helpers/server-helpers.js') 7 | 8 | // server plugins 9 | const Inert = require('inert') 10 | 11 | // server routes 12 | const Images = require('./routes/Images.js') 13 | const ReactUrls = require('./routes/ReactUrls.js') 14 | const Scripts = require('./routes/Scripts.js') 15 | 16 | const Plugins = [ Inert ] 17 | const Routes = [ Images, ReactUrls, Scripts ] 18 | 19 | module.exports = (client) => { 20 | 21 | const server = new Hapi.Server() 22 | 23 | server.connection({ port: process.env.PORT || 4000 }) 24 | server.register(Plugins, handlePlugins) 25 | server.route(Routes) 26 | 27 | return server 28 | } 29 | -------------------------------------------------------------------------------- /backend/lib/tests/runner.js: -------------------------------------------------------------------------------- 1 | require('./server.test.js') 2 | require('./auth.test.js') 3 | -------------------------------------------------------------------------------- /backend/lib/tests/server.test.js: -------------------------------------------------------------------------------- 1 | const tape = require('wrapping-tape') 2 | const createClient = require('../../dist/redis/client.js').default 3 | const createServer = require('../../dist/server.js').default 4 | 5 | var client = null 6 | var server = null 7 | 8 | const tests = tape({ 9 | setup: (t) => { 10 | client = createClient({ env: 'TEST' }) 11 | server = createServer(client) 12 | t.end() 13 | }, 14 | 15 | teardown: (t) => { 16 | server.stop() 17 | client.quit() 18 | t.end() 19 | } 20 | }) 21 | 22 | tests('Check server running', (t) => { 23 | server.inject({ method: 'GET', url: '/' }, (res) => { 24 | const actual = res.statusCode 25 | const expected = 200 26 | t.equal(actual, expected, 'Assert successful response') 27 | }) 28 | 29 | server.inject({ method: 'GET', url: '/app.js' }, (res) => { 30 | const actual = res.headers['content-type'].indexOf('javascript') > -1 31 | t.ok(actual, 'Assert app.js loaded') 32 | }) 33 | 34 | t.end() 35 | }) 36 | -------------------------------------------------------------------------------- /frontend/src/js/actions/actions_index.js: -------------------------------------------------------------------------------- 1 | export const GENERIC_ACTION = 'GENERIC_ACTION' 2 | 3 | export const genericAction = () => { 4 | return { 5 | type: GENERIC_ACTION, 6 | payload: 'some data to pass into your app' 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/js/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Header from './Header/header_index.js' 3 | import Footer from './Footer/footer_index.js' 4 | 5 | import '../../scss/style.scss' 6 | 7 | const options = { 8 | logoUrl: 'img/rhino.png' 9 | } 10 | 11 | export default class App extends React.Component { 12 | render () { 13 | return ( 14 |
15 |
19 |
20 | {this.props.children} 21 |
23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /frontend/src/js/components/Footer/footer_index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Navbar, Nav } from 'react-bootstrap' 3 | 4 | export default (props) => { 5 | return ( 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /frontend/src/js/components/Header/header_index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Navbar, Nav } from 'react-bootstrap' 3 | import { Link } from 'react-router' 4 | 5 | export default class Header extends React.Component { 6 | constructor () { 7 | super() 8 | this.state = { menuOpen: false } 9 | } 10 | 11 | render () { 12 | return ( 13 |
14 | { this.setState({ menuOpen: !this.state.menuOpen }) }} 17 | className='top-menu' 18 | fixedTop={true}> 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 39 | 40 | 41 |
42 | ) 43 | } 44 | } 45 | 46 | Header.defaultProps = { 47 | menuItems: [ 'about', 'contact' ] 48 | } 49 | -------------------------------------------------------------------------------- /frontend/src/js/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { Provider } from 'react-redux' 4 | import { createStore, applyMiddleware } from 'redux' 5 | import promise from 'redux-promise' 6 | 7 | import { Router, browserHistory } from 'react-router' 8 | import reducers from './reducers/reducers_index.js' 9 | import Routes from './routes.js' 10 | 11 | const createStoreWithMiddleware = applyMiddleware( 12 | promise 13 | )(createStore) 14 | 15 | ReactDOM.render( 16 | 17 | 18 | , 19 | document.getElementById('app') 20 | ) 21 | -------------------------------------------------------------------------------- /frontend/src/js/reducers/reducer_generic.js: -------------------------------------------------------------------------------- 1 | import { GENERIC_ACTION } from '../actions/actions_index.js' 2 | 3 | export default (state = '', action) => { 4 | switch (action.type) { 5 | case GENERIC_ACTION: 6 | return action.payload 7 | default: 8 | return state 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/js/reducers/reducers_index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import generic from './reducer_generic.js' 3 | 4 | const rootReducer = combineReducers({ 5 | generic 6 | }) 7 | 8 | export default rootReducer 9 | -------------------------------------------------------------------------------- /frontend/src/js/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Route, IndexRoute } from 'react-router' 3 | 4 | import App from './components/App.js' 5 | import Home from './views/Home/home_index.js' 6 | import About from './views/About/about_index.js' 7 | import Contact from './views/Contact/contact_index.js' 8 | 9 | export default ( 10 | 11 | 12 | 13 | 14 | 15 | ) 16 | -------------------------------------------------------------------------------- /frontend/src/js/views/About/about_index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default () => { 4 | return ( 5 |
6 |

Put something about your app here

7 |
8 | ) 9 | } 10 | -------------------------------------------------------------------------------- /frontend/src/js/views/Contact/contact_index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Grid, Row, Col } from 'react-bootstrap' 3 | 4 | const styles = { 5 | textAlign: 'center', 6 | margin: '4em auto' 7 | } 8 | const repoLink = 'https://github.com/foundersandcoders' 9 | 10 | export default (props) => { 11 | return ( 12 | 13 | 14 | 15 |

Get in touch via our repo!

16 | Your repo link 17 | 18 |
19 |
20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/js/views/Home/home_index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Grid, Row, Col } from 'react-bootstrap' 3 | import { connect } from 'react-redux' 4 | import { getUserDetails } from '../../actions/actions_index.js' 5 | 6 | export default class Home extends React.Component { 7 | render () { 8 | return ( 9 | 10 | 11 | 12 |
13 | 14 |
15 |

Your app goes here...

16 | 17 |
18 |
19 | ) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/scss/_bootstrap-navbar.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | 3 | .header-spacing { 4 | height: $header-height; 5 | } 6 | 7 | .navbar { 8 | margin-bottom: 0; 9 | } 10 | 11 | // navbar Button 12 | 13 | .navbar-toggle { 14 | border: none; 15 | transition: 0.3s ease; 16 | padding: 10px; 17 | margin-top: 15px; 18 | margin-right: 15px; 19 | } 20 | 21 | .navbar-default .navbar-toggle .icon-bar { 22 | background-color: #fff; 23 | width: 25px; 24 | height: 3px; 25 | } 26 | 27 | .navbar-default .navbar-toggle:focus { 28 | background-color: transparent; 29 | } 30 | 31 | .navbar-default .navbar-toggle:hover { 32 | background-color: rgba(221, 221, 221, 0.26); 33 | } 34 | 35 | // top navigation / header 36 | 37 | .top-menu { 38 | background-color: $header-color; 39 | border: none; 40 | border-radius: 0; 41 | 42 | .navbar-nav > li > a { 43 | color: #fff; 44 | } 45 | .navbar-brand { 46 | height: $header-height; 47 | width: $logo-size; 48 | 49 | img { 50 | margin-top: $logo-offset; 51 | width: 100%; 52 | } 53 | } 54 | } 55 | 56 | @media (min-width: 768px) { 57 | .top-menu .navbar-right li { 58 | padding: 20px 0; 59 | } 60 | } 61 | 62 | .user-logged-in { 63 | position: absolute; 64 | margin-top: 15px; 65 | color: $grey-blue; 66 | } 67 | 68 | // footer 69 | 70 | .footer { 71 | border: none; 72 | border-radius: 0; 73 | position: absolute; 74 | width: 100%; 75 | bottom: 0; 76 | background-color: $footer-color; 77 | 78 | .navbar-header, 79 | .navbar-right li { 80 | display: inline-block; 81 | color: #fff; 82 | } 83 | 84 | .navbar-right { 85 | display: inline-block; 86 | float: right; 87 | } 88 | 89 | p { 90 | margin: 2em 1.5em; 91 | color: #fff; 92 | } 93 | 94 | .navbar-brand { 95 | width: $logo-size / 1.33; 96 | 97 | img { 98 | width: 100%; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /frontend/src/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | $main-font: Lato, PT Sans, Helvetica Neue, Helvetica, Arial; 2 | 3 | // colors 4 | 5 | $header-color: #FF5854; 6 | 7 | $yellow: #FFBE63; 8 | $grey-blue: #3E6AA3; 9 | $footer-color: #2B2B2B; 10 | 11 | // heights 12 | 13 | $header-height: 100px; 14 | $footer-height: 80px; 15 | $logo-size: 7em; 16 | $logo-offset: 10px; 17 | -------------------------------------------------------------------------------- /frontend/src/scss/style.scss: -------------------------------------------------------------------------------- 1 | @import 'variables'; 2 | @import 'bootstrap-navbar'; 3 | 4 | html { 5 | position: relative; 6 | min-height: 100%; 7 | } 8 | 9 | body { 10 | font-family: $main-font; 11 | margin-bottom: $footer-height; 12 | } 13 | 14 | .about h3 { 15 | text-align: center; 16 | margin: 2em; 17 | } 18 | 19 | .home { 20 | text-align: center; 21 | } 22 | 23 | .image-container { 24 | width: 80%; 25 | max-width: 400px; 26 | margin: 4em auto; 27 | 28 | img { 29 | width: 100%; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hackathon-boilerplate", 3 | "version": "1.0.0", 4 | "description": "Some boilerplate to get us off to a quick start at hackathons", 5 | "main": "index.js", 6 | "scripts": { 7 | "nodemon": "nodemon backend/lib/index.js", 8 | "dev": "webpack-dev-server --hot --inline --content-base public/", 9 | "watch": "webpack --watch --progress", 10 | "start": "node ./backend/lib/index.js", 11 | "test": "node ./backend/lib/tests/runner.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/andrewMacmurray/hackathon-boilerplate.git" 16 | }, 17 | "author": "FAC7", 18 | "license": "ISC", 19 | "bugs": { 20 | "url": "https://github.com/andrewMacmurray/hackathon-boilerplate/issues" 21 | }, 22 | "homepage": "https://github.com/andrewMacmurray/hackathon-boilerplate#readme", 23 | "devDependencies": { 24 | "babel-cli": "^6.6.5", 25 | "babel-core": "^6.26.0", 26 | "babel-loader": "^6.2.4", 27 | "babel-preset-es2015": "^6.6.0", 28 | "babel-preset-react": "^6.5.0", 29 | "babel-preset-stage-1": "^6.5.0", 30 | "css-loader": "^0.23.1", 31 | "eslint": "^2.8.0", 32 | "eslint-plugin-react": "^5.0.1", 33 | "nodemon": "^1.9.1", 34 | "path": "^0.12.7", 35 | "react-hot-loader": "^1.3.0", 36 | "sass-loader": "^3.2.0", 37 | "style-loader": "^0.13.1", 38 | "tape": "^4.5.1", 39 | "webpack": "^1.12.14", 40 | "webpack-dev-server": "^1.14.1", 41 | "wrapping-tape": "0.0.3" 42 | }, 43 | "dependencies": { 44 | "axios": "^0.9.1", 45 | "bluebird": "^3.3.4", 46 | "env2": "^2.0.7", 47 | "hapi": "^13.2.2", 48 | "inert": "^3.2.0", 49 | "node-sass": "^3.4.2", 50 | "react": "^0.14.8", 51 | "react-bootstrap": "^0.28.4", 52 | "react-dom": "^0.14.8", 53 | "react-redux": "^4.4.5", 54 | "react-router": "^2.0.1", 55 | "redis": "^2.6.0-0", 56 | "redux": "^3.5.1", 57 | "redux-promise": "^0.5.3" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /public/img/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewMacmurray/hackathon-boilerplate/eb102d0ab1ba8dac25abe0327138395c83a18b23/public/img/react-logo.png -------------------------------------------------------------------------------- /public/img/rhino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewMacmurray/hackathon-boilerplate/eb102d0ab1ba8dac25abe0327138395c83a18b23/public/img/rhino.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hackathon Boilerplate 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | entry: './frontend/src/js/index.js', 5 | output: { 6 | filename: 'app.js', 7 | path: path.resolve(__dirname, 'public') 8 | }, 9 | module: { 10 | loaders: [ 11 | { 12 | test: /\.js?$/, 13 | exclude: path.resolve(__dirname, 'node_modules'), 14 | loader: 'react-hot-loader!babel-loader' 15 | }, 16 | { 17 | test: /\.scss$/, 18 | exclude: path.resolve(__dirname, 'node_modules'), 19 | loader: 'style-loader!css-loader!sass-loader' 20 | } 21 | ] 22 | }, 23 | resolve: { 24 | extensions: [ '', '.js', '.jsx' ] 25 | } 26 | } 27 | --------------------------------------------------------------------------------