├── .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 |
120 |
121 |
122 |
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 |
22 |
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 |
--------------------------------------------------------------------------------