Skip to content

Commit 22b75ce

Browse files
committed
Added product option form
1 parent 6aa3bcf commit 22b75ce

20 files changed

Lines changed: 573 additions & 168 deletions

File tree

locales/admin/en.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,14 @@
246246
"orders_search": "Search orders",
247247
"pageNotFound": "Page not found!",
248248
"skuTaken": "Product SKU must be unique",
249-
"product_options": "Options",
250-
"logo": "Logo"
249+
"editProductOption": "Edit option",
250+
"productVariants": "Variants",
251+
"addVariant": "Add new variant",
252+
"addOption": "Add new option",
253+
"logo": "Logo",
254+
"chooseImage": "Choose an Image",
255+
"position": "Position",
256+
"optionName": "Option name",
257+
"optionControl": "Option control",
258+
"optionControlSelect": "Drop-down list"
251259
}

src/admin/client/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import layoutProductEditShared from 'layouts/products/edit/shared'
2121
import layoutProductEditGeneral from 'layouts/products/edit/general'
2222
import layoutProductEditInventory from 'layouts/products/edit/inventory'
2323
import layoutProductEditImages from 'layouts/products/edit/images'
24-
import layoutProductEditOptions from 'layouts/products/edit/options'
24+
import layoutProductEditVariants from 'layouts/products/edit/variants'
25+
import layoutProductEditOption from 'layouts/products/edit/option'
2526
import layoutProductCategories from 'layouts/products/categories'
2627
import layoutCustomers from 'layouts/customers'
2728
import layoutCustomerEdit from 'layouts/customers/edit'
@@ -152,7 +153,8 @@ ReactDOM.render(
152153
<Route path="general" component={layoutProductEditGeneral}/>
153154
<Route path="inventory" component={layoutProductEditInventory}/>
154155
<Route path="images" component={layoutProductEditImages}/>
155-
<Route path="options" component={layoutProductEditOptions}/>
156+
<Route path="variants" component={layoutProductEditVariants}/>
157+
<Route path="option/:optionId" component={layoutProductEditOption}/>
156158
</Route>
157159

158160
<Route path="orders" component={layoutOrders}/>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import React from 'react'
2+
import ProductOption from 'modules/products/edit/option'
3+
4+
export default ({ productId, params }) => (
5+
<ProductOption productId={productId} optionId={params.optionId} />
6+
)

src/admin/client/layouts/products/edit/options.js

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/admin/client/layouts/products/edit/shared.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const ProductMenu = ({ productId }) => (
2121
<Link style={styles.link} activeStyle={styles.linkActive} to={`/admin/product/${productId}/general`}><ListItem primaryText={messages.description} leftIcon={<FontIcon className="material-icons">description</FontIcon>}/></Link>
2222
<Link style={styles.link} activeStyle={styles.linkActive} to={`/admin/product/${productId}/inventory`}><ListItem primaryText={messages.products_inventory} leftIcon={<FontIcon className="material-icons">store</FontIcon>}/></Link>
2323
<Link style={styles.link} activeStyle={styles.linkActive} to={`/admin/product/${productId}/images`}><ListItem primaryText={messages.images} leftIcon={<FontIcon className="material-icons">photo_camera</FontIcon>}/></Link>
24-
<Link style={styles.link} activeStyle={styles.linkActive} to={`/admin/product/${productId}/options`}><ListItem primaryText={messages.product_options} leftIcon={<FontIcon className="material-icons">palette</FontIcon>}/></Link>
24+
<Link style={styles.link} activeStyle={styles.linkActive} to={`/admin/product/${productId}/variants`}><ListItem primaryText={messages.productVariants} leftIcon={<FontIcon className="material-icons">palette</FontIcon>}/></Link>
2525
</List>
2626
)
2727

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import React from 'react'
2+
import ProductVariants from 'modules/products/edit/variants'
3+
4+
export default ({ productId }) => (
5+
<ProductVariants productId={productId} />
6+
)

src/admin/client/modules/app/head/components/appBar.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ export default class AppBarTop extends React.Component {
100100

101101
rightElements = <OrdersHead />
102102
}
103+
else if(location.startsWith('/admin/product/') && location.includes('/option/')){
104+
const productId = location.split('/')[3];
105+
title = title = messages.editProductOption;
106+
leftButton = <Link to={`/admin/product/${productId}/variants`}><IconButton><FontIcon color="#fff" className="material-icons">arrow_back</FontIcon></IconButton></Link>
107+
}
103108
else if(location.startsWith('/admin/product/')){
104109
title = title = messages.products_titleEdit;
105110
leftButton = <Link to="/admin/products"><IconButton><FontIcon color="#fff" className="material-icons">arrow_back</FontIcon></IconButton></Link>

src/admin/client/modules/products/actionTypes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export const PRODUCT_UPDATE_REQUEST = 'PRODUCT_UPDATE_REQUEST'
2121
export const PRODUCT_UPDATE_SUCCESS = 'PRODUCT_UPDATE_SUCCESS'
2222
export const PRODUCT_UPDATE_FAILURE = 'PRODUCT_UPDATE_FAILURE'
2323

24+
export const PRODUCT_OPTIONS_RECEIVE = 'PRODUCT_OPTIONS_RECEIVE'
25+
export const PRODUCT_VARIANTS_RECEIVE = 'PRODUCT_VARIANTS_RECEIVE'
2426
export const PRODUCT_IMAGES_RECEIVE = 'PRODUCT_IMAGES_RECEIVE'
2527
export const PRODUCT_CREATE_SUCCESS = 'PRODUCT_CREATE_SUCCESS'
2628
export const PRODUCT_DELETE_SUCCESS = 'PRODUCT_DELETE_SUCCESS'

src/admin/client/modules/products/actions.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ function receiveImages(images) {
3131
}
3232
}
3333

34+
function receiveVariants(variants) {
35+
return {
36+
type: t.PRODUCT_VARIANTS_RECEIVE,
37+
variants
38+
}
39+
}
40+
41+
function receiveOptions(options) {
42+
return {
43+
type: t.PRODUCT_OPTIONS_RECEIVE,
44+
options
45+
}
46+
}
47+
3448
export function cancelProductEdit() {
3549
return {
3650
type: t.PRODUCT_DETAIL_ERASE
@@ -320,6 +334,91 @@ export function fetchImages(productId) {
320334
}
321335
}
322336

337+
export function fetchOptions(productId) {
338+
return (dispatch, getState) => {
339+
return api.products.options.list(productId).then(({status, json}) => {
340+
dispatch(receiveOptions(json))
341+
})
342+
.catch(error => {});
343+
}
344+
}
345+
346+
export function fetchVariants(productId) {
347+
return (dispatch, getState) => {
348+
return api.products.variants.list(productId).then(({status, json}) => {
349+
dispatch(receiveVariants(json))
350+
})
351+
.catch(error => {});
352+
}
353+
}
354+
355+
export function createVariant(productId) {
356+
return (dispatch, getState) => {
357+
// todo: get price, stock_quantity and weight from product details
358+
const variant = { price: 0, stock_quantity: 0, weight: 0 };
359+
return api.products.variants.create(productId, variant).then(({status, json}) => {
360+
dispatch(receiveVariants(json))
361+
})
362+
.catch(error => {});
363+
}
364+
}
365+
366+
export function updateVariant(productId, variantId, variant) {
367+
return (dispatch, getState) => {
368+
return api.products.variants.update(productId, variantId, variant).then(({status, json}) => {
369+
dispatch(receiveVariants(json))
370+
})
371+
.catch(error => {});
372+
}
373+
}
374+
375+
export function setVariantOption(productId, variantId, optionId, valueId) {
376+
return (dispatch, getState) => {
377+
const option = { option_id: optionId, value_id: valueId };
378+
return api.products.variants.setOption(productId, variantId, option).then(({status, json}) => {
379+
dispatch(receiveVariants(json))
380+
})
381+
.catch(error => {});
382+
}
383+
}
384+
385+
export function createOption(productId, option) {
386+
return (dispatch, getState) => {
387+
return api.products.options.create(productId, option).then(({status, json}) => {
388+
dispatch(receiveOptions(json))
389+
})
390+
.catch(error => {});
391+
}
392+
}
393+
394+
export function updateOption(productId, optionId, option) {
395+
return (dispatch, getState) => {
396+
return api.products.options.update(productId, optionId, option).then(({status, json}) => {
397+
dispatch(receiveOptions(json))
398+
})
399+
.catch(error => {});
400+
}
401+
}
402+
403+
404+
export function deleteOption(productId, optionId) {
405+
return (dispatch, getState) => {
406+
return api.products.options.delete(productId, optionId).then(({status, json}) => {
407+
dispatch(receiveOptions(json))
408+
})
409+
.catch(error => {});
410+
}
411+
}
412+
413+
414+
export function deleteVariant(productId, variantId) {
415+
return (dispatch, getState) => {
416+
return api.products.variants.delete(productId, variantId).then(({status, json}) => {
417+
dispatch(receiveVariants(json))
418+
})
419+
.catch(error => {});
420+
}
421+
}
323422

324423
export function deleteImage(productId, imageId) {
325424
return (dispatch, getState) => {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import React from 'react'
2+
import { Link } from 'react-router'
3+
import { Field, reduxForm } from 'redux-form'
4+
import { TextField, SelectField } from 'redux-form-material-ui'
5+
import { CustomToggle } from 'modules/shared/form'
6+
7+
import messages from 'lib/text'
8+
import style from './style.css'
9+
10+
import Paper from 'material-ui/Paper';
11+
import FlatButton from 'material-ui/FlatButton';
12+
import RaisedButton from 'material-ui/RaisedButton';
13+
import MenuItem from 'material-ui/MenuItem';
14+
15+
const validate = values => {
16+
const errors = {}
17+
const requiredFields = ['name']
18+
19+
requiredFields.map(field => {
20+
if (values && !values[field]) {
21+
errors[field] = messages.errors_required;
22+
}
23+
})
24+
25+
return errors
26+
}
27+
28+
class ProductOptionForm extends React.Component {
29+
constructor(props) {
30+
super(props);
31+
}
32+
33+
componentDidMount() {
34+
this.props.fetchData();
35+
}
36+
37+
render() {
38+
let {
39+
handleSubmit,
40+
pristine,
41+
submitting,
42+
initialValues,
43+
deleteOption } = this.props;
44+
45+
return (
46+
<form onSubmit={handleSubmit} style={{ display: 'initial' }}>
47+
<Paper className="paper-box" zDepth={1}>
48+
<div className={style.innerBox}>
49+
<Field name="name" component={TextField} floatingLabelText={messages.optionName} fullWidth={true}/>
50+
<Field name="position" component={TextField} type="number" floatingLabelText={messages.position} fullWidth={true}/>
51+
<div className={style.shortControl}>
52+
<Field component={SelectField} autoWidth={true} fullWidth={true} name="control" floatingLabelText={messages.optionControl}>
53+
<MenuItem value="select" primaryText={messages.optionControlSelect}/>
54+
</Field>
55+
</div>
56+
<div className={style.shortControl}>
57+
<Field name="required" component={CustomToggle} label={messages.settings_fieldRequired}/>
58+
</div>
59+
</div>
60+
<div className="buttons-box">
61+
<RaisedButton label={messages.actions_delete} style={{ float: 'left'}} onClick={deleteOption} />
62+
<RaisedButton type="submit" label={messages.actions_save} primary={true} className={style.button} disabled={pristine || submitting}/>
63+
</div>
64+
</Paper>
65+
</form>
66+
)
67+
}
68+
}
69+
70+
export default reduxForm({
71+
form: 'ProductOptionForm',
72+
validate,
73+
enableReinitialize: true
74+
})(ProductOptionForm)

0 commit comments

Comments
 (0)