├── .gitignore ├── README.md ├── interview--complete ├── .babelrc ├── api │ ├── index.js │ └── properties.js ├── assets │ ├── build │ │ └── app.js │ └── index.html ├── package.json ├── server.js ├── src │ ├── components │ │ ├── Break.js │ │ ├── Button.js │ │ ├── Form.js │ │ ├── Input.js │ │ ├── PropertyLI.js │ │ └── States.js │ ├── containers │ │ ├── AddProperty.js │ │ ├── App.js │ │ └── PropertyList.js │ ├── css.scss │ └── index.js └── webpack.config.babel.js └── interview--incomplete ├── .babelrc ├── api ├── index.js └── properties.js ├── assets ├── build │ └── app.js └── index.html ├── package.json ├── server.js ├── src ├── components │ ├── Break.js │ ├── Button.js │ ├── Form.js │ ├── Input.js │ ├── PropertyLI.js │ └── States.js ├── containers │ ├── AddProperty.js │ ├── App.js │ └── PropertyList.js ├── css.scss └── index.js └── webpack.config.babel.js /.gitignore: -------------------------------------------------------------------------------- 1 | interview--complete/node_modules 2 | interview--incomplete/node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Excercise-Template-Node-React-Mongo 2 | 3 | This is a template for testing a web developer's skill in: 4 | MongoDB with Mongoose, ReactJS, Node with Express 5 | 6 | This is to test competence in: 7 | Asynchronous Functions, Promises, Mongoose, React Event Firing, Lifting State in React, HTTP. 8 | 9 | ## Software Requirements 10 | You must install MongoDB and NodeJS 11 | 12 | ## Running the Completed 13 | To start, first visit the interview--complete folder and: 14 | ``` 15 | npm install 16 | mongod 17 | npm start 18 | ``` 19 | Navigate to `www.localhost.com:8888` and you should see the application. When you add a new property it should add that property directly to the database and live update the property list. The api for the mongo data for "properties" is at `www.localhost.com:8888/api/properties` 20 | 21 | ## Running the Incomplete 22 | To start, first visit the interview--incomplete folder and: 23 | ``` 24 | npm install 25 | mongod 26 | webpack -w 27 | npm start 28 | ``` 29 | 30 | ## Database and Initial Data 31 | For your convenience, I have added initial data in the /api/properties.js. This was added so that the user knows when they have successfully connected to the database. If you accidently keep this up and you have too much data to test. Use `mongoose.connection.dropDatabase()` to drop the data. 32 | 33 | ## Instructions 34 | 35 | There are only 2 files that need to be eddited for completion: 36 | `src/container/App.js` and `api/properties.js` 37 | 38 | The developer may need to navigate to the two other container: 39 | `AddProperty.js` and `PropertyList.js`, but they DO NOT need to be editted. 40 | 41 | The developer has access to bluebird, axios, and of course fetch. 42 | -------------------------------------------------------------------------------- /interview--complete/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react", "stage-2"], 3 | } -------------------------------------------------------------------------------- /interview--complete/api/index.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | 3 | const router = express.Router() 4 | 5 | router.get('/', (req, res) => { 6 | res.send('Welcome to the API page') 7 | }) 8 | 9 | export default router 10 | -------------------------------------------------------------------------------- /interview--complete/api/properties.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import mongoose from 'mongoose' 3 | 4 | const router = express.Router() 5 | 6 | // Declare Schema 7 | let propertySchema = mongoose.Schema({ 8 | address: String, 9 | state: String, 10 | zip: String 11 | }) 12 | 13 | // Declare model 14 | let Property = mongoose.model('Property', propertySchema) 15 | 16 | // Set inital Data 17 | const initialData = [ 18 | { 19 | 'address': '2360 Westridge Center', 20 | 'state': 'California', 21 | 'zip': 89183 22 | }, { 23 | 'address': '05991 Grover Trail', 24 | 'state': 'California', 25 | 'zip': 89052 26 | }, { 27 | 'address': '8287 Caliangt Plaza', 28 | 'state': 'Arizona', 29 | 'zip': 89052 30 | }, { 31 | 'address': '35 Thompson Court', 32 | 'state': 'Texas', 33 | 'zip': 89183 34 | }, { 35 | 'address': '297 Marquette Circle', 36 | 'state': 'California', 37 | 'zip': 89052 38 | } 39 | ] 40 | 41 | Property.create(initialData, (err) => { 42 | if (err) { 43 | console.log(err) 44 | } 45 | console.log('Load of initial data complete.') 46 | }) 47 | 48 | // Get Request for all Properties 49 | router.get('/', (req, res) => { 50 | Property.find({}, (err, users) => { 51 | if (err) console.log(err) 52 | res.send(users) 53 | }) 54 | }) 55 | 56 | // Post Request for adding a Property 57 | router.post('/', (req, res) => { 58 | let newProperty = new Property(req.body) 59 | let promise = Property.create(newProperty) 60 | promise.then(() => { 61 | Property.find({}, (err, users) => { 62 | if (err) console.log(err) 63 | res.send(users) 64 | }) 65 | }) 66 | }) 67 | 68 | export default router 69 | -------------------------------------------------------------------------------- /interview--complete/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | EXERCISE 15 | 27 | 28 | 29 | 30 |
31 | 32 | 33 | -------------------------------------------------------------------------------- /interview--complete/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "interview", 3 | "version": "1.0.0", 4 | "description": "Interview Template to test MongoDB, NodeJS/Express, and ReactJS", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon --exec babel-node server.js --ignore assets/" 8 | }, 9 | "dependencies": { 10 | "axios": "^0.15.3", 11 | "bluebird": "^3.5.0", 12 | "body-parser": "^1.17.1", 13 | "express": "^4.15.2", 14 | "mongoose": "^4.9.3", 15 | "react": "^15.4.1", 16 | "react-dom": "^15.4.1" 17 | }, 18 | "devDependencies": { 19 | "babel-cli": "^6.23.0", 20 | "babel-core": "^6.23.1", 21 | "babel-loader": "^6.3.0", 22 | "babel-polyfill": "^6.23.0", 23 | "babel-preset-es2015": "^6.18.0", 24 | "babel-preset-react": "^6.16.0", 25 | "babel-preset-stage-2": "^6.18.0", 26 | "css-loader": "^0.28.0", 27 | "node-sass": "^4.5.2", 28 | "nodemon": "^1.11.0", 29 | "sass-loader": "^6.0.3", 30 | "style-loader": "^0.16.1", 31 | "webpack": "^2.2.1", 32 | "webpack-dev-server": "^2.4.1" 33 | }, 34 | "keywords": ["interview", "react", "mongodb", "node", "express"], 35 | "author": "Federico Dottin", 36 | "license": "ISC" 37 | } 38 | -------------------------------------------------------------------------------- /interview--complete/server.js: -------------------------------------------------------------------------------- 1 | import mongoose from 'mongoose' 2 | import bodyParser from 'body-parser' 3 | 4 | import apiRouter from './api' 5 | import properties from './api/properties' 6 | 7 | import express from 'express' 8 | const server = express() 9 | 10 | server.use(express.static(__dirname + '/assets')) 11 | server.use(bodyParser.json()) 12 | server.use(bodyParser.urlencoded({extended: true})) 13 | server.use('/api', apiRouter) 14 | server.use('/api/properties', properties) 15 | 16 | // start database 17 | mongoose.connect('mongodb://localhost:27017/interview') 18 | 19 | var db = mongoose.connection 20 | db.on('error', console.error.bind(console, 'connection error:')) 21 | db.once('open', function () { 22 | console.log('MongoDB is connected...') 23 | }) 24 | 25 | // catch 404 and forward to error handler 26 | server.use(function (req, res, next) { 27 | var err = new Error('Not Found') 28 | err.status = 404 29 | next(err) 30 | }) 31 | 32 | // error handler 33 | server.use(function (err, req, res, next) { 34 | // set locals, only providing error in development 35 | res.locals.message = err.message 36 | 37 | // render the error page 38 | res.status(err.status || 500) 39 | res.render('error') 40 | }) 41 | 42 | server.listen(process.env.PORT || 8888, () => { 43 | console.log('Express started. Server is online on port 8888.') 44 | }) 45 | 46 | export default server 47 | -------------------------------------------------------------------------------- /interview--complete/src/components/Break.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import '../css.scss' 3 | 4 | export default () => { 5 | return ( 6 |
7 | ) 8 | } 9 | -------------------------------------------------------------------------------- /interview--complete/src/components/Button.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import '../css.scss' 3 | 4 | export default (props) => { 5 | let style = { 6 | wrapper: {} 7 | } 8 | style.wrapper.borderRadius = props.square ? 0 : 4 9 | return ( 10 |
11 | { props.iconClass ? : null } 12 | { props.children } 13 |
14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /interview--complete/src/components/Form.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import '../css.scss' 3 | 4 | export default (props) => { 5 | return ( 6 |
7 | { props.heading ?
{ props.heading }
: null } 8 | { props.children } 9 |
10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /interview--complete/src/components/Input.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import '../css.scss' 3 | 4 | export default (props) => { 5 | function handleChange (e) { 6 | let placeholder = e.target.parentNode.getElementsByClassName('input__placeholder')[0] 7 | if (e.target.value !== '') { 8 | if (!(placeholder.classList.contains('input__placeholder--datain'))) { placeholder.classList.add('input__placeholder--datain') } 9 | } else { 10 | placeholder.classList.remove('input__placeholder--datain') 11 | } 12 | props.onChange(e.target.name, e.target.value) 13 | } 14 | 15 | const style = { 16 | wrapper: { 17 | width: (props.width * 100) + '%' 18 | } 19 | } 20 | let Input 21 | switch (props.type) { 22 | case 'select': 23 | Input = 27 | break 28 | case 'multiple': 29 | Input = 32 | break 33 | case 'area': 34 | Input =