Skip to content

Commit d0e8af7

Browse files
committed
Merge branch 'checklist' into dev
# Conflicts: # package.json # src/components/Header/Header.jsx # src/containers/HeaderContainer.js # src/services/APIService.js # src/services/AuthService.js # src/store/modules/global.js
2 parents 31f97b4 + 5295af2 commit d0e8af7

39 files changed

+1099
-106
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* node v6 (https://nodejs.org)
55

66
## Quick Start
7+
* copy `.env.example` to `.env`
78
* `npm install`
89
* `npm run dev`
910
* Navigate browser to `http://localhost:3000`

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"react-simple-dropdown": "^1.1.5",
9292
"react-slick": "^0.14.5",
9393
"react-star-rating-component": "^1.2.2",
94+
"react-table": "^3.1.4",
9495
"react-tabs": "^0.8.2",
9596
"react-timeago": "^3.1.3",
9697
"reactable": "^0.14.1",
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 './BreadcrumbItem.scss';
4+
5+
export const BreadcrumbItem = ({title}) => (
6+
<span styleName="breadcrumb-item">
7+
{title}
8+
</span>
9+
);
10+
11+
BreadcrumbItem.propTypes = {
12+
title: PropTypes.string.isRequired,
13+
};
14+
15+
export default CSSModules(BreadcrumbItem, styles);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.breadcrumb-item {
2+
background-color: transparent;
3+
4+
:global {
5+
6+
}
7+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import BreadcrumbItem from './BreadcrumbItem';
2+
3+
export default BreadcrumbItem;

src/components/Button/Button.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Button.propTypes = {
1919
Button.defaultProps = {
2020
type: 'button',
2121
size: 'normal',
22+
color: 'blue',
2223
};
2324

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

src/components/Header/Header.jsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ export function Header({
7171
</ul>
7272
</li>
7373
);
74+
} else if (user.role === 'pilot') {
75+
res = (
76+
<li styleName="pages">
77+
<ul>
78+
<li><Link to="/pilot-missions" activeClassName="active">Pilot Missions</Link></li>
79+
</ul>
80+
</li>
81+
);
7482
}
7583
return res;
7684
})()

src/components/Radiobox/Radiobox.jsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React, {PropTypes} from 'react';
2+
import CSSModules from 'react-css-modules';
3+
import styles from './Radiobox.scss';
4+
5+
const Radiobox = ({children, className, radioValue, name, value, onChange, disabled}) => (
6+
<div styleName="radiobox" className={className}>
7+
<input
8+
type="radio"
9+
id={`${name}.${radioValue}`}
10+
name={name}
11+
value={radioValue}
12+
checked={value === radioValue}
13+
onChange={onChange}
14+
disabled={disabled}
15+
/>
16+
<label htmlFor={`${name}.${radioValue}`}>
17+
<span /> {children}
18+
</label>
19+
</div>
20+
);
21+
22+
Radiobox.propTypes = {
23+
children: PropTypes.string,
24+
className: PropTypes.string,
25+
radioValue: PropTypes.string.isRequired,
26+
name: PropTypes.string.isRequired,
27+
value: PropTypes.string,
28+
onChange: PropTypes.func,
29+
disabled: PropTypes.bool,
30+
};
31+
32+
Radiobox.defaultProps = {
33+
disabled: false,
34+
};
35+
36+
export default CSSModules(Radiobox, styles);

src/components/Radiobox/Radiobox.scss

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.radiobox {
2+
height: 40px;
3+
display: flex;
4+
align-items: center;
5+
6+
input[type="radio"] {
7+
display: none;
8+
}
9+
10+
input[type="radio"] + label span {
11+
flex-shrink: 0;
12+
display: inline-block;
13+
width: 23px;
14+
height: 23px;
15+
border: 1px solid #a1a1a1;
16+
box-shadow: none;
17+
appearance: none;
18+
margin: 0 9px 0 0;
19+
background-color: transparent;
20+
vertical-align: middle;
21+
cursor: pointer;
22+
}
23+
24+
input[type="radio"]:checked + label span {
25+
background: url('icon-checkbox.png') no-repeat 50% 50%;
26+
}
27+
28+
label {
29+
font-weight: normal;
30+
cursor: pointer;
31+
line-height: 1;
32+
display: flex;
33+
align-items: center;
34+
margin-bottom: 0;
35+
}
36+
37+
input[type="radio"][disabled] + label {
38+
cursor: default;
39+
}
40+
41+
input[type="radio"][disabled] + label span {
42+
cursor: default;
43+
background-color: #efefef;
44+
border-color: #ebebeb;
45+
}
46+
}

src/components/Radiobox/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Radiobox from './Radiobox';
2+
3+
export default Radiobox;
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
import React, {PropTypes} from 'react';
22
import CSSModules from 'react-css-modules';
33
import styles from './StatusLabel.scss';
4+
import _ from 'lodash';
45

56
const statusLabels = {
6-
inProgress: 'In Progress',
7+
'in-progress': 'In Progress', // new style
8+
inProgress: 'In Progress', // old style should be removed when all code is binded to backend
79
cancelled: 'Cancelled',
810
completed: 'Completed',
11+
waiting: 'Waiting',
12+
scheduled: 'Scheduled',
913
};
1014

1115
export const StatusLabel = ({value}) => (
12-
<span styleName={`status-label_${value.toLowerCase()}`}>
16+
<span styleName={`status-label_${value.toLowerCase().replace('-', '')}`}>
1317
<span>{statusLabels[value]}</span>
1418
</span>
1519
);
1620

1721
StatusLabel.propTypes = {
18-
value: PropTypes.oneOf(['inProgress', 'cancelled', 'completed']).isRequired,
22+
value: PropTypes.oneOf(_.keys(statusLabels)).isRequired,
1923
};
2024

2125
export default CSSModules(StatusLabel, styles);

src/components/StatusLabel/StatusLabel.scss

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,17 @@
3333

3434
@extend .status-label;
3535
}
36+
37+
.status-label_waiting {
38+
background-color: #e3e3e3;
39+
background-image: url('icon-status-inprogress.png');
40+
41+
@extend .status-label;
42+
}
43+
44+
.status-label_scheduled {
45+
background-color: #4c4c4c;
46+
background-image: url('icon-status-inprogress.png');
47+
48+
@extend .status-label;
49+
}

src/components/Table/Table.jsx

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import React, {PropTypes} from 'react';
2+
import CSSModules from 'react-css-modules';
3+
import ReactTable from 'react-table';
4+
import styles from './Table.scss';
5+
import SelectPerPage from 'components/SelectPerPage';
6+
import Pagination from 'components/Pagination';
7+
import _ from 'lodash';
8+
9+
/**
10+
* Populate column objects with id in class name
11+
* this way we can pass id to the on click handler inside ThComponent
12+
* @param {Array} columns original columns
13+
* @return {Array} columns with id
14+
*/
15+
const prepareColumns = (columns) => (
16+
_.map(columns, (column) => (
17+
{...column, headerClassName: `-column-id-${column.accessor}`}
18+
))
19+
);
20+
21+
/**
22+
* Convert sorting parameter from backend format to ReactTable format
23+
* @param {String} sortBy in backend format
24+
* @return {String} in ReactTable format
25+
*/
26+
const prepareSorting = (sortBy) => {
27+
const sorting = [];
28+
29+
sortBy && sorting.push({
30+
id: sortBy.replace(/^-/, ''),
31+
asc: sortBy[0] !== '-',
32+
});
33+
34+
return sorting;
35+
};
36+
37+
/*
38+
Table header cell component
39+
use custom component to implement server-side sorting
40+
*/
41+
const ThComponent = (props) => {
42+
const {className, onChange} = props;
43+
44+
return (
45+
<th
46+
{..._.omit(props, 'toggleSort')}
47+
onClick={() => {
48+
const matchSortable = className.match(/(?:^| )-cursor-pointer(?: |$)/);
49+
if (matchSortable) {
50+
const matchColumnId = className.match(/(?:^| )-column-id-([^\s]+)(?: |$)/);
51+
const matchSortingDir = className.match(/(?:^| )-sort-([^\s]+)(?: |$)/);
52+
if (matchColumnId) {
53+
let sortDir;
54+
// if sorting direction is set and it's 'desc' we change it to 'asc'
55+
if (matchSortingDir && matchSortingDir[1] === 'desc') {
56+
sortDir = '';
57+
// if sorting direction is not set, then we set to 'asc' by default
58+
} else if (!matchSortingDir) {
59+
sortDir = '';
60+
// in this case sort direction was set to 'asc', so we change it to 'desc'
61+
} else {
62+
sortDir = '-';
63+
}
64+
onChange({sortBy: sortDir + matchColumnId[1]});
65+
}
66+
}
67+
}}
68+
>
69+
{props.children}
70+
</th>
71+
);
72+
};
73+
74+
ThComponent.propTypes = {
75+
className: PropTypes.string.isRequired,
76+
onChange: PropTypes.func.isRequired,
77+
children: PropTypes.any,
78+
};
79+
80+
export const Table = ({columns, offset, limit, total, sortBy, onChange, ...props}) => (
81+
<div styleName="smart-table">
82+
<div styleName="table-wrap">
83+
<ReactTable
84+
tableClassName={styles.table}
85+
theadClassName={styles.thead}
86+
tbodyClassName={styles.tbody}
87+
trClassName={styles.tr}
88+
showPageJump={false}
89+
showPageSizeOptions={false}
90+
showPagination={false}
91+
loading={false}
92+
pages={Math.ceil(total / limit)}
93+
pageSize={limit}
94+
minRows={0}
95+
manual
96+
column={{
97+
sortable: false,
98+
}}
99+
sorting={prepareSorting(sortBy)}
100+
thComponent={(prop) => <ThComponent {...{...prop, onChange}} />}
101+
columns={prepareColumns(columns)}
102+
{...props}
103+
/>
104+
</div>
105+
106+
<div styleName="navigation">
107+
<div styleName="perpage">
108+
<SelectPerPage
109+
value={limit}
110+
onChange={({value}) => {
111+
// adjust page number (offset) when change per page quantity (limit)
112+
const newOffset = Math.floor(offset / value);
113+
onChange({limit: value, offset: newOffset});
114+
}}
115+
/>
116+
</div>
117+
<div styleName="pagination">
118+
<Pagination
119+
forcePage={Math.ceil(offset / limit)}
120+
pageCount={Math.ceil(total / limit)}
121+
onPageChange={({selected}) => {
122+
onChange({offset: Math.ceil(selected * limit)});
123+
}}
124+
/>
125+
</div>
126+
</div>
127+
128+
</div>
129+
);
130+
131+
Table.propTypes = {
132+
columns: PropTypes.array.isRequired,
133+
offset: PropTypes.number.isRequired,
134+
limit: PropTypes.number.isRequired,
135+
total: PropTypes.number.isRequired,
136+
sortBy: PropTypes.string,
137+
onChange: PropTypes.func.isRequired,
138+
};
139+
140+
export default CSSModules(Table, styles);

0 commit comments

Comments
 (0)