Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sync inventory policy toggle between top-level variants and child variants #3004

Merged
merged 6 commits into from
Oct 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 49 additions & 11 deletions imports/plugins/core/ui/client/components/switch/switch.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class Switch extends Component {

static propTypes = {
checked: PropTypes.bool,
disabled: PropTypes.bool,
helpText: PropTypes.string,
i18nKeyHelpText: PropTypes.string,
i18nKeyLabel: PropTypes.string,
i18nKeyOnLabel: PropTypes.string,
label: PropTypes.string,
Expand All @@ -26,6 +29,16 @@ class Switch extends Component {
};
}

get isHelpMode() {
// TODO: add functionality to toggle helpMode on / off.
// When on, helpText will always show.
// When off, only validation messages will show.
// For now, all helpText will show, meaning this doesn't affect how the app currently works.
// This is here just to lay the foundation for when we add the toggle.

return true;
}

handleChange = (event) => {
if (this.props.onChange) {
const isInputChecked = !this.props.checked;
Expand Down Expand Up @@ -61,14 +74,36 @@ class Switch extends Component {
return null;
}

/**
* Render help text or validation message
* @return {ReactNode|null} react node or null
*/
renderHelpText() {
const helpMode = this.isHelpMode;
const helpText = this.props.helpText;
const i18nKey = this.props.i18nKeyHelpText;

// Show if helpMode is true
if (helpText && helpMode) {
return (
<span className="help-block">
<Components.Translation defaultValue={helpText} i18nKey={i18nKey} />
</span>
);
}

return null;
}

checkboxRef = (ref) => {
this._checkbox = ref;
}

render() {
const baseClassName = classnames({
rui: true,
switch: true
switch: true,
disabled: this.props.disabled
});

const switchControlClassName = classnames({
Expand All @@ -77,16 +112,19 @@ class Switch extends Component {
});

return (
<label className={baseClassName}>
<input
checked={this.props.checked}
onChange={this.handleChange}
ref={this.checkboxRef}
type="checkbox"
/>
<div className={switchControlClassName} />
{this.renderLabel()}
</label>
<span>
<label className={baseClassName}>
<input
checked={this.props.checked}
onChange={this.handleChange}
ref={this.checkboxRef}
type="checkbox"
/>
<div className={switchControlClassName} />
{this.renderLabel()}
</label>
{this.renderHelpText()}
</span>
);
}
}
Expand Down
11 changes: 11 additions & 0 deletions imports/plugins/included/default-theme/client/styles/forms.less
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@
input, label {
display: none;
}

&.disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
}

.rui.switch .switch-control {
Expand Down Expand Up @@ -137,6 +143,11 @@
padding: 0;
}

// Original style is inherited from Bootstrap
.rui.switch .help-block {
color: @black30;
}

.checkbox-large {
-webkit-appearance: none;
// background-color: #fafafa;
Expand Down
52 changes: 40 additions & 12 deletions imports/plugins/included/product-variant/components/variantForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,44 @@ class VariantForm extends Component {
}
}

renderInventoryPolicyField() {
if (this.props.hasChildVariants(this.variant)) {
return (
<div className="col-sm-12">
<Components.Switch
i18nKeyLabel="productVariant.inventoryPolicy"
i18nKeyOnLabel="productVariant.inventoryPolicy"
name="inventoryPolicy"
label={"Allow backorder"}
onLabel={"Allow backorder"}
checked={!this.state.inventoryPolicy}
onChange={this.handleInventoryPolicyChange}
validation={this.props.validation}
disabled={true}
helpText={"Backorder allowance is now controlled by options"}
i18nKeyHelpText={"admin.helpText.variantBackorderToggle"}
style={{ backgroundColor: "lightgrey", cursor: "not-allowed" }}
/>
</div>
);
}

return (
<div className="col-sm-12">
<Components.Switch
i18nKeyLabel="productVariant.inventoryPolicy"
i18nKeyOnLabel="productVariant.inventoryPolicy"
name="inventoryPolicy"
label={"Allow backorder"}
onLabel={"Allow backorder"}
checked={!this.state.inventoryPolicy}
onChange={this.handleInventoryPolicyChange}
validation={this.props.validation}
/>
</div>
);
}

renderQuantityField() {
if (this.props.hasChildVariants(this.variant)) {
return (
Expand Down Expand Up @@ -526,18 +564,7 @@ class VariantForm extends Component {
</div>
</div>
<div className="row">
<div className="col-sm-6">
<Components.Switch
i18nKeyLabel="productVariant.inventoryPolicy"
i18nKeyOnLabel="productVariant.inventoryPolicy"
name="inventoryPolicy"
label={"Allow backorder"}
onLabel={"Allow backorder"}
checked={!this.state.inventoryPolicy}
onChange={this.handleInventoryPolicyChange}
validation={this.props.validation}
/>
</div>
{this.renderInventoryPolicyField()}
</div>
</Components.SettingsCard>
</Components.CardGroup>
Expand Down Expand Up @@ -654,6 +681,7 @@ class VariantForm extends Component {

<div className="row">
{this.renderQuantityField()}
{this.renderInventoryPolicyField()}
</div>
</Components.CardBody>
</Components.Card>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ const wrapComponent = (Comp) => (
if (error) {
Alerts.toast(error.message, "error");
}

if (fieldName === "inventoryPolicy") {
this.updateInventoryPolicyIfChildVariants(variant);
}
});
}
}
Expand All @@ -195,6 +199,42 @@ const wrapComponent = (Comp) => (
Meteor.call("products/updateProductField", variant._id, "isVisible", !variant.isVisible);
}

/**
* @method updateInventoryPolicyIfChildVariants
* @description update parent inventory policy if variant has children
* @param {Object} variant product or variant document
* @return {undefined} return nothing
*/
updateInventoryPolicyIfChildVariants = (variant) => {
// Get all siblings, including current variant
const options = ReactionProduct.getSiblings(variant);
// Get parent
const parent = ReactionProduct.getVariantParent(variant);

// If this is not a top-level variant, update top-level inventory policy as well
if (parent && options && options.length) {
// Check to see if every variant option inventory policy is true
const inventoryPolicy = options.every((option) => {
return option.inventoryPolicy === true;
});

// If all inventory policies on children are true, update parent to be true
if (inventoryPolicy === true) {
return Meteor.call("products/updateProductField", parent._id, "inventoryPolicy", true, (error) => {
if (error) {
Alerts.toast(error.message, "error");
}
});
}
// If any child has a false inventoryPolicy, update parent to be false
return Meteor.call("products/updateProductField", parent._id, "inventoryPolicy", false, (error) => {
if (error) {
Alerts.toast(error.message, "error");
}
});
}
}

updateQuantityIfChildVariants = (variant) => {
if (this.hasChildVariants(variant)) {
const variantQuantity = ReactionProduct.getVariantQuantity(variant);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"optionTitle": "Displayed on Product Detail Page",
"price": "Purchase price",
"title": "Displayed in cart, checkout, and orders",
"variantBackorderToggle": "Backorder allowance is now controlled by options",
"variantInventoryQuantity": "Variant inventory is now controlled by options"
},
"tooltip": {
Expand Down
31 changes: 31 additions & 0 deletions lib/api/catalog.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,37 @@ export default {
}).map(this.getPublishedOrRevision);
},

/**
* @method getVariantParent
* @description Get direct parent variant
* @summary could be useful for lower level variants to get direct parents
* @param {Object} [variant] - product / variant object
* @return {Array} Parent variant or empty
*/
getVariantParent(variant) {
const parent = Products.findOne({
_id: { $in: variant.ancestors },
type: "variant"
});
return this.getPublishedOrRevision(parent);
},

/**
* @method getSiblings
* @description Get all sibling variants - variants with the same ancestor tree
* @summary could be useful for child variants relationships with top-level variants
* @param {Object} [variant] - product / variant object
* @param {String} [type] - type of variant
* @param {Boolean} [includeSelf] - include current variant in results
* @return {Array} Sibling variants or empty array
*/
getSiblings(variant, type) {
return Products.find({
ancestors: variant.ancestors,
type: type || "variant"
}).map(this.getPublishedOrRevision);
},

/**
* @method getTopVariants
* @description Get only product top level variants
Expand Down
24 changes: 24 additions & 0 deletions lib/api/products.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,30 @@ ReactionProduct.getVariants = (id, type) => {
return Catalog.getVariants(id || ReactionProduct.selectedProductId(), type);
};

/**
* @method getSiblings
* @description Get all sibling variants - variants with the same ancestor tree
* @summary could be useful for child variants relationships with top-level variants
* @param {Object} [variant] - product / variant object
* @param {String} [type] - type of variant
* @param {Boolean} [includeSelf] - include current variant in results
* @return {Array} Sibling variants or empty array
*/
ReactionProduct.getSiblings = (variant, type) => {
return Catalog.getSiblings(variant, type);
};

/**
* @method getVariantParent
* @description Get direct parent variant
* @summary could be useful for lower level variants to get direct parents
* @param {Object} [variant] - product / variant object
* @return {Array} Parent variant or empty
*/
ReactionProduct.getVariantParent = (variant) => {
return Catalog.getVariantParent(variant);
};

/**
* @method getTopVariants
* @description Get only product top level variants
Expand Down