This commit is contained in:
2026-04-01 01:17:17 +02:00
parent 0f6fe82843
commit acef745ccc
24 changed files with 1785 additions and 3 deletions

View File

@@ -0,0 +1,158 @@
.crosssellpro-block {
margin: 1.25rem 0;
}
.crosssellpro-block[data-crosssellpro-mode="checkout"] {
margin: 0 0 1rem;
border: 0;
box-shadow: none;
background: transparent;
}
.crosssellpro-block[data-crosssellpro-mode="checkout"] .card-block {
padding: 0;
}
.crosssellpro-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
margin-bottom: 1rem;
}
.crosssellpro-title {
margin: 0;
}
.crosssellpro-nav {
display: flex;
gap: 0.5rem;
}
.crosssellpro-nav-btn {
width: 2rem;
height: 2rem;
border: 1px solid #c8c8c8;
border-radius: 999px;
background: #fff;
color: #222;
line-height: 1;
cursor: pointer;
}
.crosssellpro-viewport {
overflow-x: auto;
scrollbar-width: thin;
scroll-snap-type: x mandatory;
}
.crosssellpro-track {
display: flex;
gap: 1rem;
align-items: stretch;
}
.crosssellpro-item {
flex: 0 0 calc((100% - 2rem) / 3);
min-width: 220px;
border: 1px solid #efefef;
border-radius: 6px;
padding: 0.75rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
background: #fff;
scroll-snap-align: start;
}
.crosssellpro-block[data-crosssellpro-mode="checkout"] .crosssellpro-item {
flex: 0 0 100%;
min-width: 100%;
border: 0;
padding: 0;
border-radius: 0;
background: transparent;
}
.crosssellpro-block[data-crosssellpro-mode="checkout"] .crosssellpro-viewport {
overflow-x: auto !important;
overflow-y: hidden !important;
}
.crosssellpro-block[data-crosssellpro-mode="checkout"] .crosssellpro-track {
display: flex !important;
flex-direction: row !important;
flex-wrap: nowrap !important;
align-items: stretch !important;
gap: 0 !important;
}
.crosssellpro-block[data-crosssellpro-mode="checkout"] .crosssellpro-item {
flex: 0 0 100% !important;
width: 100% !important;
max-width: 100% !important;
}
.crosssellpro-image-link {
display: block;
text-align: center;
}
.crosssellpro-image-link img {
width: 100%;
max-height: 140px;
object-fit: contain;
}
.crosssellpro-image-placeholder {
display: block;
width: 100%;
height: 140px;
background: #f7f7f7;
}
.crosssellpro-name {
font-size: 0.95rem;
margin: 0;
min-height: 2.6em;
}
.crosssellpro-name a {
color: #222;
text-decoration: none;
}
.crosssellpro-price {
font-weight: 700;
margin-top: auto;
}
.crosssellpro-cta {
width: 100%;
margin-top: 0.25rem;
}
@media (max-width: 991px) {
.crosssellpro-item {
flex-basis: calc((100% - 1rem) / 2);
min-width: 180px;
}
.crosssellpro-block[data-crosssellpro-mode="checkout"] .crosssellpro-item {
flex-basis: 100%;
min-width: 100%;
}
}
@media (max-width: 767px) {
.crosssellpro-item {
flex-basis: 100%;
min-width: 100%;
}
.crosssellpro-header {
align-items: flex-start;
flex-direction: column;
}
}

View File

@@ -0,0 +1,98 @@
document.addEventListener('DOMContentLoaded', function () {
var positionCartBlocks = function () {
var blocks = document.querySelectorAll('[data-crosssellpro-block="1"][data-crosssellpro-mode="cart"]');
Array.prototype.forEach.call(blocks, function (block) {
var cartContainer = document.querySelector('.card.cart-container');
if (cartContainer && cartContainer.parentNode) {
cartContainer.insertAdjacentElement('afterend', block);
}
});
};
var getStep = function (block) {
var track = block.querySelector('.js-crosssellpro-track');
if (!track) {
return 280;
}
var firstItem = track.querySelector('.crosssellpro-item');
if (!firstItem) {
return 280;
}
var style = window.getComputedStyle(track);
var gap = parseFloat(style.columnGap || style.gap || '0');
return firstItem.offsetWidth + gap;
};
positionCartBlocks();
document.addEventListener('click', function (event) {
var prevBtn = event.target.closest('.js-crosssellpro-prev');
if (prevBtn) {
var prevBlock = prevBtn.closest('[data-crosssellpro-block="1"]');
var prevViewport = prevBlock ? prevBlock.querySelector('.js-crosssellpro-viewport') : null;
if (prevViewport && prevBlock) {
event.preventDefault();
prevViewport.scrollBy({ left: -getStep(prevBlock), behavior: 'smooth' });
}
return;
}
var nextBtn = event.target.closest('.js-crosssellpro-next');
if (nextBtn) {
var nextBlock = nextBtn.closest('[data-crosssellpro-block="1"]');
var nextViewport = nextBlock ? nextBlock.querySelector('.js-crosssellpro-viewport') : null;
if (nextViewport && nextBlock) {
event.preventDefault();
nextViewport.scrollBy({ left: getStep(nextBlock), behavior: 'smooth' });
}
return;
}
var addBtn = event.target.closest('[data-crosssellpro-add="1"]');
if (!addBtn) {
return;
}
event.preventDefault();
var block = addBtn.closest('[data-crosssellpro-block="1"]');
if (!block) {
window.location.href = addBtn.getAttribute('href') || '/';
return;
}
var cartUrl = block.getAttribute('data-cart-url');
var returnUrl = block.getAttribute('data-return-url');
var token = block.getAttribute('data-token');
var productId = addBtn.getAttribute('data-id-product');
var qty = addBtn.getAttribute('data-qty') || '1';
var postAddUrl = addBtn.getAttribute('data-post-add-url');
if (!cartUrl || !token || !productId) {
window.location.href = returnUrl || cartUrl || '/';
return;
}
var body = new URLSearchParams();
body.set('token', token);
body.set('id_product', productId);
body.set('qty', qty);
body.set('add', '1');
body.set('action', 'update');
fetch(postAddUrl || cartUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
},
body: body.toString(),
credentials: 'same-origin'
})
.then(function () {
window.location.href = returnUrl || cartUrl;
})
.catch(function () {
window.location.href = returnUrl || cartUrl || '/';
});
});
});

View File

@@ -0,0 +1,70 @@
{if !empty($crosssellpro_products)}
{assign var='crosssellpro_is_checkout' value=(isset($crosssellpro_mode) && $crosssellpro_mode == 'checkout')}
<section
class="crosssellpro-block{if isset($crosssellpro_mode) && $crosssellpro_mode == 'cart'} card{/if}"
data-crosssellpro-block="1"
data-crosssellpro-mode="{if isset($crosssellpro_mode)}{$crosssellpro_mode|escape:'htmlall':'UTF-8'}{else}cart{/if}"
data-cart-url="{$crosssellpro_cart_url}"
data-return-url="{$crosssellpro_return_url}"
data-token="{$static_token}"
>
<div class="card-block">
<div class="crosssellpro-header">
<h2 class="h4 crosssellpro-title">{l s='Produkty, ktore moga Ci sie przydac' mod='crosssellpro'}</h2>
<div class="crosssellpro-nav" aria-hidden="true">
<button type="button" class="crosssellpro-nav-btn js-crosssellpro-prev" aria-label="{l s='Poprzednie produkty' mod='crosssellpro'}">&lsaquo;</button>
<button type="button" class="crosssellpro-nav-btn js-crosssellpro-next" aria-label="{l s='Nastepne produkty' mod='crosssellpro'}">&rsaquo;</button>
</div>
</div>
<div class="crosssellpro-viewport js-crosssellpro-viewport"{if $crosssellpro_is_checkout} style="overflow-x:auto;overflow-y:hidden;"{/if}>
<div class="crosssellpro-track js-crosssellpro-track"{if $crosssellpro_is_checkout} style="display:flex;flex-direction:row;flex-wrap:nowrap;gap:0;align-items:stretch;"{/if}>
{foreach from=$crosssellpro_products item=product}
<article class="crosssellpro-item"{if $crosssellpro_is_checkout} style="flex:0 0 100%;width:100%;max-width:100%;padding:0;border:0;background:transparent;border-radius:0;"{/if}>
<a href="{$product.url}" class="crosssellpro-image-link" title="{$product.name|escape:'htmlall':'UTF-8'}">
{if !empty($product.cover.bySize.home_default.url)}
<img
class="img-fluid"
src="{$product.cover.bySize.home_default.url}"
alt="{$product.name|escape:'htmlall':'UTF-8'}"
loading="lazy"
>
{else}
<span class="crosssellpro-image-placeholder"></span>
{/if}
</a>
<h3 class="crosssellpro-name">
<a href="{$product.url}">{$product.name}</a>
</h3>
<div class="crosssellpro-price">{$product.price}</div>
{if $product.crosssellpro_requires_selection}
<a
href="{$product.crosssellpro_cta_url}"
class="btn btn-primary crosssellpro-cta"
rel="nofollow"
>
{$product.crosssellpro_cta_label}
</a>
{else}
<a
href="{$crosssellpro_return_url}"
class="btn btn-primary crosssellpro-cta"
data-crosssellpro-add="1"
data-id-product="{$product.id_product|intval}"
data-qty="{if isset($product.minimal_quantity) && $product.minimal_quantity > 0}{$product.minimal_quantity|intval}{else}1{/if}"
data-post-add-url="{$product.crosssellpro_post_add_url}"
rel="nofollow"
>
{$product.crosssellpro_cta_label}
</a>
{/if}
</article>
{/foreach}
</div>
</div>
</div>
</section>
{/if}

View File

@@ -0,0 +1 @@
{include file='module:crosssellpro/views/templates/hook/cartCrossSell.tpl'}