diff --git a/src/ApiClient/BackendApiClient.php b/src/ApiClient/BackendApiClient.php new file mode 100644 index 0000000..5fdaa6c --- /dev/null +++ b/src/ApiClient/BackendApiClient.php @@ -0,0 +1,234 @@ +httpClient === null) { + $this->httpClient = new Client( + [ + 'base_uri' => self::TWEAKWISE_BACKEND_API_BASE_URL + ] + ); + } + + return $this->httpClient; + } + + /** + * @param string $path + * @param Store $store + * @return ResponseInterface + * @throws GuzzleException + * @throws LocalizedException + */ + public function doRequest( + string $path, + Store $store + ): ResponseInterface { + try { + return $this->getHttpClient() + ->request( + 'GET', + $path, + [ + 'headers' => [ + 'TWN-InstanceKey' => $this->config->getGeneralAuthenticationKey($store), + 'TWN-Authentication' => $this->config->getBackendApiToken($store) + ] + ] + ); + } catch (Exception $e) { + $this->logger->critical( + 'Tweakwise Backend API request failed', + [ + 'path' => $path, + 'exception' => $e->getMessage() + ] + ); + throw $e; + } + } + + /** + * @param Store $store + * @return array + */ + public function getAttributes(Store $store): array + { + $cacheKey = $this->getCacheKey('attributes', (int)$store->getId()); + if ($this->cacheExists($cacheKey)) { + return $this->getFromCache($cacheKey); + } + + $attributes = []; + try { + $response = $this->doRequest('attribute', $store); + $contents = $response->getBody()->getContents(); + $result = $this->jsonSerializer->unserialize($contents); + + if (!isset($result['Records'])) { + return []; + } + + foreach ($result['Records'] as $record) { + $attributes[] = [ + 'value' => $record['UrlName'], + 'label' => $record['Name'], + 'id' => $record['Id'] + ]; + } + + $this->cache->save($this->jsonSerializer->serialize($attributes), $cacheKey, [], $this->getCacheLifetime()); + + return $attributes; + } catch (GuzzleException | Exception $e) { + $this->logger->critical( + 'Retrieving attributes from Tweakiwse Backend API failed', + [ + 'exception' => $e->getMessage() + ] + ); + return []; + } + } + + /** + * @param string $attributeCode + * @param Store $store + * @return int|null + */ + public function getAttributeIdByCode(string $attributeCode, Store $store): ?int + { + $attributes = $this->getAttributes($store); + $index = array_search($attributeCode, array_column($attributes, 'value'), true); + + return $index !== false ? (int)$attributes[$index]['id'] : null; + } + + /** + * @param int $attributeId + * @param Store $store + * @return array + */ + public function getAttributeValues(int $attributeId, Store $store): array + { + $cacheKey = $this->getCacheKey('attribute_values', (int)$store->getId(), $attributeId); + if ($this->cacheExists($cacheKey)) { + return $this->getFromCache($cacheKey); + } + + $attributeValues = []; + try { + $response = $this->doRequest(sprintf('attribute/%s/values', $attributeId), $store); + $contents = $response->getBody()->getContents(); + $result = $this->jsonSerializer->unserialize($contents); + + if (!isset($result['Records'])) { + return []; + } + + foreach ($result['Records'] as $record) { + $attributeValues[] = [ + 'value' => $record['Value'], + 'label' => $record['Value'], + ]; + } + + $this->cache->save($this->jsonSerializer->serialize($attributeValues), $cacheKey, [], $this->getCacheLifetime()); + + return $attributeValues; + } catch (GuzzleException | Exception $e) { + $this->logger->critical( + 'Retrieving attribute valus from Tweakiwse Backend API failed', + [ + 'exception' => $e->getMessage() + ] + ); + return []; + } + } + + /** + * @param string $type + * @param int $storeId + * @param int|null $attributeId + * @return string + */ + private function getCacheKey(string $type, int $storeId, ?int $attributeId = null): string + { + if ($attributeId) { + return sprintf('tweakwise_backend_api_result_%s_%s_%s', $type, $attributeId, $storeId); + } + return sprintf('tweakwise_backend_api_result_%s_%s', $type, $storeId); + } + + /** + * @param string $cacheKey + * @return bool + */ + private function cacheExists(string $cacheKey): bool + { + /** @phpstan-ignore-next-line */ + return $this->cache->load($cacheKey) !== false; + } + + /** + * @param string $cacheKey + * @return array + */ + private function getFromCache(string $cacheKey): array + { + return (array)$this->jsonSerializer->unserialize($this->cache->load($cacheKey)); + } + + /** + * Protected function added so that cache lifetime is overwritable + * @return int + */ + protected function getCacheLifetime(): int + { + return self::CACHE_LIFETIME; + } +} diff --git a/src/Controller/Adminhtml/Ajax/AbstractFacetController.php b/src/Controller/Adminhtml/Ajax/AbstractFacetController.php new file mode 100644 index 0000000..79f6020 --- /dev/null +++ b/src/Controller/Adminhtml/Ajax/AbstractFacetController.php @@ -0,0 +1,39 @@ +config->getBackendApiToken($store); + } +} diff --git a/src/Controller/Adminhtml/Ajax/FacetAttributes.php b/src/Controller/Adminhtml/Ajax/FacetAttributes.php new file mode 100644 index 0000000..903b178 --- /dev/null +++ b/src/Controller/Adminhtml/Ajax/FacetAttributes.php @@ -0,0 +1,145 @@ +resultJsonFactory->create(); + $facetKey = $this->request->getParam('facet_key'); + $otherAttributeOption = ['value' => self::OTHER_ATTRIBUTE_VALUE, 'label' => 'Other (text field)']; + + if ($facetKey === self::OTHER_ATTRIBUTE_VALUE) { + return $result->setData([$otherAttributeOption]); + } + + $attributeValues = []; + /** @var Store $store */ + foreach ($this->storeManager->getStores() as $store) { + if ($this->isBackendApiEnabled($store)) { + $attributeValues = array_merge($this->executeBackendApiRequest($store, $facetKey), $attributeValues); + continue; + } + + $attributeValues = array_merge($this->executeDefaultRequest((int)$store->getId(), $facetKey), $attributeValues); + } + + $attributeValues[] = $otherAttributeOption; + + $attributeValues = array_values(array_unique($attributeValues, SORT_REGULAR)); + + return $result->setData($attributeValues); + } + + /** + * @param int $storeId + * @param string|null $facetKey + * @return array + * @throws Exception + */ + private function executeDefaultRequest(int $storeId, ?string $facetKey): array + { + $facetAttributeRequest = $this->requestFactory->create(); + + $filterTemplate = $this->request->getParam('filter_template'); + if ($filterTemplate) { + $facetAttributeRequest->setParameter('tn_ft', $filterTemplate); + } + + if ($facetKey && $facetAttributeRequest instanceof FacetAttributeRequest) { + $facetAttributeRequest->addFacetKey($facetKey); + } + + $categoryId = $this->helper->getTweakwiseId( + $storeId, + (int) $this->request->getParam('category_id') + ); + if ($categoryId) { + $facetAttributeRequest->setParameter('tn_cid', $categoryId); + } + + /** @var FacetAttributesResponse $response */ + $response = $this->client->request($facetAttributeRequest); + + if (!$response->getAttributes()) { + return []; + } + + $attributes = []; + foreach ($response->getAttributes() as $attribute) { + $attributes[] = [ + 'value' => $attribute['title'], + 'label' => $attribute['title'] + ]; + } + + return $attributes; + } + + /** + * @param Store $store + * @param string|null $facetKey + * @return array + */ + private function executeBackendApiRequest(Store $store, ?string $facetKey): array + { + if (!$facetKey) { + return []; + } + + $attributeId = $this->backendApiClient->getAttributeIdByCode($facetKey, $store); + if (!$attributeId) { + return []; + } + + return $this->backendApiClient->getAttributeValues($attributeId, $store); + } +} diff --git a/src/Controller/Adminhtml/Ajax/Facets.php b/src/Controller/Adminhtml/Ajax/Facets.php new file mode 100644 index 0000000..45feecf --- /dev/null +++ b/src/Controller/Adminhtml/Ajax/Facets.php @@ -0,0 +1,117 @@ +resultJsonFactory->create(); + + $facets = []; + /** @var Store $store */ + foreach ($this->storeManager->getStores() as $store) { + if ($this->isBackendApiEnabled($store)) { + $facets = array_merge($this->executeBackendApiRequest($store), $facets); + continue; + } + + $facets = array_merge($this->executeDefaultRequest((int)$store->getId()), $facets); + } + + $facets[] = ['value' => self::OTHER_ATTRIBUTE_VALUE, 'label' => 'Other (text field)']; + + $facets = array_values(array_unique($facets, SORT_REGULAR)); + + return $result->setData($facets); + } + + /** + * @param int $storeId + * @return array + * @throws Exception + */ + private function executeDefaultRequest(int $storeId): array + { + $facetRequest = $this->requestFactory->create(); + + $filterTemplate = $this->request->getParam('filter_template'); + if ($filterTemplate) { + $facetRequest->setParameter('tn_ft', $filterTemplate); + } + + $categoryId = $this->helper->getTweakwiseId( + $storeId, + (int) $this->request->getParam('category_id') + ); + if ($categoryId) { + $facetRequest->setParameter('tn_cid', $categoryId); + } + + /** @var FacetResponse $response */ + $response = $this->client->request($facetRequest); + + $facets = []; + foreach ($response->getFacets() as $facet) { + $facets[] = [ + 'value' => $facet->getFacetSettings()->getUrlKey(), + 'label' => $facet->getFacetSettings()->getTitle() + ]; + } + + return $facets; + } + + /** + * @param Store $store + * @return array + */ + private function executeBackendApiRequest(Store $store): array + { + return $this->backendApiClient->getAttributes($store); + } +} diff --git a/src/Model/Client/Request/FacetAttributeRequest.php b/src/Model/Client/Request/FacetAttributeRequest.php new file mode 100644 index 0000000..7b05be0 --- /dev/null +++ b/src/Model/Client/Request/FacetAttributeRequest.php @@ -0,0 +1,33 @@ +setPath(sprintf('%s/%s/attributes', $this->path, $facetKey)); + } +} diff --git a/src/Model/Client/Request/FacetRequest.php b/src/Model/Client/Request/FacetRequest.php new file mode 100644 index 0000000..4b3e49d --- /dev/null +++ b/src/Model/Client/Request/FacetRequest.php @@ -0,0 +1,24 @@ +data['attributes'] ?? []; + } + + /** + * @param array $attributesData + * @return $this + */ + public function setAttributes(array $attributesData): FacetAttributesResponse + { + $attributesData = $this->normalizeArray($attributesData, 'attributes'); + + foreach ($attributesData as $attributeData) { + $attribute = $this->attributeTypeFactory->create()->setData($attributeData); + + $attributes = $attribute->getValue('attribute'); + + if (isset($attributes[0])) { + $this->data['attributes'] = $attribute->getValue('attribute'); + } else { + // Only one result + $this->data['attributes'][] = $attribute->getValue('attribute'); + } + + break; + } + + return $this; + } +} diff --git a/src/Model/Client/Response/FacetResponse.php b/src/Model/Client/Response/FacetResponse.php new file mode 100644 index 0000000..680069b --- /dev/null +++ b/src/Model/Client/Response/FacetResponse.php @@ -0,0 +1,63 @@ +data['facets'] ?? []; + } + + /** + * @param array $facetsData + * @return $this + */ + public function setFacets(array $facetsData): FacetResponse + { + $facetsData = $this->normalizeArray($facetsData, 'facet'); + + $facets = []; + foreach ($facetsData as $facetData) { + $facet = $this->facetTypeFactory->create()->setData($facetData); + + // Remove tree, link and slider facets + if ( + $facet->getFacetSettings()->getSelectionType() !== 'checkbox' && + $facet->getFacetSettings()->getSelectionType() !== 'color' + ) { + continue; + } + + $facets[] = $facet; + } + + $this->data['facets'] = $facets; + return $this; + } +} diff --git a/src/Model/Config.php b/src/Model/Config.php new file mode 100644 index 0000000..0773053 --- /dev/null +++ b/src/Model/Config.php @@ -0,0 +1,24 @@ +getStoreConfig(self::BACKEND_API_TOKEN_PATH, $store); + } +} diff --git a/src/Plugin/Model/LandingPagePlugin.php b/src/Plugin/Model/LandingPagePlugin.php new file mode 100644 index 0000000..995183a --- /dev/null +++ b/src/Plugin/Model/LandingPagePlugin.php @@ -0,0 +1,31 @@ + $filterAttribute) { + if ($filterAttribute['attribute'] !== AbstractFacetController::OTHER_ATTRIBUTE_VALUE) { + continue; + } + + $result[$key]['attribute'] = $filterAttribute['attribute_other']; + $result[$key]['value'] = $filterAttribute['attribute_value_other']; + } + + return $result; + } +} diff --git a/src/etc/adminhtml/di.xml b/src/etc/adminhtml/di.xml new file mode 100644 index 0000000..c90cbb2 --- /dev/null +++ b/src/etc/adminhtml/di.xml @@ -0,0 +1,35 @@ + + + + + + Tweakwise\AttributeLandingTweakwise\Model\Client\Request\FacetRequest + + + + + + + Tweakwise\AttributeLandingTweakwise\Model\Client\Request\FacetAttributeRequest + + + + + + + + Tweakwise\AttributeLandingTweakwise\Model\Client\RequestFactory\FacetRequest + + + + + + + Tweakwise\AttributeLandingTweakwise\Model\Client\RequestFactory\FacetAttributeRequest + + + + diff --git a/src/etc/adminhtml/routes.xml b/src/etc/adminhtml/routes.xml new file mode 100644 index 0000000..8f123ff --- /dev/null +++ b/src/etc/adminhtml/routes.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/src/etc/adminhtml/system.xml b/src/etc/adminhtml/system.xml new file mode 100644 index 0000000..bc0682c --- /dev/null +++ b/src/etc/adminhtml/system.xml @@ -0,0 +1,20 @@ + + + +
+ + + + + OPTIONAL: Enter the backend API token only if you want to use the Tweakwise backend API to + retrieve attributes when creating attribute landing pages. + + Magento\Config\Model\Config\Backend\Encrypted + + +
+
+
diff --git a/src/etc/config.xml b/src/etc/config.xml new file mode 100644 index 0000000..3ff8eca --- /dev/null +++ b/src/etc/config.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/etc/di.xml b/src/etc/di.xml index ac7fa49..ea5d727 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -68,4 +68,9 @@ + + + + diff --git a/src/view/adminhtml/ui_component/emico_attributelanding_page_form.xml b/src/view/adminhtml/ui_component/emico_attributelanding_page_form.xml index be6931e..ddc501b 100644 --- a/src/view/adminhtml/ui_component/emico_attributelanding_page_form.xml +++ b/src/view/adminhtml/ui_component/emico_attributelanding_page_form.xml @@ -1,127 +1,29 @@
-
- - - Emico\AttributeLanding\Ui\Component\Product\Form\Categories\Options - - field - Tweakwise_AttributeLandingTweakwise/js/category-select - ui/grid/filters/elements/ui-select - true - false - true - false - 1 - 20 - true - - setParsed - - - - - - text - - category_id - - false - - - - - - - - - - Add Value - - false - true - - dynamicRows - - false - - - - - - true - true - Magento_Ui/js/dynamic-rows/record - container - filter_attributes - - - - - - false - Attribute - text - Tweakwise_AttributeLandingTweakwise/attributes - - - - - - - false - Value - text - Tweakwise_AttributeLandingTweakwise/attribute_values - - - - +
+ + + + - attribute - option_ - false - text - attribute - false + attribute_other - + + - value - option_ - false - text - value - false + attribute_value_other - - - - true - option_ - Params.delete - text - - - - text - actionDelete - - - + + - + Page @@ -149,7 +51,7 @@ - + Page @@ -177,7 +79,7 @@ - + Page @@ -200,5 +102,5 @@ -
+
diff --git a/src/view/adminhtml/web/js/category-select.js b/src/view/adminhtml/web/js/category-select.js deleted file mode 100644 index a39743a..0000000 --- a/src/view/adminhtml/web/js/category-select.js +++ /dev/null @@ -1,64 +0,0 @@ -define([ - 'Magento_Ui/js/form/element/ui-select', - 'jquery' -], function (Select, $) { - 'use strict'; - - return Select.extend({ - - initialize: function () { - this._super(); - this.onUpdate(this.value()); - return this; - }, - - /** - * Parse data and set it to options. - * - * @param {Object} data - Response data object. - * @returns {Object} - */ - setParsed: function (data) { - var option = this.parseData(data); - - if (data.error) { - return this; - } - - this.options([]); - this.setOption(option); - this.set('newOption', option); - }, - - /** - * Normalize option object. - * - * @param {Object} data - Option object. - * @returns {Object} - */ - parseData: function (data) { - return { - 'is_active': data.category['is_active'], - level: data.category.level, - value: data.category['entity_id'], - label: data.category.name, - parent: data.category.parent - }; - }, - - /** - * Change currently selected option - * - * @param {String} id - */ - onUpdate: function(value){ - setTimeout(function() { - if((value == '')||(value == undefined)) { - $('.filter_attributes').hide(); - } else { - $('.filter_attributes').show(); - } - }, 1000); - }, - }); -}); diff --git a/src/view/adminhtml/web/js/dynamic-rows/dynamic-rows.js b/src/view/adminhtml/web/js/dynamic-rows/dynamic-rows.js new file mode 100644 index 0000000..0f78c28 --- /dev/null +++ b/src/view/adminhtml/web/js/dynamic-rows/dynamic-rows.js @@ -0,0 +1,39 @@ +define([ + 'Magento_Ui/js/dynamic-rows/dynamic-rows', + 'uiRegistry', + 'ko' +], function (DynamicRows, registry, ko) { + 'use strict'; + + return DynamicRows.extend({ + /** + * @returns {Object} + */ + initialize: function () { + this._super(); + + this.visible = ko.observable(false); + + registry.get(`${this.parentName}.category_id`, function (categoryField) { + this.updateVisibility(categoryField.value()); + + categoryField.value.subscribe(this.updateVisibility.bind(this)); + }.bind(this)); + + return this; + }, + + /** + * Only show filter_attributes rows when a category is selected + * @param {Array|String} categoryValue + */ + updateVisibility: function (categoryValue) { + if (Array.isArray(categoryValue)) { + this.visible(false); + return; + } + + this.visible(true); + } + }); +}); diff --git a/src/view/adminhtml/web/js/filter-attribute-values.js b/src/view/adminhtml/web/js/filter-attribute-values.js deleted file mode 100644 index 2a895c5..0000000 --- a/src/view/adminhtml/web/js/filter-attribute-values.js +++ /dev/null @@ -1,39 +0,0 @@ -define([ - 'jquery', - 'uiRegistry', - 'mage/url', -], function($, registery, url) { - 'use strict'; - - $.widget('tweakwise.filter_attribute_values', { - /** - * Bind handlers to events - */ - _create: function(config, element) { - this._bindEvents(this.options.name); - }, - - _bindEvents(name) { - var input = $('input[name="' + name.replace('[value-tmp]', '[value]') + '"]'); - input.hide(); - - $('select[name="' + name + '"]').on('change', function(evt) { - var name = evt.target.name; - var facetValue = evt.target.value; - var category_id = registery.get('emico_attributelanding_page_form.emico_attributelanding_page_form.general.category_id').value(); - var select = $('select[name="' + name + '"]'); - var input = $('input[name="' + name.replace('[value-tmp]', '[value]') + '"]'); - - if (select.val() == 'tw_other') { - input.show(); - } else { - input.hide(); - input.val(select.val()); - input.change(); - } - }); - } - }); - - return $.tweakwise.filter_attribute_values; -}); diff --git a/src/view/adminhtml/web/js/filter-attributes.js b/src/view/adminhtml/web/js/filter-attributes.js deleted file mode 100644 index 8223132..0000000 --- a/src/view/adminhtml/web/js/filter-attributes.js +++ /dev/null @@ -1,128 +0,0 @@ -define([ - 'jquery', - 'uiRegistry', - 'mage/url', -], function($, registery, url) { - 'use strict'; - - $.widget('tweakwise.filter_attributes', { - /** - * Bind handlers to events - */ - _create: function(config, element) { - this._bindEvents(this.options.name); - this._initAttributes(this.options.name); - }, - - _initAttributes: function (name) { - //bind to event. So it's not triggered by the user, but can be triggered in the code. - $('select[name="' + name + '"]').on('initAttributes', function(evt) { - var name = evt.target.name; - var category_id = registery.get('emico_attributelanding_page_form.emico_attributelanding_page_form.general.category_id').value(); - var selectAttribute = $('select[name="' + name + '"]'); - var inputAttribute = $('input[name="' + name.replace('[attribute-tmp]', '[attribute]') + '"]'); - var inputValue = $('input[name="' + name.replace('[attribute-tmp]', '[value]') + '"]'); - var selectedValue = inputAttribute.val(); - var templateValue = $('select[name="tweakwise_filter_template"]'); - var facetUrl = url.build('/tweakwise/ajax/facets/category/' + category_id); - var foundSelectedValue = false; - - if (templateValue.val() !== '') { - facetUrl += '/filtertemplate/' + templateValue.val(); - } - - inputAttribute.hide(); - inputValue.hide(); - - $.getJSON(facetUrl, function( data ) { - selectAttribute.empty(); - data.data.forEach(value => { - if(value.value != selectedValue) { - selectAttribute.append($("").attr("value", value.value).text(value.label)); - } else { - foundSelectedValue = true; - selectAttribute.append($("").attr("value", value.value).text(value.label).attr("selected", "selected")); - } - }); - - if (foundSelectedValue === false && selectedValue) { - selectAttribute.val('tw_other'); - } else { - //no default value, set value to first option - inputAttribute.val(selectAttribute.val()); - } - selectAttribute.trigger('change'); - }); - }); - - $('select[name="' + name + '"]').trigger('initAttributes'); - }, - - _bindEvents(name) { - $('select[name="' + name + '"]').on('change', function(evt) { - var name = evt.target.name; - var facetValue = evt.target.value; - var category_id = registery.get('emico_attributelanding_page_form.emico_attributelanding_page_form.general.category_id').value(); - var inputValue = $('input[name="' + name.replace('[attribute-tmp]', '[value]') + '"]'); - var inputAttribute = $('input[name="' + name.replace('[attribute-tmp]', '[attribute]') + '"]'); - var selectValue = $('select[name="' + name.replace('[attribute-tmp]', '[value-tmp]') + '"]'); - var templateValue = $('select[name="tweakwise_filter_template"]'); - var facetUrl = url.build('/tweakwise/ajax/facetattributes/category/' + category_id + '/facetkey/' + facetValue); - var foundSelectedValue = false; - - if (templateValue.val() !== '') { - facetUrl += '/filtertemplate/' + templateValue.val(); - } - - //no value selected, load initial value - if (!facetValue) { - facetValue = inputAttribute.val(); - } - - if (facetValue == 'tw_other') { - inputAttribute.show(); - selectValue.val('tw_other'); - inputValue.show(); - } else { - inputAttribute.hide(); - selectValue.val(facetValue); - if (selectValue.val() != 'tw_other') { - inputValue.hide(); - inputAttribute.val(facetValue); - } else { - inputValue.show(); - } - } - - inputAttribute.change(); - - $.getJSON(facetUrl, function (data) { - selectValue.empty(); - data.data.forEach(value => { - if (value.value != inputValue.val()) { - selectValue.append($("").attr("value", value.value).text(value.label)); - } else { - foundSelectedValue = true; - selectValue.append($("").attr("value", value.value).text(value.label).attr("selected", "selected")); - } - }); - - if (foundSelectedValue === false && inputValue.val()) { - selectValue.val('tw_other'); - } else { - //no default value, set value to first option - inputValue.val(selectValue.val()); - inputValue.change(); - } - }); - }); - - //select different filter template - $('select[name="tweakwise_filter_template"]').on('change', function(evt) { - $('select[name*="[attribute-tmp]"]').trigger('initAttributes'); - }); - } - }); - - return $.tweakwise.filter_attributes; -}); diff --git a/src/view/adminhtml/web/js/form/element/filter-attribute-value.js b/src/view/adminhtml/web/js/form/element/filter-attribute-value.js new file mode 100644 index 0000000..f685c2d --- /dev/null +++ b/src/view/adminhtml/web/js/form/element/filter-attribute-value.js @@ -0,0 +1,82 @@ +define([ + 'Magento_Ui/js/form/element/select', + 'jquery', + 'mage/url', + 'uiRegistry' +], function (Select, $, urlBuilder, registry) { + return Select.extend({ + attributeFieldName: 'attribute', + otherFieldName: 'attribute_value_other', + otherValue: 'tw_other', + initialize: function () { + this._super(); + this.savedValue = this.value(); + this.subscribeAttributeValue(); + + return this; + }, + + subscribeAttributeValue: function () { + this.value.subscribe(function (newAttributeValue) { + this.setOtherFieldVisibility(newAttributeValue); + }.bind(this)); + }, + + setInitialValue: function () { + return this; + }, + + initFromAttribute: function (attribute) { + const currentValue = this.value() ? this.value() : this.savedValue; + this.fetchOptions(attribute).then(() => { + this.restoreValue(currentValue); + this.setOtherFieldVisibility(this.value()) + }); + }, + + setOtherFieldVisibility: function (selectedAttributeValue, otherFieldValue = null) { + registry.get(`${this.parentName}.${this.otherFieldName}`, function (otherField) { + const otherFieldVisible = selectedAttributeValue === this.otherValue + otherField.disabled(!otherFieldVisible); + if (selectedAttributeValue && !otherFieldVisible) { + otherField.value(''); + } else if (otherFieldValue) { + otherField.value(otherFieldValue); + } + }.bind(this)); + }, + + restoreValue: function (valueToRestore) { + const optionExists = this.options().some(function (option) { + return option.value === valueToRestore; + }.bind(this)); + + if (optionExists) { + this.value(valueToRestore); + } else { + this.value(this.otherValue); + this.setOtherFieldVisibility(this.otherValue, valueToRestore); + } + }, + + fetchOptions: function (attribute) { + const url = `${this.source.get('admin_url')}tweakwise/ajax/facetattributes`; + const formKey = $('[name="form_key"]').val(); + const filterTemplate = this.source.get('data.tweakwise_filter_template'); + const categoryId = this.source.get('data.category_id'); + + return $.ajax({ + url: url, + type: 'POST', + data: { + form_key: formKey, + category_id: categoryId, + filter_template: filterTemplate, + facet_key: attribute + } + }).done(function (response) { + this.options(response); + }.bind(this)); + } + }); +}); diff --git a/src/view/adminhtml/web/js/form/element/filter-attribute.js b/src/view/adminhtml/web/js/form/element/filter-attribute.js new file mode 100644 index 0000000..830a7d1 --- /dev/null +++ b/src/view/adminhtml/web/js/form/element/filter-attribute.js @@ -0,0 +1,139 @@ +define([ + 'Magento_Ui/js/form/element/select', + 'jquery', + 'mage/url', + 'uiRegistry' +], function (Select, $, urlBuilder, registry) { + return Select.extend({ + attributeValueFieldName: 'value', + otherFieldName: 'attribute_other', + otherValue: 'tw_other', + initialize: function () { + this._super(); + + this.savedValue = this.value(); + this.value(''); + + this.setOtherFieldVisibility(this.savedValue); + + this.fetchOptions().then(function () { + this.setRestoredValue(); + this.subscribeAttribute(); + this.subscribeFilterTemplate(); + this.subscribeCategoryId(); + this.initAttributeValueField(this.value()); + }.bind(this)); + + return this; + }, + + setInitialValue: function () { + return this; + }, + + initObservable: function () { + this._super().observe(['value']); + return this; + }, + + subscribeAttribute: function () { + this.value.subscribe(function (newAttribute) { + this.setOtherFieldVisibility(newAttribute); + this.initAttributeValueField(newAttribute); + }.bind(this)); + }, + + subscribeCategoryId: function () { + const categoryIdPath = 'emico_attributelanding_page_form.emico_attributelanding_page_form.general.category_id'; + registry.get(categoryIdPath, (categoryField) => { + categoryField.value.subscribe((newCategoryId) => { + const currentAttributeValue = this.value(); + this.fetchOptions().then(() => { + this.restoreValue(currentAttributeValue); + }); + }); + }); + }, + + subscribeFilterTemplate: function () { + const filterTemplatePath = 'emico_attributelanding_page_form.emico_attributelanding_page_form.general.tweakwise_filter_template'; + registry.get(filterTemplatePath, (filterTemplateField) => { + filterTemplateField.value.subscribe((newFilterTemplate) => { + const currentAttributeValue = this.value(); + this.fetchOptions().then(() => { + this.restoreValue(currentAttributeValue); + }); + }); + }); + }, + + setOtherFieldVisibility: function (selectedAttribute, otherFieldValue = null) { + registry.get(`${this.parentName}.${this.otherFieldName}`, function (otherField) { + const otherFieldVisible = selectedAttribute === this.otherValue + otherField.disabled(!otherFieldVisible); + if (!otherFieldVisible) { + otherField.value(''); + } else if (otherFieldValue) { + otherField.value(otherFieldValue); + } + }.bind(this)); + }, + + setRestoredValue: function () { + if (!this.savedValue) { + return; + } + + const optionExists = this.options().some(function (option) { + return option.value === this.savedValue; + }.bind(this)); + + if (optionExists) { + this.value(this.savedValue); + } else { + this.value(this.otherValue); + this.setOtherFieldVisibility(this.otherValue, this.savedValue); + } + }, + + restoreValue: function (valueToRestore) { + const optionExists = this.options().some(function (option) { + return option.value === valueToRestore; + }.bind(this)); + + if (optionExists) { + this.value(valueToRestore); + } else { + const firstOption = this.options()[0]; + if (firstOption) { + this.value(firstOption.value); + } + } + }, + + fetchOptions: function () { + const url = `${this.source.get('admin_url')}tweakwise/ajax/facets`; + const formKey = $('[name="form_key"]').val(); + const filterTemplate = this.source.get('data.tweakwise_filter_template'); + const categoryId = this.source.get('data.category_id'); + + return $.ajax({ + url: url, + type: 'POST', + data: { + form_key: formKey, + category_id: categoryId, + filter_template: filterTemplate + } + }).done(function (response) { + this.options(response); + }.bind(this)); + }, + + initAttributeValueField: function (attribute) { + registry.get(`${this.parentName}.${this.attributeValueFieldName}`, (attributeValueField) => { + attributeValueField.initFromAttribute(attribute); + }); + } + }); +}); diff --git a/src/view/adminhtml/web/template/attribute_values.html b/src/view/adminhtml/web/template/attribute_values.html deleted file mode 100644 index 374e045..0000000 --- a/src/view/adminhtml/web/template/attribute_values.html +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/src/view/adminhtml/web/template/attributes.html b/src/view/adminhtml/web/template/attributes.html deleted file mode 100644 index bdbff82..0000000 --- a/src/view/adminhtml/web/template/attributes.html +++ /dev/null @@ -1,16 +0,0 @@ -