update
This commit is contained in:
@@ -67,3 +67,9 @@ When creating or modifying overrides, PrestaShop also needs to rebuild the class
|
||||
- Module DB tables use `_DB_PREFIX_` constant (typically `ps_`).
|
||||
- PrestaShop hooks are the integration point — prefer hooks over direct core edits.
|
||||
- The `admin658c34/` directory is the custom admin panel path (security through obscurity).
|
||||
|
||||
## Custom Assistant Command
|
||||
|
||||
- If the user writes `zapisz-changelog`, create or update monthly changelog file `changelog/YYYY-MM.md` (based on current date).
|
||||
- Add an entry for the current day with a concise summary of code changes made in the current session.
|
||||
- Include touched file paths and relevant line references where possible.
|
||||
|
||||
51
changelog/2026-03.md
Normal file
51
changelog/2026-03.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Changelog 2026-03
|
||||
|
||||
## 2026-03-20
|
||||
|
||||
### Zmiany funkcjonalne
|
||||
- Podmieniono logikę modułu `ps_categoryproducts` na wyświetlanie produktów z tej samej cechy `Seria` (`id_feature = 8`) zamiast produktów z tej samej kategorii, gdy produkt ma ustawioną cechę serii.
|
||||
- Dla produktów z serią włączono tryb restrykcyjny: brak fallbacku do kategorii, jeśli nie ma dopasowań po serii.
|
||||
- Dodano i dopracowano karuzelę dla sekcji produktów powiązanych (nawigacja, responsywność, scroll poziomy).
|
||||
- Zmieniono nagłówek sekcji na: `Produkty z tej samej serii`.
|
||||
|
||||
### Najważniejsze modyfikacje w kodzie
|
||||
|
||||
1. `modules/ps_categoryproducts/ps_categoryproducts.php`
|
||||
- Stała cechy serii:
|
||||
- `SERIES_FEATURE_ID = 8` ([modules/ps_categoryproducts/ps_categoryproducts.php:44](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\ps_categoryproducts.php:44))
|
||||
- Główna logika danych widgetu:
|
||||
- pobieranie wszystkich wartości serii i filtrowanie po nich ([modules/ps_categoryproducts/ps_categoryproducts.php:213](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\ps_categoryproducts.php:213))
|
||||
- Rejestracja assetów karuzeli (z fallbackiem):
|
||||
- `registerCarouselAssets()` ([modules/ps_categoryproducts/ps_categoryproducts.php:287](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\ps_categoryproducts.php:287))
|
||||
- Pobieranie wartości serii produktu:
|
||||
- `getSeriesFeatureValueIds()` ([modules/ps_categoryproducts/ps_categoryproducts.php:381](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\ps_categoryproducts.php:381))
|
||||
- Pobieranie produktów z tej samej serii:
|
||||
- `getProductsByFeatureValue()` ([modules/ps_categoryproducts/ps_categoryproducts.php:398](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\ps_categoryproducts.php:398))
|
||||
- Wersjonowanie cache bloku:
|
||||
- `v4_strict_series_masterdb` ([modules/ps_categoryproducts/ps_categoryproducts.php:490](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\ps_categoryproducts.php:490))
|
||||
|
||||
2. `themes/InterBlue/modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl`
|
||||
- Nagłówek sekcji dla serii:
|
||||
- `Produkty z tej samej serii` ([themes/InterBlue/modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl:42](c:\visual studio code\projekty\interblue.pl\themes\InterBlue\modules\ps_categoryproducts\views\templates\hook\ps_categoryproducts.tpl:42))
|
||||
- Markup karuzeli i klasy JS:
|
||||
- wrapper i nawigacja ([themes/InterBlue/modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl:39](c:\visual studio code\projekty\interblue.pl\themes\InterBlue\modules\ps_categoryproducts\views\templates\hook\ps_categoryproducts.tpl:39), [themes/InterBlue/modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl:54](c:\visual studio code\projekty\interblue.pl\themes\InterBlue\modules\ps_categoryproducts\views\templates\hook\ps_categoryproducts.tpl:54), [themes/InterBlue/modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl:66](c:\visual studio code\projekty\interblue.pl\themes\InterBlue\modules\ps_categoryproducts\views\templates\hook\ps_categoryproducts.tpl:66))
|
||||
- Inline CSS/JS opakowany w `{literal}`:
|
||||
- style ([themes/InterBlue/modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl:25](c:\visual studio code\projekty\interblue.pl\themes\InterBlue\modules\ps_categoryproducts\views\templates\hook\ps_categoryproducts.tpl:25))
|
||||
- skrypt ([themes/InterBlue/modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl:73](c:\visual studio code\projekty\interblue.pl\themes\InterBlue\modules\ps_categoryproducts\views\templates\hook\ps_categoryproducts.tpl:73))
|
||||
|
||||
3. `modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl`
|
||||
- Analogiczne zmiany jak w override motywu:
|
||||
- `{literal}` + markup karuzeli + JS ([modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl:25](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\views\templates\hook\ps_categoryproducts.tpl:25), [modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl:39](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\views\templates\hook\ps_categoryproducts.tpl:39), [modules/ps_categoryproducts/views/templates/hook/ps_categoryproducts.tpl:77](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\views\templates\hook\ps_categoryproducts.tpl:77))
|
||||
|
||||
4. Dodatkowe pliki assetów karuzeli
|
||||
- CSS:
|
||||
- [modules/ps_categoryproducts/views/css/carousel.css](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\views\css\carousel.css)
|
||||
- JS:
|
||||
- [modules/ps_categoryproducts/views/js/carousel.js](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\views\js\carousel.js)
|
||||
- Zabezpieczenie katalogów:
|
||||
- [modules/ps_categoryproducts/views/css/index.php](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\views\css\index.php)
|
||||
- [modules/ps_categoryproducts/views/js/index.php](c:\visual studio code\projekty\interblue.pl\modules\ps_categoryproducts\views\js\index.php)
|
||||
|
||||
### Uwagi operacyjne
|
||||
- Po wdrożeniu zmian wymagane było czyszczenie cache PrestaShop, aby wymusić przebudowę bloku `ps_categoryproducts`.
|
||||
|
||||
@@ -41,6 +41,8 @@ use PrestaShop\PrestaShop\Core\Product\Search\SortOrder;
|
||||
|
||||
class Ps_Categoryproducts extends Module implements WidgetInterface
|
||||
{
|
||||
const SERIES_FEATURE_ID = 8;
|
||||
|
||||
protected $html;
|
||||
protected $templateFile;
|
||||
|
||||
@@ -213,15 +215,33 @@ class Ps_Categoryproducts extends Module implements WidgetInterface
|
||||
$params = $this->getInformationFromConfiguration($configuration);
|
||||
|
||||
if ($params) {
|
||||
$products = array();
|
||||
$seriesFeatureValueIds = $this->getSeriesFeatureValueIds((int) $params['id_product']);
|
||||
if (!empty($seriesFeatureValueIds)) {
|
||||
$products = $this->getProductsByFeatureValue(
|
||||
(int) $params['id_product'],
|
||||
(int) self::SERIES_FEATURE_ID,
|
||||
$seriesFeatureValueIds
|
||||
);
|
||||
if (empty($products)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$products = $this->getCategoryProducts($params['id_product'], $params['id_category']);
|
||||
|
||||
if (!empty($products)) {
|
||||
return array(
|
||||
'products' => $products,
|
||||
'products_source' => 'series',
|
||||
);
|
||||
}
|
||||
|
||||
$products = $this->getCategoryProducts($params['id_product'], $params['id_category']);
|
||||
if (empty($products)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array(
|
||||
'products' => $products,
|
||||
'products_source' => 'category',
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -233,6 +253,7 @@ class Ps_Categoryproducts extends Module implements WidgetInterface
|
||||
|
||||
if ($params) {
|
||||
if ((int)Configuration::get('CATEGORYPRODUCTS_DISPLAY_PRODUCTS') > 0) {
|
||||
$this->registerCarouselAssets();
|
||||
|
||||
// Need variables only if this template isn't cached
|
||||
if (!$this->isCached($this->templateFile, $params['cache_id'])) {
|
||||
@@ -263,6 +284,39 @@ class Ps_Categoryproducts extends Module implements WidgetInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
private function registerCarouselAssets()
|
||||
{
|
||||
if (empty($this->context->controller)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (method_exists($this->context->controller, 'registerStylesheet')) {
|
||||
$this->context->controller->registerStylesheet(
|
||||
'module-ps-categoryproducts-carousel',
|
||||
'modules/' . $this->name . '/views/css/carousel.css',
|
||||
array(
|
||||
'media' => 'all',
|
||||
'priority' => 150,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$this->context->controller->addCSS($this->_path . 'views/css/carousel.css');
|
||||
}
|
||||
|
||||
if (method_exists($this->context->controller, 'registerJavascript')) {
|
||||
$this->context->controller->registerJavascript(
|
||||
'module-ps-categoryproducts-carousel',
|
||||
'modules/' . $this->name . '/views/js/carousel.js',
|
||||
array(
|
||||
'position' => 'bottom',
|
||||
'priority' => 150,
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$this->context->controller->addJS($this->_path . 'views/js/carousel.js');
|
||||
}
|
||||
}
|
||||
|
||||
private function getCategoryProducts($idProduct, $idCategory)
|
||||
{
|
||||
$category = new Category($idCategory);
|
||||
@@ -324,6 +378,98 @@ class Ps_Categoryproducts extends Module implements WidgetInterface
|
||||
return $productsForTemplate;
|
||||
}
|
||||
|
||||
private function getSeriesFeatureValueIds($idProduct)
|
||||
{
|
||||
$sql = 'SELECT DISTINCT fp.`id_feature_value`
|
||||
FROM `' . _DB_PREFIX_ . 'feature_product` fp
|
||||
WHERE fp.`id_product` = ' . (int) $idProduct . '
|
||||
AND fp.`id_feature` = ' . (int) self::SERIES_FEATURE_ID . '
|
||||
AND fp.`id_feature_value` > 0
|
||||
ORDER BY fp.`id_feature_value` ASC';
|
||||
|
||||
$rows = Db::getInstance()->executeS($sql);
|
||||
if (empty($rows)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array_map('intval', array_column($rows, 'id_feature_value'));
|
||||
}
|
||||
|
||||
private function getProductsByFeatureValue($idProduct, $idFeature, array $idFeatureValues)
|
||||
{
|
||||
$nProducts = (int) Configuration::get('CATEGORYPRODUCTS_DISPLAY_PRODUCTS');
|
||||
if ($nProducts <= 0) {
|
||||
return array();
|
||||
}
|
||||
if (empty($idFeatureValues)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$idShop = (int) $this->context->shop->id;
|
||||
$idLang = (int) $this->context->language->id;
|
||||
if ($idShop <= 0) {
|
||||
$idShop = (int) Configuration::get('PS_SHOP_DEFAULT');
|
||||
}
|
||||
$idFeatureValues = array_filter(array_map('intval', $idFeatureValues));
|
||||
if (empty($idFeatureValues)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$sql = 'SELECT DISTINCT fp.`id_product`
|
||||
FROM `' . _DB_PREFIX_ . 'feature_product` fp
|
||||
INNER JOIN `' . _DB_PREFIX_ . 'product_shop` product_shop
|
||||
ON product_shop.`id_product` = fp.`id_product`
|
||||
AND product_shop.`id_shop` = ' . (int) $idShop . '
|
||||
INNER JOIN `' . _DB_PREFIX_ . 'product_lang` pl
|
||||
ON pl.`id_product` = fp.`id_product`
|
||||
AND pl.`id_shop` = ' . (int) $idShop . '
|
||||
AND pl.`id_lang` = ' . (int) $idLang . '
|
||||
WHERE fp.`id_feature` = ' . (int) $idFeature . '
|
||||
AND fp.`id_feature_value` IN (' . implode(',', $idFeatureValues) . ')
|
||||
AND fp.`id_product` != ' . (int) $idProduct . '
|
||||
AND product_shop.`active` = 1
|
||||
AND product_shop.`visibility` IN ("both", "catalog")
|
||||
ORDER BY RAND()
|
||||
LIMIT ' . (int) $nProducts;
|
||||
|
||||
$rows = Db::getInstance()->executeS($sql);
|
||||
if (empty($rows)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$ids = array_map('intval', array_column($rows, 'id_product'));
|
||||
if (empty($ids)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$showPrice = (bool) Configuration::get('CATEGORYPRODUCTS_DISPLAY_PRICE');
|
||||
$assembler = new ProductAssembler($this->context);
|
||||
$presenterFactory = new ProductPresenterFactory($this->context);
|
||||
$presentationSettings = $presenterFactory->getPresentationSettings();
|
||||
$presenter = new ProductListingPresenter(
|
||||
new ImageRetriever(
|
||||
$this->context->link
|
||||
),
|
||||
$this->context->link,
|
||||
new PriceFormatter(),
|
||||
new ProductColorsRetriever(),
|
||||
$this->context->getTranslator()
|
||||
);
|
||||
|
||||
$presentationSettings->showPrices = $showPrice;
|
||||
|
||||
$productsForTemplate = array();
|
||||
foreach ($ids as $id) {
|
||||
$productsForTemplate[] = $presenter->present(
|
||||
$presentationSettings,
|
||||
$assembler->assembleProduct(array('id_product' => (int) $id)),
|
||||
$this->context->language
|
||||
);
|
||||
}
|
||||
|
||||
return $productsForTemplate;
|
||||
}
|
||||
|
||||
private function getInformationFromConfiguration($configuration)
|
||||
{
|
||||
if (empty($configuration['product'])) {
|
||||
@@ -341,7 +487,7 @@ class Ps_Categoryproducts extends Module implements WidgetInterface
|
||||
|
||||
if (!empty($id_product) && !empty($id_category)) {
|
||||
|
||||
$cache_id = 'ps_categoryproducts|'.$id_product.'|'.$id_category;
|
||||
$cache_id = 'ps_categoryproducts|v4_strict_series_masterdb|'.$id_product.'|'.$id_category;
|
||||
|
||||
return array(
|
||||
'id_product' => $id_product,
|
||||
|
||||
93
modules/ps_categoryproducts/views/css/carousel.css
Normal file
93
modules/ps_categoryproducts/views/css/carousel.css
Normal file
@@ -0,0 +1,93 @@
|
||||
.category-products .category-products-carousel-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.category-products .products.category-products-carousel {
|
||||
display: flex !important;
|
||||
flex-wrap: nowrap !important;
|
||||
gap: 16px;
|
||||
overflow-x: auto !important;
|
||||
overflow-y: hidden !important;
|
||||
scroll-behavior: smooth;
|
||||
scroll-snap-type: x mandatory;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.category-products .products.category-products-carousel::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.category-products .products.category-products-carousel > article.product-miniature {
|
||||
flex: 0 0 calc(25% - 12px) !important;
|
||||
max-width: calc(25% - 12px) !important;
|
||||
scroll-snap-align: start;
|
||||
}
|
||||
|
||||
.category-products .category-products-carousel__nav {
|
||||
position: absolute;
|
||||
top: 42%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 20;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border: 0;
|
||||
border-radius: 50%;
|
||||
background: #ffffff;
|
||||
color: #112c50;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s ease, background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.category-products .category-products-carousel__nav:hover {
|
||||
background: #f2f7ff;
|
||||
}
|
||||
|
||||
.category-products .category-products-carousel__nav[disabled] {
|
||||
opacity: 0.35;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.category-products .category-products-carousel__nav--prev {
|
||||
left: -12px;
|
||||
}
|
||||
|
||||
.category-products .category-products-carousel__nav--next {
|
||||
right: -12px;
|
||||
}
|
||||
|
||||
.category-products .category-products-carousel__nav span {
|
||||
font-size: 28px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 1199.98px) {
|
||||
.category-products .products.category-products-carousel > article.product-miniature {
|
||||
flex: 0 0 calc(33.3333% - 11px) !important;
|
||||
max-width: calc(33.3333% - 11px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.category-products .products.category-products-carousel > article.product-miniature {
|
||||
flex: 0 0 calc(50% - 8px) !important;
|
||||
max-width: calc(50% - 8px) !important;
|
||||
}
|
||||
|
||||
.category-products .category-products-carousel__nav {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 479.98px) {
|
||||
.category-products .products.category-products-carousel > article.product-miniature {
|
||||
flex: 0 0 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
}
|
||||
33
modules/ps_categoryproducts/views/css/index.php
Normal file
33
modules/ps_categoryproducts/views/css/index.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* 2007-2016 PrestaShop
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License (AFL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://opensource.org/licenses/afl-3.0.php
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to http://www.prestashop.com for more information.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright 2007-2016 PrestaShop SA
|
||||
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
|
||||
* International Registered Trademark & Property of PrestaShop SA
|
||||
*/
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
header('Location: ../');
|
||||
exit;
|
||||
87
modules/ps_categoryproducts/views/js/carousel.js
Normal file
87
modules/ps_categoryproducts/views/js/carousel.js
Normal file
@@ -0,0 +1,87 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function getStep(track) {
|
||||
var firstItem = track.querySelector('article.product-miniature');
|
||||
if (!firstItem) {
|
||||
return Math.max(240, Math.floor(track.clientWidth * 0.8));
|
||||
}
|
||||
|
||||
var style = window.getComputedStyle(track);
|
||||
var gap = parseFloat(style.columnGap || style.gap || '0') || 0;
|
||||
|
||||
return Math.ceil(firstItem.getBoundingClientRect().width + gap);
|
||||
}
|
||||
|
||||
function updateNavState(track, prevBtn, nextBtn) {
|
||||
var maxScrollLeft = Math.max(0, track.scrollWidth - track.clientWidth);
|
||||
var canScroll = maxScrollLeft > 8;
|
||||
|
||||
if (!canScroll) {
|
||||
prevBtn.disabled = true;
|
||||
nextBtn.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
prevBtn.disabled = track.scrollLeft <= 2;
|
||||
nextBtn.disabled = track.scrollLeft >= maxScrollLeft - 2;
|
||||
}
|
||||
|
||||
function initCarousel(root) {
|
||||
var track = root.querySelector('.js-category-products-track');
|
||||
var prevBtn = root.querySelector('.js-category-products-prev');
|
||||
var nextBtn = root.querySelector('.js-category-products-next');
|
||||
|
||||
if (!track || !prevBtn || !nextBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
prevBtn.addEventListener('click', function () {
|
||||
track.scrollBy({
|
||||
left: -getStep(track),
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
|
||||
nextBtn.addEventListener('click', function () {
|
||||
track.scrollBy({
|
||||
left: getStep(track),
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
|
||||
var ticking = false;
|
||||
track.addEventListener('scroll', function () {
|
||||
if (ticking) {
|
||||
return;
|
||||
}
|
||||
|
||||
ticking = true;
|
||||
window.requestAnimationFrame(function () {
|
||||
updateNavState(track, prevBtn, nextBtn);
|
||||
ticking = false;
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('resize', function () {
|
||||
updateNavState(track, prevBtn, nextBtn);
|
||||
});
|
||||
|
||||
updateNavState(track, prevBtn, nextBtn);
|
||||
}
|
||||
|
||||
function onReady() {
|
||||
var carousels = document.querySelectorAll('.js-category-products-carousel');
|
||||
if (!carousels.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
carousels.forEach(initCarousel);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', onReady);
|
||||
} else {
|
||||
onReady();
|
||||
}
|
||||
})();
|
||||
33
modules/ps_categoryproducts/views/js/index.php
Normal file
33
modules/ps_categoryproducts/views/js/index.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* 2007-2016 PrestaShop
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
*
|
||||
* This source file is subject to the Academic Free License (AFL 3.0)
|
||||
* that is bundled with this package in the file LICENSE.txt.
|
||||
* It is also available through the world-wide-web at this URL:
|
||||
* http://opensource.org/licenses/afl-3.0.php
|
||||
* If you did not receive a copy of the license and are unable to
|
||||
* obtain it through the world-wide-web, please send an email
|
||||
* to license@prestashop.com so we can send you a copy immediately.
|
||||
*
|
||||
* DISCLAIMER
|
||||
*
|
||||
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||||
* versions in the future. If you wish to customize PrestaShop for your
|
||||
* needs please refer to http://www.prestashop.com for more information.
|
||||
*
|
||||
* @author PrestaShop SA <contact@prestashop.com>
|
||||
* @copyright 2007-2016 PrestaShop SA
|
||||
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
|
||||
* International Registered Trademark & Property of PrestaShop SA
|
||||
*/
|
||||
|
||||
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
|
||||
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
|
||||
header('Cache-Control: no-store, no-cache, must-revalidate');
|
||||
header('Cache-Control: post-check=0, pre-check=0', false);
|
||||
header('Pragma: no-cache');
|
||||
header('Location: ../');
|
||||
exit;
|
||||
@@ -1,4 +1,4 @@
|
||||
{*
|
||||
{*
|
||||
* 2007-2016 PrestaShop
|
||||
*
|
||||
* NOTICE OF LICENSE
|
||||
@@ -22,17 +22,58 @@
|
||||
* @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0)
|
||||
* International Registered Trademark & Property of PrestaShop SA
|
||||
*}
|
||||
<section>
|
||||
{literal}<style>
|
||||
.category-products .products.category-products-carousel{display:flex!important;flex-wrap:nowrap!important;gap:16px;overflow-x:auto!important;overflow-y:hidden!important;scroll-behavior:smooth;scroll-snap-type:x mandatory;-ms-overflow-style:none;scrollbar-width:none}
|
||||
.category-products .products.category-products-carousel::-webkit-scrollbar{display:none}
|
||||
.category-products .products.category-products-carousel>article.product-miniature{flex:0 0 calc(25% - 12px)!important;max-width:calc(25% - 12px)!important;scroll-snap-align:start}
|
||||
.category-products .category-products-carousel-wrapper{position:relative}
|
||||
.category-products .category-products-carousel__nav{position:absolute;top:42%;transform:translateY(-50%);z-index:20;width:38px;height:38px;border:0;border-radius:50%;background:#fff;color:#112c50;box-shadow:0 2px 10px rgba(0,0,0,.18);display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
|
||||
.category-products .category-products-carousel__nav[disabled]{opacity:.35;cursor:default}
|
||||
.category-products .category-products-carousel__nav--prev{left:-12px}
|
||||
.category-products .category-products-carousel__nav--next{right:-12px}
|
||||
.category-products .category-products-carousel__nav span{font-size:28px;line-height:1}
|
||||
@media (max-width:1199.98px){.category-products .products.category-products-carousel>article.product-miniature{flex:0 0 calc(33.3333% - 11px)!important;max-width:calc(33.3333% - 11px)!important}}
|
||||
@media (max-width:767.98px){.category-products .products.category-products-carousel>article.product-miniature{flex:0 0 calc(50% - 8px)!important;max-width:calc(50% - 8px)!important}.category-products .category-products-carousel__nav{display:none}}
|
||||
@media (max-width:479.98px){.category-products .products.category-products-carousel>article.product-miniature{flex:0 0 100%!important;max-width:100%!important}}
|
||||
</style>{/literal}
|
||||
<section class="category-products js-category-products-carousel">
|
||||
<h2>
|
||||
{if $products|@count == 1}
|
||||
{l s='%s other product in the same category:' sprintf=[$products|@count] d='Modules.Categoryproducts.Shop'}
|
||||
{if isset($products_source) && $products_source == 'series'}
|
||||
{if $products|@count == 1}
|
||||
{l s='Similar product from the same series:' d='Modules.Categoryproducts.Shop'}
|
||||
{else}
|
||||
{l s='Similar products from the same series:' d='Modules.Categoryproducts.Shop'}
|
||||
{/if}
|
||||
{else}
|
||||
{l s='%s other products in the same category:' sprintf=[$products|@count] d='Modules.Categoryproducts.Shop'}
|
||||
{if $products|@count == 1}
|
||||
{l s='%s other product in the same category:' sprintf=[$products|@count] d='Modules.Categoryproducts.Shop'}
|
||||
{else}
|
||||
{l s='%s other products in the same category:' sprintf=[$products|@count] d='Modules.Categoryproducts.Shop'}
|
||||
{/if}
|
||||
{/if}
|
||||
</h2>
|
||||
<div>
|
||||
{foreach from=$products item="product"}
|
||||
{include file="catalog/_partials/miniatures/product.tpl" product=$product}
|
||||
{/foreach}
|
||||
<div class="category-products-carousel-wrapper">
|
||||
<button
|
||||
type="button"
|
||||
class="category-products-carousel__nav category-products-carousel__nav--prev js-category-products-prev"
|
||||
aria-label="{l s='Previous products' d='Modules.Categoryproducts.Shop'}"
|
||||
>
|
||||
<span aria-hidden="true">‹</span>
|
||||
</button>
|
||||
<div class="products category-products-carousel js-category-products-track">
|
||||
{foreach from=$products item="product"}
|
||||
{include file="catalog/_partials/miniatures/product.tpl" product=$product}
|
||||
{/foreach}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="category-products-carousel__nav category-products-carousel__nav--next js-category-products-next"
|
||||
aria-label="{l s='Next products' d='Modules.Categoryproducts.Shop'}"
|
||||
>
|
||||
<span aria-hidden="true">›</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
{literal}<script>
|
||||
(function(){if(window.__ibSeriesCarouselInit){return;}window.__ibSeriesCarouselInit=true;function s(t){var i=t.querySelector('article.product-miniature');if(!i){return Math.max(240,Math.floor(t.clientWidth*.8));}var c=window.getComputedStyle(t),g=parseFloat(c.columnGap||c.gap||'0')||0;return Math.ceil(i.getBoundingClientRect().width+g);}function u(t,p,n){var m=Math.max(0,t.scrollWidth-t.clientWidth),c=m>8;if(!c){p.disabled=true;n.disabled=true;return;}p.disabled=t.scrollLeft<=2;n.disabled=t.scrollLeft>=m-2;}function init(r){var t=r.querySelector('.js-category-products-track'),p=r.querySelector('.js-category-products-prev'),n=r.querySelector('.js-category-products-next');if(!t||!p||!n){return;}p.addEventListener('click',function(){t.scrollBy({left:-s(t),behavior:'smooth'});});n.addEventListener('click',function(){t.scrollBy({left:s(t),behavior:'smooth'});});var k=false;t.addEventListener('scroll',function(){if(k){return;}k=true;window.requestAnimationFrame(function(){u(t,p,n);k=false;});});window.addEventListener('resize',function(){u(t,p,n);});u(t,p,n);}var all=document.querySelectorAll('.js-category-products-carousel');all.forEach(init);}());
|
||||
</script>{/literal}
|
||||
|
||||
34
themes/InterBlue/assets/img/cms/serie-simon10-tile-1.svg
Normal file
34
themes/InterBlue/assets/img/cms/serie-simon10-tile-1.svg
Normal file
@@ -0,0 +1,34 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="320" height="320" viewBox="0 0 320 320" role="img" aria-labelledby="title desc">
|
||||
<title id="title">SIMON 10</title>
|
||||
<desc id="desc">Kafelek serii SIMON 10 z linkiem do oferty.</desc>
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" stop-color="#f7f3eb"/>
|
||||
<stop offset="1" stop-color="#f3ebe1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="circleBg" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#b8b8b8"/>
|
||||
<stop offset="1" stop-color="#7c7c7c"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<rect width="320" height="320" fill="url(#bg)"/>
|
||||
<circle cx="160" cy="118" r="95" fill="url(#circleBg)"/>
|
||||
|
||||
<rect x="90" y="95" width="74" height="74" rx="14" fill="#dfd7cc"/>
|
||||
<rect x="98" y="103" width="58" height="58" rx="10" fill="#f2ede5"/>
|
||||
<rect x="162" y="95" width="74" height="74" rx="14" fill="#c9c9c9"/>
|
||||
<rect x="170" y="103" width="58" height="58" rx="10" fill="#f0f0f0"/>
|
||||
|
||||
<g fill="#f0c7bf">
|
||||
<path d="M24 247h20v6H24z"/>
|
||||
<path d="M276 247h20v6h-20z"/>
|
||||
<path d="M32 235h6v20h-6z"/>
|
||||
<path d="M282 235h6v20h-6z"/>
|
||||
<path d="M42 241l8-8 4 4-8 8z"/>
|
||||
<path d="M278 241l-8-8-4 4 8 8z"/>
|
||||
</g>
|
||||
|
||||
<text x="160" y="235" text-anchor="middle" fill="#1f4978" font-size="36" font-family="Arial, sans-serif" font-weight="700">SIMON 10</text>
|
||||
<text x="160" y="266" text-anchor="middle" fill="#1f4978" font-size="20" font-family="Arial, sans-serif" font-weight="700">SKONTAKTUJ SIMON</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -22,22 +22,54 @@
|
||||
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
|
||||
* International Registered Trademark & Property of PrestaShop SA
|
||||
*}
|
||||
<section class="category-products clearfix mt-3">
|
||||
{literal}<style>
|
||||
.category-products .products.category-products-carousel{display:flex!important;flex-wrap:nowrap!important;gap:16px;overflow-x:auto!important;overflow-y:hidden!important;scroll-behavior:smooth;scroll-snap-type:x mandatory;-ms-overflow-style:none;scrollbar-width:none}
|
||||
.category-products .products.category-products-carousel::-webkit-scrollbar{display:none}
|
||||
.category-products .products.category-products-carousel>article.product-miniature{flex:0 0 calc(25% - 12px)!important;max-width:calc(25% - 12px)!important;scroll-snap-align:start}
|
||||
.category-products .category-products-carousel-wrapper{position:relative}
|
||||
.category-products .category-products-carousel__nav{position:absolute;top:42%;transform:translateY(-50%);z-index:20;width:38px;height:38px;border:0;border-radius:50%;background:#fff;color:#112c50;box-shadow:0 2px 10px rgba(0,0,0,.18);display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
|
||||
.category-products .category-products-carousel__nav[disabled]{opacity:.35;cursor:default}
|
||||
.category-products .category-products-carousel__nav--prev{left:-12px}
|
||||
.category-products .category-products-carousel__nav--next{right:-12px}
|
||||
.category-products .category-products-carousel__nav span{font-size:28px;line-height:1}
|
||||
@media (max-width:1199.98px){.category-products .products.category-products-carousel>article.product-miniature{flex:0 0 calc(33.3333% - 11px)!important;max-width:calc(33.3333% - 11px)!important}}
|
||||
@media (max-width:767.98px){.category-products .products.category-products-carousel>article.product-miniature{flex:0 0 calc(50% - 8px)!important;max-width:calc(50% - 8px)!important}.category-products .category-products-carousel__nav{display:none}}
|
||||
@media (max-width:479.98px){.category-products .products.category-products-carousel>article.product-miniature{flex:0 0 100%!important;max-width:100%!important}}
|
||||
</style>{/literal}
|
||||
<section class="category-products clearfix mt-3 js-category-products-carousel">
|
||||
<h2>
|
||||
{*if $products|@count == 1}
|
||||
{l s='%s other product in the same category:' sprintf=[$products|@count] d='Shop.Theme.Catalog'}
|
||||
{if isset($products_source) && $products_source == 'series'}
|
||||
{l s='Produkty z tej samej serii' d='Shop.Theme.Catalog'}
|
||||
{else}
|
||||
{l s='%s other products in the same category:' sprintf=[$products|@count] d='Shop.Theme.Catalog'}
|
||||
{/if*}
|
||||
{if $products|@count == 1}
|
||||
{l s='Podobny produkt z tej samej kategorii' sprintf=[$products|@count] d='Shop.Theme.Catalog'}
|
||||
{else}
|
||||
{l s='Podobne produkty z tej samej kategorii' sprintf=[$products|@count] d='Shop.Theme.Catalog'}
|
||||
{if $products|@count == 1}
|
||||
{l s='Podobny produkt z tej samej kategorii' d='Shop.Theme.Catalog'}
|
||||
{else}
|
||||
{l s='Podobne produkty z tej samej kategorii' d='Shop.Theme.Catalog'}
|
||||
{/if}
|
||||
{/if}
|
||||
</h2>
|
||||
<div class="products">
|
||||
{foreach from=$products item="product"}
|
||||
{include file="catalog/_partials/miniatures/product-light.tpl" product=$product}
|
||||
{/foreach}
|
||||
<div class="category-products-carousel-wrapper">
|
||||
<button
|
||||
type="button"
|
||||
class="category-products-carousel__nav category-products-carousel__nav--prev js-category-products-prev"
|
||||
aria-label="{l s='Poprzednie produkty' d='Shop.Theme.Catalog'}"
|
||||
>
|
||||
<span aria-hidden="true">‹</span>
|
||||
</button>
|
||||
<div class="products category-products-carousel js-category-products-track">
|
||||
{foreach from=$products item="product"}
|
||||
{include file="catalog/_partials/miniatures/product-light.tpl" product=$product}
|
||||
{/foreach}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="category-products-carousel__nav category-products-carousel__nav--next js-category-products-next"
|
||||
aria-label="{l s='Następne produkty' d='Shop.Theme.Catalog'}"
|
||||
>
|
||||
<span aria-hidden="true">›</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
{literal}<script>
|
||||
(function(){if(window.__ibSeriesCarouselInit){return;}window.__ibSeriesCarouselInit=true;function s(t){var i=t.querySelector('article.product-miniature');if(!i){return Math.max(240,Math.floor(t.clientWidth*.8));}var c=window.getComputedStyle(t),g=parseFloat(c.columnGap||c.gap||'0')||0;return Math.ceil(i.getBoundingClientRect().width+g);}function u(t,p,n){var m=Math.max(0,t.scrollWidth-t.clientWidth),c=m>8;if(!c){p.disabled=true;n.disabled=true;return;}p.disabled=t.scrollLeft<=2;n.disabled=t.scrollLeft>=m-2;}function init(r){var t=r.querySelector('.js-category-products-track'),p=r.querySelector('.js-category-products-prev'),n=r.querySelector('.js-category-products-next');if(!t||!p||!n){return;}p.addEventListener('click',function(){t.scrollBy({left:-s(t),behavior:'smooth'});});n.addEventListener('click',function(){t.scrollBy({left:s(t),behavior:'smooth'});});var k=false;t.addEventListener('scroll',function(){if(k){return;}k=true;window.requestAnimationFrame(function(){u(t,p,n);k=false;});});window.addEventListener('resize',function(){u(t,p,n);});u(t,p,n);}var all=document.querySelectorAll('.js-category-products-carousel');all.forEach(init);}());
|
||||
</script>{/literal}
|
||||
|
||||
@@ -46,6 +46,39 @@
|
||||
{/literal}
|
||||
{else}
|
||||
{block name='cms_content'}
|
||||
{if $cms.id == 9}
|
||||
<style>
|
||||
.series-grid-start {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.series-grid-start__tile {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 320px;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.series-grid-start__tile:hover {
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
|
||||
.series-grid-start__tile img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="series-grid-start">
|
||||
<a class="series-grid-start__tile" href="{$urls.base_url}gniazda-wlaczniki-i-akcesoria/?seria=simon-10" title="SIMON 10" target="_blank" rel="noopener noreferrer">
|
||||
<img src="{$urls.theme_assets}img/cms/serie-simon10-tile-1.svg" alt="SIMON 10 - skontaktuj SIMON">
|
||||
</a>
|
||||
</div>
|
||||
{/if}
|
||||
{$cms.content nofilter}
|
||||
{/block}
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user