Przykładowe modyfikacje

Integracja z edrone - dostosowanie szablonu

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.
Darmowe szablony Comarch od wersji 2018.1 posiadają wbudowaną integrację z zaawansowanym systemem do zarządzania relacjami z klientami edrone. Jeżeli korzystasz z innych szablonów, to ten artykuł może być dla Ciebie pomocny. Poniżej znajduje się lista kroków, które należy wykonać w celu integracji z edrone.
  1. Ustawiamy swój App ID z edrone w panelu administracyjnym (Ustawienia/Ustawienia Sklepu/Ogólne/Integracja z edrone)
  2. W głównym folderze szablonu tworzymy folder edrone, w którym umieszczamy pliki, których zawartość znajduje się w kolejnych punktach
  3. W pliku html, na końcu sekcji head includujemy plik edrone/head.html (dodajemy {% include ‘edrone/head.html’ -%} ) {% if config.External.Edrone.AppId != '' -%} <script type="text/javascript"> (function (srcjs) { window._edrone = window._edrone || {}; _edrone.app_id = '{{ config.External.Edrone.AppId }}'; _edrone.version = '1.0.0'; _edrone.platform_version = '{{ config.Version }}'; _edrone.platform = 'comarch'; _edrone.shop_lang = '{{ page.Language }}';{% if page.PageId != config.DefinedPages.Order.Id -%} _edrone.action_type = 'other'; {% if usr.Authenticated -%} {% if customer.Address.Name != '' -%} {% assign FullName = customer.Address.Name -%} {% if FullName contains ' ' -%} {% assign HalfName = FullName | Split:' ' -%} {% endif %} _edrone.first_name = '{% if HalfName[0] -%}{{ HalfName[0] }}{% else %}{{ FullName }}{% endif %}'; {% if HalfName[1] -%} _edrone.last_name = '{{ HalfName[1] }}'; {% endif %} {% endif -%} {% if customer.Address.Country != '' -%} _edrone.country = '{{ customer.Address.Country }}'; {% endif -%} {% if customer.Address.City != '' -%} _edrone.city = '{{ customer.Address.City }}'; {% endif -%} {% if customer.Address.Phone != '' -%} _edrone.phone = '{{ customer.Address.PhoneNo }}'; {% endif -%} _edrone.email = '{{ customer.Email }}'; {% endif -%} {% endif -%}var doc = document.createElement('script'); doc.type = 'text/javascript'; doc.async = true; doc.src = ('https:' == document.location.protocol ? 'https:' : 'http:') + srcjs; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(doc, s); })("//d3bo67muzbfgtl.cloudfront.net/edrone_2_0.js"); </script> {% endif -%}
  4. W pliku html, na końcu sekcji body includujemy pliki edrone/product-presentation.html oraz edrone/subscribe.html
    • product-presentation.html
    {% if page.PageId == config.DefinedPages.ProductDetails.Id and config.External.Edrone.AppId != '' -%} <script type="text/javascript"> {% if productdetails -%} {% assign product-presentation = productdetails -%} {% elseif product-details -%} {% assign product-presentation = product-details -%} {% endif -%} window._edrone = window._edrone || {}; _edrone.action_type = 'product_view'; _edrone.product_ids = '{{ product-presentation.Product.Id }}'; _edrone.product_titles = '{{ product-presentation.Product.Name }}'; {% if product-presentation.Product.ImageId > 0 -%} _edrone.product_images = 'http:{{ page.BaseHref }}img/large/{{ product-presentation.Product.ImageId }}/{% if product-presentation.Product.Images[0].Name != "" -%}{{ product-presentation.Product.Images[0].Name }}{% else %}.jpg{% endif %}'; {% endif -%} {% if product-presentation.Product.Code != '' -%} _edrone.product_skus = '{{ product-presentation.Product.Code }}'; {% endif -%} _edrone.product_urls = '{{ page.CanonicalLink }}'; _edrone.product_category_ids = '{{ page.GroupId }}'; _edrone.product_category_names = '{{ page.CurrentSiteNode.Name }}'; </script> {% endif %}
    Wskazówka
    Jeżeli w swoim szablonie obiekt ze szczegółami produktu ma inną nazwę, to należy zmodyfikować warunek.
    • subscribe.html
    {% if config.External.Edrone.AppId != '' -%} {% if page.PageId == config.DefinedPages.Home.Id or page.PageId == config.DefinedPages.CustomerProfile.Id -%} <script type="text/javascript"> $(document).ajaxSuccess(function(a,b,c,d) { if(d && d.action && (d.action.Action == 'Newsletter/Subscribe' || d.action.Action == 'newsletter/subscribe') && d.action.Result){ var queryString = c.data.split('&'); for(i=0; i<queryString.length; i++){ if(queryString[i].indexOf('email') != -1){ var email = queryString[i].split('=')[1].replace('%40','@'); } } _edrone.customer_tags = 'From Newsletter Subscribe'; _edrone.email = email; _edrone.subscriber_status = '1'; _edrone.action_type = 'subscribe'; _edrone.init(); } }); </script> {% endif %} {% if page.PageId == config.DefinedPages.CustomerProfile.Id -%} <script type="text/javascript"> $(document).ajaxSuccess(function(a,b,c,d) { if(d && d.action && (d.action.Action == 'Newsletter/Unsubscribe' || d.action.Action == 'newsletter/unsubscribe') && d.action.Result){ var queryString = c.data.split('&'); for(i=0; i<queryString.length; i++){ if(queryString[i].indexOf('email') != -1){ var email = queryString[i].split('=')[1].replace('%40','@'); } } _edrone.customer_tags = 'From Newsletter Subscribe'; _edrone.email = email; _edrone.subscriber_status = '0'; _edrone.action_type = 'subscribe'; _edrone.init(); } }); </script> {% endif %} {% endif %}
    Wskazówka
    Jeżeli w swoim szablonie umożliwiasz zapis do newslettera z innych stron niż strona główna i profil klienta oraz pozwalasz na wypisanie poza profilem klienta, to należy zmodyfikować powyższy plik.
    Uwaga
    W wersji 2018.7 z domyślnych szablonów Comarch usunięto akcję dotyczącą zapisu i wypisu z newslettera, ponieważ dane przekazywane są przez Comarch e-Sklep w tle.
  5. W szablonach Agat, Bursztyn i Opal, w tym samym miejscu includujemy również plik edrone/add-to-cart.html {% if page.PageId == config.DefinedPages.ProductDetails.Id and config.External.Edrone.AppId != '' -%} <script type="text/javascript"> $(document).ajaxSuccess(function(a,b,c,d) { if(d && d.action && (d.action.Action == 'Cart/Add' || d.action.Action == 'cart/add') && d.action.Result ){ _edrone.action_type = 'add_to_cart'; _edrone.init(); } }); </script> {% endif %}
    Wskazówka
    Plik należy dodać do wszystkich szablonów, które nie są oparte o najnowszą wersję szablonu Szafir (w tym szablonie dodawanie do koszyka w edrone obsłużone jest bezpośrednio w funkcjach szablonu – spowodowane jest to możliwością dodawania kilku towarów jednocześnie czego inne szablony nie posiadają).
  6. W szablonie Bursztyn, w tym samym miejscu includujemy również plik edrone/add-to-cart-with-data.html {% if config.External.Edrone.AppId != '' -%} {% if page.PageId == config.DefinedPages.Home.Id or page.PageId == config.DefinedPages.ProductList.Id -%} <script type="text/javascript"> $(document).ajaxSuccess(function(a,b,c,d) { if(d && d.action && (d.action.Action == 'Cart/Add' || d.action.Action == 'cart/add') && d.action.Result ){ window._edrone = window._edrone || {};var products = d.collection["customer.Cart"].Products; var size = products.length; var product = products[size-1];_edrone.product_ids = product.Id; _edrone.product_titles = product.NameNoHtml; if(product.ImageId > 0){ _edrone.product_images = 'http:{{ page.BaseHref }}img/large/'+product.ImageId+'/.jpg'; } if(product.Code != ''){ _edrone.product_skus = product.Code; } _edrone.product_urls = 'http:{{ page.BaseHref }}'+product.Url; _edrone.product_category_ids = product.DefaultGroup; _edrone.action_type = 'add_to_cart'; _edrone.init(); } }); </script> {% endif %} {% endif %}
    Wskazówka
    Kod działa dla dodawania do koszyka ze strony głównej i listy towarów. Jeżeli pozwalasz na dodawanie do koszyka z innych stron, należy go zmodyfikować.
  7. W pliku z podziękowaniami za zakup (Agat – partials/cart/summary.html, Bursztyn i Opal – order/thank-you.html, Szafir – order/thx.html), na końcu pliku (tuż pod includem rich-snippetów), includujemy plik edrone/order.html {% if config.External.Edrone.AppId != '' -%} <script type="text/javascript"> _edrone.action_type = 'order';{% if cart -%} {% assign order = cart -%} {% endif -%}{% assign FullName = order.Customer.DeliveryAddress.Name -%} {% if FullName contains ' ' -%} {% assign HalfName = FullName | Split:' ' -%} {% endif %} _edrone.first_name = '{% if HalfName[0] -%}{{ HalfName[0] }}{% else %}{{ FullName }}{% endif %}'; {% if HalfName[1] -%} _edrone.last_name = '{{ HalfName[1] }}'; {% endif %} _edrone.country = '{{ order.Customer.DeliveryAddress.Country }}'; {% if order.Customer.DeliveryAddress.City != '' -%} _edrone.city = '{{ order.Customer.DeliveryAddress.City }}'; {% endif %} {% if order.Customer.DeliveryAddress.PhoneNo != '' -%} _edrone.phone = '{{ order.Customer.DeliveryAddress.PhoneNo }}'; {% endif %} _edrone.email = '{{ order.Customer.Email }}'; {% assign size = order.PlacedOrder.Products | Size -%} var ids = ''; var titles = ''; var images = ''; var skus = ''; var urls = ''; var categoryIds = ''; var counts = ''; {% for product in order.PlacedOrder.Products -%} ids += '{{ product.Id }}{% if forloop.index != size -%}|{% endif -%}'; titles += '{{ product.NameNoHtml }}{% if forloop.index != size -%}|{% endif -%}'; images += '{% if product.ImageId > 0 -%}http:{{ page.BaseHref }}img/large/{{ product.ImageId }}/.jpg{% endif -%}{% if forloop.index != size -%}|{% endif -%}'; skus += '{{ product.Code }}{% if forloop.index != size -%}|{% endif -%}'; urls += 'http:{{ page.BaseHref }}{{ product.Url }}{% if forloop.index != size -%}|{% endif -%}'; categoryIds += '{{ product.DefaultGroup }}{% if forloop.index != size -%}|{% endif -%}'; counts += '{{ product.Quantity | Normalize }}{% if forloop.index != size -%}|{% endif -%}'; {% endfor -%} _edrone.product_ids = ids; _edrone.product_titles = titles; _edrone.product_images = images; _edrone.product_skus = skus; _edrone.product_urls = urls; _edrone.product_category_ids = categoryIds; _edrone.product_counts = counts; _edrone.order_id = '{{ order.PlacedOrder.Id }}'; _edrone.base_currency = '{{ order.Currency }}'; _edrone.order_currency = '{{ order.Currency }}'; _edrone.base_payment_value = '{{ order.OrderTotalValue | ToString | Replace:",","." }}'; _edrone.order_payment_value = '{{ order.OrderTotalValue | ToString | Replace:",","." }}'; _edrone.init(); </script> {% endif -%}
    Wskazówka
    Jeżeli w swoim szablonie używasz dla koszyka innej nazwy obiektu niż cart lub order, to powyższy kod należy zmodyfikować.
  Edrone i dodawanie towarów w szablonie Szafir:
  1. Plik js/init-ui1.js
  • Funkcja serialAddingToCart
Na samym początku funkcji znajduje się pętla “each”, a w niej instrukcja warunkowa „if (quantity > 0)”. Na końcu tej instrukcji warunkowej wklejamy poniższy kod: if(typeof _edrone !== 'undefined') { var product = form.parents('.product-item-lq'); var base = $('[data-base]').data('base'); if (first) { _edrone.product_ids = productId; _edrone.product_titles = product.find('.product-name-lq').text(); _edrone.product_images = base + product.find('img').data('src'); _edrone.product_skus = product.find('.product-code-lq').text(); _edrone.product_urls = base + product.find('a').attr('href'); _edrone.product_category_ids = $('[data-group-id]').data('group-id'); _edrone.product_category_names = $('[data-group-name]').data('group-name'); first = false; } else { _edrone.product_ids += '|' + productId; _edrone.product_titles += '|' + product.find('.product-name-lq').text(); _edrone.product_images += '|' + base + product.find('img').data('src'); _edrone.product_skus += '|' + product.find('.product-code-lq').text(); _edrone.product_urls += '|' + base + product.find('a').attr('href'); _edrone.product_category_ids += '|' + $('[data-group-id]').data('group-id'); _edrone.product_category_names += '|' + $('[data-group-name]').data('group-name'); } } Kilkanaście linijek niżej (w tej samej funkcji) jest instrukcja warunkowa “if (resGuardian)”. Na początku tej instrukcji warunkowej wklejamy poniższy kod: if(typeof _edrone !== 'undefined') { _edrone.action_type = 'add_to_cart'; _edrone.init(); }
  • Funkcja addManyProducts
Na samym początku funkcji znajduje się pętla “for(var i=0; i<products.length; i++)” iterująca po produktach. Tuż przed nią należy dodać poniższą linijkę: var first = true; Następnie, w tej pętli znajduje się instrukcja warunkowa „if(products[i].askForPriceChild == false && products[i].quantity > 0)”. Na samym jej końcu należy dodać poniższy kod: if(parameters && !validationError && products[i].supply != undefined && typeof _edrone !== 'undefined'){ if(first){ _edrone.product_ids = products[i].supply; _edrone.product_images = products[i].img; _edrone.product_skus = products[i].code; var name = _edrone.product_titles; var url = _edrone.product_urls; var categoryId = _edrone.product_category_ids; var categoryName = _edrone.product_category_names; first = false; } else { _edrone.product_ids += '|' + products[i].supply; _edrone.product_images += '|' + products[i].img; _edrone.product_skus += '|' + products[i].code; _edrone.product_titles += '|' + name; _edrone.product_urls += '|' + url; _edrone.product_category_ids += '|' + categoryId; _edrone.product_category_names += '|' + categoryName; } } Kilkanaście linijek niżej (w tej samej funkcji) jest “$.post”. Na samym jego początku należy dodać poniższy kod: if(typeof _edrone !== 'undefined') { _edrone.action_type = 'add_to_cart'; _edrone.init(); }
  • Dodanie funkcji simpleAddToCartSuccess
Na końcu pliku js/init-ui1.js należy dodać poniższy kod: function simpleAddToCartSuccess(e){ updateClientArea(); if(typeof _edrone !== 'undefined') { if ($('.add-from-presentation-lq').index() == -1) { var product = $(e.currentTarget).parents('.product-item-lq'); var base = product.parents('[data-base]').data('base'); _edrone.product_ids = product.data('product-id'); _edrone.product_titles = product.find('.product-name-lq').text(); _edrone.product_images = base + product.find('img').attr('src'); _edrone.product_skus = product.find('.product-code-lq').text(); _edrone.product_urls = base + product.find('a').attr('href'); _edrone.product_category_ids = product.parents('[data-group-id]').data('group-id'); _edrone.product_category_names = product.parents('[data-group-name]').data('group-name'); } _edrone.action_type = 'add_to_cart'; _edrone.init(); } } Następnie należy zmodyfikować “event”, który jest wywoływany przy kliknięciu w element z klasą “add-to-cart-update-client-area-lq”. Zmieniamy zawartość tego eventa na: app.post(e, simpleAddToCartSuccess, e); Cały event powinien wyglądać następująco: $('body').on('click', '.add-to-cart-update-client-area-lq', function(e) { app.post(e, simpleAddToCartSuccess, e); });
  1. Plik js/init-ui2.js
  • Funkcja serialAddingFromWishlist
Na samym początku funkcji znajduje się pętla “each”. Tuż przed nią należy dodać poniższą linijkę: var first = true; Następnie, w tej pętli znajduje się instrukcja warunkowa „if (quantity > 0)”. Na samym jej końcu należy dodać poniższy kod: if(typeof _edrone !== 'undefined') { if (first) { _edrone.product_ids = productId; _edrone.product_titles = productContainer.find('.product-name-lq').text(); _edrone.product_images = base + productContainer.find('img').attr('src'); _edrone.product_skus = productContainer.find('.product-code-lq').text(); _edrone.product_urls = base + productContainer.find('a').attr('href'); _edrone.product_category_ids = categoryId; first = false; } else { _edrone.product_ids += '|' + productId; _edrone.product_titles += '|' + productContainer.find('.product-name-lq').text(); _edrone.product_images += '|' + base + productContainer.find('img').attr('src'); _edrone.product_skus += '|' + productContainer.find('.product-code-lq').text(); _edrone.product_urls += '|' + base + productContainer.find('a').attr('href'); _edrone.product_category_ids += '|' + categoryId; } } Kilkanaście linijek niżej (w tej samej funkcji) jest instrukcja warunkowa “if (resGuardian)”. Na początku tej instrukcji warunkowej wklejamy poniższy kod: if(typeof _edrone !== 'undefined') { _edrone.action_type = 'add_to_cart'; _edrone.init(); }

Nowe Inpost API - aktualizacja paczkomatów i punktów odbioru osobistego w szablonach

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.

Szablon Szafir

Wskazówka
Pliki potrzebne do aktualizacji paczkomatów i punktów odbioru osobistego: Pobierz pliki
  1. Wrzuć pliki „collection-points-scripts.html” i „collection-points-styles.html” do głównego folderu z szablonem (tam gdzie „__layout.html”).
  2. Wrzuć plik „collection-points.js” do folderu „js”.
  3. Wrzuć plik “collection-points.css” do folderu “css”.
  4. Podmień plik “collection-points.html”, który znajduje się w folderze „order/delivery-partials”.
  5. W pliku „__layout.html”, na końcu sekcji „<head>” dodaj poniższą linijkę: {% include 'collection-points-styles.html' %}
  6. W pliku „__layout.html”, na końcu sekcji „<body>” dodaj poniższą linijkę: {% include 'collection-points-scripts.html' %}
  7. Dodaj tłumaczenie: - SearchInpostInfo: "Szukaj po mieście, adresie i nazwie paczkomatu”
  8. (punkt opcjonalny – poprawienie styli – bez niego paczkomaty i POO będą działać ale będzie brzydko wyglądać i każdy to zauważy) W pliku “order/delivery-partials/delivery-section.html”, pod formularzem „<form class="no-message-lq delivery-form-lq">” należy utworzyć nowy kontener “<div class="clear-after-ui"></div>”, a następnie wkleić do niego dwa ostatnie kontenery z klasą “f-left-ui” wraz z ich zawartością. <div class="clear-after-ui"> <div class="f-left-ui half-ui-with-space-ui cart-option-ui delivery-address-lq address-ui delivery-address-ui"> <p> {% if order.SelectedDelivery.CollectionPointTypeId == 0 -%} {{translations.ShippingAddress}} {% else -%} {{translations.MyData}} {% endif -%} </p> <div class="address-template-container-lq" data-address-type="0"> {% assign lackOfPhone = false -%} {% assign countryMismatch = false -%}{% if order.SelectedDelivery.PhoneRequired and deliveryAddress.PhoneNo == '' -%} {% assign lackOfPhone = true -%} {% endif -%} {% comment -%} {% if page.Cookies.deliveryAddressId == null or page.Cookies.deliveryAddressId == '' -%} {% for profileAddress in customer.DeliveryAddresses -%} {% if profileAddress.Default and profileAddress.Country != config.DefaultCountry -%} {% assign countryMismatch = true -%} {% break -%} {% endif -%} {% endfor -%} {% if countryMismatch == false and deliveryAddress.Country != config.DefaultCountry -%} {% assign countryMismatch = true -%} {% endif -%} {% elseif page.Cookies.deliveryAddressId == -1 -%} {% assign countryMismatch = true -%} {% else -%} {% for profileAddress in customer.DeliveryAddresses -%} {% if profileAddress.Id == page.Cookies.deliveryAddressId -%} {% if profileAddress.Country != deliveryAddress.Country %} {% assign countryMismatch = true -%} {% endif -%} {% break -%} {% endif -%} {% endfor -%} {% endif -%} {% endcomment -%} {% if page.Cookies.delivCountryChanged == 'true' -%} {% assign countryMismatch = true -%} {% endif -%} {% if deliveryAddress.Name == '' or lackOfPhone or countryMismatch -%} {% include 'order/delivery-partials/address-form.html' with 'orderDa' -%} {% else -%} {% include 'order/delivery-partials/address-presentation.html' with 'orderDa' -%} {% endif -%} </div> </div> <div class="f-left-ui half-ui-with-space-ui cart-option-ui invoice-address-lq address-ui invoice-address-ui"> <p>{{translations.InvoiceData}}</p> <div class="address-template-container-lq" data-address-type="1"> {% if invoiceAddress.Name == '' and order.Customer.CustomerDetailsEditable -%} {% include 'order/delivery-partials/address-form.html' with 'inv-add' -%} {% else -%} {% include 'order/delivery-partials/address-presentation.html' with 'inv-add'-%} {% endif -%} </div> </div> </div>

Szablon Agat

Wskazówka
Pliki potrzebne do aktualizacji paczkomatów i punktów odbioru osobistego: Pobierz pliki
  1. Podmień plik ‘order.js’, który znajduje się w folderze ‘js’.
  2. Wrzuć plik ‘collection-points.css’ do folderu ‘css’.
  3. a) Jeśli kod szablonu nie był modyfikowany, podmień plik ‘delivery-adress.html’, który znajduje się w folderze ‘partials/cart’. b) Jeśli kod szablonu był modyfikowany, podmień obecny widok mapy z paczkomatami następującym: {% if cart.SelectedDelivery.CollectionPointTypeId == 2 or cart.SelectedDelivery.CollectionPointTypeId == 1 -%} <div class="geowidget-container" id="map"> {% if settings.googleMapsKey == '' or cart.SelectedDelivery.CollectionPointTypeId == 1 -%} <div id="collection-points-searcher" class="collection-points-search-box {% if cart.SelectedDelivery.CollectionPointTypeId == 1 -%} personal-collection-points {% endif -%}"> <input placeholder="{% if cart.SelectedDelivery.CollectionPointTypeId == 1 -%} {{translations.TypeAdress}} {% else -%}{{translations.AddCodeOrAdress}}{% endif -%}" type="search" id="machines-filter-input" class="collection-point-search-input"> <span class="fa fa-search"></span> <div class="collection-points-result hidden"><ul></ul></div> </div> {% elsif settings.googleMapsKey != '' and cart.SelectedDelivery.CollectionPointTypeId == 2 -%} <div id="easypack-map"></div> {% endif -%} </div> {% endif -%}
  4. W pliku ‘_layout.html’, na końcu sekcji ‘<head>’ dodaj poniży kod: {% if page.PageId == config.DefinedPages.Order.Id -%} <link rel="stylesheet" type="text/css" href="https://geowidget.easypack24.net/css/easypack.css"> <link rel="stylesheet" type="text/css" href="css/collection-points.css"> {% endif -%}
  5. W pliku ‘_layout.html’, na końcu sekcji ‘<body>’ dodaj poniży kod: {% if page.PageId == config.DefinedPages.Order.Id -%} <script async src="https://geowidget.easypack24.net/js/sdk-for-javascript.js"></script> {% endif -%}
  6. Dodaj tłumaczenia:  - TypeAdress: "Wpisz adres"  - AddCodeOrAdress: "Wpisz kod paczkomatu lub adres"
  7. Aby paczkomaty działały poprawnie również przy przechodzeniu na poprzedni i następny krok koszyka: a) Jeśli kod szablonu nie był modyfikowany, podmień plik ‘init.js’, który znajduje się w folderze ‘js’. b) Jeśli kod szablonu był modyfikowany, wywołaj funkcje: orderCollectionPoints.easyPackAsyncInit(); orderCollectionPoints.getCollectionPoints(); w tych funkcjach, które są wywoływane przy zmianie sekcji na ‘CustomerData’ w koszyku: Zmienione fragmenty pliku ‘init.js’ to: orderPrevStep: function (e) { var container = $('.shopping-cart'); var data = { __template: 'partials/cart/cart-template.html', __csrf: __CSRF, __action: 'Order/StepPrev' }; $.post(null, data, function (result) { if (result.action.Result) { $('.shopping-cart').html(result.template); if ($('body').find('#invoice-address-data').index() >= 0) { application.uiSetSwitchNameWidthInInvoice(); } if ($('#easypack-map').length > 0) { orderCollectionPoints.easyPackAsyncInit(); } if($('#collection-points-searcher').length > 0) { orderCollectionPoints.getCollectionPoints(); } if ($('.geowidget-container').length > 0) { $('#main-section .save-delivery-address-data').addClass('order-next-step').removeClass('choose-collection-point'); } application.loadImages(); application.uiScrollToTop(container); application.uiCheckLabels(); } else { if (result.action.Code != 100) { if (result.action.Validation != null) { errorMessage = '<div class="title">' + result.action.Message + '</div>' + '<p>' + result.action.Validation[0].Error + '</p>'; } else { errorMessage = '<div class="title">' + result.action.Message + '</div>'; } application.createMessage(errorMessage); } } }); }, orderNextStep: function (e) { if ($('#delivery-address-data').index() >= 0) { var allEdited = true; $('#delivery-address-data input[required]').each(function () { if (allEdited === false) { return; } else { if ($(this).val() == '') { allEdited = false; } } }); if (allEdited === false) { var errorMessage = $(e.currentTarget).next().next().val(); application.createMessage(errorMessage, 3000); return; } else if ($('input[name="invoice"]').hasClass('company') && $('input[name="tin"]').val() == '' && $('input[name="tin"]').prop('required')) { var message = $('input[name="company"]').attr('data-info'); application.createMessage(message, 2000); } else { var data = $('#delivery-address-data').serializeArray(); var validate = application.uiValidateForm($('#delivery-address-data'));if (validate) { $.post(null, data, function (result) { if (result.action.Result) { var container = $('body'); var data = { __template: 'partials/cart/cart-template.html', __csrf: __CSRF, __action: 'Order/StepNext' }; $.post(null, data, function (result) { if (result.action.Result) { $('.shopping-cart').replaceWith(result.template); if ($('body').find('#invoice-address-data').index() >= 0) { application.uiSetSwitchNameWidthInInvoice(); } application.loadImages(); application.uiSetSwitchNameWidthInSummaryCheckboxes(); application.uiScrollToTop(container); application.uiCheckLabels(); if ($('#easypack-map').length > 0) { orderCollectionPoints.easyPackAsyncInit(); } if($('#collection-points-searcher').length > 0) { orderCollectionPoints.getCollectionPoints(); } } else { if (result.action.Code != 100) { if (result.action.Validation != null) { errorMessage = '<div class="title">' + result.action.Message + '</div>' + '<p>' + result.action.Validation[0].Error + '</p>'; } else { errorMessage = '<div class="title">' + result.action.Message + '</div>'; } application.createMessage(errorMessage, 3000); } } }); } else { if (result.action.Code != 100) { if (result.action.Validation != null) { errorMessage = '<div class="title">' + result.action.Message + '</div>' + '<p>' + result.action.Validation[0].Error + '</p>'; } else { errorMessage = '<div class="title">' + result.action.Message + '</div>'; } application.createMessage(errorMessage, 3000); return; } } }); } } } else { var container = $('body'); var data = { __template: 'partials/cart/cart-template.html', __csrf: __CSRF, __action: 'Order/StepNext' }; $.post(null, data, function (result) { if (result.action.Result) { if ((result.template).indexOf('name="sel-del-met" value="' + 1 + '"') !== -1 || (result.template).indexOf('name="sel-del-met" value="' + 2 + '"') !== -1) { application.orderPrevStep(); } else { $('.shopping-cart').replaceWith(result.template); if ($('body').find('#invoice-address-data').index() >= 0) { application.uiSetSwitchNameWidthInInvoice(); } if ($('#easypack-map').length > 0) { orderCollectionPoints.easyPackAsyncInit(); } if($('#collection-points-searcher').length > 0) { orderCollectionPoints.getCollectionPoints(); } application.loadImages(); application.uiSetSwitchNameWidthInSummaryCheckboxes(); application.uiScrollToTop(container); application.uiCheckLabels(); } } else { if (result.action.Code != 100) { if (result.action.Validation != null) { errorMessage = '<div class="title">' + result.action.Message + '</div>' + '<p>' + result.action.Validation[0].Error + '</p>'; } else { errorMessage = '<div class="title">' + result.action.Message + '</div>'; } application.createMessage(errorMessage); } } }); } },

Szablon Opal

Wskazówka
Pliki potrzebne do aktualizacji paczkomatów i punktów odbioru osobistego: Pobierz pliki
  1. Wrzuć plik ‘collection-points.js’ do folderu ‘js’.
  2. Wrzuć plik ‘collection-points.css’ do folderu ‘css’.
  3. Podmień plik ’delivery-points-partial.html’, który znajduje się w folderze ‘order’.
  4. a) Jeśli kod szablonu nie był modyfikowany, podmień plik ‘data-form.html’, który znajduje się w folderze ‘order/cst-data’. b) Jeśli kod szablonu był modyfikowany, podmień obecny widok mapy z paczkomatami linijką: {% include 'order/delivery-points-partial.html' -%}
  5. W pliku ‘_layout.html’, na końcu sekcji ‘<head>’ dodaj poniży kod: {% if page.PageId == config.DefinedPages.Order.Id -%} <link rel="stylesheet" type="text/css" href="https://geowidget.easypack24.net/css/easypack.css"> <link rel="stylesheet" type="text/css" href="css/collection-points.css"> {% endif -%}
  6. W pliku ‘order-cart.html’, do bloku ‘{% block PageBodyEnd -%}’ dodaj poniży kod: <script async src="https://geowidget.easypack24.net/js/sdk-for-javascript.js"></script> <script src="js/collection-points.js"></script>
  7. Dodaj tłumaczenie:  - TypeAdress: "Wpisz adres"
  8. Aby paczkomaty działały poprawnie również przy przechodzeniu na poprzedni i następny krok koszyka: a) Jeśli kod szablonu nie był modyfikowany, podmień plik ‘order.js’, który znajduje się w folderze ‘js’. b) Jeśli kod szablonu był modyfikowany, wywołaj funkcje: orderCollectionPoints.easyPackAsyncInit(); orderCollectionPoints.getCollectionPoints(); w tych funkcjach, które są wywoływane przy zmianie sekcji na ‘customerData’ w koszyku: Zmienione fragmenty pliku ‘order.js’, to: orderForm.on('click', '#customer-data-form', function (e) { e.preventDefault(); $.get('', {__template: 'order/cst-data/data-form.html'}, function (res) { orderForm.find('.content').html(res.template); if ($('#easypack-map').length > 0) { orderCollectionPoints.easyPackAsyncInit(); } if($('#collection-points-searcher').length > 0) { orderCollectionPoints.getCollectionPoints(); } window.sessionStorage.setItem('continueWithoutRegistration', true); }); }); var initStep = (function f(e, step) { if (!$('.order').hasClass('cart-empty')) { stepsLinks(); }if (step && step == 'ThankYou') { $("#order-form").find("script").each(function () { eval($(this).text()); }); if (window.sessionStorage.getItem('continueWithoutRegistration')) { window.sessionStorage.removeItem('continueWithoutRegistration'); } } if (step && step == 'CustomerData') { window.sessionStorage.setItem('toSkip', true); if (window.sessionStorage.getItem('continueWithoutRegistration')) { $.get('', {__template: 'order/cst-data/data-form.html'}, function (res) { orderForm.find('.content').html(res.template); if ($('#easypack-map').length > 0) { orderCollectionPoints.easyPackAsyncInit(); } if($('#collection-points-searcher').length > 0) { orderCollectionPoints.getCollectionPoints(); } }); } } return f; })(); orderForm.on('click', '.recalculate, #remove-points', function (e) { e.preventDefault(); $.post(null, { __collection: 'customer.Cart.Value', __template: 'order/cart.html', __csrf: __CSRF, __action: 'cart/Recalculate' }, function (d) { CreateTooltip(d.action); showTooltip(); $('.order-content').replaceWith(d.template); lazyCheck(); UpdateSmallCrt(d.collection); orderCollectionPoints.checkPointsAvailability(); }); });

Szablon Bursztyn

Wskazówka
Pliki potrzebne do aktualizacji paczkomatów i punktów odbioru osobistego: Pobierz pliki
    1. Wrzuć plik „collectionpoints.js” do folderu „js”.
    2. Wrzuć plik “collectionpoints.css” do folderu “css” .
    3. W pliku html w sekcji <head> pod koniec znaczników <link> dodaj poniższy kod: {% if page.PageId == config.DefinedPages.Order.Id -%} <link rel="stylesheet" type="text/css" href="https://geowidget.easypack24.net/css/easypack.css"> <link rel="stylesheet" type="text/css" href="css/collectionpoints.css"> {% endif -%}
    4. W pliku ‘order-cart.html’, do bloku ‘{% block PageBodyEnd -%}’ dodaj poniży kod: <script async src="https://geowidget.easypack24.net/js/sdk-for-javascript.js"></script> <script src="js/collection-points.js"></script>
    5. Plik cart.html: a) Jeśli kod szablonu nie był modyfikowany, wystarczy podmienić plik, który znajduje się w katalogu ‘order’. b) Jeśli kod szablonu był modyfikowany, podmień obecny widok mapy z paczkomatami następującym kodem: {% if cart.SelectedDelivery.CollectionPointTypeId == 2 or cart.SelectedDelivery.CollectionPointTypeId == 1 -%} <div class="col-sm-12 geowidget-container" id="map"> <h2> {% if cart.SelectedDelivery.CollectionPointTypeId == 1 -%} {{translations.Crt_ChooseCollectionPoint}} {% else -%} {{translations.Crt_ChooseInpostMachine}} {% endif -%} </h2>{% if settings.googleMapsKey == '' or cart.SelectedDelivery.CollectionPointTypeId == 1 -%} <div id="collection-points-searcher" class="collection-points-search-box {% if cart.SelectedDelivery.CollectionPointTypeId == 1 -%} personal-collection-points {% endif -%}"> <input placeholder="{% if cart.SelectedDelivery.CollectionPointTypeId == 1 -%} {{transla-tions.TypeAdress}} {% else -%}{{translations.Crt_MachineCodeOrAdress}}{% endif -%}" type="search" id="machines-filter-input" class="collection-point-search-input"> <span class="fa fa-search"></span> <div class="collection-points-result hidden"><ul></ul></div> </div> {% elsif settings.googleMapsKey != '' and cart.SelectedDelivery.CollectionPointTypeId == 2 -%} <div id="easypack-map"></div> {% endif -%} </div> {% endif -%}
    6. Plik order-short-info.html : a) Jeśli kod szablonu nie był modyfikowany, podmień plik znajdujący się w folderze ‘order’ b) Jeśli kod szablonu był modyfikowany, podmień linijkę {% assign displayOptions = include %} (znajdującą się na początku dokumentu), poniższym kodem: {% if page.POST.__include %} {% assign displayOptions = page.POST.__include %} {% else %} {% assign displayOptions = include %} {% endif %}
    7. Plik cart-products-partial.html: a) Jeśli kod szablonu nie był modyfikowany, podmień plik znajdujący się w folderze ‘order’ b) Jeśli kod szablonu był modyfikowany, proszę usunąć poniższy kod znajdujący się pod koniec znacznika <div class=”custom-collection”> : {% if settings.googleMapsKey != '' and deliveryData.CollectionPoints[deliveryData.CollectionPointId].Latitude <> "" and deliveryData.CollectionPoints[deliveryData.CollectionPointId].Longitude <> "" and step == 'summary' -%} <p class="collection-localization"> <span class="glyphicon glyphicon-map-marker"></span> {{translations.Crt_LocateOnMap}} </p> <div id="map-canvas" data-step="{{step}}" data-type="{{deliveryData.CollectionPointTypeId}}" data-lat="{{deliveryData.CollectionPoints[deliveryData.CollectionPointId].Latitude}}" data-lng="{{deliveryData.CollectionPoints[deliveryData.CollectionPointId].Longitude}}"></div> {% endif -%}
    8. Aby paczkomaty działały poprawnie podczas przechodzenia na poprzedni oraz następny krok koszyka należy: a) Jeśli kod szablonu nie był modyfikowany, podmień plik ‘order.js’ znajdujący się w folderze ‘js’ b) Jeśli kod szablonu był modyfikowany, należy wywołać orderCollectionPoints.cpCheck() oraz orderCollectionPoints .checkPackage() na końcu funkcji, która przenosi użytkownika do poprzedniego kroku koszyka.Funkcja orderCollectionPoints.cpCheck() jest odpowiedzialna za inicjalizację mapy bądź listy paczkomatów na danej stronie. Z kolei orderCollectionPoints.checkPackage() sprawdza czy paczkomat do którego ma być zrealizowane zamówienie został już wybrany.Funkcja orderCollectionPoints.cpCheck() należałoby również dodać do zdarzenia obsługującego zmianę sposobu dostawy.Ostatnią rzeczą jaką trzeba zrobić na koniec zamówienia, kiedy klient zrealizuje już transakcję, to dodanie linijki sessionStorage.removeItem(‘paczka’). Ma ona na celu usunięcia informacji o wybranym paczkomacie.
Wskazówka
Przeczytaj więcej o nowym API serwisu InPost.

Obsługa kanałów do zgody na newsletter w darmowych szablonach graficznych Comarch e-Sklep

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.
Listę zgód wyświetlanych w newsletterze możemy znaleźć w tablicy przechowywanej w obiekcie config.TOS.Consents.Newsletter. Poniższa pętla pozwoli nam wyświetlić wszystkie zgody znajdujące się w obiekcie Newsletter:
{% for tos in config.TOS.Consents.Newsletter -%}
<label class="statement tos-name">{{tos.Text}}</label> {% endif -%}  Jeżeli interesuje nas pokazanie wszystkich kanałów znajdujących się w danej zgodzie to warto najpierw przypisać ich ilość do konkretnej zmiennej liquidowej. W każdej pojedynczej zgodzie kanały możemy znaleźć w obiekcie Channels.
{% assign channelsSize = tos.Channels | Size -%}
Teraz wystarczy tylko napisać prostą pętle „for” Poniżej przykład wykorzystujący listę.
{% if channelsSize > 0 -%}
<ul class="channels disabled">
    {% for chn in tos.Channels -%}
    <li class="channel">
        <input id="channel{{ chn.Key }}" type="checkbox" name="channelKey" value="{{ chn.Key }}" disabled/>
        <div class="channel-switch input-switch "><div class="switch-button"></div></div>
        <label for="channel{{ chn.Key }}">{{chn.Name}}</label>
    </li>
    {% endfor -%}
</ul>
{% endif -%}

Szablon Agat

1.Zmień pętlę for wyświetlającą checkboxy ze zgodami w pliku 'footer.html', który znajduje się w folderze 'partials/common' {% for tos in config.TOS.Consents.Newsletter -%} <div class="input-group switches"> <label class="switch"> <span class="switch-name summary-tos-switch-name">{% if tos.Required -%}* {% endif -%}{{ tos.Text }}</span> <input class="switch-input" type="checkbox" name="tos" value="{{ tos.Id }}" {% if tos.Required -%} data-required="req" {% endif -%}/> <span class="switch-label"></span> <span class="switch-handle"></span> </label> </div> {% endfor -%} {% for tos in config.TOS.Consents.Newsletter -%} {% assign channelsSize = tos.Channels | Size -%} <li class="{% if channelsSize > 0 -%}multiple-choice {% endif -%} {% if tos.Required -%} required-consent{% endif -%}"> <div class="input-group switches"> {% if tos.Statement %} <p class="multiple-switch">{% if tos.Required -%}* {% endif -%}{{ tos.Text }}</p> {% else %} <span class="main-consent-warning tos-warning"><span class="fa fa-exclamation-triangle"></span>{{translations.MainConsentWarning}}</span> <label class="switch"> <span class="switch-name summary-tos-switch-name">{% if tos.Required -%}* {% endif -%}{{ tos.Text }}</span> <input class="switch-input" type="checkbox" name="tos" value="{{ tos.Id }}" {% if tos.Required -%} data-required="req" {% endif -%}/> <span class="switch-label"></span> <span class="switch-handle"></span> </label> {% endif -%} {% if channelsSize > 0 -%} <span class="channel-warning tos-warning"><span class="fa fa-exclamation-triangle"></span>{{translations.ChannelWarning}}</span> {% for chn in tos.Channels -%} <label class="switch channel-switch {% if tos.Statement == false %} unactive-switch{% endif -%}"> <span class="switch-name">{{chn.Name}}</span> <input class="switch-input channel-checkbox" type="checkbox" name="channelKey" value="{{ chn.Key }}" {% if tos.Required -%} data-required="req" {% endif -%} {% if tos.Statement == false %} disabled{% endif -%}/> <span class="switch-label"></span> <span class="switch-handle"></span> </label> {% endfor -%} {% endif -%} </div> </li> {% endfor -%} 2.W pliku 'init.js' uzupełnij funkcję uiValidateForm o walidację pól kanałów. Plik znajduje się w folderze 'js'. uiValidateForm: function (form) { var formRequiredInputs = form.find('input[required], textarea[required]'); var tosInputs = form.find('input[type="checkbox"]'); var formInvalidInfo = form.data('invalid'); var emailInvalidInfo = form.data('email-invalid'); var postCodeInvalidInfo = form.data('pcode-invalid'); var tinInvalidInfo = form.data('tin-invalid'); var tosRequiredInfo = form.data('tos-invalid'); var warningTosInfos = form.find('.tos-warning'); var tosRequiredValid = true; var tinInputValid = true; var emailInputValid = true; var postCodeInputValid = true; var isEmpty = false; var checkedChannels = []; var channelsInputs; $.each(formRequiredInputs, function (key, value) { $(value).addClass('validationStyles'); if ($(value).val() == '') { isEmpty = true; } if ($(value).attr('name') === 'email') { emailInputValid = application.uiValidateEmail($(value).val()); } if ($(value).attr('name') === 'zipCode') { postCodeInputValid = application.uiValidatePostCode($(value).val(), $(value).attr('pattern')); } if ($(value).attr('name') === 'tin') { tinInputValid = application.uiValidateTinCode($(value).val(), $(value).attr('pattern')); } }); $.each(tosInputs, function (key, value) { if ($(value).data('required') === 'req') { if (!$(value).prop('checked')) { $(value).closest('label').addClass('required-checkbox-warning'); } if ($(value).hasClass('channel-checkbox')) { channelsInputs = $(value).closest('.input-group').find('.channel-checkbox'); $.each(channelsInputs, function (key, value) { if ($(value).prop('checked')) { checkedChannels.push($(value)); } }); if (checkedChannels.length > 0) { tosRequiredValid = true } else { tosRequiredValid = false } if (warningTosInfos.length > 0) { $.each(warningTosInfos, function (key, value) { if ($(value).is(":visible")) { tosRequiredValid = false; } }); } } else if (!$(value).prop('checked')) { tosRequiredValid = false; } } }); if (tosRequiredValid && tinInputValid && postCodeInputValid && emailInputValid && !isEmpty) { return true; } else if (!emailInputValid) { application.createMessage(emailInvalidInfo); } else if (!postCodeInputValid) { application.createMessage(postCodeInvalidInfo); } else if (!tinInputValid) { application.createMessage(tinInvalidInfo); } else if (!tosRequiredValid) { application.createMessage(tosRequiredInfo); } else { application.createMessage(formInvalidInfo); } }, uiValidateForm: function (form) { var formRequiredInputs = form.find('input[required], textarea[required]'); var tosInputs = form.find('input[type="checkbox"]'); var formInvalidInfo = form.data('invalid'); var emailInvalidInfo = form.data('email-invalid'); var postCodeInvalidInfo = form.data('pcode-invalid'); var tinInvalidInfo = form.data('tin-invalid'); var tosRequiredInfo = form.data('tos-invalid'); var warningTosInfos = form.find('.tos-warning'); var tosRequiredValid = true; var tinInputValid = true; var emailInputValid = true; var postCodeInputValid = true; var isEmpty = false; var checkedChannels = []; var channelsInputs; $.each(formRequiredInputs, function (key, value) { $(value).addClass('validationStyles'); if ($(value).val() == '') { isEmpty = true; } if ($(value).attr('name') === 'email') { emailInputValid = application.uiValidateEmail($(value).val()); } if ($(value).attr('name') === 'zipCode') { postCodeInputValid = application.uiValidatePostCode($(value).val(), $(value).attr('pattern')); } if ($(value).attr('name') === 'tin') { tinInputValid = application.uiValidateTinCode($(value).val(), $(value).attr('pattern')); } }); $.each(tosInputs, function (key, value) { if ($(value).data('required') === 'req') { if (!$(value).prop('checked')) { $(value).closest('label').addClass('required-checkbox-warning'); } if ($(value).hasClass('channel-checkbox')) { channelsInputs = $(value).closest('.input-group').find('.channel-checkbox'); $.each(channelsInputs, function (key, value) { if ($(value).prop('checked')) { checkedChannels.push($(value)); } }); if (checkedChannels.length > 0) { tosRequiredValid = true } else { tosRequiredValid = false } if (warningTosInfos.length > 0) { $.each(warningTosInfos, function (key, value) { if ($(value).is(":visible")) { tosRequiredValid = false; } }); } } else if (!$(value).prop('checked')) { tosRequiredValid = false; } } }); if (tosRequiredValid && tinInputValid && postCodeInputValid && emailInputValid && !isEmpty) { return true; } else if (!emailInputValid) { application.createMessage(emailInvalidInfo); } else if (!postCodeInputValid) { application.createMessage(postCodeInvalidInfo); } else if (!tinInputValid) { application.createMessage(tinInvalidInfo); } else if (!tosRequiredValid) { application.createMessage(tosRequiredInfo); } else { application.createMessage(formInvalidInfo); } }, 3.Aby walidacja działała poprawnie, dodaj dodatkowe funkcje w pliku 'init.js', w obiekcie application (uicheckIfParentChecked, uicheckIfTosChannelChecked) uicheckIfParentChecked: function(e) { var parentBox = $(e.currentTarget).closest('.multiple-choice'); var elementParentTos = parentBox.find('input[name=tos]'); var consentWarning = parentBox.find('.main-consent-warning'); var channelWarning = parentBox.find('.channel-warning'); var tosChannels = parentBox.find('.channel-checkbox'); var checkedChannels = []; var elementParentTosChecked, parentTosName; if (elementParentTos.length > 0) { elementParentTosChecked = $(elementParentTos[0]).prop('checked'); parentTosName = parentBox.find('.input-group > label .switch-name'); consentWarning = $(elementParentTos[0]).closest('.input-group').find('.main-consent-warning'); if (!elementParentTosChecked) { $(parentTosName[0]).css({ 'color': '#ff3a3a', 'transition': '200ms' }); $(consentWarning[0]).show(); } if (!$(e.currentTarget).prop('checked') || elementParentTosChecked) { $(parentTosName[0]).css({ 'color': '', 'transition': '200ms' }); $(consentWarning[0]).hide(); } if (!$(e.currentTarget).prop('checked') && !elementParentTosChecked) { $(parentTosName[0]).css({ 'color': '', 'transition': '200ms' }); $(consentWarning[0]).hide(); $(e.currentTarget).prop('disabled', true); $(e.currentTarget).closest('.channel-switch').addClass('unactive-switch'); } if ($(e.currentTarget).prop('checked')) { $(channelWarning[0]).hide(); } else { $.each(tosChannels, function (key, value) { if ($(value).prop('checked')) { checkedChannels.push($(value)); } }); if (checkedChannels.length === 0 && elementParentTosChecked) { $(channelWarning[0]).show(); } } } }, uicheckIfTosChannelChecked: function (e) { var parentBox = $(e.currentTarget).closest('.multiple-choice'); var tosChannels = parentBox.find('.channel-checkbox'); var checkedChannels = []; var channelWarning = parentBox.find('.channel-warning'); var consentWarning = parentBox.find('.main-consent-warning'); var isChecked = $(e.currentTarget).prop('checked'); var tosName = $(e.currentTarget).closest('label').find('.switch-name'); $.each(tosChannels, function (key, value) { if ($(value).prop('checked')) { checkedChannels.push($(value)); } }); if (checkedChannels.length > 0 && !isChecked) { $(channelWarning[0]).hide(); $.each(tosChannels, function (key, value) { $(value).closest('.channel-switch').addClass('unactive-switch'); $(value).prop('disabled', true); $(value).prop("checked", false); }); } if (checkedChannels.length > 0 && isChecked) { $(channelWarning[0]).hide(); $(tosName[0]).css({ 'color': '', 'transition': '200ms' }); $(consentWarning[0]).hide(); } if (checkedChannels.length === 0 && isChecked) { $(channelWarning[0]).show(); $.each(tosChannels, function (key, value) { $(value).closest('.channel-switch').removeClass('unactive-switch'); $(value).removeAttr('disabled'); }); } if (checkedChannels.length === 0 && !isChecked) { $(channelWarning[0]).hide(); $.each(tosChannels, function (key, value) { $(value).closest('.channel-switch').addClass('unactive-switch'); $(value).prop('disabled', true); }); } }, 4.Wywołaj nowe funkcje w pliku 'init.js', w obiekcie application.events $('body').on('click', '.channel-checkbox', function (e) { self.uicheckIfParentChecked(e); }); $('body').on('click', '.multiple-choice .input-group > label .switch-input[name=tos]', function (e) { self.uicheckIfTosChannelChecked(e); }); 5.Dodaj tłumaczenia: - MainConsentWarning: "Wyraź zgodę" - ChannelWarning: "Wybierz przynajmniej jeden kanał"   6.Uzupełnij plik '480plus.css', który znajduje się w folderze 'css', o style: .multiple-switch { margin: 0 0 20px; } .multiple-choice .main-consent-warning, .multiple-choice .channel-warning, .input-group.switches .main-consent-warning, .input-group.switches .channel-warning { display: none; position: relative; font-size: 11px; color: #ff3a3a; top: -20px; left: -51px; } .multiple-choice .main-consent-warning .fa, .multiple-choice .channel-warning .fa, .input-group.switches .main-consent-warning .fa, .input-group.switches .channel-warning .fa { margin: 0 5px 0 0; } .input-group label.required-checkbox-warning .switch-name { color: #ff3a3a !important; } .input-group label.required-checkbox-warning .switch-name a.cp_link { color: #ff3a3a; } form .input-group .switch.unactive-switch { opacity: .5; cursor: not-allowed; }

Szablon Bursztyn

W pliku homepage.html proszę zastąpić poniższy kod: {% for tos in config.TOS.Consents.Newsletter -%} {% if tos.Statement -%} <label>{{tos.Text}}</label> {% else -%} <input name="tos" id="tos{{ tos.Id }}" type="checkbox" value="{{ tos.Id }}" {% if tos.Required -%} required {% endif -%} /> <div class="input-switch "><div class="switch-button"></div></div> <label for="tos{{ tos.Id }}">{% if tos.Required -%}*{% endif -%} {{tos.Text}}</label> {% endif -%} {% endfor -%} następującym: {% for tos in config.TOS.Consents.Newsletter -%} {% assign channelsSize = tos.Channels | Size -%} <div class="single-tos {% if channelsSize > 0 -%} channels {% endif %}"> {% if tos.Statement -%} <label class="statement tos-name">{{tos.Text}}</label> {% else -%} <div class="tos-switch"> <input name="tos" id="tos{{ tos.Id }}" type="checkbox" value="{{ tos.Id }}" {% if tos.Required -%} required {% endif -%} /> <div class="input-switch "><div class="switch-button"></div></div> <label for="tos{{ tos.Id }}">{% if tos.Required -%}*{% endif -%} {{tos.Text}}</label> </div> {% endif -%} {% if channelsSize > 0 -%} <ul class="channels disabled"> {% for chn in tos.Channels -%} <li class="channel"> <input id="channel{{ chn.Key }}" type="checkbox" name="channelKey" value="{{ chn.Key }}" disabled/> <div class="channel-switch input-switch "><div class="switch-button"></div></div> <label for="channel{{ chn.Key }}">{{chn.Name}}</label> </li> {% endfor -%} </ul> {% endif -%} </div> {% endfor -%}

Szablon Opal

W pliku homepage.html proszę zastąpić poniższy kod: {% for tos in config.TOS.Consents.Newsletter -%} {% if tos.Statement -%} <label>{{tos.Text}}</label> {% else -%} <input name="tos" id="tos{{ tos.Id }}" type="checkbox" value="{{ tos.Id }}" {% if tos.Required -%} required {% endif -%} /> <div class="input-switch "><div class="switch-button"></div></div> <label for="tos{{ tos.Id }}">{% if tos.Required -%}*{% endif -%} {{tos.Text}}</label> {% endif -%} {% endfor -%} następującym: {% for tos in config.TOS.Consents.Newsletter -%} {% assign channelsSize = tos.Channels | Size -%} <div class="single-tos"> {% if tos.Statement -%} <label class="statement-reg tos-name">{{tos.Text}}</label> {% else -%} <div class="tos-switch single-switch"> <input name="tos" id="tos{{ tos.Id }}" type="checkbox" value="{{ tos.Id }}" {% if tos.Required -%} required {% endif -%} /> <div class="input-switch "><div class="switch-button"></div></div> <label for="tos{{ tos.Id }}">{% if tos.Required -%}*{% endif -%} {{tos.Text}}</label> </div> {% endif -%} {% if channelsSize > 0 -%} <ul class="channels-list"> {% for chn in tos.Channels -%} <li class="channel single-switch"> <input id="channel{{ chn.Key }}" type="checkbox" name="channelKey" value="{{ chn.Key }}"/> <div class="input-switch "><div class="switch-button"></div></div> <label for="channel{{ chn.Key }}">{{chn.Name}}</label> </li> {% endfor -%} </ul> {% endif -%} </div> {% endfor -%}

Kanał e-mail

Jeśli wprowadziłeś powyższe zmiany, a w swoich zgodach posiadasz tylko kanał e-mail i chcesz zaoszczędzić zbędnego klikania swoim klientom to poniżej znajduje się instrukcja co należy zmienić w kodzie. Te zmiany sprawią, że kanał e-mail będzie zawsze zaznaczony i nie będzie się go dało odznaczyć.

Szafir

Plik init-ui2.js
Znajdź funkcję toggleChannels. W niej jest zdeklarowana zmienna var inputs = container.find('[name=channelKey]');. Zmień ją na var inputs = container.find('[name=channelKey]:not([type=hidden])');
Pliki home.html, sign-up-no-address.html, common/address-form-register-full.html, customer-profile/your-account/employee-update.html, order/cart.html, __loginconsents.liquid
Znajdź w tych plikach taki fragment kodu: {% if channelsSize > 0 -%} {{ translations.ChooseOption }} {% endif -%} {% for channel in tos.Channels -%} {% endfor -%} Usuń go, a w jego miejsce wklej ten fragment kodu: {% if channelsSize > 0 -%} {% assign onlyEmail = false -%} {% if channelsSize == 1 and tos.Channels[0].Type == 1 -%} {% assign onlyEmail = true -%} {% endif -%} {% if onlyEmail == false -%} {{ translations.ChooseOption }} {% endif -%} {% endif -%} {% for channel in tos.Channels -%} {% endfor -%}

Agat

Pliki partials/cart/delivery-and-payment.html, partials/common/footer.html, partials/common/registration-consents.html, __loginconsents.liquid
Znajdź w tych plikach taki fragment kodu: {% if channelsSize > 0 -%} {{translations.ChannelWarning}} {% for chn in tos.Channels -%} {% endfor -%} {% endif -%} Usuń go, a w jego miejsce wklej ten fragment kodu: {% if channelsSize > 0 -%} {% assign onlyEmail = false -%} {% if channelsSize == 1 and tos.Channels[0].Type == 1 -%} {% assign onlyEmail = true -%} {% endif -%} {% if onlyEmail == false -%} {{translations.ChannelWarning}} {% endif -%} {% for chn in tos.Channels -%} {% endfor -%} {% endif -%}

Bursztyn

Plik init.js
Wyszukaj frazę channels i zamień wszystkie jej wystąpienia w tym pliku na channels:not(.only-email)
Pliki homepage.html, customer/registration.html, order/stepsummary.html, __loginconsents.liquid
Znajdź w tych plikach taki fragment kodu: {% if channelsSize > 0 -%}
    • {% for chn in tos.Channels -%}
{% endfor -%} {% endif -%} Usuń go, a w jego miejsce wklej ten fragment kodu: {% if channelsSize > 0 -%} {% assign onlyEmail = false -%} {% if channelsSize == 1 and tos.Channels[0].Type == 1 -%} {% assign onlyEmail = true -%} {% endif -%}
    • {% for chn in tos.Channels -%}
    • {% if onlyEmail == false -%} {% else -%} {% endif -%}
{% endfor -%} {% endif -%}

Opal

Plik init.js
Wyszukaj frazę channels-list i zamień wszystkie jej wystąpienia w tym pliku na channels-list:not(.only-email)
Pliki homepage.html, customer/registration.html, order/stepsummary.html, __loginconsents.liquid
Znajdź w tych plikach taki fragment kodu: {% if channelsSize > 0 -%}
    • {% for chn in tos.Channels -%}
{% endfor -%} {% endif -%} Usuń go, a w jego miejsce wklej ten fragment kodu: {% if channelsSize > 0 -%} {% assign onlyEmail = false -%} {% if channelsSize == 1 and tos.Channels[0].Type == 1 -%} {% assign onlyEmail = true -%} {% endif -%}
    • {% for chn in tos.Channels -%}
    • {% if onlyEmail == false -%} {% else -%} {% endif -%}
{% endfor -%} {% endif -%}

Jak dostosować szablony do integracji z Google Tag Manager?

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.

1. Wstęp

W związku ze zmianami w Comarch e-Sklep od wersji 2018.6, które poszerzają zakres integracji z Google Tag Manager, szablony w wersji starszej będą wymagały wprowadzenia zmian, które pozwolą na wykorzystanie w pełni funkcjonalności opisanych w artykule Integracja z Google Tag Manager.
Uwaga
Poniższa konfiguracja przeznaczona jest dla użytkowników posiadających e-Sklep w wersji 2018.6 lub wyższej ale z szablonami w starszej wersji. Pliki szablonu, fragmenty ich kodu oraz modyfikacje przedstawione w instrukcji dotyczą darmowych szablonów Comarch. W przypadku szablonów indywidualnych miejsce wklejenia skryptów niezbędnych do dostosowania szablonu powinien wskazać twórca szablonu

2. Implementacja skryptów

Jeżeli przed wersją 2018.6 integracja była już konfigurowana to konieczne będzie usunięcie starego skryptu dodanego do ostatniej strony zamówienia. W tym celu w panelu administracyjnym sklepu przejdź do obszaru Wygląd sklepu / Ustawienia / Edytuj ustawienia zaawansowane / Edytuj HTML i usuń z pliku lastpagescripts.html poniższy skrypt: <script> dataLayer.push({{ page.GTMDataLayer }}); dataLayer.push({event:'UaTrack',VirtualPath:null}); </script> W zależności od szablonu plik lastpagescripts.html będzie zlokalizowany w innym miejscu w strukturze plików:
  • Szablon Bursztyn, Opal i Szafir - order / lastpagescripts.html
  • Szablon Agat - partials / cart / lastpagescripts.html
Następnym krokiem będzie implementacja dwóch nowych skryptów. W pliku _layout.html znajdującym się bezpośrednio w obszarze Edytuj HTML zamień istniejący skrypt znajdujący się na początku sekcji <body> <script>var dataLayer=[{{ page.GTMDataLayer }}];</script> na nowy: <script>var dataLayer={{ page.GtmDL }};</script> Kolejny skrypt należy umieścić w pliku odpowiedzialnym za koszyk <script> (function(){ var i,dl={{ page.GtmDL }}; for(i in dl) dataLayer.push(dl[i]); })(); </script> W zależności od szablonu będzie to inny plik - nazwa i lokalizacja pliku oraz miejsce wklejenia powyższego skryptu wskazane poniżej:
  • Szablon Bursztyn, Opal - order-cart-container.html
  • Szablon Agat - partials / cart / cart-template.html
  • Szablon Szafir - order / cart.html
oraz w pliku order / thx.html  
Wskazówka
Pamiętaj o zapisaniu i publikacji wszystkich zmian wykonanych w szablonie. Po przygotowaniu szablonu zgodnie z powyższymi instrukcjami możesz przejść do konfiguracji integracji Twojego sklepu z Google Tag Manager opisanej w artykule Integracja z Google Tag Manager.

3. Przydatne linki

Google Tag Manager: Pomoc Strona serwisu Dokumentacja programistyczna Google Analytics: Strona serwisu Dokumentacja programistyczna

Dodawanie kanałów do płatności online w szablonach

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.

Dodawanie kanałów do płatności online

Kanały w płatnościach online to inaczej sposób zapłaty. W tym artykule dowiesz się, jak je dodać w swoim szablonie.

1. Bursztyn i Opal

W pliku order/cart.html dodaj poniższy kod: {% for delivery in cart.DeliveryMethods %} {% for payment in delivery.Payments %} {% if payment.Channels[0] and payment.Id == cart.SelectedDeliveryPaymentId %}
{% for channel in payment.Channels -%} {% endfor -%}
{% endif %} {% endfor %} {% endfor %} Następnie konieczne jest również ostylowanie dodanego fragmentu, aby był spójny z resztą szablonu. Na końcu pliku scss/main2.scss dodaj poniższe style: .payment-channels{ background-color: $middleColor; display: flex; flex-wrap: wrap; margin-top: 11px; } .payment-channel{ display: inline-block; width: calc(100%/6 - 30px); min-width: 100px; height: 50px; margin: 15px; position: relative; [type=radio]{ position: absolute; opacity: 0; width: 0; height: 0; & + img{ cursor: pointer; max-width: 100px; max-height: 50px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } &:checked + img{ outline: 1px solid $primaryColor; } } }
Wskazówka
O tym jak skompilować i zminifikować pliki scss dowiesz się z artykułu o kompilacji i minifikacji plików.
Gdy kanały są już dodane oraz dobrze wyświetlają się w szablonie, kolejny krok to włączenie ich działania. W tym celu na końcu pliku js/order.js należy wkleić poniższy kod: $('body').on('change', '.payment-channels [name=channel]', function (e) { e.preventDefault(); var id = $('.payment-channels').data('id'); var channel = $(e.currentTarget).val(); $.post(null, { __csrf: __CSRF, __action: 'Order/DeliveryChange', id: id, channel: channel }); });
Wskazówka
O tym jak zminifikować pliki js dowiesz się z artykułu o kompilacji i minifikacji plików.
Na koniec dodaj walidację, dzięki której nie będzie się dało złożyć zamówienia, jeśli żaden kanał nie został wybrany. W pliku js/order.js odnajdź funkcję oznaczoną komentarzem //next i dodaj w niej (w pierwszym warunku if ($(this).hasClass('start'))) w zależności od szablonu: Bursztyn: if($('.payment-channels').index() != -1 && $('.payment-channels [name=channel]:checked').index() == -1){ var tt = $('
'); tt.append('
'+$('.payment-channels').data('validation')+'
'); $('body').append(tt); setTimeout(function(){tt.fadeOut(function(){tt.remove();});},3000); return tt; } Opal: if($('.payment-channels').index() != -1 && $('.payment-channels [name=channel]:checked').index() == -1){ var tt; if (sessionStorage.getItem('tooltip') != null){ tt = $(".tt.err"); } else { tt = $('
'); tt.append(''+$('.payment-channels').data('validation')+''); $('body').append(tt); sessionStorage.setItem('tooltip', 'err'); } showTooltip(); return; } Ostatnim krokiem jest dodanie komunikatu, który będzie wyświetlany w przypadku niezaznaczenia żadnego kanału. W tym celu w panelu administracyjnym (Wygląd sklepu/ Ustawienia/ Tłumaczenia) dodaj tłumaczenie o kluczu ChoosePaymentChannel i treści Wybierz sposób zapłaty (lub jakiejkolwiek innej, którą uważasz za stosowną).

2. Agat:

W pliku partials/cart/cart-content.html dodaj poniższy kod: {% for deliveryMethodLvl in cart.DeliveryMethods -%} {% if deliveryMethodLvl.Name == cart.SelectedDelivery.Name %} {% for payment in deliveryMethodLvl.Payments -%} {% if payment.Channels[0] and payment.Id == cart.SelectedDeliveryPaymentId %}
{% for channel in payment.Channels -%} {% endfor -%}
{% endif %} {% endfor -%} {% endif %} {% endfor -%} Następnie konieczne jest również ostylowanie dodanego fragmentu, aby był spójny z resztą szablonu. Na końcu pliku scss/main2.scss dodaj poniższe style: .payment-channels{ background-color: $bgColor; border: 1px solid $lighterColor; box-shadow: 2px 2px 1px 0 $bgColorFont; margin: 0 auto 20px; display: inline-flex; flex-wrap: wrap; width: calc(100% - 2px); @media only screen and (min-width: 481px) { width: calc(50% - 22px); min-width: 298px; margin: 0 10px 20px } @media only screen and (min-width: 769px) { width: calc(100% - 22px); } @media only screen and (min-width: 1441px) { width: calc(75% - 22px); } } .payment-channel{ display: inline-block; width: calc(100%/6 - 30px); min-width: 100px; height: 50px; margin: 15px; position: relative; [type=radio]{ position: absolute; opacity: 0; width: 0; height: 0; & + img{ cursor: pointer; max-width: 100px; max-height: 50px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } &:checked + img{ outline: 1px solid $primaryColor; } } }
Wskazówka
O tym jak skompilować i zminifikować pliki scss dowiesz się z artykułu o kompilacji i minifikacji plików.
Gdy kanały są już dodane oraz dobrze wyświetlają się w szablonie, kolejny krok to włączenie ich działania. W tym celu na końcu pliku js/order.js należy wkleić poniższy kod: $('body').on('change', '.payment-channels [name=channel]', function (e) { e.preventDefault(); var id = $('.payment-channels ').data('id'); var channel = $(e.currentTarget).val(); $.post(null, { __csrf: __CSRF, __action: 'Order/DeliveryChange', id: id, channel: channel }); });
Wskazówka
O tym jak zminifikować pliki js dowiesz się z artykułu o kompilacji i minifikacji plików.
Na koniec dodaj walidację, dzięki której nie będzie się dało złożyć zamówienia, jeśli żaden kanał nie został wybrany. W pliku js/order.js odnajdź funkcję orderNextStep i dodaj w niej (na początku else'a warunku if ($('#delivery-address-data').index() >= 0)) kolejny warunek do sprawdzenia: if($('.payment-channels').index() != -1 && $('.payment-channels [name=channel]:checked').index() == -1){ application.createMessage($('.payment-channels').data('validation'), 3000); return; } Ostatnim krokiem jest dodanie komunikatu, który będzie wyświetlany w przypadku niezaznaczenia żadnego kanału. W tym celu w panelu administracyjnym (Wygląd sklepu/ Ustawienia/ Tłumaczenia) dodaj tłumaczenie o kluczu ChoosePaymentChannel i treści Wybierz sposób zapłaty (lub jakiejkolwiek innej, którą uważasz za stosowną).

3. Szafir

W pliku order/delivery-partials/delivery-section.html dodaj poniższy kod: {% for indexString in matchedDeliveriesIndexesArray -%} {% assign index = indexString | ToInt -%} {% if order.SelectedDelivery.Name == order.DeliveryMethods[index].Name -%} {% for payment in order.DeliveryMethods[index].Payments -%} {% if payment.Id == order.SelectedDelivery.Payment.Id and payment.Channels[0] -%}
{% for channel in payment.Channels -%} {% endfor -%}
{% endif -%} {% endfor -%} {% endif -%} {% endfor -%} Następnie konieczne jest również ostylowanie dodanego fragmentu, aby był spójny z resztą szablonu. Na końcu pliku scss/globals/_globals2.scss dodaj poniższe style: .order-ui .payment-channels-ui{ padding: 0; margin: 0 30px 15px 0; width: calc(100% - 30px); display: flex; flex-wrap: wrap; .message-bar-ui{ width: 100%; margin: 0; } } .payment-channel-ui:not(.checkbox-ui):not(.radio-ui){ width: calc(100%/6 - 30px); min-width: 100px; height: 50px; margin: 15px; position: relative; [type=radio]{ position: absolute; opacity: 0; width: 0; height: 0; & + img{ cursor: pointer; max-width: 100px; max-height: 50px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } &:checked + img{ outline: 1px solid $primaryColor; } } } oraz w pliku scss/globals/_globals-m.scss: .order-ui .payment-channels-ui { margin-right: 0; width: 100%; }
Wskazówka
O tym jak skompilować i zminifikować pliki scss dowiesz się z artykułu o kompilacji i minifikacji plików.
Gdy kanały są już dodane oraz dobrze wyświetlają się w szablonie, kolejny krok to włączenie ich działania. W tym celu na końcu pliku js/init-ui2.js należy wkleić poniższy kod: function choosePaymentChannel(e) { var form = $('.delivery-form-lq'); var data = form.serializeArray(); data.push({ name: '__csrf', value: __CSRF }); data.push({ name: 'channel', value: $(e.currentTarget).val() }); $.post('', data); $('.payment-channels-lq .form-error-lq').remove(); }; $('body').on('change', '.payment-channel-lq [name=channel]', function (e) { choosePaymentChannel(e); });
Wskazówka
O tym jak zminifikować pliki js dowiesz się z artykułu o kompilacji i minifikacji plików.
Na koniec dodaj walidację, dzięki której nie będzie się dało złożyć zamówienia, jeśli żaden kanał nie został wybrany. W pliku js/init-ui1.js odnajdź funkcję addOrder i dodaj w niej (zaraz pod pierwszym warunkiem if (ajaxModalSelector && this.hiddingClass)) kolejny warunek do sprawdzenia: if ($('.payment-channels-lq').index() != -1 && $('.payment-channel-lq [name=channel]:checked').index() == -1) { $('.payment-channels-lq').append('
' + __translations.ChoosePaymentMethod + '
'); guardian = false; } Ostatnim krokiem jest dodanie komunikatu, który będzie wyświetlany w przypadku niezaznaczenia żadnego kanału. W tym celu w pliku _layout.html odnajdź skrypt ze zmienną __translations i dopisz w nim kolejną linijkę: ChoosePaymentMethod: '{{ translations.ChoosePaymentMethod | H }}'. Następnie w panelu administracyjnym (Wygląd sklepu/ Ustawienia/ Tłumaczenia) dodaj tłumaczenie o kluczu ChoosePaymentMethod i treści Wybierz sposób zapłaty (lub jakiejkolwiek innej, którą uważasz za stosowną).

Obsługa towaru archiwalnego

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.

Obsługa towaru archiwalnego

W tym artykule dowiesz się jak obsłużyć towar archiwalny na stronie ze szczegółami towaru w swoim szablonie.

Szafir

W pliku product/product-presentation-data.html wyszukaj frazę header. Jest to element, w którym trzeba dodać warunek, aby ostatecznie wyglądało to tak: {% if product.Archival == false -%} . . . {% endif -%} Następnie wyszukaj frazę add-to-cart-container-ui. Jest to kontener, w którym na samym początku wyświetlane są punkty lojalnościowe i flagi. Trzeba je uwarunkować, żeby nie pojawiały się przy towarze archiwalnym. Zamiast nich powinna się pojawić informacja o tym, że jest to towar archiwalny. Początek tego kontenera powinien wyglądać następująco: {% if product.Archival -%}
{{ translations.ArchivalProductInfo }}
{% else -%} {% if activeProduct.Points != null and usr.Authenticated -%} {% capture quantityUnit -%}1 {{ product.SaleUnit }}{% endcapture -%}
{{ translations.EarnLoyaltyPoints | Format:quantityUnit }}: {{ activeProduct.Points }} {{ translations.PointsShortcut }}
{% endif -%}
    • {% assign flagsQuantity = product.Flags | Size -%} {% for flag in product.Flags limit: 3 -%}
    • {% if flag.Type == "Reward" -%} {{ translations.PointsProduct }}{% if usr.Authenticated and activeProduct.PointsPrice != null -%}: {{ activeProduct.PointsPrice }} {{ translations.PointsShortcut }}/{{ product.SaleUnit }}{% endif -%} {% else -%} {{ flag.Text }} {% endif -%}
{% endfor -%} {% endif -%}   Parę linijek niżej znajdują się etykiety {{ translations.ChooseProductVariant }} i {{ translations.Availability }}: {{ activeProduct.Availability.Text }}. Trzeba zmodyfikować ich warunki tak, aby nie wyświetlały się w przypadku towaru archiwalnego. Powinno to wyglądać w ten sposób: {% if tableView and product.Archival == false -%} {{ translations.ChooseProductVariant }} {% endif -%} {% if activeProduct.Availability.Text != '' and product.Archival == false -%}
{{ translations.Availability }}: {{ activeProduct.Availability.Text }}
{% endif -%} W podobny sposób należy ukryć całą resztę dotyczącą różnych danych o towarze. Aby to zrobić należy wyszukać taki warunek {% if activeProduct.StockLevel.Control and tableView == false -%} i linijkę wyżej dopisać {% if product.Archival == false -%}. Natomiast zamknięcie tego warunku trzeba będzie dopisać dużo dalej. Aby znaleźć odpowiednie miejsce należy wyszukać taką frazę CheaperInSet. Jest to etykieta przycisku przewijającego do sekcji z zestawami (o ile takowe istnieją dla danego towaru). Pod tym przyciskiem znajduje się {% endif -%}, który zamyka warunek sprawdzający właśnie istnienie owych zestawów. Pod nim trzeba dopisać jeszcze jedno {% endif -%}, aby zamknąć warunek, który dodaliśmy wyżej. Ostatecznie powinno to wyglądać tak: {% if product.Archival == false -%} {% if activeProduct.StockLevel.Control and tableView == false -%} . . . {% if product.Sets != null and product.Sets != empty -%}
{{ translations.CheaperInSet }}
{% endif -%} {% endif -%} . . . Na końcu pliku scss/globals/_globals2.scss wklej taki kod: .archival-product-info-ui{ position: absolute; top: -40px; right: 0; text-transform: uppercase; color: $primaryColor; font-style: italic; } A na końcu pliku scss/globals/_globals-m.scss wklej taki kod: .archival-product-info-ui{ right: 20px; }
Wskazówka
Zwróć uwagę, aby ten styl znajdował się wewnątrz głównej klamry, która otwiera się w pierwszej linijce pliku i zamyka w ostatniej
Wskazówka
Pamiętaj, że pliki scss należy zminifikować. O tym jak skompilować i zminifikować pliki scss dowiesz się z artykułu o kompilacji i minifikacji plików.

Agat

W pliku product-page.html wyszukaj frazę button-container. Jest to klasa kontenera, w którym znajdują się najważniejsze dane związane z towarem. Pod tym kontenerem wklej taki kod: {% if product.Archival -%}
{{translations.ArchivalProductInfo}}
{% endif -%} Pozostając w tym samym pliku (product-page.html) wyszukaj frazę config.Reviews.Enabled. Jest to część warunku, odpowiedzialnego za dodawanie opinii o produkcie. Zmodyfikuj go tak, aby wyglądał następująco: {% if product.Archival -%} {% if config.Reviews.Enabled == true and productD.Archival == false -%} {% endif -%} W pliku partials/product/product-popup.html wyszukaj frazę usr.Authenticated. W tym pliku będą dwa wystąpienia takiej frazy. W obu to część warunku. W obu przypadkach zmodyfikuj te warunki, aby wyglądały tak: {% if usr.Authenticated and product.Archival == false -%} W tym samym pliku (partials/product/product-popup.html) wyszukaj frazę add-to-cart-popup. Jest to klasa kontenera, który trzeba zmodyfikować, aby wyglądał tak:
Dalej w tym samym pliku (partials/product/product-popup.html) wyszukaj frazę AddToCartForm. Jest to ID formularza odpowiedzialnego za dodawanie towaru do koszyka. Linijkę wyżej otwórz taki warunek: {% if product.Archival == false -%} Będzie on obejmował resztę kodu znajdującego się w tym pliku. Na końcu znajdziesz kontener z klasą ask-for-price-popup. Pod tym kontenerem zamknij wcześniej otworzony warunek dopisując linijkę niżej {% endif -%}. Na końcu pliku scss/main2.scss wklej taki kod: .archival{ padding-bottom: 20px; } .archival-info{ background: $pageNameColor; color: $primaryColor; padding: 20px; margin-bottom: 20px; text-transform: uppercase; @media only screen and (min-width: 481px) { box-shadow: 2px 2px 1px 0 $bgColorFont; } }
Wskazówka
Pamiętaj, że pliki scss należy zminifikować. O tym jak skompilować i zminifikować pliki scss dowiesz się z artykułu o kompilacji i minifikacji plików.

Bursztyn

W pliku productdetails.html wyszukaj frazę itemprop="offers". Jest to jeden z atrybutów kontenera, na którego początku trzeba wkleić taki kod: {% if _pd.Archival -%}
{{ translations.ArchivalProductInfo }}
{% endif %} W tym samym pliku (productdetails.html) wyszukaj frazę usr.Authenticated. W tym pliku będą dwa wystąpienia takiej frazy. W obu to część warunku. W obu przypadkach zmodyfikuj te warunki, aby wyglądały tak: {% if usr.Authenticated and _pd.Archival == false -%} Dalej w tym samym pliku (productdetails.html) wyszukaj frazę class="availability". Linijkę wyżej nad elementem, który ma taką klasę dopisz {% if _pd.Archival == false %}. Będzie to początek warunku, który trzeba zakończyć trochę niżej. Aby znaleźć to miejsce wyszukaj frazę {% if customer.HidePrices %}. Jest to warunek przed, którym trzeba zakończyć ten nasz nowy, który właśnie dodaliśmy. Aby tego dokonać należy linijkę wyżej dopisać {% endif %}. Kolejną frazą w tym pliku (productdetails.html), którą trzeba wyszukać jest add-to-cart. Jest to klasa przycisku dodawania towaru do koszyka. Będą dwa wystąpienia. Interesuje nas tylko pierwsze, które dotyczy danego towaru. Drugie wystąpienie dotyczy zestawów i znajduje się w warunku {% if set.Price <> null %}. To nas nie interesuje więc zostańmy przy tym pierwszym. Linijkę wyżej dopisz {% if _pd.Archival == false %}. Pod tym przyciskiem znajdują się jeszcze dwa przyciski (powiadamiania o dostępności i pytania o cenę). Pod tym drugim należy zamknąć warunek dopisując linijkę niżej {% endif %}. Będąc przy tych przyciskach możemy zauważyć element nav z klasą options. Linijkę wyżej wklej {% if _pd.Archival == false %}, a zaraz za końcem tego elementu dopisz linijkę niżej {% endif %}. Ostatni warunek w tym pliku (productdetails.html), który trzeba zmienić znajdziesz wyszukując frazę productuserreviews.TotalItems. Trzeba ją podmienić, aby wyglądała tak: {% unless productuserreviews.TotalItems == 0 and _pd.Archival %} Plik productdetails/opinions-partial.html należy zmodyfikować tak, żeby wyglądał w ten sposób: {% if productuserreviews.TotalItems == 0 %} {% if _pd.Archival == false -%} . . . {% endif -%} {% else %} {% for o in productuserreviews.Reviews %} . . . {% endfor %} {% if _pd.Archival == false -%} {% endif -%} {% endif%} Na końcu pliku scss/main2.scss wklej: .archival-product-info{ padding: 15px 20px; border-radius: 5px 0 0 5px; box-shadow: rgba(0,0,0,.2) -2px 2px 10px; background: #F5F5F5; position: relative; right: -20px; left: -20px; width: calc(100% + 40px); margin: 10px 0 30px; font-style: italic; }
Wskazówka
Pamiętaj, że pliki scss należy zminifikować. O tym jak skompilować i zminifikować pliki scss dowiesz się z artykułu o kompilacji i minifikacji plików.

Opal

W pliku productdetails.html wyszukaj frazę details-section. Jest to klasa kontenera, nad którym trzeba wkleić linijkę wyżej taki kod: {% if _pd.Archival -%}
{{ translations.ArchivalProductInfo }}
{% endif %} Następnie wyszukaj frazę usr.Authenticated. W tym pliku będą dwa wystąpienia takiej frazy. W obu to część warunku. W obu przypadkach zmodyfikuj te warunki, aby wyglądały tak: {% if usr.Authenticated and _pd.Archival == false -%} Kolejna fraza to class="stock". Będzie to klasa elementu, który trzeba "ubrać" w warunek {% if _pd.Archival == false %}. Będzie to początek warunku, który trzeba zakończyć linijkę pod elementem poprzez dopisanie {% endif %}. Teraz wyszukaj frazę class="attributes". Linijkę wyżej nad elementem, który ma taką klasę dopisz {% if _pd.Archival == false %}. Będzie to początek warunku, który trzeba zakończyć trochę niżej. Aby znaleźć to miejsce wyszukaj frazę {% if customer.HidePrices %}. Jest to warunek przed, którym trzeba zakończyć ten nasz nowy, który właśnie dodaliśmy. Aby tego dokonać należy linijkę wyżej dopisać {% endif %}. Następna fraza to add-to-cart. Jest to klasa przycisku dodawania towaru do koszyka. Będą dwa wystąpienia. Interesuje nas tylko pierwsze, które dotyczy danego towaru. Drugie wystąpienie dotyczy zestawów i znajduje się w warunku {% if set.Price <> null %}. To nas nie interesuje więc zostańmy przy tym pierwszym. Linijkę wyżej dopisz {% if _pd.Archival == false %}. Pod tym przyciskiem znajdują się jeszcze dwa przyciski (powiadamiania o dostępności i pytania o cenę). Pod tym drugim należy zamknąć warunek dopisując linijkę niżej {% endif %}. Będąc przy tych przyciskach możemy zauważyć element nav z klasą options. Linijkę wyżej wklej {% if _pd.Archival == false %}, a zaraz za końcem tego elementu dopisz linijkę niżej {% endif %}. Na końcu pliku scss/main2.scss wklej: .archival-product-info{ background: #f7f7f7; color: $placeholderColorFont; padding: 20px; margin-top: 20px; text-align: center; }
Wskazówka
Pamiętaj, że pliki scss należy zminifikować. O tym jak skompilować i zminifikować pliki scss dowiesz się z artykułu o kompilacji i minifikacji plików.

Topaz

Wzależności od wybranego wariantu szczegółów towaru zmiany będą inne. Wersja z miniaturkami zdjęć po lewej W pliku elements/product/product-details-1.html wyszukaj frazę product-details__images. Zastąp kontener z tą klasą poniższym kodem:
{% for flag in product.Flags -%} {{ flag.Text }} {% endfor %}
{% if product.Images[1] -%}
    • {% for image in product.Images -%}
    • {% if image.Id != -1 -%} {{image | Img: 'compact'}} {% else -%} {{'css/img/img-placeholder.jpg' | Img, translations.DefaultImage}} {% endif -%}
{% endfor -%}
{% for image in product.Images -%}
{% if image.Id != -1 -%} {{image | Img: 'grande'}} {% else -%} {{'css/img/img-placeholder.jpg' | Img, translations.DefaultImage}} {% endif -%} {% if product.Archival -%}
{{translations.ArchivalProduct}}
{% endif -%}
{% endfor -%}
{% elseif product.Images[0] -%}
{{product.Images[0] | Img: 'grande'}} {% if product.Archival -%}
{{translations.ArchivalProduct}}
{% endif -%}
{% else -%}
{{'css/img/img-placeholder.jpg' | Img, translations.DefaultImage}} {% if product.Archival -%}
{{translations.ArchivalProduct}}
{% endif -%}
{% endif -%} {% if product.Images[0] -%} {% endif -%} {% if product.Archival == false -%}
{% if product.DeliveryCost != null and product.DeliveryCost > 0 -%} | {% endif -%} {% if stockLevel.Control -%}
{{ translations.Com_StockLvl }}:  {% unless stockLevel.Type == 2 or stockLevel.Type == 4 or stockLevel.Text == null -%} {{ stockLevel.Text }} {% endunless -%} {% if stockLevel.Type == 4 -%} {{ stockLevel.Value | Normalize }} {{ product.SaleUnit }} {% endif -%} {% if stockLevel.Type == 1 or stockLevel.Type == 2 -%} {% unless stockLevel.ImageUrl == null -%} {{stockLevel.ImageUrl | Img: '', stockLevel.Text}} {% endunless -%} {% endif -%}
| {% endif -%}
{{ translations.Com_Availability }}:  {% unless product.Availability.Text == '' or product.Availability.Type == 2 -%} {{ product.Availability.Text }} {% endunless -%} {% if product.Availability.Type == 1 or product.Availability.Type == 2 -%} {{product.Availability.ImageUrl | Img: '', product.Availability.Text}} {% endif -%} {% unless product.Availability.Date == null -%} ({{ product.Availability.Date | Date:'d' }}) {% endunless -%}
{% endif -%}
  Następnie wyszukaj frazę product-details__add-to-cart. Na początku kontenera z tą klasą wklej poniższy kod: {% if product.Archival -%}
{{translations.ArchivalProductInfo}}
{% endif -%} Kawałek niżej znajdziesz kontener z klasą product-details__reviewsContainer. Podmień go poniższym kodem: {% unless product.Archival and product.RatingCount == 0 -%}
{% include 'partials/common/product-rating.html', rating: product.Rating -%} ({{ product.RatingCount }})
{% endunless -%} Teraz wyszukiaj basic-unit-ratio. Podmień wszystko co jest pod tą linijką, aż do linijki, w której znajduje się kontener z klasą product-details__info-container (tej linijki nie podmieniaj) poniższym kodem: {% if product.Archival == false -%} {% if product.Clip or product.Batch -%}
{% for attribute in product.Supplies.AttributeNames -%} {{ attribute.Name | H }}
{% endfor -%}
{% elseif product.AttributesPolyvalent != empty -%} {% for attribute in product.AttributesPolyvalent -%}
{{ attribute.Name }}
{% for value in attribute.Values -%} {{ value.Value | H }} {% endfor -%}
{% endfor -%} {% endif -%} {% if product.Batch == null or product.Batch == false -%} {% if product.AttributesEditable != empty -%} {% assign i = 0 -%} {% for attribute in product.AttributesEditable -%}
{% assign i = i | Plus: 1 -%} {% endfor -%} {% endif -%} {% endif -%} {% if product.Units[1] -%}
{{ translations.Com_MeasureUnit }} {% for unit in product.Units -%} {% endfor -%}
{% endif -%} {% endif -%} Następnie znajdź kontener z klasą product-details__other-options i podmień go (wraz z jego zawartością) poniższym kodem: <codeclass="lang:ruby decode:true"> {% if product.Archival == false -%}
{{ translations.Com_RecommendProduct }}
{{ translations.Com_AskAboutProduct }}
{% endif -%} Teraz znajdź kontener z klasą product-details__prices. Na samym początku w tym kontenerze będzie warunek {% if customer.Authenticated == true -%}. Zmień go na {% if customer.Authenticated == true and product.Archival == false -%}. Następnie znajdź kontener z klasą product-details__button-group i podmień go (wraz z jego zawartością) na poniższy kod: {% if product.Archival == false -%}
{% if product.AskForPrice == false or customer.HidePrices == false -%} {% unless product.Availability.Status == 3 or noProductInStock == true -%}
{% if stockLevel -%} {% assign stockValue = stockLevel.Value | ToString | Split: "," -%} {% endif -%} {% if stockLevel and stockLevel.Control -%} {% assign stockValue = stockLevel.Value | ToString | Split: ',' -%} {% endif -%}
{% endunless -%} {% endif -%} {% if customer.HidePrices == true -%} {% else -%} {% endif -%}
{% else -%} {{translations.ArchivalProductCheckInfo}} {% endif -%} Teraz w pliku css/layout.css dodaj poniższy kod: .product-details .product-details__image__archival{position:absolute;bottom:0;left:0;right:0;padding:20px;font-size:18px;background:rgba(204,204,204,0.9);color:#fff;text-align:center}.product-details .product-details__archivalProductInfo{background:{{settings.$breadcrumbsBgColor}};color:{{settings.breadcrumbsFontColor}};padding:30px;margin-bottom:30px;text-align:center}.product-details .parent-category-link{color:{{settings.linkFontColor}};margin-top:50px;display:inline-block}.product-details .parent-category-link svg{fill:{{settings.linkFontColor}};height:19px;width:19px;vertical-align:middle} Wersja z miniaturkami zdjęć pod głównym zdjęciem W pliku elements/product/product-details-2.html wyszukaj frazę product-details__images. Zastąp kontener z tą klasą poniższym kodem:
{% for flag in product.Flags -%} {{ flag.Text }} {% endfor %}
{% if product.Images[1] -%}
{% for image in product.Images -%}
{% if image.Id != -1 -%} {{image | Img: 'grande'}} {% else -%} {{'css/img/img-placeholder.jpg' | Img, translations.DefaultImage}} {% endif -%} {% if product.Archival -%}
{{translations.ArchivalProduct}}
{% endif -%}
{% endfor -%}
    • {% for image in product.Images -%}
    • {% if image.Id != -1 -%} {{image | Img: 'compact'}} {% else -%} {{'css/img/img-placeholder.jpg' | Img, translations.DefaultImage}} {% endif -%}
{% endfor -%} {% elseif product.Images[0] -%}
{{product.Images[0] | Img: 'grande'}} {% if product.Archival -%}
{{translations.ArchivalProduct}}
{% endif -%}
{% else -%}
{{'css/img/img-placeholder.jpg' | Img, translations.DefaultImage}} {% if product.Archival -%}
{{translations.ArchivalProduct}}
{% endif -%}
{% endif -%} {% if product.Archival == false -%}
{{ translations.Com_RecommendProduct }}
{{ translations.Com_AskAboutProduct }}
{% endif -%} {% if product.Images[0] -%} {% endif -%}
  Następnie wyszukaj frazę product-details__add-to-cart. Na początku kontenera z tą klasą wklej poniższy kod: {% if product.Archival -%}
{{translations.ArchivalProductInfo}}
{% endif -%} Teraz wyszukiaj {% if product.Clip or product.Batch -%}. Podmień wszystko co jest pod tą linijką (łącznie z nią), aż do linijki {% include 'partials/common/after-adding-to-cart-popup.html' %} (jej nie podmieniaj) poniższym kodem: {% if product.Archival == false -%} {% if product.Clip or product.Batch -%}
{% for attribute in product.Supplies.AttributeNames -%} {{ attribute.Name | H }}
{% endfor -%}
{% elseif product.AttributesPolyvalent != empty -%} {% for attribute in product.AttributesPolyvalent -%}
{{ attribute.Name }}
{% for value in attribute.Values -%} {{ value.Value | H }} {% endfor -%}
{% endfor -%} {% endif -%} {% if product.Batch == null or product.Batch == false -%} {% if product.AttributesEditable != empty -%} {% assign i = 0 -%} {% for attribute in product.AttributesEditable -%}
{% assign i = i | Plus: 1 -%} {% endfor -%} {% endif -%} {% endif -%} {% endif -%} {% if product.Units[1] -%}
{{ translations.Com_MeasureUnit }} {% for unit in product.Units -%} {% endfor -%}
{% endif -%} {% if product.Archival == false -%}
{% if stockLevel.Control -%}
{{ translations.Com_StockLvl }}:  {% unless stockLevel.Type == 2 or stockLevel.Type == 4 or stockLevel.Text == null -%} {{ stockLevel.Text }} {% endunless -%} {% if stockLevel.Type == 4 -%} {{ stockLevel.Value | Normalize }} {{ product.SaleUnit }} {% endif -%} {% if stockLevel.Type == 1 or stockLevel.Type == 2 -%} {% unless stockLevel.ImageUrl == null -%} {{stockLevel.ImageUrl | Img: '', stockLevel.Text}} {% endunless -%} {% endif -%}
{% endif -%}
{{ translations.Com_Availability }} {% unless product.Availability.Text == '' or product.Availability.Type == 2 -%} {{ product.Availability.Text }}:  {% endunless -%} {% if product.Availability.Type == 1 or product.Availability.Type == 2 -%} {{product.Availability.ImageUrl | Img: '', product.Availability.Text}} {% endif -%} {% unless product.Availability.Date == null -%} ({{ product.Availability.Date | Date:'d' }}) {% endunless -%}
{% if product.DeliveryCost != null and product.DeliveryCost > 0 -%} {% endif -%}
{% if product.AskForPrice == false or customer.HidePrices == false -%} {% unless product.Availability.Status == 3 or noProductInStock == true -%}
{% if stockLevel -%} {% assign stockValue = stockLevel.Value | ToString | Split: "," -%} {% endif -%} {% if stockLevel and stockLevel.Control -%} {% assign stockValue = stockLevel.Value | ToString | Split: ',' -%} {% endif -%}
{% endunless -%} {% endif -%}
{% if customer.HidePrices == true -%} {% else -%} {% endif -%} {% if customer.Authenticated == true -%} {% endif -%}
{% else -%} {{translations.ArchivalProductCheckInfo}} {% endif -%}   Teraz w pliku css/layout.css dodaj poniższy kod: .product-details .product-details__image__archival{position:absolute;bottom:0;left:0;right:0;padding:20px;font-size:18px;background:rgba(204,204,204,0.9);color:#fff;text-align:center}.product-details .product-details__archivalProductInfo{background:{{settings.$breadcrumbsBgColor}};color:{{settings.breadcrumbsFontColor}};padding:30px;margin-bottom:30px;text-align:center}.product-details .parent-category-link{color:{{settings.linkFontColor}};margin-top:50px;display:inline-block}.product-details .parent-category-link svg{fill:{{settings.linkFontColor}};height:19px;width:19px;vertical-align:middle}

Obsługa funkcji autocomplete w wyszukiwarce szablonu

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.

Obsługa funkcji autocomplete w wyszukiwarce szablonu

W tym artykule dowiesz się jak zaimplementować funkcję autocomplete w swoim szablonie. Funkcja ta wyświetli klientowi listę towarów, które będą najlepiej pasowały do wpisanej przez niego frazy w wyszukiwarce.

Szafir

W pliku common/header/header.html wyszukaj frazę quick-search-form-lq. Zmodyfikuj element, do którego należy ta klasa, aby wyglądał tak:
Na końcu pliku js/init-ui2.js wklej ten kod:
<class="lang:ruby decode:true">function autocomplete(e) { var phrase = $(e.currentTarget).val().replace(/[!@#$%^&*()+={}\[\]:;"'<,>.?\~`\\|]*/gi, ''); if (phrase != '' && phrase.length > 1) { $.get(location.pathname, { __action: 'Get/SearchAutocomplete', search: phrase }).then(function (res) { if($(window).width() > 1280){ var right = 'right: 50px'; } else if($(window).width() > 680){ var right = ''; } else { var right = 'right: 61px'; } var loader = ''; $('.quick-search-form-lq.autocomplete-form-lq').append(loader); var url = res.action.Redirect302; $.get(url, {__csrf:__CSRF, __collection:'products.Products|page.BaseHref|currency|config.Products.ShowCode'}, function(res) { $('.autocomplete-lq').remove(); var list = res.collection['products.Products']; var showCode = res.collection['config.Products.ShowCode']; if (list.length > 0){ if (list.length > 5) { var size = 5; } else { var size = list.length; } var baseHref = res.collection['page.BaseHref']; var currency = res.collection['currency']; var autocompleteList = '
 
'; var regPhrase = '(' + phrase.split(' ').join('|') + ')'; var reg = new RegExp(regPhrase, 'gi'); for (i = 0; i < size; i++) { var nameNoQuotes = list[i].Name.replace(/"/g, '"'); var name = list[i].Name.replace(reg, function(str) {return ''+str+''}); if(showCode){ var code = list[i].Code.replace(reg, function(str) {return ''+str+''}); } else { var code = ''; } if(list[i].Price){ var price = list[i].Price.toPrice() + ' ' + currency; } else { var price = ''; } if(list[i].ImageId != -1){ var img = ''; } else { var img = ''; } var product = ' ' + '
' + img + '
' + '
' + '
' + name + '
' + '
' + code + '
' + '
' + '
' + price + '
' + ''; autocompleteList += product; } autocompleteList += '
'; $('.quick-search-form-lq.autocomplete-form-lq').append(autocompleteList); } $('.loader-for-autocomplete-lq').remove(); }); }); } else { $('.autocomplete-lq').remove(); $('.loader-for-autocomplete-lq').remove(); } }; $('body').on('input', '.quick-search-form-lq.autocomplete-form-lq [name="search"]', function (e) { if(timeOutAutocomplete!=null){ clearTimeout(timeOutAutocomplete); timeOutAutocomplete=null; } timeOutAutocomplete = setTimeout(function(){ autocomplete(e); }, 300); }); //variable to clearTimeout in autocomplete function var timeOutAutocomplete = null; $('body').on('blur', '.quick-search-form-lq.autocomplete-form-lq [name="search"]', function () { setTimeout(function () { $('.autocomplete-lq').remove(); $('.loader-for-autocomplete-lq').remove(); },200); });
Na końcu pliku scss/globals/_header.scss wklej ten kod: .autocomplete-ui{ position: absolute; z-index: 1000; width: 100%; box-shadow: 0px 8px 10px 0px rgba(0, 0, 0, 0.2); @media only screen and (max-width: 1280px) { max-width: 550px; left: 50%; transform: translateX(calc(-50% - 25px)); text-align: left; } @media only screen and (max-width: 680px) { left: auto; transform: none; } .product-ui{ display: block; padding: 10px; background: $bgColor; border: 1px solid $lightBorderColor; border-bottom: none; font-size: 16px; color: $primaryColorFont; transition: all ease 300ms; &:last-child{ border-bottom: 1px solid $lightBorderColor; } > div{ display: inline-block; vertical-align: top; margin-right: 10px; &:last-child{ width: 100px; margin-right: 0; text-align: right; } } .img-ui{ width: 50px; height: 50px; position: relative; } img{ max-width: 100%; max-height: 100%; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .names-ui{ width: calc(100% - 50px - 100px - 20px); } .name-ui{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: $primaryColor; transition: all ease 300ms; } .code-ui{ font-size: 12px; color: $bgColorFont; transition: all ease 300ms; } &:hover{ background: $btnSolidHoverBgColor; color: $btnSolidHoverTextColor; .name-ui, .code-ui{ color: $btnSolidHoverTextColor; } } } }
Wskazówka
Pamiętaj, że pliki scss i js należy zminifikować. O tym jak skompilować i zminifikować pliki scss i js dowiesz się z artykułu o kompilacji i minifikacji plików.

Agat

W pliku partials/common/header.html wyszukaj frazę id="search-form". Zmodyfikuj element, do którego należy to ID, aby wyglądał tak: W pliku js/init.js wyszukaj frazę events:. Linijkę wyżej wklej kod:
<class="lang:ruby decode:true ">autocomplete: function(e){ var phrase = $(e.currentTarget).val().replace(/[!@#$%^&*()+={}\[\]:;"'<,>.?\~`\\|]*/gi, ''); if (phrase != '' && phrase.length > 1) { $.get(location.pathname, { __action: 'Get/SearchAutocomplete', search: phrase }).then(function (result) { if (result.action.Result) { var loader = ''; $('#search-form').append(loader); var url = result.action.Redirect302; $.get(url, {__csrf:__CSRF, __collection:'products.Products|page.BaseHref|currency'}, function(res) { $('.autocomplete').remove(); var list = res.collection['products.Products']; if (list.length > 0){ if (list.length > 5) { var size = 5; } else { var size = list.length; } var baseHref = res.collection['page.BaseHref']; var currency = res.collection['currency']; var autocompleteList = '
 
'; var regPhrase = '(' + phrase.split(' ').join('|') + ')'; var reg = new RegExp(regPhrase, 'gi'); for (i = 0; i < size; i++) { var nameNoQuotes = list[i].Name.replace(/"/g, '"'); var name = list[i].Name.replace(reg, function(str) {return ''+str+''}); if(list[i].Price){ var price = list[i].Price.toPrice() + ' ' + currency; } else { var price = ''; } if(list[i].ImageId != -1){ var img = '' } else { var img = ''; } var product = '' + '
' + img + '
' + '
' + '
' + name + '
' + '
' + price + '
' + '
' + ''; autocompleteList += product; } autocompleteList += '
'; $('#search-form').append(autocompleteList); } $('.loader-for-autocomplete').remove(); }); } else if (result.action.Code != 100) { application.createMessage(result.action); } }); } else { $('.autocomplete').remove(); $('.loader-for-autocomplete').remove(); } },
Natomiast pod koniec pliku tuż pod takim kodem: $('body').on('blur', 'input', function () { application.uiCheckLabels(); }); Wklej taki kod: $('body').on('input', '#header-section #search-form.autocomplete-form [name="search"]', function (e) { if(timeOutAutocomplete!=null){ clearTimeout(timeOutAutocomplete); timeOutAutocomplete=null; } timeOutAutocomplete = setTimeout(function(){ self.autocomplete(e); }, 300); });
//variable to clearTimeout in autocomplete function var timeOutAutocomplete = null;$('body').on('blur', '#header-section #search-form.autocomplete-form [name="search"]', function () { setTimeout(function () { $('.autocomplete').remove(); $('.loader-for-autocomplete').remove(); }, 200); });
Na końcu pliku scss/main2.scss wklej kod: .autocomplete{ position: absolute; z-index: 101; width: 100%; @media only screen and (max-width: 1024px) { width: calc(100% - 240px); } @media only screen and (max-width: 768px) { width: calc(100% - 20px); } box-shadow: 0px 8px 10px 0px rgba(0, 0, 0, 0.2); .product{ display: block; padding: 10px; background: $bgColor; border: 1px solid $primaryColor; border-bottom: none; font-size: 16px; color: $bgDarkerColorFont; &:last-child{ border-bottom: 1px solid $primaryColor; } > div{ display: inline-block; vertical-align: top; margin-right: 10px; &:last-child{ margin-right: 0; } } .img{ width: 50px; height: 50px; position: relative; } img{ max-width: 100%; max-height: 100%; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .names{ width: calc(100% - 50px - 10px); } .name{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .price{ margin-top: 5px; font-size: 12px; color: $primaryColor; opacity: 0.63; } } }
Wskazówka
Pamiętaj, że pliki scss i js należy zminifikować. O tym jak skompilować i zminifikować pliki scss i js dowiesz się z artykułu o kompilacji i minifikacji plików.

Bursztyn

W pliku page/header.html wyszukaj frazę SearchForm. Zmodyfikuj element, do którego należy to ID, aby wyglądał tak: Na końcu pliku js/init2.js wklej ten kod:
<class="lang:ruby decode:true ">$('header').on('input', '#SearchForm.autocomplete-form [name="search"]', function (e) { if(timeOutAutocomplete!=null){ clearTimeout(timeOutAutocomplete); timeOutAutocomplete=null; } timeOutAutocomplete = setTimeout(function(){ var phrase = $(e.currentTarget).val().replace(/[!@#$%^&*()+={}\[\]:;"'<,>.?\~`\\|]*/gi, ''); if (phrase != '' && phrase.length > 1) { $.get(location.pathname, { __action: 'Get/SearchAutocomplete', search: phrase }).then(function (d) { var a = d.action; if (!a.Result) CreateTooltip(a); else if (a.Redirect302){ var loader = ''; $('#SearchForm').append(loader); var url = a.Redirect302; $.get(url, {__csrf:__CSRF, __collection:'productlist.Products|page.BaseHref|currency'}, function(res) { $('.autocomplete').remove(); var list = res.collection['productlist.Products']; if (list.length > 0){ if (list.length > 5) { var size = 5; } else { var size = list.length; } var baseHref = res.collection['page.BaseHref']; var currency = res.collection['currency']; var autocompleteList = '
 
'; var regPhrase = '(' + phrase.split(' ').join('|') + ')'; var reg = new RegExp(regPhrase, 'gi'); for (i = 0; i < size; i++) { var nameNoQuotes = list[i].Name.replace(/"/g, '"'); var name = list[i].Name.replace(reg, function(str) {return ''+str+''}); if(list[i].Price){ var price = list[i].Price.toPrice() + ' ' + currency; } else { var price = ''; } if(list[i].ImageId != -1){ var img = '' } else { var img = ''; } var product = ' ' + '
' + img + '
' + '
' + '
' + name + '
' + '
' + price + '
' + '
' + ''; autocompleteList += product; } autocompleteList += '
'; $('#SearchForm').append(autocompleteList); } $('.loader-for-autocomplete').remove(); }); } }); } else { $('.autocomplete').remove(); $('.loader-for-autocomplete').remove(); } }, 300); }); //variable to clearTimeout in autocomplete function var timeOutAutocomplete = null; $('header').on('blur', '#SearchForm.autocomplete-form [name="search"]', function () { setTimeout(function () { $('.autocomplete').remove(); $('.loader-for-autocomplete').remove(); }, 200); });
Na końcu pliku scss/main2.scss wklej ten kod:
<class="lang:ruby decode:true ">.autocomplete{ position: absolute; top: 43px; left: 0; z-index: 101; width: 100%; box-shadow: 0px 8px 10px 0px rgba(0, 0, 0, 0.2); .product{ display: block; padding: 10px; background: $bgColor; border: 1px solid $primaryColor; border-bottom: none; font-size: 14px; color: $secondaryColorFont; transition: all ease 300ms; &:last-child{ border-bottom: 1px solid $primaryColor; } > div{ display: inline-block; vertical-align: top; margin-right: 10px; &:last-child{ margin-right: 0; } } .img{ width: 50px; height: 50px; position: relative; } img{ max-width: 100%; max-height: 100%; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .names{ width: calc(100% - 50px - 10px); } .name{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .price{ margin-top: 5px; font-size: 12px; color: $primaryColor; opacity: 0.63; transition: all ease 300ms; } &:hover{ background: $primaryColor; color: $primaryColorFont; .price{ color: $primaryColorFont; opacity: 1; } } } }
Wskazówka
Pamiętaj, że pliki scss i js należy zminifikować. O tym jak skompilować i zminifikować pliki scss i js dowiesz się z artykułu o kompilacji i minifikacji plików.

Opal

W pliku page/header.html wyszukaj frazę SearchForm. Zmodyfikuj element, do którego należy to ID, aby wyglądał tak: Na końcu pliku js/init2.js wklej ten kod:
<class="lang:ruby decode:true ">$('header').on('input', '#SearchForm.autocomplete-form [name="search"]', function (e) { if(timeOutAutocomplete!=null){ clearTimeout(timeOutAutocomplete); timeOutAutocomplete=null; } timeOutAutocomplete = setTimeout(function(){ var phrase = $(e.currentTarget).val().replace(/[!@#$%^&*()+={}\[\]:;"'<,>.?\~`\\|]*/gi, ''); if (phrase != '' && phrase.length > 1) { $.get(location.pathname, { __action: 'Get/SearchAutocomplete', search: phrase }).then(function (d) { var a = d.action; if (!a.Result) CreateTooltip(a); else if (a.Redirect302){ var loader = ''; $('#SearchForm').append(loader); var url = a.Redirect302; $.get(url, {__csrf:__CSRF, __collection:'productlist.Products|page.BaseHref|currency|config.Products.ShowCode'}, function(res) { $('.autocomplete').remove(); var list = res.collection['productlist.Products']; var showCode = res.collection['config.Products.ShowCode']; if (list.length > 0){ if (list.length > 5) { var size = 5; } else { var size = list.length; } var baseHref = res.collection['page.BaseHref']; var currency = res.collection['currency']; var input = $('#SearchForm').find('[name=search]'); var width = input.width(); var top = input.offset().top + input.height() - 16; var autocompleteList = '
 
'; var regPhrase = '(' + phrase.split(' ').join('|') + ')'; var reg = new RegExp(regPhrase, 'gi'); for (i = 0; i < size; i++) { var nameNoQuotes = list[i].Name.replace(/"/g, '"'); var name = list[i].Name.replace(reg, function(str) {return ''+str+''}); if(showCode){ var code = list[i].Code.replace(reg, function(str) {return ''+str+''}); } else { var code = ''; } if(list[i].Price){ var price = list[i].Price.toPrice() + ' ' + currency; } else { var price = ''; } if(list[i].ImageId != -1){ var img = '' } else { var img = ''; } var product = ' ' + '
' + img + '
' + '
' + '
' + name + '
' + '
' + code + '
' + '
' + '
' + price + '
' + ''; autocompleteList += product; } autocompleteList += '
'; $('#SearchForm').parent().append(autocompleteList); } $('.loader-for-autocomplete').remove(); }); } }); } else { $('.autocomplete').remove(); $('.loader-for-autocomplete').remove(); } }, 300); }); //variable to clearTimeout in autocomplete function var timeOutAutocomplete = null; $('header').on('blur', '#SearchForm.autocomplete-form [name="search"]', function () { setTimeout(function () { $('.autocomplete').remove(); $('.loader-for-autocomplete').remove(); }, 200); });
Na końcu pliku scss/main2.scss wklej ten kod:
<class="lang:ruby decode:true ">.autocomplete{ position: absolute; left: calc(50% - 20px); transform: translateX(-50%); z-index: 1000; box-shadow: 0px 8px 10px 0px rgba(0, 0, 0, 0.2); .product{ display: block; padding: 10px; background: $bgColor; border: 1px solid $primaryColor; border-bottom: none; font-size: 16px; color: $secondaryColorFont; transition: all 300ms ease; &:last-child{ border-bottom: 1px solid $primaryColor; } > div{ display: inline-block; vertical-align: top; margin-right: 10px; &:last-child{ width: 100px; margin-right: 0; text-align: right; } } .img{ width: 50px; height: 50px; position: relative; } img{ max-width: 100%; max-height: 100%; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .names{ width: calc(100% - 50px - 100px - 20px); } .name{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .code{ font-size: 12px; opacity: 0.63; } &:hover{ background: $primaryColor; color: $primaryColorFont; } } }
Wskazówka
Pamiętaj, że pliki scss i js należy zminifikować. O tym jak skompilować i zminifikować pliki scss i js dowiesz się z artykułu o kompilacji i minifikacji plików.

Obsługa szczegółów zamówienia dla klienta niezalogowanego

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.

Obsługa szczegółów zamówienia dla klienta niezalogowanego

W tym artykule dowiesz się jak zmodyfikować szablon, aby klienci niezalogowani mogli w profilu klienta zobaczyć szczegóły zamówienia do którego posiadają link.

Szafir

W pliku customer-profile.html w pierwszym warunku dopisz and customer-profile.Order == null tak żeby warunek wyglądał w ten sposób: {% if customer.Authenticated == false and config.DefinedPages.CustomerProfile.Id == page.PageId and customer-profile.Order == null -%} W pliku customer-profile/orders/order-details.html wyszukaj frazę {{ page.Url }}?{{ pageQueryName }}. Będzie to część atrybutu przypisanego do przycisku powrotu do listy zamówień. Ten przycisk trzeba uwarunkować, aby zamiast niego pojawiał się link prowadzący do strony głównej sklepu. Ostatecznie powinno to wyglądać tak jak poniższy kod: {% if customer.Authenticated -%} {% else -%} {{ translations.BackToShopping }} {% endif -%} Pozostając dalej w tym samym pliku (customer-profile/orders/order-details.html) wyszukaj frazę Order/Cancel. Jest to nazwa jednej z interesujących nas akcji. Kawałek dalej znajdują się pozostałe dwie (Order/Accept i Order/RestorePayment). Pod każdą z nich wklej: Nadal w tym samym pliku (customer-profile/orders/order-details.html) wyszukaj frazę copy-to-cart-lq. Jest to klasa przycisku odpowiedzialnego za kopiowanie zamówienia do koszyka. Trzeba w nim dodać atrybut data-hash="{{ order.Hash }}" tak, aby ostatecznie przycisk wyglądał następująco: Następnie w pliku js/init-ui2.js znajdź funkcję copyToCart i zmodyfikuj jej początek, aby wyglądała tak: var id = $(e.currentTarget).data('id'); var hash = $(e.currentTarget).data('hash'); var data = { orderId: id, hash: hash, __csrf: __CSRF, __action: 'Order/Copy' };
Wskazówka
Pamiętaj, że pliki js należy zminifikować. O tym jak skompilować i zminifikować pliki js dowiesz się z artykułu o kompilacji i minifikacji plików.

Bursztyn lub Opal

W pliku customerprofile.html podmień warunek, aby wyglądał tak: {% if customer.Authenticated and page.PageId == config.DefinedPages.CustomerProfile.Id %}
{% include 'customer/profile.html' %}
{% elseif customer.Authenticated == false and page.PageId == config.DefinedPages.CustomerProfile.Id and customerprofile.Order != null %}
{% include 'customer/profile.html' %}
{% else %}
{% include 'customer/login.html' %}
{% endif %} W pliku customer/profile-orders.html wyszukaj frazę Order/RestorePayment. Jest to nazwa jednej z interesujących nas akcji. Kawałek dalej znajdują się pozostałe dwie (Order/Cancel i Order/Accept). Pod każdą z nich wklej: Nadal w tym samym pliku (customer/profile-orders.html) wyszukaj frazę copy-to-cart. Jest to klasa przycisku odpowiedzialnego za kopiowanie zamówienia do koszyka. Trzeba go zmodyfikować tak, aby wyglądał następująco: Następnie w pliku js/profile.js znajdź funkcję copyToCart i zmodyfikuj jej początek, aby wyglądała tak: var id = $(e.currentTarget).data('id'); var hash = $(e.currentTarget).data('hash'); var newLocation = $(e.currentTarget).data('url'); var added = $(e.currentTarget).data('added'); var copy = $(e.currentTarget).data('copy'); var notCopied = $(e.currentTarget).data('not-copied'); var data = [ { name: '__action', value: 'Order/Copy' }, { name: 'orderId', value: id }, { name: 'hash', value: hash }, { name: '__csrf', value: __CSRF } ];
Wskazówka
Pamiętaj, że pliki js należy zminifikować. O tym jak skompilować i zminifikować pliki js dowiesz się z artykułu o kompilacji i minifikacji plików.

Agat

W pliku customer-profile.html podmień warunek, aby wyglądał tak: {% if page.PageId == config.DefinedPages.CustomerProfile.Id and customer-profile.Order != null %} {% include 'partials/customer/order-details.html' -%} {% elseif customer.Authenticated and page.PageId == config.DefinedPages.CustomerProfile.Id %} W pliku partials/customer/order-details.html wyszukaj frazę Order/Accept. Jest to nazwa jednej z interesujących nas akcji. Kawałek dalej znajdują się pozostałe dwie (Order/RestorePayment i Order/Cancel). Pod każdą z nich wklej: Będąc przy akcji Order/Cancel dodaj warunek do inputa z templatką tak, aby wyglądał następująco: {% if customer.Authenticated == false and page.PageId == config.DefinedPages.CustomerProfile.Id and customer-profile.Order != null -%} {% else -%} {% endif -%} Nadal będąc przy tej akcji (Order/Cancel) zmodyfikuj przycisk odpowiadający za anulowanie zamówienia tak, aby wyglądał jak tu: Pozostając w tym samym pliku (partials/customer/order-details.html) wyszukaj frazę copy-to-cart. Jest to klasa przycisku odpowiedzialnego za kopiowanie zamówienia do koszyka. Trzeba w nim dodać atrybut data-hash="{{ order.Hash }}" tak, aby ostatecznie przycisk wyglądał następująco: Następnie w pliku js/init2.js znajdź funkcję copyToCart i zmodyfikuj jej początek, aby wyglądała tak: var copyButton = $(e.currentTarget); var copySection = copyButton.parents('.copy-section'); var id = copyButton.data('id'); var hash = copyButton.data('hash'); var data = [ { name: '__action', value: 'Order/Copy' }, { name: 'orderId', value: id }, { name: 'hash', value: hash }, { name: '__csrf', value: __CSRF } ]; W pliku js/init.js znajdź funkcję cancelOrder i podmień w niej posta, żeby wyglądał tak: $.post(url, data, function (result) { if (result.action.Result) { if($(e.currentTarget).hasClass('not-authenticated')){ application.createMessage(message); $('#main-section').html(result.template); application.loadImages(); } else { window.templateChanged['under-execution'] = true; application.createMessage(message); if (parent.index() != -1) { $('.under-execution').eq(1).replaceWith(result.template); } application.uiPreventScrolling(); } } else if (result.action.Code != 100) { application.createMessage(result.action); } }); W pliku partials/customer/order-details-products.html do kontenera z klasą cart-items dodaj warunek, tak aby ostatecznie kontener wyglądał następująco:
Na końcu pliku scss/main2.scss dodaj taki kod: @media only screen and (min-width: 481px) { .cart-items.not-authenticated .cart-item { width: calc(50% - 40px); display: inline-block; } } @media only screen and (min-width: 769px) { .cart-items.not-authenticated .cart-item { width: calc(50% - 25px); } } @media only screen and (min-width: 1025px) { .cart-items.not-authenticated { width: 66%; } } Na końcu pliku scss/mobile2.scss dodaj taki kod: .cart-items.not-authenticated{ text-align: center; margin: 20px auto; }
Wskazówka
Pamiętaj, że pliki js i scss należy zminifikować. O tym jak skompilować i zminifikować pliki js i scss dowiesz się z artykułu o kompilacji i minifikacji plików.

Obsługa funkcji przypomnienia o wystawieniu opinii za zakupiony towar

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.

Obsługa funkcji przypomnienia o wystawieniu opinii za zakupiony towar

W wersji sklepu oznaczonej numerkiem 2019.5 w Panelu Administracyjnym doszła funkcja przypomnienia o wystawieniu opinii za zakupiony towar. Ta funkcja będzie wysyłać maila z owym przypomnieniem. W tym mailu będzie również link do szczegółów danego towaru. W tym artykule dowiesz się jak zmodyfikować swój szablon, aby po kliknięciu w ten link otworzyła się strona ze szczegółami danego towaru, a w niej był już otwarty i gotowy do uzupełnienia formularz wystawienia opinii.

Agat

W pliku js/init.js znajdź funkcję addReview i w niej zaraz pod zmienną validate dodaj taki kod: if(window.location.hash.includes('#rate')){ var hash = window.location.hash.split('&')[1].split('=')[1]; data.push({ name: 'orderHash', value: hash }); } A następnie w pliku js/init2.js na samym końcu dodaj taki kod: function autoOpenReviewsForm(){ $('.new-review-label').trigger('click'); $('html, body').animate({ scrollTop: $('.new-review-label').offset().top - 81 }, 500); } $(document).ready(function () { if(window.location.hash.includes('#rate')){ autoOpenReviewsForm(); } });
Wskazówka
Z tego artykułu dowiesz się jak zminifikować swoje pliki js

Bursztyn

W pliku js/init.js wyszukaj frazę #opinion-form .primary-action. Będzie to trigger funkcji do wystawiania opinii. W tej funkcji pod zmienną data dodaj taki kod: if(window.location.hash.includes('#rate')){ var hash = window.location.hash.split('&')[1].split('=')[1]; data.push({ name: 'orderHash', value: hash }); } A następnie w pliku js/details.js na samym końcu dodaj taki kod: $(document).ready(function () { if(window.location.hash.includes('#rate')){ $('#add-first-review').trigger('click'); } });
Wskazówka
Z tego artykułu dowiesz się jak zminifikować swoje pliki js

Opal

W pliku js/init.js wyszukaj frazę #opinion-form .primary-action. Będzie to trigger funkcji do wystawiania opinii. W tej funkcji pod zmienną data dodaj taki kod: if(window.location.hash.includes('#rate')){ var hash = window.location.hash.split('&')[1].split('=')[1]; data.push({ name: 'orderHash', value: hash }); } A następnie w pliku js/details.js na samym końcu dodaj taki kod: $(document).ready(function () { if(window.location.hash.includes('#rate')){ $('#main-rating > .glyphicon').trigger('click'); } });
Wskazówka
Z tego artykułu dowiesz się jak zminifikować swoje pliki js

Obsługa załączników do zamówienia

Obsługa załączników do zamówienia

W tym artykule dowiesz się jak zmodyfikować swój szablon, aby było możliwe dodanie załączników do składanego zamówienia.
Wskazówka
W tym artykule trzeba będzie wprowadzać zmiany w plikach js oraz scss. Z tego artykułu dowiesz się jak je kompilować i minifikować.

Szafir

Na potrzeby tej funkcji zdecydowaliśmy się na drobne zmiany w wyglądzie elementów związanych z dodawaniem notatki do zamówienia. Poniższe fragmenty kodu uwzględniają owe zmiany. Na początku dodaj w swoim szablonie nowe frazy w zakładce Tłumaczenia: AddAttachement - Dodaj załącznik AddMessage - Dodaj wiadomość YourMessage - Twoja wiadomość AttachementsNotAdded - Nie dodano wszystkich załączników Attachements - Załączniki W pliku _layout.html w elemencie head znajduje się skrypt, który dodaje do zmiennej __translations różne tłumaczenia. Dodaj tam taką linijkę: AttachementsNotAdded: "{{ translations.AttachementsNotAdded | H }}", Jeśli miałeś w swoim szablonie funkcję do importowania koszyka z pliku to w pliku common/navigation-bars/main-navigation-bar-partial.html wyszukaj frazę id="file", a następnie w tym inpucie, w którym występuje to ID dodaj klasę import-cart-input-lq. Następnie w pliku js/init.js wyszukaj frazę [name="file"] i zamień ją na .import-cart-input-lq. W pliku order/cart.html wyszukaj frazę note-lq. Jest to klasa kontenera, w którym znajdują się wszystkie elementy związane z dodawaniem notatki do zamówienia. Wytnij cały ten kontener czyli taki kod: {% if usr.Authenticated -%}
{% if order.Note == '' -%} {% include 'order/note-partials/note-form.html' -%} {% else -%} {% include 'order/note-partials/note-presentation.html' -%} {% endif -%}
{% endif -%}
i wklej go pod taką linijką: {% include 'common/order-summary.html' -%} Następnie trzeba w całości podmienić zawartość plików order/note-partials/note-form.html oraz order/note-partials/note-presentation.html. Poniżej właściwa zawartość obu plików: order/note-partials/note-form.html {% assign isExpanded = include -%}
{% if config.Orders.AttachmentsEnabled -%}
{% endif -%} {% if isExpanded == true or isExpanded == 'true' -%} {% else -%} {% endif -%} {% if config.Orders.AttachmentsEnabled -%}
{% endif -%}
{% if config.Orders.AttachmentsEnabled -%}
{% include 'order/attachements.html' -%}
{% endif -%} order/note-partials/note-presentation.html {% if config.Orders.AttachmentsEnabled -%}
{% endif -%} {% if config.Orders.AttachmentsEnabled -%}
{% endif -%}
{{order.Note}}
{% if config.Orders.AttachmentsEnabled -%}
{% include 'order/attachements.html' -%}
{% endif -%}
Kolejnym krokiem będzie dodanie nowego pliku HTML o nazwie attachements. Należy go dodać do folderu order. Poniżej zawartość tego pliku: order/attachements.html {% for file in order.Attachments -%} {% assign type = file.Name | Split: '.' | Last -%} {% if type == "jfif" or type == "jpe" or type == "jpeg" or type == "jpg" -%} {% assign icon = 'ti-image' -%} {% elseif type == "csv" or type == "txt" -%} {% assign icon = 'ti-receipt' -%} {% elseif type == "docx" or type == "odt" -%} {% assign icon = 'ti-write' -%} {% elseif type == "xlsx" or type == "ods" -%} {% assign icon = 'ti-bar-chart' -%} {% elseif type == "pdf" -%} {% assign icon = 'ti-agenda' -%} {% else -%} {% assign icon = 'ti-file' -%} {% endif -%}
{{ file.Name }}
{% endfor -%}
Następnie w pliku customer-profile/orders/order-details.html wyszukaj frazę {% if order.Note != "" -%}. W tym warunku znajdują się dodane notatki do zamówienia. Pod tym warunkiem wklej poniższy kod: {% if config.Orders.AttachmentsEnabled -%}
{% for file in order.Attachments -%} {% assign type = file.Name | Split: '.' | Last -%} {% if type == "jfif" or type == "jpe" or type == "jpeg" or type == "jpg" -%} {% assign icon = 'ti-image' -%} {% elseif type == "csv" or type == "txt" -%} {% assign icon = 'ti-receipt' -%} {% elseif type == "docx" or type == "odt" -%} {% assign icon = 'ti-write' -%} {% elseif type == "xlsx" or type == "ods" -%} {% assign icon = 'ti-bar-chart' -%} {% elseif type == "pdf" -%} {% assign icon = 'ti-agenda' -%} {% else -%} {% assign icon = 'ti-file' -%} {% endif -%} {% endfor -%}
{% endif -%}
Na samym końcu pliku js/init-ui2.js dodaj poniższy kod: function processFilesList(fileInput, fileList) { var file = fileList.pop(); if (file) { var fd = new FormData(); fd.append('__CSRF',__CSRF); fd.append('__action','Order/AttachmentAdd'); fd.append('__template','order/attachements.html'); fd.append('file', file); $.ajax({data: fd, processData: false, contentType: false, type: 'POST', success: function(data){ fileInput.val(''); if(data.action.Result){ $('.attachements-lq').html(data.template); } else { if(window.AttachementsNotAdded == undefined){ window.AttachementsNotAdded = []; } var attachement = ' '+file.name+': '+data.action.Message+' '; window.AttachementsNotAdded.push(attachement); } processFilesList(fileInput, fileList); } }); } else { $('.during-ajax-modal-lq').addClass('hidden-lq'); fileInput.removeClass('prevent-double-change-lq'); if(window.AttachementsNotAdded){ var popup = ' '+ ' '+ ' '+ __translations.AttachementsNotAdded+ ''+ ' '+ window.AttachementsNotAdded.join('')+ ' '+ ' '; $('body').append(popup); $('body').addClass('modal-opened-ui'); window.AttachementsNotAdded = undefined; } } }; function addAttachementInOrder(e) { $('.during-ajax-modal-lq').removeClass('hidden-lq'); var fileInput = $(e.currentTarget); fileInput.addClass('prevent-double-change-lq'); var fileList = Object.values(e.currentTarget.files); processFilesList(fileInput, fileList); }; $('body').on('change', '.add-attachement-in-order-lq:not(.prevent-double-change-lq)', function (e) { addAttachementInOrder(e); }); function removeAttachementInOrder(e) { $.post('', {__csrf: __CSRF, __action: 'Order/AttachmentDelete', id: $(e.currentTarget).data('id')}, function(result) { $(e.currentTarget).parents('.file-container-lq').remove(); }); }; $('body').on('click', '.remove-attachement-in-order-lq', function (e) { removeAttachementInOrder(e); }); W pliku scss/globals/_globals-m.scss wyszukaj frazę &.confirm-in-cart-ui. Zmień tam top: 93px na top: 70px. Usuń też right: 20px. W pliku scss/globals/_globals2.scss wyszukaj frazę .add-note-in-cart-ui. Dopisz do niej po spacji w tej samej linijce .ti-notepad a następnie usuń margin-top: -10px i dodaj font-size: 20px. W pliku scss/globals/partials/_order.scss wyszukaj frazę .note-ui. Nadpisz wszystkie style znajdujące się w tej klasie poniższym kodem: margin-top: 20px; border-top: 1px solid $lightBorderColor; border-bottom: 1px solid $lightBorderColor; padding: 15px; .half-ui{ padding: 10px; float: left; &:first-child{ border-right: 1px solid $lightBorderColor; } } button { padding: 0; line-height: 1; } .note-in-order-ui{ margin-top: 20px; position: relative; .edit-ui{ margin-left: 15px; vertical-align: baseline; } } .label-note-in-cart-ui{ color: $labelsColor; cursor: default; } .form-to-confirm-in-cart-ui{ resize: none; } .confirm-in-cart-ui{ top: -20px; } .file-container-ui{ background: $bgColor; border: 1px solid $lightBorderColor; border-radius: 20px; margin: 0 10px 10px 0; padding: 5px 15px 10px; display: inline-block; &:first-child{ margin-top: 20px; } .icon-ui{ font-size: 20px; vertical-align: middle; } .remove-attachement-in-order-ui{ vertical-align: middle; margin-left: 10px; cursor: pointer !important; } }

Dodawanie wyboru działu na formularzu kontaktowym

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.
Ta instrukcja przeznaczona jest dla standardowych szablonów Comarch starszych niż wersja 2019.6. Jeśli posiadasz w sklepie kilka adresów do kontaktu, możesz umożliwić swoim klientom wybór jednego z nich na formularzu kontaktowym. Aby to zrobić, skorzystaj z poniższych instrukcji. Poszczególne pliki, które należy zmodyfikować znajdują się w Panelu administracyjnym w sekcji: Wygląd sklepu/ Ustawienia/ Więcej/ Edytuj ustawienia zaawansowane następnie Więcej/ Edytuj HTML.
Wskazówka
Pamiętaj, aby wprowadzone zmiany w szablonie Zapisać i Opublikować.

1. Bursztyn i Opal

1.1. W pliku contact.html odnajdź element z id "ContactSendForm" i powyżej pola e-mail dodaj poniższy kod: {% if config.Contact.Contacts[0] -%} <select name="contactId" class="department" required> {% for contact in config.Contact.Contacts -%} <option value="{{ contact.Id }}">{{ contact.Name }}</option> {% endfor -%} </select> {% endif -%} 1.2. Aby wygląd nowego pola był spójny z resztą formularza, w pliku scss/contact.scss odnajdź selektor ".contact-info .contact-form input, .contact-info .contact-form textarea" i zamień go na: .contact-info .contact-form input, .contact-info .contact-form textarea, .contact-info .contact-form select 1.3. Skompiluj i zminifikuj pliki scss.
Wskazówka
O tym jak skompilować i zminifikować pliki scss dowiesz się z artykułu o kompilacji i minifikacji plików.
1.4. Aby walidacja działała poprawnie, w pliku js/init.js, odnajdź funkcję resetForm(form) i podmień ją na: function resetForm(form) { var inputs = form.find('input:not([disabled]):not([type=hidden]), select:not([disabled]):not(".department"), textarea:not([disabled])'); inputs.each(function () { $(this).val(''); }); $('.loader-icon').remove(); $('.send-contact-form').removeClass('posting'); } 1.5. Zminifikuj pliki js.
Wskazówka
O tym jak zminifikować pliki js dowiesz się z artykułu o kompilacji i minifikacji plików.
 

2. Agat

2.1. W pliku contact.html odnajdź element z id "contact-form" i powyżej pola e-mail dodaj poniższy kod: {% if config.Contact.Contacts[0] -%} <div class="input-group"> <span class="required-fields-info">{{ translations.Department }} *</span> <select name="contactId" required> {% for contact in config.Contact.Contacts -%} <option value="{{ contact.Id }}">{{ contact.Name }}</option> {% endfor -%} </select> </div> {% endif -%} 2.2. Aby wygląd nowego pola był spójny z resztą formularza, w pliku scss/mobile1.scss odnajdź selektor "input, textarea" i zamień go na: input, textarea, .contact-form-popup select 2.3. Skompiluj i zminifikuj pliki scss.
Wskazówka
O tym jak skompilować i zminifikować pliki scss dowiesz się z artykułu o kompilacji i minifikacji plików.
2.4. Dodaj tłumaczenie dla frazy Department [Dział] Tłumaczenie należy dodać w Panelu administracyjnym: Wygląd sklepu/ Ustawienia na zakładce Tłumaczenia.

3. Szafir

3.1. W pliku common/navigation-bars/navigation-contact.html odnajdź element z klasą "form-lq form-ui" i wewnątrz niego dodaj poniższy kod: {% if config.Contact.Contacts[0] -%} <div> <label>{{ translations.Department }}<span class='required-ui'>*</span></label> <span class="select-background-ui w100-ui"> <select name="contactId" required> {% for contact in config.Contact.Contacts -%} <option value="{{ contact.Id }}">{{ contact.Name }}</option> {% endfor -%} </select> </span> <i class="ti-angle-down select-arrow-ui"></i> </div> {% endif -%} 3.2. Dodaj tłumaczenie dla frazy Department [Dział] Tłumaczenie należy dodać w Panelu administracyjnym: Wygląd sklepu/ Ustawienia na zakładce Tłumaczenia. 3.3. Aby walidacja działała poprawnie, w pliku js/init-ui2.js, odnajdź funkcję sendContactForm(e) i podmień ją na: function sendContactForm(e) { if(app.validationBeforePost(e) != 'error'){ $('.during-ajax-modal-lq').removeClass('hidden-lq'); var form = $(e.currentTarget).parents('.form-lq'); var inputs = form.find('input, textarea, select'); var data = inputs.serializeArray(); data.push({name: "__csrf", value: __CSRF}); $.post('', data, function(result) { $('.during-ajax-modal-lq').addClass('hidden-lq'); if (result.action.Result) { var message = form.data('success'); app.temporaryMessage(form, message); $.each(inputs, function (index, value) { if ($(value).attr('name') !== 'contactId' && $(value).attr('name') !== '__action') { $(value).val(''); } }); form.find('.message-lq').remove(); } else { var message = '<p>' + result.action.Message + '</p>'; if(result.action.Code != 100){ app.message(form, message); } } }); $(e.currentTarget).removeClass('error-lq'); } else { $(e.currentTarget).addClass('error-lq'); } } 3.4. Zminifikuj pliki js.
Wskazówka
O tym jak zminifikować pliki js dowiesz się z artykułu o kompilacji i minifikacji plików.

Obsługa cross-sellingu po złożeniu zamówienia

Obsługa cross-sellingu po złożeniu zamówienia

W tym artykule dowiesz się jak zmodyfikować swój szablon, aby po złożeniu zamówienia wyświetliła się sekcja z Cross-Sellingiem.
Wskazówka
W tym artykule trzeba będzie wprowadzać zmiany w plikach js oraz scss. Z tego artykułu dowiesz się jak je kompilować i minifikować.

Szafir

W pliku order/thx.html wyszukaj frazę date-format.html. Pod linijką z tą frazą będzie się znajdował warunek, który trzeba zastąpić poniższym kodem: {% if order.SelectedDelivery.Payment.MethodType == 2 and order.PlacedOrder.ExternalPayment.Success == false -%} {% assign externalPaymentError = true -%} {% endif -%} {% if externalPaymentError or crossSellingSize > 0 -%} Dalej w tym samym pliku (order/thx.html) wyszukaj frazę lastpagescripts.html. Nad linijką z tą frazą będzie się znajdowało zamknięcie warunku, które trzeba zastąpić poniższym kodem: {% elseif crossSellingSize > 0 -%}{{ translations.CrossSellingInfo }} {% for product in cross-selling.Products -%} {{ product | Img:'compact' }} {{ product | Img:'large' }} {{ product.Name }} {% unless customer.HidePrices -%} {% if product.Price == null or product.AskForPrice -%} {{ translations.AskForPrice }} {% else -%} product.Price -%} class="price-container-ui" {% endif -%}> {{ product.Price | ToPrice }} {{ currency }} {% if customer.Cart.SubtotalPrices -%} {{ translations.Netto | Downcase }} {% else -%} {{ translations.Brutto | Downcase }} {% endif -%} {% if product.PreviousPrice > product.Price -%} {{ product.PreviousPrice | ToPrice }} {{ currency }} {% if customer.Cart.SubtotalPrices -%} {{ translations.Netto | Downcase }} {% else -%} {{ translations.Brutto | Downcase }} {% endif -%} {% endif -%} {% endif -%} {% endunless -%}{% endfor -%} Następnie w pliku js/init-ui1.js wyszukaj frazę 1019. Będzie to linijka z warunkiem dotyczącym kodu błędu. Pod tym warunkiem wklej poniższy kod: if ($('.crossselling-slider-in-cart-lq').length) { window.UIFeatures.initSlider('.crossselling-slider-in-cart-lq', { dots: true, slidesToShow: 3, slidesToScroll: 3, arrows: false, infinite: false, responsive: [{ breakpoint: 1279, settings: { slidesToShow: 1.7, slidesToScroll: 1, arrows: false, infinite: false, dots: false } }] }); $('.crossselling-slider-in-cart-lq img').each(function () { var src = $(this).data('src'); $(this).attr('src', src); }) } Na samym końcu pliku js/init-ui2.js wklej poniższy kod: $(document).ready(function () { if ($('.crossselling-slider-in-cart-lq').length) { UIFeatures.initSlider('.crossselling-slider-in-cart-lq', { dots: true, slidesToShow: 3, slidesToScroll: 3, arrows: false, infinite: false, responsive: [{ breakpoint: 1279, settings: { slidesToShow: 1.7, slidesToScroll: 1, arrows: false, infinite: false, dots: false } }] }); $('.crossselling-slider-in-cart-lq img').each(function () { var src = $(this).data('src'); $(this).attr('src', src); }) } }); Na końcu pliku scss/globals/_globals2.scss wklej poniższy kod: .cross-selling-ui{ padding: 40px 0; .minibox-product-container-ui{ padding: 10px; } .minibox-product-ui{ margin: 0; height: 300px; width: 100%; } }

Topaz

W Panelu Administratora przejdź do zakładki Tłumaczenia (Wygląd sklepu -> Ustawienia -> Tłumaczenia) i dodaj tam frazę CrossSellingInfo - Inni kupili również. Następnie przejdź do zakładki Obiekty (Wygląd sklepu -> Ustawienia -> Trzykropek w prawym górnym rogu -> Edytuj ustawienia zaawansowane -> Obiekty) i dodaj tam nowy obiekt typu Nowości i Promocje o nazwie cross-selling. Po jego dodaniu wejdź w jego ustawienia i wybierz Typ Cross-Selling. Następnie pozostając w tych ustawieniach przejdź na zakładkę Strony i zaznacz tam check-box Zamówienie. Zapisz to wszystko kliknięciem w dyskietkę w prawym górnym rogu. W pliku partials/product-item.html wyszukaj frazę config.Products.ShowCode, a następnie usuń warunek (wraz z jego zawartością), którego częścią jest ta fraza. Następnie w pliku staticElements/cart/cart.html wyszukaj frazę cart--step-five. Zastąp kontener z tą klasą (wraz z jego zawartością) poniższym kodem:
<class="cart cart--step-five {% if crossSellingSize ><p> 0 -%} with-crossselling {% endif -%}"> <class="cart--step-five-content-container">{{translations.Thx}}, {{ translations.Crt_Order }} nr {{ cart.PlacedOrder.Id }} {{translations.Crt_BeenPlaced}}{{ translations.Crt_DetailsOnMail }} {% if cart.NotDeterminedDeliveryCost or cart.SelectedDelivery.NotDeterminedDeliveryCost %} {{ translations.Crt_NotDeterminedDeliveryCostInfo }}. {% endif -%} {{ translations.Crt_CheckSPAM }}{{ translations.Crt_BackToShop }} {% if cart.SelectedDelivery.Payment.MethodType == 2 and cart.PlacedOrder.ExternalPayment.Success == false -%} <class="cart cart--unfinished-payment">

{{ translations.Crt_RestoreTitle }}

{{ translations.Crt_RestoreText }}

<class="shoppingCart__form--restore inputs-container-js"> {{ translations.IPay }} {{ translations.Crt_BackToShop }} {% endif -%} {% if crossSellingSize > 0 -%} <class="cart--step-five-crossselling-container"> {{ translations.CrossSellingInfo }} <class="cart--step-five-crossselling-container"> {% include 'partials/product-item.html' with product -%} <class="cart--step-five-crossselling-container">{% assign products = cross-selling.Products | Randomize -%} {% for product in products -%} {% unless product.Url == null and product.Url == '' -%} {% endunless -%} {% endfor -%}{% endif -%}
Teraz w pliku js/layout1.js (lub layout0.js jeśli nie ma layout1.js, lub layout.js jeśli nie ma dwóch poprzednich) wyszukaj zmienną var cartFunctions. W niej należy znaleźć funkcję init (zaraz na samym początku) i dodać na jej końcu linijkę this.initializeCrossSellingSlider();. Następnie trzeba wyszukać linijkę z frazą $('.activeCart').removeClass('activeCart'); (będzie w funkcji placeOrder) i pod nią dodać cartFunctions.initializeCrossSellingSlider();. Teraz trzeba dodać poniższy kod pod funkcją placeOrder: initializeCrossSellingSlider: function () { $('.crossselling-slider').slick({ infinite: false, slidesToShow: 3, slidesToScroll: 1, responsive: [ { breakpoint: 1441, settings: { slidesToShow: 2 } }, { breakpoint: 769, settings: { slidesToShow: 1 } } ] }); }, Następnie w pliku css/layout.css dodaj poniższy kod: .cart--step-five.with-crossselling{display:block}.cart--step-five a{display:block;width:calc(100% - 50px);max-width:387px;margin-bottom:35px}.crossselling-slider{padding:0}.crossselling-slider .slick-track{margin:0 -20px}.crossselling-slider .slick-prev,.crossselling-slider .slick-next{background:{{settings.sliderBgColor}}}.crossselling-slider .slick-prev:before,.crossselling-slider .slick-next:before{border:solid {{settings.sliderArrowColor}};border-width:0 1px 1px 0;display:inline-block;padding:3px;width:4px;height:4px;top:14px;content:" "}.crossselling-slider .slick-prev:hover,.crossselling-slider .slick-next:hover{background:{{settings.sliderHoverBgColor}}}.crossselling-slider .slick-prev:hover:before,.crossselling-slider .slick-next:hover:before{border-color:{{settings.sliderHoverArrowColor}}}.crossselling-slider .slick-prev{left:-40px}.crossselling-slider .slick-prev:before{left:16px;transform:rotate(135deg);-webkit-transform:rotate(135deg)}.crossselling-slider .slick-next{right:-40px}.crossselling-slider .slick-next:before{left:14px;transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}.crossselling-slider button.slick-prev.slick-arrow.slick-disabled,.crossselling-slider button.slick-arrow.slick-disabled{display:none !important} Teraz w pliku css/layout-m.css dodaj poniższy kod: .cart--step-five-crossselling-container{margin-top:60px}.crossselling-slider .slick-prev{left:0}.crossselling-slider .slick-next{right:0} Następnie w pliku css/layout-d.css dodaj poniższy kod: .cart.with-crossselling{display:block}.cart--step-five.with-crossselling .cart--step-five-content-container{width:30%;margin-right:9%;display:inline-block;vertical-align:top}.cart--step-five-crossselling-container{display:inline-block;width:60%;vertical-align:top}

Obsługa dodawania załączników do reklamacji i zwrotów

Obsługa dodawania załączników do reklamacji i zwrotów

W tym artykule dowiesz się jak zmodyfikować swój szablon, aby przy składaniu reklamacji i zwrotów było możliwe dołączenie załączników.
Wskazówka
Na podstawie tego artykułu trzeba będzie wprowadzać zmiany w plikach js oraz scss. Pamiętaj, że te pliki należy zminifikować. O tym jak skompilować i zminifikować pliki js oraz scss dowiesz się z artykułu o kompilacji i minifikacji plików.

Szafir

W pliku product/cart-product.html wyszukaj frazę note-ui. Nad linijką z tą frazą wklej poniższy kod:
{% if config.Complaints.AttachmentsEnabled -%}
<div>
<label class="add-attachement-label-ui" for="file"><i class="ti-clip"></i> {{ translations.AddAttachement }}</label> {% capture maxSize -%}{{config.Complaints.AttachmentMaxSize}}B{% endcapture -%} {% for i in (1..config.Complaints.AttachmentsMaxCount) -%}
<div class="input-file-container-ui"><input class="add-attachement-in-complaint-ui add-attachement-in-complaint-lq" accept="{{ config.Complaints.AttachmentExtensions }}" name="file" type="file" data-file-size="{{ config.Complaints.AttachmentMaxSize }}" data-size-exceeded="{{ translations.Com_FileSizeExceeded | Format: maxSize }}" data-invalid-file="{{ translations.Com_InvalidFile | Format: config.Complaints.AttachmentExtensions }}" /> <i class="ti-close clear-file-input-ui clear-file-input-lq hidden-lq"></i></div>
{% endfor -%}
</div>
{% endif -%}
  Następnie w pliku customer-profile/complaints/complaints.html wyszukaj frazę complaint.Response. Będzie to linijka z warunkiem. Pod tym warunkiem wklej poniższy kod:
{% assign complaintsSize = customer-profile.Complaint.Attachments | Size -%}
{% if config.Complaints.AttachmentsEnabled and complaintsSize &gt; 0 -%}</code>
<div class="attachements-ui remarks-ui">

{% for file in customer-profile.Complaint.Attachments -%} {% assign type = file.Name | Split: '.' | Last -%} {% if type == "jfif" or type == "jpe" or type == "jpeg" or type == "jpg" -%} {% assign icon = 'ti-image' -%} {% elseif type == "csv" or type == "txt" -%} {% assign icon = 'ti-receipt' -%} {% elseif type == "docx" or type == "odt" -%} {% assign icon = 'ti-write' -%} {% elseif type == "xlsx" or type == "ods" -%} {% assign icon = 'ti-bar-chart' -%} {% elseif type == "pdf" -%} {% assign icon = 'ti-agenda' -%} {% else -%} {% assign icon = 'ti-file' -%} {% endif -%}
<div class="file-container-ui file-container-lq"><a href="{{ file.Url }}"><i class="{{ icon }} icon-ui"></i> <span class="line-height-1-ui va-mid-ui">{{ file.Name }}</span></a></div>
{% endfor -%}

</div>
{% endif -%}
  Teraz w pliku js/init-ui2.js wyszukaj frazę sendComplaintSuccess(e). Będzie to funkcja, którą należy usunąć. Usuń również jej wywołanie, które jest tuż pod nią. Na samym końcu tego pliku (js/init-ui2.js) wklej poniższy kod:
function addAttachementInComplaint(e) {
window.attachementsInComplaint = [];
var filesArr = $(e.currentTarget).parents('.form-lq').find('.add-attachement-in-complaint-lq');
filesArr.each(function () {
if(this.files.length &gt; 0){
var maxSize = $(this).data('file-size');
if(this.files[0].size &gt; maxSize){
var message = $(this).data('size-exceeded');
var popup = '</code>
<div class="message-popup-background-ui errors-lq cancel-lq container-to-delete-lq" style="overflow: auto;">

'+ '
<div class="message-popup-ui box-ui after-adding-to-cart-popup-ui message-popup-on-background-lq" style="position: absolute; top: 0; transform: none; margin: 10% auto;">

'+ '
<div class="box-ui product-added-to-cart-ui" style="position: relative; padding: 20px;">'+ __translations.AttachementsNotAdded+ '<i class="ti-close cancel-lq" style="position: absolute; right: 20px; top: 20px; cursor: pointer;"></i>'+ '</div>
'+ '
<div style="padding: 20px;"><strong>'+this.files[0].name+'</strong>: '+message+'</div>
'+ '

</div>
'+ '

</div>
'; $('body').append(popup); $('body').addClass('modal-opened-ui'); $(this).val(''); } else { window.attachementsInComplaint.push(this.files[0]); $(this).next().removeClass('hidden-lq'); } } }); }; $('body').on('change', '.add-attachement-in-complaint-lq', function (e) { addAttachementInComplaint(e); }); function sendComplaint(e) { if(app.validationBeforePost(e) != 'error'){ var form = $(e.currentTarget).parents('.form-lq'); var dataFromHTML = form.find('input:not([disabled]), select:not([disabled]), textarea:not([disabled])').serializeArray(); var fileList = window.attachementsInComplaint; if(fileList != undefined){ var filesSize = fileList.length; } var fd = new FormData(); fd.append('__csrf', __CSRF); for(var i=0; i&lt;dataFromHTML.length; i++){ fd.append(dataFromHTML[i].name,dataFromHTML[i].value); } if (filesSize &gt; 0) { window.AttachementsErrors = 0; function addAttachementError(file, message) { if(window.AttachementsNotAdded == undefined){ window.AttachementsNotAdded = []; } var attachement = '
<div style="padding: 20px;"><strong>'+file.name+'</strong>: '+message+'</div>
'; window.AttachementsNotAdded.push(attachement); window.AttachementsErrors += 1; }; var input = form.find('[name=file]').eq(0); var extensions = input.attr('accept').split(', '); var fileSize = input.data('file-size'); for (var i=0; i&lt;filesSize; i++) { var file = fileList.pop(); if(file.size &lt; fileSize){ var extensionArr = file.name.split('.'); var extension = '.' + extensionArr[extensionArr.length - 1].toLowerCase(); var wrongExtension = true; for(var j=0; j&lt;extensions.length; j++){ if(extension == extensions[j]){ fd.append('file'+i, file); var wrongExtension = false; } } if(wrongExtension){ var message = input.data('invalid-file'); addAttachementError(file, message); } } else { var message = input.data('size-exceeded'); addAttachementError(file, message); } } if(window.AttachementsNotAdded){ var popup = '
<div class="message-popup-background-ui errors-lq cancel-lq container-to-delete-lq" style="overflow: auto;">

'+ '
<div class="message-popup-ui box-ui after-adding-to-cart-popup-ui message-popup-on-background-lq" style="position: absolute; top: 0; transform: none; margin: 10% auto;">

'+ '
<div class="box-ui product-added-to-cart-ui" style="position: relative; padding: 20px;">'+ __translations.AttachementsNotAdded+ '<i class="ti-close cancel-lq" style="position: absolute; right: 20px; top: 20px; cursor: pointer;"></i>'+ '</div>
'+ window.AttachementsNotAdded.join('')+ '

</div>
'+ '

</div>
'; $('body').append(popup); $('body').addClass('modal-opened-ui'); window.AttachementsNotAdded = undefined; window.AttachementsErrors = undefined; } } $.ajax({ data: fd, processData: false, contentType: false, type: 'POST', success: function (data) { $('.add-attachement-in-complaint-lq').val(''); if (data.action.Result) { form.parents('.add-complaint-form-lq').find('.success-message-lq').removeClass('hidden-lq'); form.addClass('hidden-lq'); } else { app.serverMessage(data, form, e); } } }); } }; $('body').on('click', '.send-complaint-lq', function(e) { sendComplaint(e); }); $('body').on('click', '.clear-file-input-lq', function(e) { var index = $(e.currentTarget).parent().index(); $(e.currentTarget).prev().val(''); window.attachementsInComplaint.splice(index - 1, 1); $(e.currentTarget).addClass('hidden-lq'); });
  W plikach scss/globals/_globals2.scss i scss/globals/_globals-m.scss znajdź linijkę z frazą .select-background-ui, input[type="date"]{ i zamień ją na .select-background-ui, input[type="date"], input[type="file"]{.

Topaz

W Panelu Administratora przejdź do zakładki Tłumaczenia (Wygląd sklepu/ Ustawienia/ Tłumaczenia) i dodaj tam trzy frazy:
  • ReportDetails – Szczegóły zgłoszenia,
  • ReportSuccessInfo – Na adres e-mail zostało wysłane potwierdzenie. Oczekuj kolejnych wiadomości e-mail o zmianie statusu zgłoszenia.,
  • AttachementsNotAdded – Nie dodano wszystkich załączników.
W pliku partials/customer/order-content.html wyszukaj frazę popupDialog__complain. Kontener z wyszukaną klasą podmień (wraz z zawartością) poniższym kodem:
<div class="popupDialog popupDialog__complain popupDialog-js">
<div class="popupDialog__wrapper">

<span class="popupDialog__title">{{ translations.SendComplaintOrReturn }}</span>
<div class="complain__product">

<figure><img class="productImage-js" alt="" /></figure>

</div>

<div class="form complain__form inputs-container-js" data-success-info="{{ translations.ReportSuccessInfo }}">

<div class="form__checkBox checkBoxes__container checkBoxes__container--radio radioContainer-js">

<div class="radioComplaint-js"><input id="radio-complaint" class="checkbox-input" name="__action" type="radio" value="Order/ComplaintAdd" data-name="complaint" /> <label for="radio-complaint"> {{ translations.Prf_OrderComplaint }} </label></div>

<div class="radioReturn-js"><input id="radio-return" class="checkbox-input" name="__action" type="radio" value="Order/ReturnAdd" data-name="return" /> <label for="radio-return"> {{ translations.Prf_OrderReturn }} </label></div>


</div>
<strong>{{ translations.ReportDetails }}</strong>
<div class="form__input-wrapper complaintInputWrapper-js">

{% if config.Complaints.Defects &lt;&gt; null -%} {% assign defectsSize = config.Complaints.Defects | Size -%}<select class="form__input-value form__input-value-js" name="defectId" required="">{% if defectsSize &gt; 1 -%}
<option value="-1">{% if defectsSize &gt; 1 -%}* {% endif -%} {{translations.ChooseComplaintCause}}</option>{% endif -%} {% for def in config.Complaints.Defects -%}
<option value="{{def.Id}}">{{def.Name}}</option>{% endfor -%}
</select>

{% endif -%}
<p class="form__invalid-input form__validation-required-js" style="display: none;">{{ translations.Com_FieldIsMandatory }}</p>


</div>

<div class="form__input-wrapper complaintInputWrapper-js"><input id="defectDate" class="form__input-value form__icon_padding form__input-value-js" max="{{config.Now | Date: 'yyyy-MM-dd'}}" min="{{order.Date | Date: 'yyyy-MM-dd'}}" name="defectDate" type="date" value="{{config.Now | Date: 'yyyy-MM-dd'}}" placeholder="yyyy-MM-dd" /> <label class="form__input-info" for="defectDate">* {{translations.Prf_DefectDate}}</label></div>

<div class="form__input-wrapper complaintInputWrapper-js">

{% if config.Complaints.Requests &lt;&gt; null -%} {% assign requestsSize = config.Complaints.Requests | Size -%}<select class="form__input-value form__input-value-js" name="requestId" required="">{% if requestsSize &gt; 1 -%}
<option value="-1">{% if requestsSize &gt; 1 -%}* {% endif %} {{translations.Prf_ComplainRequest}}</option>{% endif %} {% for req in config.Complaints.Requests -%}
<option value="{{req.Id}}">{{req.Name}}</option>{% endfor -%}
</select>

{% endif -%}
<p class="form__invalid-input form__validation-required-js" style="display: none;">{{ translations.Com_FieldIsMandatory }}</p>


</div>
{% assign returnTypes = config.Complaints.Returns | Size -%} {% if returnTypes &gt; 1 -%}
<div class="form__input-wrapper returnInputWrapper-js">

<select class="form__input-value form__input-value-js" name="returnId" required="">
<option value="-1">{% if returnTypes &gt; 1 -%}* {% endif %} {{translations.ChooseReturn}}</option>{% for return in config.Complaints.Returns -%}
<option value="{{return.Id}}">{{return.Name}}</option>{% endfor -%}
</select>
<p class="form__invalid-input form__validation-required-js" style="display: none;">{{ translations.Com_FieldIsMandatory }}</p>


</div>
{% endif %}
<div class="form__input-wrapper min-js"><label class="quantityField__label">{{translations.Com_Quantity}}</label> <button class="button-minus-add-product" type="button" data-field="quantity"> </button> <input class="quantity-field quantity__field-js" max="" min="1" name="quantity" type="number" value="1" data-decimal="false" /> <button class="button-plus-add-product" type="button" data-field="quantity"> </button></div>

<div class="form__input-wrapper"><input id="accountNumber" class="form__input-value form__input-value-js" maxlength="50" name="accountNumber" type="text" /> <label class="form__input-info form__input-info-js" for="accountNumber">{{translations.Crt_BankAccountNumber}}</label></div>

<div class="form__input-wrapper">{{translations.Prf_AdditionalInfo}} <textarea class="form__input-value" name="message"></textarea></div>

<div class="form__input-wrapper"><span class="form__requiredFields--info">* {{ translations.Com_RequiredFields }}</span></div>
{% if config.Complaints.AttachmentsEnabled -%}
<div class="form__input-wrapper attachementsInputWrapper" data-not-added-info="{{translations.AttachementsNotAdded}}">

<div class="form__attachementsLabel">{{ translations.AddAttachment }}</div>
{% capture maxSize -%}{{config.Complaints.AttachmentMaxSize}}B{% endcapture -%} {% for i in (1..config.Complaints.AttachmentsMaxCount) -%}
<div class="form__attachement-input-container"><input class="addAttachementInComplaint-js" accept="{{ config.Complaints.AttachmentExtensions }}" name="file" type="file" data-file-size="{{ config.Complaints.AttachmentMaxSize }}" data-size-exceeded="{{ translations.Com_FileSizeExceeded | Format: maxSize }}" data-invalid-file="{{ translations.Com_InvalidFile | Format: config.Complaints.AttachmentExtensions }}" /></div>
{% endfor -%}

</div>
{% endif -%} <input class="productId-js" name="no" type="hidden" value="" /> <input name="orderId" type="hidden" value="{{order.Id}}" /> <button class="primary-action-button orderComplaintOrReturnAdd-js" type="button" data-id="{{order.Id}}">{{translations.Report}}</button>
</div>
</div>
</div>
  Teraz w pliku js/layout1.js (lub layout0.js jeśli nie ma layout1.js, lub layout.js jeśli nie ma dwóch poprzednich) znajdź zmienną var customerProfile, a w niej funkcję events. Tuż przed nią dodaj poniższy kod:
addAttachementInComplaint: function(e) {
window.attachementsInComplaint = [];
var filesArr = $(e.currentTarget).parents('.inputs-container-js').find('.addAttachementInComplaint-js');
filesArr.each(function () {
if(this.files.length &gt; 0){
var maxSize = $(this).data('file-size');
if(this.files[0].size &gt; maxSize){
var message = $('[data-not-added-info]').data('not-added-info') + '. </code></code><strong>' + this.files[0].name + '</strong><code class="lang:ruby decode:true ">: ' + $(this).data('size-exceeded');
app.showTemporaryPopup(message, 'error', '', 8000);
$(this).val('');
$(this).next().hide();
} else {
window.attachementsInComplaint.push(this.files[0]);
$(this).next().show();
}
}
});
},
sendComplaint: function(e) {
if(app.validationBeforePost(e) != 'error'){
var form = $(e.currentTarget).parents('.inputs-container-js');
var dataFromHTML = form.find('input:not([disabled]), select:not([disabled]), textarea:not([disabled])').serializeArray();
var fileList = window.attachementsInComplaint;
if(fileList != undefined){
var filesSize = fileList.length;
}
var fd = new FormData();
fd.append('__csrf', __CSRF);
for(var i=0; i&lt;dataFromHTML.length; i++){ fd.append(dataFromHTML[i].name,dataFromHTML[i].value); } if (filesSize &gt; 0) {
window.AttachementsErrors = 0;
function addAttachementError(file, message) {
if(window.AttachementsNotAdded == undefined){
window.AttachementsNotAdded = [];
}
var attachement = '</code>
<div style="padding: 20px;"><strong>'+file.name+'</strong>: '+message+'</div>
'; window.AttachementsNotAdded.push(attachement); window.AttachementsErrors += 1; }; var input = form.find('[name=file]').eq(0); var extensions = input.attr('accept').split(', '); var fileSize = input.data('file-size'); for (var i=0; i&lt;filesSize; i++) { var file = fileList.pop(); if(file.size &lt; fileSize){ var extensionArr = file.name.split('.'); var extension = '.' + extensionArr[extensionArr.length - 1].toLowerCase(); var wrongExtension = true; for(var j=0; j&lt;extensions.length; j++){ if(extension == extensions[j]){ fd.append('file'+i, file); var wrongExtension = false; } } if(wrongExtension){ var message = input.data('invalid-file'); addAttachementError(file, message); } } else { var message = input.data('size-exceeded'); addAttachementError(file, message); } } if(window.AttachementsNotAdded){ var message = $('[data-not-added-info]').data('not-added-info') + window.AttachementsNotAdded.join(''); app.showTemporaryPopup(message, 'error', '', 8000); window.AttachementsNotAdded = undefined; window.AttachementsErrors = undefined; } } $.ajax({ data: fd, processData: false, contentType: false, type: 'POST', success: function (data) { $('.addAttachementInComplaint-js').val(''); if (data.action.Result) { var message = form.data('success-info'); app.showTemporaryPopup(message, 'success', '', 8000); $('.clearFileInput-js').hide(); app.hidePopup(e); sessionStorage.removeItem('complains'); sessionStorage.removeItem('order' + (e.currentTarget).dataset.id); $('.order' + (e.currentTarget).dataset.id).remove(); customerProfile.showOrderDetails(e); } else { app.showTemporaryPopup(result.action.Description, 'error', '', 8000); } } }); } },
  Następnie w funkcji events dodaj poniższy kod:
mainSection.on('change', '.addAttachementInComplaint-js', function (e) {
customerProfile.addAttachementInComplaint(e);
});
mainSection.on('click', '.orderComplaintOrReturnAdd-js', function(e) {
customerProfile.sendComplaint(e);
});
mainSection.on('click', '.clearFileInput-js', function(e) {
var index = $(e.currentTarget).parent().index();
$(e.currentTarget).prev().val('');
window.attachementsInComplaint.splice(index - 1, 1);
$(e.currentTarget).hide();
});
Teraz wyszukaj funkcję orderComplaintOrReturnAdd i usuń ją. Następnie wyszukaj wywołanie tej funkcji (w funkcji events) i również usuń. Następnie w pliku css/layout.css dodaj poniższy kod:
.customer .popupDialog__complain .popupDialog__wrapper{max-width:700px;padding-bottom:0}.customer .complain__product{width:40%;padding:0;margin:40px 0;vertical-align:top;display:inline-block;border:none}.customer .complain__product figure{width:100%;height:auto;margin:0 0 20px}.customer .complain__form{width:calc(60% - 84px);display:inline-block;margin:40px}.customer .complain__form>strong{display:block;margin-bottom:20px}.customer .complain__form textarea{margin-top:10px;padding:10px}.customer .customer__orderContent .checkBoxes__container--radio{margin-top:0}.customer .form__attachement-input-container{position:relative}.customer .form__clear-attachement-input{position:absolute;top:11px;cursor:pointer}
Teraz w pliku css/layout-m.css dodaj poniższy kod:
.customer .complain__product{width:100%;margin:20px 0}.customer .complain__form{width:100%;margin:20px 0}
 

Obsługa dostawy o typie negocjowalnym w szablonach

Wskazówka
Od 1 czerwca 2025 roku zakończyliśmy aktualizację oraz wydawanie nowych wersji szablonów Bursztyn, Agat i Opal, a tym samym ich wsparcie, aby skupić się na nowoczesnych rozwiązaniach, które jeszcze lepiej odpowiadają na potrzeby dynamicznie rozwijającego się rynku e-commerce. Zachęcamy Was do przejścia na nasze nowe, udoskonalone szablony. Dla sklepów B2C polecamy szablony Topaz, One Page Shop oraz Dla Gastronomii, natomiast dla platform B2B idealnym wyborem będą Rubin i Szafir.

Obsługa dostawy o typie negocjowalnym w szablonach

W tym artykule dowiesz się jak zmodyfikować swój szablon, aby obsłużyć dostawy o typie negocjowalnym.

Szafir

Na samym początku pliku common/order-summary.html jest warunek z kilkoma assignami. W warunku dodaj taką linijkę {% assign notDeterminedDeliveryCost = order.Delivery.NotDeterminedDeliveryCost -%}, a w elsie dodaj taką {% assign notDeterminedDeliveryCost = order.SelectedDelivery.NotDeterminedDeliveryCost -%}. Kawałek niżej znajduje się warunek {% if order.NotDeterminedDeliveryCost and order.OrderStep != 'Start' -%}. Zamień go na poniższy kod: {% if order.NotDeterminedDeliveryCost or order.SelectedDelivery.NotDeterminedDeliveryCost -%} {% assign notDeterminedDeliveryCost = true -%} {% endif -%} {% if notDeterminedDeliveryCost and order.OrderStep == 'Start' -%} Następnie w tym samym pliku wyszukaj frazę {% unless order.NotDeterminedDeliveryCost -%} i zamień ją na warunek {% if order.NotDeterminedDeliveryCost == false and notDeterminedDeliveryCost == false -%}. Pamiętaj aby kilka linijek niżej zamienić {% endunless -%} na {% endif -%}. Kawałek dalej znajduje się warunek {% if paymentCost != null -%}. Zamień go na {% if paymentCost != null and order.NotDeterminedDeliveryCost == false and notDeterminedDeliveryCost == false -%} Teraz w pliku order/delivery-partials/delivery-section.html, wyszukaj warunek {% if order.NotDeterminedDeliveryCost == false -%} i zamień go na {% if order.NotDeterminedDeliveryCost == false and deliveryMethod.NotDeterminedDeliveryCost == false -%}. Następnie wyszukaj warunek {% if order.NotDeterminedDeliveryCost == false -%} i zamień go na {% if order.NotDeterminedDeliveryCost == false and order.SelectedDelivery.NotDeterminedDeliveryCost == false -%} W pliku order/cart.html znajdź warunek {% if order.NotDeterminedDeliveryCost and order.OrderStep == 'Start' -%}. Zamień go na poniższy kod: {% if order.NotDeterminedDeliveryCost or order.SelectedDelivery.NotDeterminedDeliveryCost -%} {% assign notDeterminedDeliveryCost = true -%} {% endif -%} {% if notDeterminedDeliveryCost and order.OrderStep == 'Start' -%}

Agat

W pliku partials/cart/cart-content.html znajdź warunek {% if cart.NotDeterminedDeliveryCost == true %} i zamień go na {% if cart.NotDeterminedDeliveryCost or cart.SelectedDelivery.NotDeterminedDeliveryCost %}. Kawałek dalej w tym samym pliku znajdź warunek {% if cart.NotDeterminedDeliveryCost == false %}. Zamień go na poniższy kod: {% if deliveryMethod.Name == cart.SelectedDelivery.Name %} {% assign notDeterminedDeliveryCost = cart.SelectedDelivery.NotDeterminedDeliveryCost -%} {% else %} {% assign notDeterminedDeliveryCost = deliveryMethod.NotDeterminedDeliveryCost -%} {% endif %} {% if cart.NotDeterminedDeliveryCost == false and notDeterminedDeliveryCost == false %} Kawałek niżej znajdź warunek {% if cart.NotDeterminedDeliveryCost == false %} i zamień go na {% if cart.NotDeterminedDeliveryCost == false and deliveryMethodLvl.NotDeterminedDeliveryCost == false %}. Dalej w tym samym pliku (partials/cart/cart-content.html), oraz w plikach partials/cart/delivery-and-payment.html i partials/cart/summary.html znajdź frazę {% unless cart.NotDeterminedDeliveryCost -%} i zamień ją na {% if cart.NotDeterminedDeliveryCost == false and cart.SelectedDelivery.NotDeterminedDeliveryCost == false -%}. Pamiętaj aby kilka linijek niżej zamienić {% endunless -%} na {% endif -%}. We wszystkich przypadkach przesuń ten {% endif -%} tak aby warunek obejmował też dane związane z kosztami płatności. Podobną zmianę należy wprowadzić w pliku partials/customer/order-details.html. Frazę {% unless customer-profile.Order.NotDeterminedDeliveryCost -%} należy zamienić na {% if customer-profile.Order.NotDeterminedDeliveryCost == false and customer-profile.Order.Delivery.NotDeterminedDeliveryCost == false -%} W plikach partials/cart/delivery-and-payment.html i partials/cart/summary.html znajdź warunek {% if cart.NotDeterminedDeliveryCost -%} i zamień go na {% if cart.NotDeterminedDeliveryCost or cart.SelectedDelivery.NotDeterminedDeliveryCost %}

Bursztyn lub Opal

Na początku pliku order/cart.html znajdź warunek {% if cart.NotDeterminedDeliveryCost %} i zamień go na {% if cart.NotDeterminedDeliveryCost or cart.SelectedDelivery.NotDeterminedDeliveryCost %}. Następnie trochę dalej znajdź warunek {% if cart.NotDeterminedDeliveryCost == false %} i zamień go na {% if cart.NotDeterminedDeliveryCost == false and delivery.NotDeterminedDeliveryCost == false %}. Kawałek dalej znajdziesz warunek {% if delivery.FreeDeliveryFrom > cart.ProductsTotalValue and cart.NotDeterminedDeliveryCost == false and delivery.TotalValue > 0 %}. Zamień go na {% if delivery.FreeDeliveryFrom > cart.ProductsTotalValue and cart.NotDeterminedDeliveryCost == false and delivery.NotDeterminedDeliveryCost == false and delivery.TotalValue > 0 %} Następnie w pliku order/stepsummary.html znajdź frazę {{translations.Crt_DeliveryCost}} i całą linijkę z tą frazą nadpisz poniższym kodem: {% if deliveryData.NotDeterminedDeliveryCost == false %}{{translations.Crt_DeliveryCost}}{{deliveryData.TotalValue|ToPrice}} {{cartCurrency}}{% endif %}

Topaz

W pliku staticElements/cart/cart.html znajdź warunek (występuje dwa razy) {% if cart.NotDeterminedDeliveryCost == true %} i zamień go na {% if cart.NotDeterminedDeliveryCost or cart.SelectedDelivery.NotDeterminedDeliveryCost %} (w obu przypadkach). Następnie wyszukaj frazę {{deliveryMethod.DateText}}. Pod linijką z tą frazą znajduje się warunek. Zastąp go wraz z jego zawartością poniższym kodem: {% if deliveryMethod.NotDeterminedDeliveryCost == false and cart.NotDeterminedProductsDeliveryCost == false -%} {{ deliveryMethod.TotalValue | ToPrice }} {{ currency }} {% endif -%} Teraz wyszukaj frazę {{ payment.DueDateText }}. Pod linijką z tą frazą znajduje się warunek. Zastąp go wraz z jego zawartością poniższym kodem: {% if deliveryMethodLvl.NotDeterminedDeliveryCost == false and cart.NotDeterminedProductsDeliveryCost == false -%} {{ payment.TotalValue | ToPrice }} {{ currency }} {% endif -%}  

Obsługa lookbooka w szablonie Topaz

Obsługa lookbooka w szablonie Topaz

W tym artykule dowiesz się jak zmodyfikować swój szablon, aby obsługiwał lookbooka.

Topaz

W Panelu Administratora przejdź do zakładki Tłumaczenia (Wygląd sklepu -> Ustawienia -> Tłumaczenia) i dodaj tam dwie frazy. ShowProducts - Pokaż towary, Lookbook - Lookbook. Następnie przejdź do zakładki Bannery (Wygląd sklepu -> Ustawienia -> Bannery) i dodaj tam banner o nazwie lookbook. Po jego dodaniu wejdź w jego ustawienia i zaznacz Lookbook zamiast Standardowy. Resztę uzupełnij wedle własnego uznania. W polu Nazwa (Lookbook) będzie nazwa lookbooka, która będzie się wyświetlać w sklepie. Teraz przejdź do zakładki Strony (Wygląd sklepu -> Ustawienia -> Trzykropek w prawym górnym rogu -> Edytuj ustawienia zaawansowane -> Strony) i przy stronie Lookbook wpisz lookbook.html, a następnie wejdź w ustawienia danej strony i zaznacz check-box header [Nagłówek]. Zapisz to wszystko kliknięciem w dyskietkę w prawym górnym rogu. W pliku elements/header/header-1.html (lub header-2.html - w zależności od wybranej w szablonie wersji nagłówka) wyszukaj frazę config.DefinedPages.Contact. Pod linijką z tą frazą wklej poniższy kod: {% for banner in config.Banners -%} {% assign imagesQuantity = banner.Images | Size -%} {% if banner.Name == 'lookbook' and imagesQuantity == 0 -%} {% assign fakeLookbook = true -%} {% endif -%} {% endfor -%} {% assign lookbooksQuantity = config.Lookbook.List | Size -%} {% if lookbooksQuantity > 1 -%} {{ translations.Lookbook }}
{% elseif lookbooksQuantity == 1 or fakeLookbook -%} {{ config.DefinedPages.Lookbook | A }} {% endif -%}
Następnie w folderze staticElements dodaj folder lookbook, a w nim plik lookbook.html, który będzie zawierał poniższy kod: {% include 'partials/common/breadcrumbs.html' -%} {% assign lookbooksQuantity = config.Lookbook.List | Size -%} {% if lookbooksQuantity > 1 -%} {% assign lookbook = config.Lookbook.Current -%} {% elseif lookbooksQuantity == 1 -%} {% assign lookbook = config.Lookbook.List[0] -%} {% endif -%}
{% if lookbooksQuantity > 0 -%}

{{ lookbook.Text }}

{% for image in lookbook.Images -%}
{% if image.JSON != "" -%}
{{translations.ShowProducts | Upcase}}
{% endif -%}
{% endfor -%}
{% else -%}

{{ translations.Lookbook }}

{{'css/img/img-placeholder.jpg' | Img: '', translations.DefaultImage}}
560x630
{{'css/img/img-placeholder.jpg' | Img: '', translations.DefaultImage}}
700x850
{{'css/img/img-placeholder.jpg' | Img: '', translations.DefaultImage}}
1260x540
{{'css/img/img-placeholder.jpg' | Img: '', translations.DefaultImage}}
630x950
{{'css/img/img-placeholder.jpg' | Img: '', translations.DefaultImage}}
560x630
{% endif -%}
Teraz w pliku css/layout.css dodaj poniższy kod: .lookbookSection{width:85%;max-width:1400px;margin:50px auto 150px}.lookbook__name{margin-bottom:100px}.lookbook__imageContainer{position:relative;vertical-align:top}.lookbook__imageContainer:nth-child(5n+1){display:inline-block;width:40%;margin-right:calc(10% - 5px)}.lookbook__imageContainer:nth-child(5n+2){display:inline-block;width:50%}.lookbook__imageContainer:nth-child(5n+3){width:90%}.lookbook__imageContainer:nth-child(5n+4){display:inline-block;width:45%;margin-right:calc(15% - 5px)}.lookbook__imageContainer:nth-child(5n){display:inline-block;width:40%}.lookbook__imageContainer img{max-width:100%;max-height:100%;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}.lookbook__imagePlaceholder{background-color:#f2f2f2}.lookbook__imagePlaceholderSize{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%)}.lookbook__productsToggleButton{position:absolute;right:0;bottom:0;padding:20px;background:#fff;width:250px;text-align:center;cursor:pointer}.lookbook__productsContainer{position:absolute;right:0;bottom:59px;padding:0 20px;background:#fff;width:250px}.lookbook__product{position:relative;padding:20px 0;border-bottom:1px solid #f2f2f2}.lookbook__productName{color:{{settings.inputLabelColor}};overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin-bottom:10px}.lookbook__productPrice{font-size:18px;font-weight:700;display:inline-block;width:calc(100% - 50px)}.lookbook__checkProductLink{position:absolute;right:0;bottom:20px;color:{{settings.linkFontColor}}} Następnie w pliku css/layout-m.css dodaj poniższy kod: .lookbookSection{margin:20px auto}.lookbookSection .lookbook__imageContainer{width:100%;margin:0 0 100px}.lookbookSection .lookbook__imageContainer:last-child{margin-bottom:20px}.lookbook__name{margin-bottom:20px}.lookbook__showProductsButton{bottom:0;right:auto;left:50%;transform:translate(-50%, 50%);border:1px solid #f2f2f2}.lookbook__hideProductsButton,.lookbook__productsContainer{position:fixed;width:calc(100% - 40px);z-index:102}.lookbook__productsContainerBackgroundMobile{position:fixed;top:0;right:0;bottom:0;left:0;background:rgba(255,255,255,0.8);z-index:101} Teraz w pliku js/layout1.js (lub layout0.js jeśli nie ma layout1.js, lub layout.js jeśli nie ma dwóch poprzednich) dodaj poniższy kod: if($('.lookbookImageContainer-js').length > 0){ var setLookbookImages = function () { $('.lookbookImageContainer-js').each(function(index){ if(index == 0){ var heightRatio = 1.13; } else if(index == 1){ var heightRatio = 1.22; var marginTopRatio = 0.39; } else if(index == 2){ var heightRatio = 0.43; var marginTopRatio = 0.25; var marginBottomRatio = 0.36; } else if(index == 3){ var heightRatio = 1.52; } else if(index == 4){ var heightRatio = 1.13; var marginTopRatio = 0.46; } var width = $(this).width(); var height = width * heightRatio; $(this).css('height', height); if($(window).width() > 768){ if(marginTopRatio){ var marginTop = height * marginTopRatio; $(this).css('margin-top', marginTop); } if(marginBottomRatio){ var marginBottom = height * marginBottomRatio; $(this).css('margin-bottom', marginBottom); } } else { if(marginTopRatio){ $(this).css('margin-top', 0); } if(marginBottomRatio){ $(this).css('margin-bottom', 100); } } }) }; setLookbookImages(); $(window).resize(function() { setLookbookImages(); }); var addLookbookProducts = function () { var url = $('[data-product-details-url]').data('product-details-url'); var checkTranslation = $('[data-check-translation]').data('check-translation'); $('[data-lookbook-products]').each(function(){ var productContainer = $(this); var products = JSON.stringify(productContainer.data('lookbook-products')); var objProducts = JSON.parse(products); $.each(objProducts, function (index, value) { var productUrl = url + ',,' + value.id; var data = {__collection: 'product-details.Product.Name|product-details.Product.Price'}; $.get(productUrl, data, function (res) { productContainer.append( '
'+ '
'+res.collection['product-details.Product.Name']+'
'+ '
'+res.collection['product-details.Product.Price']+' '+__curr+'
'+ ''+checkTranslation+'
'+ '
' ); }); }); }); }; addLookbookProducts(); $('body').on('click', '.showLookbookProducts-js', function (e) { $(e.currentTarget).siblings().show(); $(e.currentTarget).hide(); }); $('body').on('click', '.hideLookbookProducts-js', function (e) { $(e.currentTarget).siblings().hide(); $(e.currentTarget).next().show(); $(e.currentTarget).hide(); }); } Następnie w pliku js/layout0.js (lub layout.js jeśli nie ma layout0.js) znajdź zmienną var headerFunctions i podmień ją na jeden z poniższych kodów. Pierwszy jest do nagłówka z domyślnie ukrytą wyszukiwarką, a drugi do nagłówka z domyślnie odkrytą wyszukiwarką. header-1 var headerFunctions = { init: function () { headerFunctions.events(); }, events: function () { var pageHeader = $('.pageHeader-js'); pageHeader.on('click', '.showSearchSection-js', function() { $('.pageHeader-js .headerSearchForm-js').toggle(200); $('.pageHeader-js .sitesNavigation-js').hide(200); $('.pageHeader-js .mainNavigation-js').hide(200); $('.pageHeader-js .lookbooksNavigation-js').hide(200); $('.openCategoryMenu-js, .openPagesMenu-js, .openLookbooksMenu-js').removeClass('activeButton-js'); $('.searchFormPhrase-js').focus(); }); pageHeader.on('click', '.showMenuSection-js', function() { $('.pageHeader-js .headerMainMenu-js').toggle(200); }); pageHeader.on('click', '.mainMenuMobileCloseBtn-js', function() { $('.pageHeader-js .headerMainMenu-js').hide(200); }); pageHeader.on('click', '.openCategoryMenu-js', function() { $('.pageHeader-js .sitesNavigation-js').hide(200); $('.pageHeader-js .languageAndCurrencyMenu-js').hide(200); $('.pageHeader-js .headerSearchForm-js').hide(200); $('.pageHeader-js .lookbooksNavigation-js').hide(200); $('.pageHeader-js .mainNavigation-js').toggle(200); $('.openLanguageAndCurrencyMenu-js, .openPagesMenu-js, .openLookbooksMenu-js').removeClass('activeButton-js'); $(this).toggleClass('activeButton-js'); app.loadImages(); }); pageHeader.on('click', '.openLanguageAndCurrencyMenu-js', function() { $('.pageHeader-js .sitesNavigation-js').hide(200); $('.pageHeader-js .mainNavigation-js').hide(200); $('.pageHeader-js .headerSearchForm-js').hide(200); $('.pageHeader-js .lookbooksNavigation-js').hide(200); $('.pageHeader-js .languageAndCurrencyMenu-js').toggle(200); $('.openCategoryMenu-js, .openPagesMenu-js, .openLookbooksMenu-js').removeClass('activeButton-js'); $(this).toggleClass('activeButton-js'); }); pageHeader.on('click', '.openPagesMenu-js', function() { $('.pageHeader-js .mainNavigation-js').hide(200); $('.pageHeader-js .languageAndCurrencyMenu-js').hide(200); $('.pageHeader-js .headerSearchForm-js').hide(200); $('.pageHeader-js .lookbooksNavigation-js').hide(200); $('.pageHeader-js .sitesNavigation-js').toggle(200); $('.openCategoryMenu-js, .openLanguageAndCurrencyMenu-js, .openLookbooksMenu-js').removeClass('activeButton-js'); $(this).toggleClass('activeButton-js'); }); pageHeader.on('click', '.openLookbooksMenu-js', function() { $('.pageHeader-js .mainNavigation-js').hide(200); $('.pageHeader-js .languageAndCurrencyMenu-js').hide(200); $('.pageHeader-js .headerSearchForm-js').hide(200); $('.pageHeader-js .sitesNavigation-js').hide(200); $('.pageHeader-js .lookbooksNavigation-js').toggle(200); $('.openCategoryMenu-js, .openLanguageAndCurrencyMenu-js, .openPagesMenu-js').removeClass('activeButton-js'); $(this).toggleClass('activeButton-js'); }); pageHeader.on('change', '.customerLanguageChange-js', function(e) { if($(this).is(':checked')) {app.customerLanguageChange(e)} }); pageHeader.on('change', '.customerCurrencyChange-js', function(e) { if($(this).is(':checked')) {app.customerCurrencyChange(e)} }); pageHeader.on('click', '.openUndercategoryMenu-js', function(e) { var undercategory = $(e.currentTarget).closest('li').find('ul').first(); $(undercategory).toggle(200); }); } }; header-2 var headerFunctions = { init: function () { headerFunctions.events(); }, events: function () { var pageHeader = $('.pageHeader-js'); pageHeader.on('click', '.showSearchSection-js', function() { $('.pageHeader-js .headerSearchForm-js').toggle(200); $('.searchFormPhrase-js').focus(); }); pageHeader.on('click', '.showMenuSection-js', function() { $('.pageHeader-js .headerMainMenu-js').toggle(200); }); pageHeader.on('click', '.mainMenuMobileCloseBtn-js', function() { $('.pageHeader-js .headerMainMenu-js').hide(200); }); pageHeader.on('click', '.openCategoryMenu-js', function() { $('.pageHeader-js .sitesNavigation-js').hide(200); $('.pageHeader-js .languageAndCurrencyMenu-js').hide(200); $('.pageHeader-js .lookbooksNavigation-js').hide(200); $('.pageHeader-js .mainNavigation-js').toggle(200); $('.openLanguageAndCurrencyMenu-js, .openPagesMenu-js, .openLookbooksMenu-js').removeClass('activeButton-js'); $(this).toggleClass('activeButton-js'); app.loadImages(); }); pageHeader.on('click', '.openLanguageAndCurrencyMenu-js', function() { $('.pageHeader-js .sitesNavigation-js').hide(200); $('.pageHeader-js .mainNavigation-js').hide(200); $('.pageHeader-js .lookbooksNavigation-js').hide(200); $('.pageHeader-js .languageAndCurrencyMenu-js').toggle(200); $('.openCategoryMenu-js, .openPagesMenu-js, .openLookbooksMenu-js').removeClass('activeButton-js'); $(this).toggleClass('activeButton-js'); }); pageHeader.on('click', '.openPagesMenu-js', function() { $('.pageHeader-js .mainNavigation-js').hide(200); $('.pageHeader-js .languageAndCurrencyMenu-js').hide(200); $('.pageHeader-js .lookbooksNavigation-js').hide(200); $('.pageHeader-js .sitesNavigation-js').toggle(200); $('.openCategoryMenu-js, .openLanguageAndCurrencyMenu-js, .openLookbooksMenu-js').removeClass('activeButton-js'); $(this).toggleClass('activeButton-js'); }); pageHeader.on('click', '.openLookbooksMenu-js', function() { $('.pageHeader-js .mainNavigation-js').hide(200); $('.pageHeader-js .languageAndCurrencyMenu-js').hide(200); $('.pageHeader-js .sitesNavigation-js').hide(200); $('.pageHeader-js .lookbooksNavigation-js').toggle(200); $('.openCategoryMenu-js, .openLanguageAndCurrencyMenu-js, .openPagesMenu-js').removeClass('activeButton-js'); $(this).toggleClass('activeButton-js'); }); pageHeader.on('change', '.customerLanguageChange-js', function(e) { if($(this).is(':checked')) {app.customerLanguageChange(e)} }); pageHeader.on('change', '.customerCurrencyChange-js', function(e) { if($(this).is(':checked')) {app.customerCurrencyChange(e)} }); pageHeader.on('click', '.openUndercategoryMenu-js', function(e) { var undercategory = $(e.currentTarget).closest('li').find('ul').first(); $(undercategory).toggle(200); }); } };