Skip to content

Commit

Permalink
Store level merchandiser
Browse files Browse the repository at this point in the history
  • Loading branch information
rbayet committed Jan 11, 2019
1 parent ea19e18 commit 4a2210f
Show file tree
Hide file tree
Showing 20 changed files with 606 additions and 54 deletions.
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) == 1) && (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,57 @@ define([
}
},

switchScope: function(useDefaultPositions) {
if (parseInt(useDefaultPositions, 10) == 1) {
// 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("{}");
this.blacklistedProducts([]);

// Recreate required observers/subscriptions.
this.observe(['editPositions']);
this.editPositions.subscribe(function () { this.value(JSON.stringify(this.editPositions())); }.bind(this));

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

return false;
},

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

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

Expand Down Expand Up @@ -275,6 +345,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->getUseDefaultPositions()) {
$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->getUseDefaultPositions()) {
$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->getUseDefaultPositions()) {
$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

0 comments on commit 4a2210f

Please sign in to comment.