Skip to content

Commit f0bdb55

Browse files
authored
Merge pull request #20 from kantorm/orders
orders list and new order form
2 parents 6859ed3 + 090db36 commit f0bdb55

File tree

7 files changed

+229
-0
lines changed

7 files changed

+229
-0
lines changed

client/src/api/normalize.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,28 @@ const serializers = {
9191
keyForAttribute: 'camelCase',
9292
}),
9393
},
94+
95+
orders: {
96+
serializer: new Serializer('orders', {
97+
keyForAttribute: 'camelCase',
98+
attributes: [
99+
'orderDate',
100+
'requiredDate',
101+
'shippedDate',
102+
'shipVia',
103+
'freight',
104+
'shipName',
105+
'shipAddress',
106+
'shipCity',
107+
'shipRegion',
108+
'shipPostalCode',
109+
'shipCountry',
110+
],
111+
}),
112+
deserializer: new Deserializer({
113+
keyForAttribute: 'camelCase',
114+
}),
115+
},
94116
};
95117

96118
export const normalize = (type, data) => {

client/src/components/App.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class App extends Component {
3737
<NavLink href="/#/categories">Categories</NavLink>
3838
</NavItem>
3939
<NavItem>
40+
<NavLink href="/#/orders">Orders</NavLink>
4041
<NavLink href="/#/customers">Customers</NavLink>
4142
</NavItem>
4243
<NavItem>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React, { Component, PropTypes } from 'react';
2+
import { push } from 'react-router-redux';
3+
import { connect } from 'react-redux';
4+
import { get, find, omit } from 'lodash';
5+
6+
import { ErrorAlert, Loading, EditHeader } from '../UI';
7+
import { withResource } from '../../hocs';
8+
import OrderForm from './OrderForm';
9+
import {
10+
fetchList,
11+
getMany,
12+
} from '../../store/api';
13+
14+
export class OrderEdit extends Component {
15+
componentWillMount() {
16+
const { params, fetchResource } = this.props;
17+
18+
if (params.id) {
19+
fetchResource({ id: params.id });
20+
}
21+
}
22+
23+
render() {
24+
const { isNew, error, loading, resource, onSubmit, orders } = this.props;
25+
26+
if (error) {
27+
return (<ErrorAlert {...error} />);
28+
}
29+
30+
if (loading) {
31+
return (<Loading/>);
32+
}
33+
34+
return (
35+
<div>
36+
<EditHeader {...this.props}>{ isNew ? 'New Order' : resource.title }</EditHeader>
37+
<OrderForm initialValues={resource} isNew={isNew} onSubmit={onSubmit} />
38+
</div>
39+
);
40+
}
41+
}
42+
43+
export const mapStateToProps = (state, props) => ({
44+
categories: getMany(state, 'orders'),
45+
});
46+
47+
export const mapDispatchToProps = dispatch => ({
48+
redirectToIndex: () => dispatch(push('/orders')),
49+
});
50+
51+
export default connect(mapStateToProps, mapDispatchToProps)(
52+
withResource('orders')(OrderEdit),
53+
);
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import React, { Component, PropTypes } from 'react';
2+
import { isEmpty } from 'lodash';
3+
import { Field, reduxForm } from 'redux-form';
4+
import { Button, Form, Col, Row } from 'reactstrap';
5+
6+
import { InputField, required } from '../../forms';
7+
8+
class OrderForm extends Component {
9+
render() {
10+
const { handleSubmit, onSubmit, onDelete, reset, isNew, submitSucceeded } = this.props;
11+
12+
if (isNew && submitSucceeded) {
13+
setTimeout(() => reset());
14+
}
15+
16+
const submitOnChange = () => {
17+
if (!isNew) {
18+
setTimeout(() => handleSubmit(onSubmit)(), 0);
19+
}
20+
};
21+
22+
return (
23+
<Form onSubmit={handleSubmit}>
24+
<Row>
25+
<Col xs="10" sm="8" md="6">
26+
<Field
27+
name="orderDate"
28+
label="Order date"
29+
component={InputField}
30+
onChange={submitOnChange}
31+
/>
32+
<Field
33+
name="shippedDate"
34+
label="Shipped date"
35+
component={InputField}
36+
onChange={submitOnChange}
37+
/>
38+
<Field
39+
name="shipAddress"
40+
label="Ship address"
41+
component={InputField}
42+
onChange={submitOnChange}
43+
/>
44+
<Field
45+
name="shipCity"
46+
label="Ship city"
47+
component={InputField}
48+
onChange={submitOnChange}
49+
/>
50+
<Field
51+
name="shipRegion"
52+
label="Ship region"
53+
component={InputField}
54+
onChange={submitOnChange}
55+
/>
56+
</Col>
57+
<Col xs="2">
58+
{
59+
isNew
60+
? <Button color="success">+</Button>
61+
: <Button color="danger" onClick={onDelete}>X</Button>
62+
}
63+
</Col>
64+
</Row>
65+
</Form>
66+
);
67+
}
68+
}
69+
70+
export default reduxForm({
71+
enableReinitialize: true,
72+
form: 'order',
73+
})(OrderForm);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import React, { Component, PropTypes } from 'react';
2+
import { Link } from 'react-router';
3+
import { connect } from 'react-redux';
4+
import { get, find, keyBy } from 'lodash';
5+
import { Button } from 'reactstrap';
6+
7+
import { fetchList, getMap, getMany } from '../../store/api';
8+
import { withResourceList } from '../../hocs';
9+
import { ListHeader, ListTable } from '../UI';
10+
11+
const formatDate = date => (new Date(date)).toLocaleString();
12+
13+
export class OrderList extends Component {
14+
componentWillMount() {
15+
const {resourceList} = this.props;
16+
this.props.fetchResourceList({sort: '-orderDate', ...resourceList.params});
17+
}
18+
19+
render() {
20+
const { resourceList, onFilter, categories } = this.props;
21+
const columns = [
22+
{
23+
attribute: 'order_date',
24+
header: 'Order date',
25+
minWidth: '50px',
26+
rowRender: order => formatDate(order.orderDate),
27+
},
28+
{
29+
attribute: 'shippedDate',
30+
header: 'Shipped date',
31+
rowRender: order => formatDate(order.shippedDate),
32+
sortable: true,
33+
},
34+
{
35+
attribute: 'shipAddress',
36+
header: 'Ship address',
37+
rowRender: order => order.shipAddress,
38+
sortable: true,
39+
},
40+
{
41+
attribute: 'shipCity',
42+
header: 'Ship city',
43+
rowRender: order => order.shipCity,
44+
sortable: true,
45+
},
46+
{
47+
attribute: 'shipRegion',
48+
header: 'Ship region',
49+
rowRender: order => order.shipRegion,
50+
sortable: true,
51+
},
52+
];
53+
54+
return (
55+
<div>
56+
<Button tag={Link} to={'/orders/new'}>New Order</Button>
57+
<ListTable {...this.props} columns={columns} />
58+
</div>
59+
);
60+
}
61+
}
62+
63+
export const mapStateToProps = state => ({
64+
ordersById: getMap(state, 'orders'),
65+
orders: getMany(state, 'orders'),
66+
});
67+
68+
export const mapDispatchToProps = dispatch => ({
69+
fetchOrders: () => dispatch(fetchList('orders', { page: { size: 999 } })),
70+
});
71+
72+
export default connect(mapStateToProps, mapDispatchToProps)(
73+
withResourceList('orders')(OrderList),
74+
);

client/src/components/Orders/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export OrderList from './OrderList';
2+
export OrderEdit from './OrderEdit';

client/src/components/Routes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { CategoryList } from './Categories';
1212
import { UserList, UserEdit } from './Users';
1313
import { CustomerList, CustomerEdit } from './Customers';
1414
import { Login } from './Auth';
15+
import { OrderList, OrderEdit } from './Orders';
1516
import { ProductList, ProductEdit } from './Products';
1617

1718
const UserIsAuthenticated = UserAuthWrapper({ authSelector: getUser });
@@ -32,6 +33,9 @@ export class Routes extends PureComponent {
3233
<Router history={history}>
3334
<Route path="/" component={UserIsAuthenticated(App)}>
3435
<IndexRoute component={Dashboard}/>
36+
<Route path="/orders" component={OrderList}/>
37+
<Route path="/orders/new" component={OrderEdit}/>
38+
<Route path="/orders/:id" component={OrderEdit}/>
3539
<Route path="/posts" component={PostList}/>
3640
<Route path="/posts/new" component={PostEdit}/>
3741
<Route path="/posts/:id" component={PostEdit}/>

0 commit comments

Comments
 (0)