-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3121 from reactioncommerce/folusho-convert-taxclo…
…ud-panel-to-react-3093 Convert TaxCloud panel to React
- Loading branch information
Showing
11 changed files
with
285 additions
and
61 deletions.
There are no files selected for viewing
1 change: 1 addition & 0 deletions
1
imports/plugins/included/taxes-taxcloud/client/components/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as TaxCloudSettingsForm } from "./taxCloudSettingsForm"; |
66 changes: 66 additions & 0 deletions
66
imports/plugins/included/taxes-taxcloud/client/components/taxCloudSettingsForm.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import React from "react"; | ||
import PropTypes from "prop-types"; | ||
import { Form } from "/imports/plugins/core/ui/client/components"; | ||
import { Components } from "@reactioncommerce/reaction-components"; | ||
import { TaxCloudPackageConfig } from "../../lib/collections/schemas"; | ||
|
||
/** | ||
* @file TaxCloudSettingsForm is a React Component used to change TaxCloud | ||
* settings. | ||
* @module TaxCloudSettingsForm | ||
*/ | ||
|
||
/** | ||
* @method TaxCloudSettingsForm | ||
* @summary renders a form for updating TaxCloud settings. | ||
* @param {Object} props - some data for use by this component. | ||
* @property {Function} handleSubmit - a function for saving new TaxCloud settings. | ||
* @property {Array} hiddenFields - the fields (of the TaxCloud Package) to hide from the form. | ||
* @property {Object} settings - the value of the "settings" field in the TaxCloud Package. | ||
* @property {Object} shownFields - info about the fields the form is to show. | ||
* @since 1.5.2 | ||
* @return {Node} - a React node containing the TaxCloud settings form. | ||
*/ | ||
const TaxCloudSettingsForm = (props) => { | ||
const { handleSubmit, hiddenFields, settings, shownFields } = props; | ||
return ( | ||
<div className="rui taxcloud-settings-form"> | ||
{!settings.taxcloud.apiLoginId && | ||
<div className="alert alert-info"> | ||
<Components.Translation defaultValue="Add API Login ID to enable" i18nkey="admin.taxSettings.taxcloudCredentials" /> | ||
<a href="https://www.taxcloud.com/" target="_blank">TaxCloud</a> | ||
</div> | ||
} | ||
<Form | ||
schema={TaxCloudPackageConfig} | ||
doc={{ settings }} | ||
docPath={"settings.taxcloud"} | ||
name={"settings.taxcloud"} | ||
fields={shownFields} | ||
hideFields={hiddenFields} | ||
onSubmit={handleSubmit} | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
/** | ||
* @name TaxCloudSettingsForm propTypes | ||
* @type {propTypes} | ||
* @param {Object} props - React PropTypes | ||
* @property {Function} handleSubmit - a function that saves new TaxCloud settings. | ||
* @property {Array} hiddenFields - an array of the TaxCloud Package's fields | ||
* to hide from the settings form. | ||
* @property {Object} settings - the value of the "settings" field in the TaxCloud Package. | ||
* @property {Object} shownFields - info about the fields of the TaxCloud Package | ||
* that the settings form will allow users to change. | ||
* @return {Array} React propTypes | ||
*/ | ||
TaxCloudSettingsForm.propTypes = { | ||
handleSubmit: PropTypes.func, | ||
hiddenFields: PropTypes.arrayOf(PropTypes.string), | ||
settings: PropTypes.object, | ||
shownFields: PropTypes.object | ||
}; | ||
|
||
export default TaxCloudSettingsForm; |
1 change: 1 addition & 0 deletions
1
imports/plugins/included/taxes-taxcloud/client/containers/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default as TaxCloudSettingsFormContainer } from "./taxCloudSettingsFormContainer"; |
67 changes: 67 additions & 0 deletions
67
imports/plugins/included/taxes-taxcloud/client/containers/taxCloudSettingsFormContainer.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { compose, withProps } from "recompose"; | ||
import { composeWithTracker, registerComponent } from "@reactioncommerce/reaction-components"; | ||
import { Meteor } from "meteor/meteor"; | ||
import { Reaction, i18next } from "/client/api"; | ||
import { TaxCloudPackageConfig } from "../../lib/collections/schemas"; | ||
import { TaxCloudSettingsForm } from "../components"; | ||
|
||
/** | ||
* @file This is a container for TaxCloudSettingsForm. | ||
* @module taxCloudSettingsFormContainer | ||
*/ | ||
|
||
const handlers = { | ||
/** | ||
* handleSubmit | ||
* @method | ||
* @summary event handler for when new TaxCloud settings are submitted. | ||
* @param {Object} event - event info. | ||
* @param {Object} changedInfo - info about the new TaxCloud settings. | ||
* @param {String} targetField - where to save the new settings in the TaxCloud Package. | ||
* @since 1.5.2 | ||
* @return {null} - returns nothing | ||
*/ | ||
handleSubmit(event, changedInfo, targetField) { | ||
if (!changedInfo.isValid) { | ||
return; | ||
} | ||
Meteor.call("package/update", "taxes-taxcloud", targetField, changedInfo.doc.settings.taxcloud, (error) => { | ||
if (error) { | ||
Alerts.toast( | ||
i18next.t("admin.update.updateFailed", { defaultValue: "Failed to update TaxCloud settings." }), | ||
"error" | ||
); | ||
return; | ||
} | ||
Alerts.toast( | ||
i18next.t("admin.update.updateSucceeded", { defaultValue: "TaxCloud settings updated." }), | ||
"success" | ||
); | ||
}); | ||
} | ||
}; | ||
|
||
const composer = (props, onData) => { | ||
const shownFields = { | ||
["settings.taxcloud.apiKey"]: TaxCloudPackageConfig._schema["settings.taxcloud.apiKey"], | ||
["settings.taxcloud.apiLoginId"]: TaxCloudPackageConfig._schema["settings.taxcloud.apiLoginId"] | ||
}; | ||
const hiddenFields = [ | ||
"settings.taxcloud.enabled", | ||
"settings.taxcloud.refreshPeriod", | ||
"settings.taxcloud.taxCodeUrl" | ||
]; | ||
|
||
const shopId = Reaction.getShopId(); | ||
const packageSub = Meteor.subscribe("Packages", shopId); | ||
if (packageSub.ready()) { | ||
const packageData = Reaction.getPackageSettings("taxes-taxcloud"); | ||
onData(null, { settings: packageData.settings, shownFields, hiddenFields }); | ||
} | ||
}; | ||
|
||
registerComponent("TaxCloudSettingsForm", TaxCloudSettingsForm, [ | ||
withProps(handlers), composeWithTracker(composer) | ||
]); | ||
|
||
export default compose(withProps(handlers), composeWithTracker(composer))(TaxCloudSettingsForm); |
18 changes: 1 addition & 17 deletions
18
imports/plugins/included/taxes-taxcloud/client/settings/taxcloud.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,5 @@ | ||
<template name="taxCloudSettings"> | ||
|
||
{{#unless packageData.settings.taxcloud.apiLoginId}} | ||
<div class="alert alert-info"> | ||
<span data-i18n="admin.taxSettings.taxcloudCredentials">Add API Login ID to enable</span> | ||
<a href="https://www.taxcloud.com/" target="_blank">TaxCloud</a> | ||
</div> | ||
{{/unless}} | ||
|
||
<div> | ||
{{#autoForm collection=Collections.Packages schema=packageConfigSchema doc=packageData type="update" id="taxcloud-update-form"}} | ||
<div class="panel-body"> | ||
{{> afQuickField name='settings.taxcloud.apiLoginId' class='form-control'}} | ||
{{> afQuickField name='settings.taxcloud.apiKey' class='form-control'}} | ||
<!-- {{> afQuickField name='settings.taxcloud.refreshPeriod' class='form-control'}} | ||
{{> afQuickField name='settings.taxcloud.taxCodeUrl' class='form-control'}} --> | ||
</div> | ||
{{> shopSettingsSubmitButton}} | ||
{{/autoForm}} | ||
{{> React taxCloudForm}} | ||
</div> | ||
</template> |
52 changes: 10 additions & 42 deletions
52
imports/plugins/included/taxes-taxcloud/client/settings/taxcloud.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,15 @@ | ||
import { Meteor } from "meteor/meteor"; | ||
import { Template } from "meteor/templating"; | ||
import { AutoForm } from "meteor/aldeed:autoform"; | ||
import { Packages } from "/lib/collections"; | ||
import { TaxCodes } from "/imports/plugins/core/taxes/lib/collections"; | ||
import { Reaction, i18next } from "/client/api"; | ||
import { TaxCloudPackageConfig } from "../../lib/collections/schemas"; | ||
import { TaxCloudSettingsFormContainer } from "../containers"; | ||
|
||
Template.taxCloudSettings.helpers({ | ||
packageConfigSchema() { | ||
return TaxCloudPackageConfig; | ||
}, | ||
packageData() { | ||
return Packages.findOne({ | ||
name: "taxes-taxcloud", | ||
shopId: Reaction.getShopId() | ||
}); | ||
} | ||
}); | ||
|
||
|
||
AutoForm.hooks({ | ||
"taxcloud-update-form": { | ||
onSuccess: function () { | ||
if (!TaxCodes.findOne({ taxCodeProvider: "taxes-taxcloud" })) { | ||
Meteor.call("taxcloud/getTaxCodes", (err, res) => { | ||
if (res && Array.isArray(res)) { | ||
Alerts.toast(i18next.t("admin.taxSettings.shopTaxMethodsSaved"), | ||
"success"); | ||
res.forEach((code) => { | ||
Meteor.call("taxes/insertTaxCodes", Reaction.getShopId(), code, | ||
"taxes-taxcloud"); | ||
}); | ||
} | ||
}); | ||
} else { | ||
Alerts.toast(i18next.t("admin.taxSettings.shopTaxMethodsAlreadySaved"), | ||
"success"); | ||
} | ||
}, | ||
onError: function (operation, error) { | ||
return Alerts.toast( | ||
`${i18next.t("admin.taxSettings.shopTaxMethodsFailed")} ${error}`, | ||
"error"); | ||
} | ||
/** | ||
* @method taxCloudForm | ||
* @summary returns a component for updating the TaxCloud settings for | ||
* this app. | ||
* @since 1.5.2 | ||
* @return {Object} - an object containing the component to render. | ||
*/ | ||
taxCloudForm() { | ||
return { component: TaxCloudSettingsFormContainer }; | ||
} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { Meteor } from "meteor/meteor"; | ||
import { Match } from "meteor/check"; | ||
import { Factory } from "meteor/dburles:factory"; | ||
import { expect } from "meteor/practicalmeteor:chai"; | ||
import { sinon } from "meteor/practicalmeteor:sinon"; | ||
import { Packages } from "/lib/collections"; | ||
import { Reaction } from "/server/api"; | ||
|
||
describe("Update Package", function () { | ||
let sandbox; | ||
|
||
beforeEach(function () { | ||
sandbox = sinon.sandbox.create(); | ||
}); | ||
|
||
afterEach(function () { | ||
sandbox.restore(); | ||
}); | ||
|
||
describe("package/update", function () { | ||
it("should throw an 'Access Denied' error for non-admins", function (done) { | ||
const pkgUpdateSpy = sandbox.spy(Packages, "update"); | ||
const examplePackage = Factory.create("examplePackage"); | ||
|
||
function updatePackage() { | ||
return Meteor.call("package/update", examplePackage.name, "settings", {}); | ||
} | ||
expect(updatePackage).to.throw(Meteor.Error, /Access Denied/); | ||
expect(pkgUpdateSpy).to.not.have.been.called; | ||
|
||
return done(); | ||
}); | ||
|
||
it("should throw an error when supplied with an argument of the wrong type", function (done) { | ||
const pkgUpdateSpy = sandbox.spy(Packages, "update"); | ||
sandbox.stub(Reaction, "getShopId", () => "randomId"); | ||
sandbox.stub(Reaction, "hasPermission", () => true); | ||
|
||
function updatePackage(packageName, field, value) { | ||
return Meteor.call("package/update", packageName, field, value); | ||
} | ||
expect(() => updatePackage([], "someField", { foo: "bar" })).to.throw(Match.Error, /Match error: Expected string, got object/); | ||
expect(() => updatePackage("somePackage", [], { foo: "bar" })).to.throw(Match.Error, /Match error: Expected string, got object/); | ||
expect(() => updatePackage("somePackage", "someField", "")).to.throw(Match.Error, /Match error: Expected object, got string/); | ||
expect(pkgUpdateSpy).to.not.have.been.called; | ||
|
||
return done(); | ||
}); | ||
|
||
it("should be able to update any Package", function (done) { | ||
const packageUpdateSpy = sandbox.spy(Packages, "update"); | ||
const oldPackage = Factory.create("examplePackage"); | ||
|
||
sandbox.stub(Reaction, "getShopId", () => oldPackage.shopId); | ||
sandbox.stub(Reaction, "hasPermission", () => true); | ||
const packageName = oldPackage.name; | ||
const newValues = { | ||
enabled: true, | ||
apiUrl: "http://foo-bar.com/api/v1" | ||
}; | ||
Meteor.call("package/update", packageName, "settings", newValues); | ||
expect(packageUpdateSpy).to.have.been.called; | ||
const updatedPackage = Packages.findOne({ name: packageName }); | ||
expect(oldPackage.settings.enabled).to.not.equal(updatedPackage.settings.enabled); | ||
expect(oldPackage.settings.apiUrl).to.not.equal(updatedPackage.settings.apiUrl); | ||
|
||
return done(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import { Meteor } from "meteor/meteor"; | ||
import { check } from "meteor/check"; | ||
import { Packages } from "/lib/collections"; | ||
import { Reaction } from "/server/api"; | ||
|
||
/** | ||
* @method updatePackage | ||
* @summary updates the data stored for a certain Package. | ||
* @param {String} packageName - the name of the Package to update. | ||
* @param {String} field - the part of the Package's data that is to | ||
* be updated. | ||
* @param {Object} value - the new data that's to be stored for the said | ||
* Package. | ||
* @since 1.5.1 | ||
* @return {Object} - returns an object with info about the update operation. | ||
*/ | ||
export function updatePackage(packageName, field, value) { | ||
check(packageName, String); | ||
check(field, String); | ||
check(value, Object); | ||
|
||
const userId = Meteor.userId(); | ||
const shopId = Reaction.getShopId(); | ||
if (!Reaction.hasPermission([packageName], userId, shopId)) { | ||
throw new Meteor.Error("access-denied", `Access Denied. You don't have permissions for the ${packageName} package.`); | ||
} | ||
|
||
const updateResult = Packages.update({ | ||
name: packageName, | ||
shopId: shopId | ||
}, { | ||
$set: { | ||
[field]: value | ||
} | ||
}); | ||
if (updateResult !== 1) { | ||
throw new Meteor.Error("server-error", `An error occurred while updating the package ${packageName}.`); | ||
} | ||
return updateResult; | ||
} | ||
|
||
Meteor.methods({ | ||
"package/update": updatePackage | ||
}); |