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

Store level merchandiser feature #1247

Merged
merged 1 commit into from
Jan 16, 2019
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
5 changes: 4 additions & 1 deletion src/module-elasticsuite-catalog/i18n/de_DE.csv
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,8 @@ Pinned,Angeheftet
Products,Produkte
Categories,Kategorien
Attributes,Attribute
"Refine search","Suche einschränken"
"Clear search","Suche abbrechen"
"Your search returned no results.","Ihre Suche ergab keine Treffer."
"Your search returned no results.","Ihre Suche ergab keine Treffer."
"Clear product positions","Produktpositionen löschen"
"Clear all products positions and blacklist status ? All products will be reset to be visible and in 'Automatic Sort'.","Alle Produktpositionen und Blacklist-Status löschen ? Alle Produkte werden auf 'Sichtbar' und 'Automatische Sortierung' zurückgesetzt."
5 changes: 4 additions & 1 deletion src/module-elasticsuite-catalog/i18n/en_US.csv
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,8 @@ Pinned,Pinned
Products,Products
Categories,Categories
Attributes,Attributes
"Refine search","Refine search"
"Clear search","Clear search"
"Your search returned no results.","Your search returned no results."
"Your search returned no results.","Your search returned no results."
"Clear product positions","Clear product positions"
"Clear all products positions and blacklist status ? All products will be reset to be visible and in 'Automatic Sort'.","Clear all products positions and blacklist status ? All products will be reset to be visible and in 'Automatic Sort'."
3 changes: 3 additions & 0 deletions src/module-elasticsuite-catalog/i18n/fr_FR.csv
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,8 @@ No,Non
Products,Produits
Categories,Catégories
Attributes,Attributs
"Refine search","Affiner votre recherche"
"Clear search","Annuler la recherche"
"Your search returned no results.","Votre recherche n'a donné aucun résultat."
"Clear product positions","Réinitialiser les positions"
"Clear all products positions and blacklist status ? All products will be reset to be visible and in 'Automatic Sort'.","Réinitialiser les positions et le masquage des produits ? Tous les produits seront rétablis en 'Tri Automatique' et visibles."
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
<item name="allowSearch" xsi:type="boolean">false</item>
<item name="messages" xsi:type="array">
<item name="emptyText" xsi:type="string" translate="true"><![CDATA[No search result for the current search.]]></item>
<item name="resetAllQuestionText" xsi:type="string" translate="true"><![CDATA[Clear all products positions and blacklist status ? All products will be reset to be visible and in 'Automatic Sort'.]]></item>
</item>
<item name="imports" xsi:type="array">
<item name="loadUrl" xsi:type="string">${ $.provider }:data.product_sorter_load_url</item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
a.action-reset {
margin: 0 1rem;
}
.admin__control-text {
width: auto;
}
}

.bottom-links {
Expand All @@ -50,9 +53,22 @@
margin: 20px 100px 20px 100px;
}

.product-list-container {
position: relative;

.admin__data-grid-loading-mask {
position: absolute;
}
}

button.reset {
float: right;
}

.product-list {

margin: 20px 0 0;
margin: 0;
display: inline-block;

li {
box-sizing: content-box;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ define([
'Magento_Ui/js/form/element/abstract',
'jquery',
'Smile_ElasticsuiteCatalog/js/form/element/product-sorter/item',
'Magento_Ui/js/modal/confirm',
'MutationObserver',
'ko'
], function (Component, $, Product, MutationObserver, ko) {
], function (Component, $, Product, confirm, MutationObserver, ko) {
'use strict';

return Component.extend({
Expand All @@ -30,24 +31,37 @@ define([
maxRefreshInterval: 1000,
imports: {
formData: "${ $.provider }:data",
blacklistedProducts: "${ $.provider }:data.blacklisted_products"
blacklistedProducts: "${ $.provider }:data.blacklisted_products",
defaultBlacklistedProducts: "${ $.provider }:data.default.blacklisted_products",
defaultSortedProducts: "${ $.provider }:data.default.sorted_products"
},
links: {
blacklistedProducts: "${ $.provider }.data.blacklisted_products"
blacklistedProducts: "${ $.provider }.data.blacklisted_products",
defaultBlacklistedProducts: "${ $.provider }:data.default.blacklisted_products",
defaultSortedProducts: "${ $.provider }:data.default.sorted_products"
},
messages : {
emptyText : $.mage.__('Your product selection is empty.'),
automaticSort : $.mage.__('Automatic Sort'),
manualSort : $.mage.__('Manual Sort'),
showMore : $.mage.__('Show more'),
search : $.mage.__('Search'),
searchLabel : $.mage.__('Refine search'),
clearSearch : $.mage.__('Clear search'),
noResultsText : $.mage.__('Your search returned no results.'),
showMore : $.mage.__('Show more')
previewOnlyModeText : $.mage.__('Preview Only Mode'),
resetAllText : $.mage.__('Clear product positions'),
resetAllQuestionText : $.mage.__('Clear all products positions and blacklist status ?')
},
forceLoading : false,
allowBlacklist : false,
allowSearch: false,
previewOnlyMode : false,
blacklistedProducts: [],
defaultSortedProducts: "{}",
defaultBlacklistedProducts: [],
storeSortedProducts: "{}",
storeBlacklistedProducts: [],
modules: {
provider: '${ $.provider }'
}
Expand All @@ -65,8 +79,10 @@ define([
this.currentSize = this.pageSize;
this.enabled = this.loadUrl != null;
this.search = ko.observable("");
this.previewOnlyMode = (this.scopeSwitcher != null) && (parseInt(this.scopeSwitcher, 10) == 0) && (this.formData.store_id != 0);
this.initialSwitchCopy = this.previewOnlyMode;

this.observe(['products', 'countTotalProducts', 'currentSize', 'editPositions', 'loading', 'showSpinner', 'blacklistedProducts']);
this.observe(['products', 'countTotalProducts', 'currentSize', 'editPositions', 'loading', 'showSpinner', 'blacklistedProducts', 'previewOnlyMode']);

this.editPositions.subscribe(function () { this.value(JSON.stringify(this.editPositions())); }.bind(this));

Expand Down Expand Up @@ -100,6 +116,58 @@ define([
}
},

switchScope: function(useStorePositions) {
if (parseInt(useStorePositions, 10) == 0) {
// Backup current store level positions and blacklist.
this.storeSortedProducts = JSON.stringify(this.editPositions());
this.storeBlacklistedProducts = this.blacklistedProducts().slice(0);
// Switch positions and blacklist.
this.editPositions = JSON.parse(this.defaultSortedProducts);
this.blacklistedProducts(this.defaultBlacklistedProducts.slice(0));
} else {
if (this.initialSwitchCopy) {
// Copy current (default) positions and blacklist to store level.
this.storeSortedProducts = JSON.stringify(this.editPositions());
this.storeBlacklistedProducts = this.blacklistedProducts().slice(0);
this.initialSwitchCopy = false;
}
// Restore store level positions and blacklist.
this.editPositions = JSON.parse(this.storeSortedProducts);
this.blacklistedProducts(this.storeBlacklistedProducts.slice(0));
}
// Recreate required observers/subscriptions.
this.observe(['editPositions']);
this.editPositions.subscribe(function () { this.value(JSON.stringify(this.editPositions())); }.bind(this));

this.previewOnlyMode(!this.previewOnlyMode());

this.refreshProductList();
},

resetAllProducts: function() {
confirm({
content: this.messages.resetAllQuestionText,
actions: {
/**
* Confirm action.
*/
confirm: function () {
this.editPositions = JSON.parse("{}");
// Recreate required observers/subscriptions.
this.observe(['editPositions']);
this.editPositions.subscribe(function () { this.value(JSON.stringify(this.editPositions())); }.bind(this));

this.blacklistedProducts([]);
this.provider().data['blacklisted_products'] = this.blacklistedProducts();

this.refreshProductList();
}.bind(this)
}
});

return false;
},

refreshProductList: function () {
if (this.refreshRateLimiter !== undefined) {
clearTimeout();
Expand Down Expand Up @@ -243,6 +311,9 @@ define([
},

toggleSortType: function (product) {
if (this.previewOnlyMode()) {
return;
}
var products = this.products();
var editPositions = this.editPositions();

Expand Down Expand Up @@ -275,6 +346,9 @@ define([
},

toggleBlackListed: function(product) {
if (this.previewOnlyMode()) {
return;
}
var state = !product.isBlacklisted();
product.setIsBlacklisted(state);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,51 +10,68 @@

<span class="title" data-bind="text: label"></span>

<div class="elasticsuite-admin-product-sorter-preview-mode" data-bind="if: hasProducts()">
<div class="message message-info" visible="previewOnlyMode">
<div data-bind="html: messages.previewOnlyModeText"></div>
</div>
</div>

<div class="admin__field" data-bind="if: allowSearch && (hasProducts() || hasSearch())">
<label class="admin__field-label" data-bind="text: messages.searchLabel"></label>
<div class="admin__field-control">
<p class="top-search">
<input type="text" id="elasticsuite-preview-search" class="admin__control-text" data-bind="textInput : search, event: {keypress: enterSearch}"/>
<a href="#" data-bind="click: searchProducts, text: messages.search" class="action-default action-search"></a>
<a href="#" data-bind="click: resetSearch, text: messages.clearSearch, visible: search" class="action-reset"></a>
</p>
</div>
</div>

<div class="elasticsuite-admin-product-sorter-empty" data-bind="if: !hasProducts()">
<div class="message message-info">
<div data-bind="html: messages.emptyText" visible="!hasSearch()"></div>
<div data-bind="html: messages.noResultsText" visible="hasSearch()"></div>
</div>
</div>

<p class="top-search" data-bind="if: allowSearch && (hasProducts() || hasSearch())">
<input type="text" id="elasticsuite-preview-search" class="admin__control-text" data-bind="textInput : search, event: {keypress: enterSearch}"/>
<a href="#" data-bind="click: searchProducts, text: messages.search" class="action-default action-search"></a>
<a href="#" visible="search" data-bind="click: resetSearch, text: messages.clearSearch" class="action-reset"></a>
</p>

<div data-bind="if: hasProducts()">

<ul data-bind="foreach: products, afterRender: enableSortableList" class="product-list">
<li class="product-list-item" data-bind="
attr: {'data-product-id': getId()},
css: {'manual-sorting': hasPosition(), 'automatic-sorting': !hasPosition(), 'blacklisted': isBlacklisted() }
">
<div class="draggable-handle" data-bind="if: hasPosition()"></div>
<div class="blacklist-handle" data-bind="if: $parent.allowBlacklist">
<a data-bind="click: $parent.toggleBlackListed.bind($parent), attr: { title: isBlacklisted() ? $t('Blacklisted') : $t('Visible') }"></a>
</div>

<div class="product-image"><img data-bind="attr: { src: getImageUrl() }" /></div>

<div class="info">
<h1><span data-bind="html: getName(), attr: {title: getName()"></span></h1>
<p class="sku" data-bind="text: getSku()"></p>
<p class="price" data-bind="text: getFormattedPrice()"></p>
<p class="stock" data-bind="text: getStockLabel()"></p>
</div>

<div class="admin__actions-switch" data-role="switcher" data-bind="click: $parent.toggleSortType.bind($parent)">
<input type="checkbox" class="admin__actions-switch-checkbox" simple-checked="hasPosition()" ko-value="hasPosition()" />
<label class="admin__actions-switch-label">
<span class="admin__actions-switch-text"
attr="'data-text-on': $parent.messages.manualSort, 'data-text-off': $parent.messages.automaticSort">
</span>
</label>
</div>
</li>
</ul>
<div class="product-list-container">
<div class="admin__data-grid-loading-mask" visible="previewOnlyMode"></div>
<button id="reset-all-products" type="button" class="action-default action-secondary scalable reset" data-bind="title: messages.resetAllText, click: resetAllProducts" data-ui-id="adminhtml-product-sorter-reset-button">
<span text="messages.resetAllText"></span>
</button>
<ul data-bind="foreach: products, afterRender: enableSortableList" class="product-list">
<li class="product-list-item" data-bind="
attr: {'data-product-id': getId()},
css: {'manual-sorting': hasPosition(), 'automatic-sorting': !hasPosition(), 'blacklisted': isBlacklisted() }
">
<div class="draggable-handle" data-bind="if: hasPosition()"></div>
<div class="blacklist-handle" data-bind="if: $parent.allowBlacklist">
<a data-bind="click: $parent.toggleBlackListed.bind($parent), attr: { title: isBlacklisted() ? $t('Blacklisted') : $t('Visible') }"></a>
</div>

<div class="product-image"><img data-bind="attr: { src: getImageUrl() }" /></div>

<div class="info">
<h1><span data-bind="html: getName(), attr: {title: getName()"></span></h1>
<p class="sku" data-bind="text: getSku()"></p>
<p class="price" data-bind="text: getFormattedPrice()"></p>
<p class="stock" data-bind="text: getStockLabel()"></p>
</div>

<div class="admin__actions-switch" data-role="switcher" data-bind="click: $parent.toggleSortType.bind($parent)">
<input type="checkbox" class="admin__actions-switch-checkbox" simple-checked="hasPosition()" ko-value="hasPosition()" />
<label class="admin__actions-switch-label">
<span class="admin__actions-switch-text"
attr="'data-text-on': $parent.messages.manualSort, 'data-text-off': $parent.messages.automaticSort">
</span>
</label>
</div>
</li>
</ul>
</div>

<p class="bottom-links" data-bind="visible: hasMoreProducts()">
<a href="#" data-bind="click: showMoreProducts, text: messages.showMore" class="show-more-link"></a>
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,17 @@ class Position extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
*/
public function getProductPositionsByCategory($category)
{
$storeId = 0;
if (is_object($category)) {
if ($category->getUseStorePositions()) {
$storeId = $category->getStoreId();
}
$category = $category->getId();
}

$select = $this->getBaseSelect()
->where('category_id = ?', (int) $category)
->where('store_id = ?', (int) $storeId)
->where('position IS NOT NULL')
->columns(['product_id', 'position']);

Expand All @@ -61,32 +66,47 @@ public function getProductPositionsByCategory($category)
*/
public function getProductBlacklistByCategory($category)
{
$storeId = 0;
if (is_object($category)) {
if ($category->getUseStorePositions()) {
$storeId = $category->getStoreId();
}
$category = $category->getId();
}

$select = $this->getBaseSelect()
->columns(['product_id'])
->where('category_id = ?', (int) $category)
->where('store_id = ?', (int) $storeId)
->where('is_blacklisted = ?', (int) true);

return $this->getConnection()->fetchCol($select);
}

/**
* Save the product postions.
* Save the product positions.
*
* @param CategoryInterface $category Saved category.
*
* @return \Smile\ElasticsuiteVirtualCategory\Model\ResourceModel\Category\Product\Position
*/
public function saveProductPositions(CategoryInterface $category)
{
// Can be 0 if not on a store view.
$storeId = (int) $category->getStoreId();

// If on a store view, and no store override of positions, clean up existing store records.
if ($storeId && !$category->getUseStorePositions()) {
$category->setSortedProducts([]);
$category->setBlacklistedProducts([]);
}

$newProductPositions = $category->getSortedProducts();
$blacklistedProducts = $category->getBlacklistedProducts() ?? [];

$deleteConditions = [
$this->getConnection()->quoteInto('category_id = ?', (int) $category->getId()),
$this->getConnection()->quoteInto('store_id = ?', $storeId),
];

if (!empty($newProductPositions) || !empty($blacklistedProducts)) {
Expand All @@ -97,6 +117,7 @@ public function saveProductPositions(CategoryInterface $category)
$insertData[] = [
'category_id' => $category->getId(),
'product_id' => $productId,
'store_id' => $storeId,
'position' => $newProductPositions[$productId] ?? null,
'is_blacklisted' => in_array($productId, $blacklistedProducts),
];
Expand Down
Loading