Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit faf238f

Browse files
Crud for products
1 parent 3132d60 commit faf238f

File tree

9 files changed

+153
-2
lines changed

9 files changed

+153
-2
lines changed

‎app/resources/product_resource.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
class ProductResource < JSONAPI::Resource
2-
attributes :product_name
2+
attributes :product_name, :created_at
3+
4+
paginator :paged
35
end

‎client/src/api/normalize.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ const serializers = {
5858
}),
5959
},
6060

61+
products: {
62+
serializer: new Serializer('products', {
63+
keyForAttribute: 'camelCase',
64+
attributes: [
65+
'productName',
66+
'createdAt'
67+
],
68+
}),
69+
deserializer: new Deserializer({
70+
keyForAttribute: 'camelCase',
71+
}),
72+
},
73+
6174
roles: {
6275
serializer: new Serializer('roles', {
6376
keyForAttribute: 'camelCase',

‎client/src/components/App.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ export class App extends Component {
4343
userIsAdmin && <NavLink href="/#/users">Users</NavLink>
4444
}
4545
</NavItem>
46+
<NavItem>
47+
<NavLink href="/#/products">Products</NavLink>
48+
</NavItem>
4649
</Nav>
4750
<Nav navbar className="ml-auto">
4851
<NavDropdown isOpen={this.state.isOpen} toggle={this.toggle}>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React, { Component, PropTypes } from 'react';
2+
import { push } from 'react-router-redux';
3+
import { connect } from 'react-redux';
4+
5+
import { ErrorAlert, Loading, EditHeader } from '../UI';
6+
import { withResource } from '../../hocs';
7+
import ProductForm from './ProductForm';
8+
import { getMany, fetchList } from '../../store/api';
9+
10+
export class ProductEdit extends Component {
11+
componentWillMount() {
12+
const { params, fetchResource } = this.props;
13+
14+
if (params.id) {
15+
fetchResource({ id: params.id });
16+
}
17+
}
18+
19+
render() {
20+
const { isNew, error, loading, resource, onSubmit } = this.props;
21+
22+
if (error) {
23+
return (<ErrorAlert {...error} />);
24+
}
25+
26+
if (loading) {
27+
return (<Loading />);
28+
}
29+
30+
return (
31+
<div>
32+
<EditHeader {...this.props}>{ isNew ? 'New Product' : resource.productName }</EditHeader>
33+
<ProductForm initialValues={resource} onSubmit={onSubmit}></ProductForm>
34+
</div>
35+
);
36+
}
37+
}
38+
39+
40+
export const mapDispatchToProps = dispatch => ({
41+
redirectToIndex: () => dispatch(push('/products')),
42+
});
43+
44+
export default connect(null, mapDispatchToProps)(
45+
withResource('products')(ProductEdit),
46+
);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React, { Component, PropTypes } from 'react';
2+
import { isEmpty } from 'lodash';
3+
import { Field, reduxForm } from 'redux-form';
4+
import { Button, Form } from 'reactstrap';
5+
6+
import { InputField, MultiselectField, required } from '../../forms';
7+
8+
class ProductForm extends Component {
9+
render() {
10+
const { handleSubmit, pristine, reset, submitting } = this.props;
11+
12+
return (
13+
<Form onSubmit={handleSubmit}>
14+
<div>
15+
<Field
16+
name="productName"
17+
label="Name"
18+
component={InputField}
19+
/>
20+
</div>
21+
<div>
22+
<Button disabled={pristine || submitting} color="primary">Submit</Button>
23+
<Button disabled={pristine || submitting} onClick={reset}>Undo Changes</Button>
24+
</div>
25+
</Form>
26+
);
27+
}
28+
}
29+
30+
const validate = (values) => {
31+
const errors = required(values, 'productName');
32+
return errors;
33+
};
34+
35+
export default reduxForm({
36+
enableReinitialize: true,
37+
form: 'product',
38+
validate,
39+
})(ProductForm);
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React, { Component, PropTypes } from 'react';
2+
import { Link } from 'react-router';
3+
import { find, keyBy } from 'lodash';
4+
5+
import { ListTable } from '../UI';
6+
import { withResourceList } from '../../hocs';
7+
import { Button } from 'reactstrap';
8+
9+
const formatDate = date => (new Date(date)).toLocaleString();
10+
11+
export class ProductList extends Component {
12+
componentWillMount() {
13+
const { resourceList } = this.props;
14+
this.props.fetchResourceList({ sort: '-createdAt', ...resourceList.params });
15+
}
16+
17+
render() {
18+
const columns = [
19+
{
20+
attribute: 'productName',
21+
header: 'Name',
22+
rowRender: product => <Link to={`/products/${product.id}`}>{product.productName}</Link>,
23+
sortable: true,
24+
},
25+
{
26+
attribute: 'createdAt',
27+
header: 'Created At',
28+
rowRender: product => formatDate(product.createdAt),
29+
sortable: true,
30+
}
31+
];
32+
33+
return (
34+
<div>
35+
<Button tag={Link} to={'/products/new'}>New Product</Button>
36+
<ListTable {...this.props} columns={columns} />
37+
</div>
38+
);
39+
}
40+
}
41+
42+
export default withResourceList('products')(ProductList);

‎client/src/components/Products/index.js

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

‎client/src/components/Routes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { PostList, PostEdit } from './Posts';
99
import { CategoryList, CategoryEdit } from './Categories';
1010
import { UserList, UserEdit } from './Users';
1111
import { Login } from './Auth';
12+
import { ProductList, ProductEdit } from './Products';
1213

1314
const UserIsAuthenticated = UserAuthWrapper({ authSelector: getUser });
1415
const UserIsAdmin = UserAuthWrapper({
@@ -34,6 +35,9 @@ export class Routes extends PureComponent {
3435
<Route path="/categories" component={CategoryList}/>
3536
<Route path="/users" component={UserIsAdmin(UserList)}/>
3637
<Route path="/users/:id" component={UserIsAdmin(UserEdit)}/>
38+
<Route path="/products" component={ProductList}/>
39+
<Route path="/products/new" component={ProductEdit}/>
40+
<Route path="/products/:id" component={ProductEdit}/>
3741
</Route>
3842
<Route path="/login" component={Login}/>
3943
</Router>

‎config/routes.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
jsonapi_resources :categories
44
jsonapi_resources :comments
55
jsonapi_resources :posts
6-
jsonapi_resources :products
76
jsonapi_resources :users
87
jsonapi_resources :roles
98
jsonapi_resources :orders
109
jsonapi_resources :customers
1110
jsonapi_resources :suppliers
11+
jsonapi_resources :products
1212
end

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /