diff --git a/bikeshop_project/assets/js/bikes/components/BikeModal/index.jsx b/bikeshop_project/assets/js/bikes/components/BikeModal/index.jsx new file mode 100644 index 0000000..b5a5f5a --- /dev/null +++ b/bikeshop_project/assets/js/bikes/components/BikeModal/index.jsx @@ -0,0 +1,110 @@ +import React from 'react'; +import Dialog from 'material-ui/Dialog'; +import FlatButton from 'material-ui/FlatButton'; +import TextField from 'material-ui/TextField'; +import DatePicker from 'material-ui/DatePicker'; +import RaisedButton from 'material-ui/RaisedButton'; +import moment from 'moment-timezone'; +import Size from '../Size'; +import Source from '../Source'; +import Checkbox from 'material-ui/Checkbox'; + +/** + * A modal dialog can only be closed by selecting one of the actions. + */ +export default class BikeModal extends React.Component { + constructor(props) { + super(props); + + this.state = { + open: props.open, + bike: undefined, + }; + } + componentWillReceiveProps(newProps) { + this.setState({ open: newProps.open || false, bike: newProps.bike || false }); + } + handleClose = () => { + this.setState({ open: false }); + }; + + render() { + const styles = { + block: { + maxWidth: 250, + }, + checkbox: { + marginBottom: 16, + }, + }; + + const actions = [ + , + , + ]; + + let form; + + + if (this.state.bike !== undefined) { + const timezone = moment.tz.guess(); + const { + make, + price, + claimed_at, + claimed_by, + colour, + cpic_searched_at, + created_at, + size, + serial_number, + source, + stripped } = this.state.bike; + const createdAtFormatted = moment(created_at).tz(timezone).fromNow(); + + form = ( +
+
+
+
+
+
+ + + +
+ +
+
+ ); + } + + return ( +
+ + {(form) ? form :
Unable to edit bike.
} + +
+
+ ); + } +} \ No newline at end of file diff --git a/bikeshop_project/assets/js/bikes/components/BikeTable/index.jsx b/bikeshop_project/assets/js/bikes/components/BikeTable/index.jsx new file mode 100644 index 0000000..bdefe7a --- /dev/null +++ b/bikeshop_project/assets/js/bikes/components/BikeTable/index.jsx @@ -0,0 +1,104 @@ +import React from 'react'; +import { polyFill } from 'es6-promise'; +import moment from 'moment-timezone'; +import fetch from 'isomorphic-fetch'; +import { Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn } from 'material-ui/Table'; +import FlatButton from 'material-ui/FlatButton'; +import { friendlySize } from '../Size'; +import BikeModal from '../BikeModal'; + +function checkStatus(response) { + if (response.status >= 200 && response.status < 300) { + return response; + } + const error = new Error(response.statusText); + error.response = response; + throw error; +} + +function parseJSON(response) { + return response.json(); +} + +export default class BikeTable extends React.Component { + constructor(props) { + super(props); + + this.state = { + bikes: [], + bikeModal: { + open: false, + bike: undefined, + }, + }; + + this.handleEditBike = this.handleEditBike.bind(this); + } + + componentDidMount() { + fetch('/api/v1/bikes/') + .then(checkStatus) + .then(parseJSON) + .then((data) => { + this.setState({ bikes: data }); + console.log('request succeeded with JSON response', data); + }) + .catch((error) => { + console.log('request failed', error); + }); + } + + handleEditBike(bike) { + console.log('Bike edit!'); + this.setState({ + bikeModal: { + open: true, + bike, + }, + }); + } + + render() { + const bikeRows = this.state.bikes.map(bike => ( + + {friendlySize(bike.size)} + {bike.colour} + {bike.make} + {bike.serial_number} + {bike.state} + {bike.claimed_by} + + + )); + + return ( +
+
+

Bikes

+ + + + Size + Colour + Make + Serial number + State + Claimed by + + + + + {bikeRows.length ? + bikeRows : + + {'No members currently signed in.'} + + } + +
+ +
+
+ ); + } +} diff --git a/bikeshop_project/assets/js/bikes/components/Size/index.jsx b/bikeshop_project/assets/js/bikes/components/Size/index.jsx new file mode 100644 index 0000000..42eb4d4 --- /dev/null +++ b/bikeshop_project/assets/js/bikes/components/Size/index.jsx @@ -0,0 +1,47 @@ +import React, { PropTypes } from 'react'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; + +const sizes = ['C', 'S', 'M', 'L', 'XL']; + +export const friendlySize = (size) => { + switch (size) { + case 'C': + return 'child'; + case 'S': + return 'small'; + case 'M': + return 'medium'; + case 'L': + return 'large'; + case 'XL': + return 'extra large'; + default: + return 'unknown'; + } +}; + +const Size = ({ size }) => { + const items = sizes.map(s => + , + ); + + return ( +
+ + + {items} + +
+ ); +}; + +Size.propTypes = { + size: PropTypes.string, +}; + +export default Size; diff --git a/bikeshop_project/assets/js/bikes/components/Source/index.jsx b/bikeshop_project/assets/js/bikes/components/Source/index.jsx new file mode 100644 index 0000000..5ef46d2 --- /dev/null +++ b/bikeshop_project/assets/js/bikes/components/Source/index.jsx @@ -0,0 +1,44 @@ +import React, { PropTypes } from 'react'; +import SelectField from 'material-ui/SelectField'; +import MenuItem from 'material-ui/MenuItem'; + +const sources = ['COS_BIKE_DIVERSION_PILOT', 'UOFS', 'DROP_OFF']; + +export const friendly = (s) => { + switch (s) { + case 'COS_BIKE_DIVERSION_PILOT': + return 'City of Saskatoon Bike Diversion Pilot'; + case 'UOFS': + return 'University of Saskatchewan'; + case 'DROP_OFF': + return 'Drop Off'; + default: + return undefined; + }; +}; + +const Source = ({ source }) => { + const items = sources.map(s => + , + ); + + return ( +
+ + + {items} + +
+ ); +}; + +Source.propTypes = { + source: PropTypes.string, +}; + +export default Source; diff --git a/bikeshop_project/assets/js/bikes/index.jsx b/bikeshop_project/assets/js/bikes/index.jsx index 6b0ab20..95aac39 100644 --- a/bikeshop_project/assets/js/bikes/index.jsx +++ b/bikeshop_project/assets/js/bikes/index.jsx @@ -1,6 +1,9 @@ import React from 'react'; import ReactDOM from 'react-dom'; import injectTapEventPlugin from 'react-tap-event-plugin'; +import getMuiTheme from 'material-ui/styles/getMuiTheme'; +import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; +import BikeTable from './components/BikeTable'; // Needed for onTouchTap // http://stackoverflow.com/a/34015469/988941 @@ -10,11 +13,9 @@ injectTapEventPlugin(); class App extends React.Component { render() { return ( -
-
-

Bikes

-
-
+ + + ); } }