Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* node v6 (https://nodejs.org)

## Quick Start
* copy `.env.example` to `.env`
* `npm install`
* `npm run dev`
* Navigate browser to `http://localhost:3000`
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"react-simple-dropdown": "^1.1.5",
"react-slick": "^0.14.5",
"react-star-rating-component": "^1.2.2",
"react-table": "^3.1.4",
"react-tabs": "^0.8.2",
"react-timeago": "^3.1.3",
"reactable": "^0.14.1",
Expand Down
15 changes: 15 additions & 0 deletions src/components/BreadcrumbItem/BreadcrumbItem.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, {PropTypes} from 'react';
import CSSModules from 'react-css-modules';
import styles from './BreadcrumbItem.scss';

export const BreadcrumbItem = ({title}) => (
<span styleName="breadcrumb-item">
{title}
</span>
);

BreadcrumbItem.propTypes = {
title: PropTypes.string.isRequired,
};

export default CSSModules(BreadcrumbItem, styles);
7 changes: 7 additions & 0 deletions src/components/BreadcrumbItem/BreadcrumbItem.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.breadcrumb-item {
background-color: transparent;

:global {

}
}
3 changes: 3 additions & 0 deletions src/components/BreadcrumbItem/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import BreadcrumbItem from './BreadcrumbItem';

export default BreadcrumbItem;
1 change: 1 addition & 0 deletions src/components/Button/Button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Button.propTypes = {
Button.defaultProps = {
type: 'button',
size: 'normal',
color: 'blue',
};

export default CSSModules(Button, styles, {allowMultiple: true});
8 changes: 8 additions & 0 deletions src/components/Header/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ export function Header({
</ul>
</li>
);
} else if (user.role === 'pilot') {
res = (
<li styleName="pages">
<ul>
<li><Link to="/pilot-missions" activeClassName="active">Pilot Missions</Link></li>
</ul>
</li>
);
}
return res;
})()
Expand Down
36 changes: 36 additions & 0 deletions src/components/Radiobox/Radiobox.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, {PropTypes} from 'react';
import CSSModules from 'react-css-modules';
import styles from './Radiobox.scss';

const Radiobox = ({children, className, radioValue, name, value, onChange, disabled}) => (
<div styleName="radiobox" className={className}>
<input
type="radio"
id={`${name}.${radioValue}`}
name={name}
value={radioValue}
checked={value === radioValue}
onChange={onChange}
disabled={disabled}
/>
<label htmlFor={`${name}.${radioValue}`}>
<span /> {children}
</label>
</div>
);

Radiobox.propTypes = {
children: PropTypes.string,
className: PropTypes.string,
radioValue: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
value: PropTypes.string,
onChange: PropTypes.func,
disabled: PropTypes.bool,
};

Radiobox.defaultProps = {
disabled: false,
};

export default CSSModules(Radiobox, styles);
46 changes: 46 additions & 0 deletions src/components/Radiobox/Radiobox.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.radiobox {
height: 40px;
display: flex;
align-items: center;

input[type="radio"] {
display: none;
}

input[type="radio"] + label span {
flex-shrink: 0;
display: inline-block;
width: 23px;
height: 23px;
border: 1px solid #a1a1a1;
box-shadow: none;
appearance: none;
margin: 0 9px 0 0;
background-color: transparent;
vertical-align: middle;
cursor: pointer;
}

input[type="radio"]:checked + label span {
background: url('icon-checkbox.png') no-repeat 50% 50%;
}

label {
font-weight: normal;
cursor: pointer;
line-height: 1;
display: flex;
align-items: center;
margin-bottom: 0;
}

input[type="radio"][disabled] + label {
cursor: default;
}

input[type="radio"][disabled] + label span {
cursor: default;
background-color: #efefef;
border-color: #ebebeb;
}
}
3 changes: 3 additions & 0 deletions src/components/Radiobox/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Radiobox from './Radiobox';

export default Radiobox;
10 changes: 7 additions & 3 deletions src/components/StatusLabel/StatusLabel.jsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import React, {PropTypes} from 'react';
import CSSModules from 'react-css-modules';
import styles from './StatusLabel.scss';
import _ from 'lodash';

const statusLabels = {
inProgress: 'In Progress',
'in-progress': 'In Progress', // new style
inProgress: 'In Progress', // old style should be removed when all code is binded to backend
cancelled: 'Cancelled',
completed: 'Completed',
waiting: 'Waiting',
scheduled: 'Scheduled',
};

export const StatusLabel = ({value}) => (
<span styleName={`status-label_${value.toLowerCase()}`}>
<span styleName={`status-label_${value.toLowerCase().replace('-', '')}`}>
<span>{statusLabels[value]}</span>
</span>
);

StatusLabel.propTypes = {
value: PropTypes.oneOf(['inProgress', 'cancelled', 'completed']).isRequired,
value: PropTypes.oneOf(_.keys(statusLabels)).isRequired,
};

export default CSSModules(StatusLabel, styles);
14 changes: 14 additions & 0 deletions src/components/StatusLabel/StatusLabel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,17 @@

@extend .status-label;
}

.status-label_waiting {
background-color: #e3e3e3;
background-image: url('icon-status-inprogress.png');

@extend .status-label;
}

.status-label_scheduled {
background-color: #4c4c4c;
background-image: url('icon-status-inprogress.png');

@extend .status-label;
}
140 changes: 140 additions & 0 deletions src/components/Table/Table.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React, {PropTypes} from 'react';
import CSSModules from 'react-css-modules';
import ReactTable from 'react-table';
import styles from './Table.scss';
import SelectPerPage from 'components/SelectPerPage';
import Pagination from 'components/Pagination';
import _ from 'lodash';

/**
* Populate column objects with id in class name
* this way we can pass id to the on click handler inside ThComponent
* @param {Array} columns original columns
* @return {Array} columns with id
*/
const prepareColumns = (columns) => (
_.map(columns, (column) => (
{...column, headerClassName: `-column-id-${column.accessor}`}
))
);

/**
* Convert sorting parameter from backend format to ReactTable format
* @param {String} sortBy in backend format
* @return {String} in ReactTable format
*/
const prepareSorting = (sortBy) => {
const sorting = [];

sortBy && sorting.push({
id: sortBy.replace(/^-/, ''),
asc: sortBy[0] !== '-',
});

return sorting;
};

/*
Table header cell component
use custom component to implement server-side sorting
*/
const ThComponent = (props) => {
const {className, onChange} = props;

return (
<th
{..._.omit(props, 'toggleSort')}
onClick={() => {
const matchSortable = className.match(/(?:^| )-cursor-pointer(?: |$)/);
if (matchSortable) {
const matchColumnId = className.match(/(?:^| )-column-id-([^\s]+)(?: |$)/);
const matchSortingDir = className.match(/(?:^| )-sort-([^\s]+)(?: |$)/);
if (matchColumnId) {
let sortDir;
// if sorting direction is set and it's 'desc' we change it to 'asc'
if (matchSortingDir && matchSortingDir[1] === 'desc') {
sortDir = '';
// if sorting direction is not set, then we set to 'asc' by default
} else if (!matchSortingDir) {
sortDir = '';
// in this case sort direction was set to 'asc', so we change it to 'desc'
} else {
sortDir = '-';
}
onChange({sortBy: sortDir + matchColumnId[1]});
}
}
}}
>
{props.children}
</th>
);
};

ThComponent.propTypes = {
className: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
children: PropTypes.any,
};

export const Table = ({columns, offset, limit, total, sortBy, onChange, ...props}) => (
<div styleName="smart-table">
<div styleName="table-wrap">
<ReactTable
tableClassName={styles.table}
theadClassName={styles.thead}
tbodyClassName={styles.tbody}
trClassName={styles.tr}
showPageJump={false}
showPageSizeOptions={false}
showPagination={false}
loading={false}
pages={Math.ceil(total / limit)}
pageSize={limit}
minRows={0}
manual
column={{
sortable: false,
}}
sorting={prepareSorting(sortBy)}
thComponent={(prop) => <ThComponent {...{...prop, onChange}} />}
columns={prepareColumns(columns)}
{...props}
/>
</div>

<div styleName="navigation">
<div styleName="perpage">
<SelectPerPage
value={limit}
onChange={({value}) => {
// adjust page number (offset) when change per page quantity (limit)
const newOffset = Math.floor(offset / value);
onChange({limit: value, offset: newOffset});
}}
/>
</div>
<div styleName="pagination">
<Pagination
forcePage={Math.ceil(offset / limit)}
pageCount={Math.ceil(total / limit)}
onPageChange={({selected}) => {
onChange({offset: Math.ceil(selected * limit)});
}}
/>
</div>
</div>

</div>
);

Table.propTypes = {
columns: PropTypes.array.isRequired,
offset: PropTypes.number.isRequired,
limit: PropTypes.number.isRequired,
total: PropTypes.number.isRequired,
sortBy: PropTypes.string,
onChange: PropTypes.func.isRequired,
};

export default CSSModules(Table, styles);
Loading