Skip to content

Commit 6859ed3

Browse files
authored
Merge pull request #19 from czikarito/display_products_on_frontend
Crud for products
2 parents 8f46169 + 0d03377 commit 6859ed3

File tree

9 files changed

+152
-4
lines changed

9 files changed

+152
-4
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: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,18 @@ const serializers = {
5757
},
5858
}),
5959
},
60-
60+
products: {
61+
serializer: new Serializer('products', {
62+
keyForAttribute: 'camelCase',
63+
attributes: [
64+
'productName',
65+
'createdAt'
66+
],
67+
}),
68+
deserializer: new Deserializer({
69+
keyForAttribute: 'camelCase',
70+
}),
71+
},
6172
customers: {
6273
serializer: new Serializer('customers', {
6374
keyForAttribute: 'camelCase',
@@ -69,7 +80,6 @@ const serializers = {
6980
keyForAttribute: 'camelCase'
7081
}),
7182
},
72-
7383
roles: {
7484
serializer: new Serializer('roles', {
7585
keyForAttribute: 'camelCase',

client/src/components/App.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ export class App extends Component {
4444
userIsAdmin && <NavLink href="/#/users">Users</NavLink>
4545
}
4646
</NavItem>
47+
<NavItem>
48+
<NavLink href="/#/products">Products</NavLink>
49+
</NavItem>
4750
</Nav>
4851
<Nav navbar className="ml-auto">
4952
<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);
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
@@ -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 { ProductList, ProductEdit } from './Products';
1516

1617
const UserIsAuthenticated = UserAuthWrapper({ authSelector: getUser });
1718
const UserIsAdmin = UserAuthWrapper({
@@ -37,6 +38,9 @@ export class Routes extends PureComponent {
3738
<Route path="/categories" component={CategoryList}/>
3839
<Route path="/users" component={UserIsAdmin(UserList)}/>
3940
<Route path="/users/:id" component={UserIsAdmin(UserEdit)}/>
41+
<Route path="/products" component={ProductList}/>
42+
<Route path="/products/new" component={ProductEdit}/>
43+
<Route path="/products/:id" component={ProductEdit}/>
4044
<Route path="/customers" component={UserIsAdmin(CustomerList)}/>
4145
<Route path="/customers/new" component={UserIsAdmin(CustomerEdit)}/>
4246
<Route path="/customers/:id" component={UserIsAdmin(CustomerEdit)}/>

config/routes.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
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 :employees
109
jsonapi_resources :orders
1110
jsonapi_resources :customers
1211
jsonapi_resources :suppliers
12+
jsonapi_resources :products
1313
end

0 commit comments

Comments
 (0)