Skip to content

Commit 5ae6e01

Browse files
authored
Merge pull request #2 from topcoderinc/missionPlanner
Mission planner
2 parents 94182f9 + 52c501b commit 5ae6e01

File tree

64 files changed

+2783
-35
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2783
-35
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ node_modules
55
.idea
66
dist
77
coverage
8+
.tmp

README.md

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## DSP app
1+
# dsp-fronted
22

33
## Requirements
44
* node v6 (https://nodejs.org)
@@ -11,7 +11,7 @@
1111

1212

1313
## Configuration
14-
Configuration files are located under `config` dir.
14+
Configuration files are located under `config` dir.
1515
See Guild https://github.com/lorenwest/node-config/wiki/Configuration-Files
1616

1717
|Name|Description|
@@ -32,7 +32,27 @@ See Guild https://github.com/lorenwest/node-config/wiki/Configuration-Files
3232
|`dev`|Start app in the dev mode.|
3333
|`lint`|Lint all `.js` files.|
3434
|`lint:fix`|Lint and fix all `.js` files. [Read more on this](http://eslint.org/docs/user-guide/command-line-interface.html#fix).|
35-
36-
37-
## Video
38-
http://take.ms/WZkTO
35+
|`test`|Run tests using [mocha-webpack](https://github.com/webpack/mocha-loader) for all `*.spec.(js|jsx)` files in the `src` dir.|
36+
37+
## Google Map
38+
In this project module [react-google-maps](https://github.com/tomchentw/react-google-maps) is used to work with google maps. So it can be used for any new functionality.
39+
40+
# Challenges
41+
42+
## [30055900](https://www.topcoder.com/challenge-details/30055900)
43+
## DONE
44+
- All modules were rewritten almost from the scratch because the previous code was very buggy, hard to support and too far from the redux way which is used in the new project. This was the biggest job. Current code is much more robust and is 99% stateless.
45+
- For most important parts detailed unit tests are written.
46+
- Redrawing mission on the map was optimised, no unnecessary redrawing.
47+
- Readme file was cleaned and updated with information about tests and module used to implement google maps for future developers.
48+
49+
## ADDITIONALLY
50+
- These small things from `kbowerma` was added:
51+
- - I know this was not in the challenge req but another thing that would be nice is if the label for PARAM4 changed to “Heading” only if NAV_WAYPOINT is selected. and PARAMA1 label changed to “hold time” only if NAV_WAYPOINT is selected.
52+
- - IT should be, but home and take off should be pinned together with the first click, but then should be able to be dragged or updated with text separately
53+
- All modules integrated with current project styles.
54+
- Test environment was set up. It uses `Mocha`, `Chai` and `Enzyme`. Also, it supports `jsx`, `css-modules` and `webpack resolve aliases`. Even though it's implicitly the scope of the challenge, it was a tangible part.
55+
56+
## NOTES
57+
- As there is no Authorization implemented in the project. In the API I've hardcoded automatic registering and authorization of a dumb user to make requests to the server.
58+
- A lot of files in the repository had the `crlf` line endings. Though `eslint` and `.editorconfig` prescribe using `lf` line endings. So all files were converted to `lf` line endings to pass the linting process and follow configuration.

blueprints/component/files/src/components/__name__/__name__.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import styles from './<%= pascalEntityName %>.scss';
44

55
export const <%= pascalEntityName %> = () => (
66
<div styleName="<%= dashesEntityName %>">
7+
<%= pascalEntityName %>
78
</div>
89
);
910

1011
<%= pascalEntityName %>.propTypes = {
11-
foo: PropTypes.string.isRequired,
12+
// foo: PropTypes.string.isRequired,
1213
};
1314

1415
export default CSSModules(<%= pascalEntityName %>, styles);

blueprints/component/files/src/components/__name__/__name__.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
.<%= dashesEntityName %> {
2+
background-color: transparent;
3+
24
:global {
35

46
}

blueprints/route/files/src/routes/__name__/components/__name__View.js

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import React, {PropTypes} from 'react';
2+
import CSSModules from 'react-css-modules';
3+
import styles from './<%= pascalEntityName %>View.scss';
4+
5+
export const <%= pascalEntityName %>View = () => (
6+
<div styleName="<%= dashesEntityName %>-view">
7+
<%= pascalEntityName %>View
8+
</div>
9+
);
10+
11+
<%= pascalEntityName %>View.propTypes = {
12+
// foo: PropTypes.string.isRequired,
13+
};
14+
15+
export default CSSModules(<%= pascalEntityName %>View, styles);

blueprints/route/files/src/routes/__name__/components/__name__View.scss

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
.<%= camelEntityName %>View {
1+
.<%= dashesEntityName %>-view {
2+
background-color: transparent;
3+
24
:global {
35

46
}

blueprints/route/files/src/routes/__name__/containers/__name__Container.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { asyncConnect } from 'redux-connect';
2-
import {actions} from '../modules/<%= pascalEntityName %>';
2+
import { actions } from '../modules/<%= pascalEntityName %>';
33

44
import <%= pascalEntityName %>View from '../components/<%= pascalEntityName %>View';
55

blueprints/route/files/src/routes/__name__/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export default (store) => ({
66
require.ensure([], (require) => {
77
const <%= pascalEntityName %> = require('./containers/<%= pascalEntityName %>Container').default;
88
const reducer = require('./modules/<%= pascalEntityName %>').default;
9+
910
injectReducer(store, { key: '<%= camelEntityName %>', reducer });
1011
cb(null, <%= pascalEntityName %>);
1112
}, '<%= pascalEntityName %>');

blueprints/route/files/src/routes/__name__/modules/__name__.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const SAMPLE = '<%= pascalEntityName %>/SAMPLE';
1111

1212

1313
export const sample2 = () => async (dispatch, getState) => {
14-
14+
getState(); // to pass eslint from the begining
1515
};
1616

1717
export const actions = {
@@ -23,5 +23,8 @@ export const actions = {
2323
// Reducer
2424
// ------------------------------------
2525
export default handleActions({
26-
[SAMPLE]: (state, {payload}) => state,
26+
[SAMPLE]: (state, {payload}) => {
27+
payload; // to pass eslint from the begining
28+
return state;
29+
},
2730
}, {});

config/default.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55
module.exports = {
66
PORT: process.env.PORT || 3000,
77
GOOGLE_API_KEY: process.env.GOOGLE_API_KEY || 'AIzaSyCrL-O319wNJK8kk8J_JAYsWgu6yo5YsDI',
8+
//API_BASE_PATH: process.env.API_BASE_PATH || 'http://localhost:3000',
9+
API_BASE_PATH: process.env.API_BASE_PATH || 'https://kb-dsp-server-dev.herokuapp.com',
810
};

package.json

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"start": "cross-env NODE_ENV=production node server",
99
"build": "cross-env NODE_ENV=production webpack --bail --progress --build --tc",
1010
"lint": "eslint --ext jsx --ext js .",
11-
"lint:fix": "npm run lint -- --fix"
11+
"lint:fix": "npm run lint -- --fix",
12+
"test": "mocha-webpack --require setup-test.js --webpack-config webpack.config-test.js \"src/**/*.spec.(jsx|js)\""
1213
},
1314
"author": "",
1415
"license": "MIT",
@@ -34,6 +35,7 @@
3435
"express": "^4.14.0",
3536
"extract-text-webpack-plugin": "^1.0.1",
3637
"file-loader": "^0.9.0",
38+
"flexboxgrid": "^6.3.1",
3739
"history": "^2.0.0",
3840
"html-webpack-plugin": "^2.22.0",
3941
"imports-loader": "^0.6.5",
@@ -50,8 +52,11 @@
5052
"react-css-modules": "^3.7.10",
5153
"react-date-picker": "^5.3.28",
5254
"react-dom": "^15.3.2",
55+
"react-flexbox-grid": "^0.10.2",
56+
"react-google-maps": "^6.0.1",
5357
"react-modal": "^1.5.2",
5458
"react-redux": "^4.0.0",
59+
"react-redux-toastr": "^4.2.2",
5560
"react-router": "^2.8.1",
5661
"react-router-redux": "^4.0.0",
5762
"react-select": "^1.0.0-rc.2",
@@ -73,15 +78,23 @@
7378
"yargs": "^4.0.0"
7479
},
7580
"devDependencies": {
81+
"chai": "^3.5.0",
82+
"css-modules-require-hook": "^4.0.5",
83+
"enzyme": "^2.6.0",
7684
"eslint": "^3.7.1",
7785
"eslint-config-airbnb": "^12.0.0",
7886
"eslint-plugin-babel": "^3.3.0",
7987
"eslint-plugin-import": "^1.16.0",
8088
"eslint-plugin-jsx-a11y": "^2.2.2",
8189
"eslint-plugin-react": "^6.3.0",
90+
"jsdom": "^9.8.3",
91+
"mocha": "^3.2.0",
92+
"mocha-webpack": "^0.7.0",
8293
"nodemon": "^1.8.1",
94+
"react-addons-test-utils": "^15.4.1",
8395
"webpack-dev-middleware": "^1.8.3",
84-
"webpack-hot-middleware": "^2.13.0"
96+
"webpack-hot-middleware": "^2.13.0",
97+
"webpack-node-externals": "^1.5.4"
8598
},
8699
"engines": {
87100
"node": "6.7.0"

setup-test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
const hook = require('css-modules-require-hook');
2+
const sass = require('node-sass');
3+
4+
/*
5+
take care of css modules
6+
*/
7+
hook({
8+
extensions: ['.scss', '.css'],
9+
generateScopedName: '[local]___[hash:base64:5]',
10+
preprocessCss: (data, file) => sass.renderSync({ file }).css,
11+
});
12+
13+
/*
14+
init jsdom to simulate browser
15+
*/
16+
const jsdom = require('jsdom').jsdom;
17+
18+
const exposedProperties = ['window', 'navigator', 'document'];
19+
20+
global.document = jsdom('');
21+
global.window = document.defaultView;
22+
Object.keys(document.defaultView).forEach((property) => {
23+
if (typeof global[property] === 'undefined') {
24+
exposedProperties.push(property);
25+
global[property] = document.defaultView[property];
26+
}
27+
});
28+
29+
global.navigator = {
30+
userAgent: 'node.js',
31+
};

src/components/Button/Button.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,21 @@ import _ from 'lodash';
44
import cn from 'classnames';
55
import styles from './Button.scss';
66

7-
export const Button = ({children, color, ...rest}) => (
8-
<button {..._.omit(rest, 'styles')} styleName={cn('button', `color-${color}`)}>
7+
export const Button = ({children, color, size, ...rest}) => (
8+
<button {..._.omit(rest, 'styles')} styleName={cn('button', `color-${color}`, `size-${size}`)}>
99
{children}
1010
</button>
1111
);
1212

1313
Button.propTypes = {
1414
children: PropTypes.string.isRequired,
1515
color: PropTypes.string.isRequired,
16+
size: PropTypes.string,
1617
};
1718

1819
Button.defaultProps = {
1920
type: 'button',
21+
size: 'normal',
2022
};
2123

2224
export default CSSModules(Button, styles, {allowMultiple: true});

src/components/Button/Button.scss

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
.button {
2-
padding: 13px 10px;
32
min-width: 115px;
43
color: white;
54
border: none;
@@ -12,4 +11,13 @@
1211

1312
.color-blue {
1413
background: #315b95;
15-
}
14+
}
15+
16+
.size-normal {
17+
padding: 13px 10px;
18+
}
19+
20+
.size-medium {
21+
height: 38px;
22+
padding: 0 10px;
23+
}

src/components/TextField/TextField.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import styles from './TextField.scss';
55

66
export const TextField = (props) => (
77
<div styleName="text-field">
8-
<input {..._.pick(props, 'type', 'value', 'onChange')} />
8+
<input {..._.pick(props, 'type', 'value', 'onChange', 'placeholder')} />
99
</div>
1010
);
1111

src/containers/AppContainer.jsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,21 @@ import React, { PropTypes } from 'react';
22
import { ReduxAsyncConnect } from 'redux-connect';
33
import { Router } from 'react-router';
44
import { Provider } from 'react-redux';
5+
import ReduxToastr from 'react-redux-toastr';
56

67
const AppContainer = ({ history, routes, routerKey, store }) => (
78
<Provider store={store}>
8-
<Router history={history} render={(props) => <ReduxAsyncConnect {...props} />} key={routerKey}>{routes}</Router>
9+
<div>
10+
<Router history={history} render={(props) => <ReduxAsyncConnect {...props} />} key={routerKey}>{routes}</Router>
11+
<ReduxToastr
12+
timeOut={3000}
13+
newestOnTop={false}
14+
preventDuplicates
15+
position="top-right"
16+
transitionIn="fadeIn"
17+
transitionOut="fadeOut"
18+
/>
19+
</div>
920
</Provider>
1021
);
1122

src/routes/Dashboard/modules/Dashboard.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import { handleActions } from 'redux-actions';
66

77

88
export const sendRequest = (values) => new Promise((resolve) => {
9+
/* eslint-disable no-alert */
910
alert(JSON.stringify(values, null, 2));
11+
/* eslint-enable no-alert */
1012
resolve();
1113
});
1214

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React, { PropTypes } from 'react';
2+
import CSSModules from 'react-css-modules';
3+
import { Link } from 'react-router';
4+
import styles from './MissionListView.scss';
5+
6+
export const MissionListView = ({ missions, deleteMission }) => (
7+
<div styleName="mission-list-view">
8+
<div styleName="wrap">
9+
<div styleName="header">
10+
<h1 styleName="title">Mission List</h1>
11+
<Link to="/mission-planner" styleName="create-btn">Create New Mission</Link>
12+
</div>
13+
<div styleName="panel">
14+
{missions.length ? (
15+
<table styleName="my-request-table">
16+
<thead styleName="thead">
17+
<tr>
18+
<th styleName="th">Mission Name</th>
19+
<th styleName="th" />
20+
<th styleName="th" />
21+
<th styleName="th" />
22+
</tr>
23+
</thead>
24+
<tbody>
25+
{missions.map((mission) => (
26+
<tr styleName="tr" key={mission.id}>
27+
<td styleName="td">{mission.missionName}</td>
28+
<td styleName="td"><Link to={`/mission-planner/${mission.id}`}>Edit</Link></td>
29+
<td styleName="td"><a href={mission.downloadLink} target="_blank" rel="noopener noreferrer">Download</a></td>
30+
<td styleName="td"><a href="#" onClick={(event) => { event.preventDefault(); deleteMission(mission.id); }}>Delete</a></td>
31+
</tr>
32+
))}
33+
</tbody>
34+
</table>
35+
) : (
36+
<span>No missions found.</span>
37+
)}
38+
</div>
39+
</div>
40+
</div>
41+
);
42+
43+
MissionListView.propTypes = {
44+
missions: PropTypes.array.isRequired,
45+
deleteMission: PropTypes.func.isRequired,
46+
};
47+
48+
export default CSSModules(MissionListView, styles);

0 commit comments

Comments
 (0)