Szablon Szafir (dedykowany dla B2B)

Rozbudowana stopka w szablonach Topaz i Szafir - jak ją skonfigurować?

Wstęp

Stopka to jeden z podstawowych elementów każdego sklepu internetowego. Oprócz wymaganych informacji, które muszą się w niej znaleźć, może zawierać zbiór innych odniesień do konkretnych sekcji w Twoim e-Sklepie, do Twojego bloga lub profili w social mediach. Odnośniki mogą pozytywnie wpływać na wizerunek Twojego sklepu, przyczyniając się do wsparcia sprzedaży i generując większy ruch na stronie. W Kreatorze wyglądu obok standardowych widoków znajduje się dodatkowy, który jest bardziej rozbudowany. W przypadku szablonu Topaz jest to trzeci widok (footer-3), zaś w przypadku Szafira - drugi widok (footer-2). Dzięki modyfikacji obszaru możesz jeszcze bardziej spersonalizować swój sklep poprzez uzupełnienie stopki w więcej przydatnych informacji.

Konfiguracja rozbudowanej stopki

Jak przystąpić do edycji stopki?

Po zaimportowaniu z Kreatora wyglądu stopka będzie wyświetlać się w ograniczonym zakresie. Niezmiennie jej edycji dokonasz z perspektywy zakładek: Wygląd Sklepu/Ustawienia a następnie sekcji Nagłówek/Stopka. To tutaj zdecydujesz o ilości grup stron, konkretnych elementach w ich obrębie a następnie o kolejności w jakiej będą prezentować się w e-Sklepie. Również z tego poziomu dodasz odwołania do Twoich profili na portalach społecznościowych oraz ustawisz favicon dla swojego sklepu internetowego.

Więcej możliwości edycji stopki

Rozbudowana stopka w szablonie Topaz oraz Szafir umożliwia dodanie większej ilości grup stron niż dotychczas. Dzięki temu z łatwością zamieścisz w niej wszystko czego potrzebujesz. Dodatkowo sekcję uzupełniono o grafiki partnerów logistycznych oraz obsługiwanych operatorów płatności.

Przykładowy wygląd rozbudowanej stopki w szablonie Topaz

Przykładowy wygląd rozbudowanej stopki w szablonie Szafir

Jak skonfigurować poszczególne elementy?

    • Elementy 1-5: dodawanie kolejnych grup stron oraz zmiana ich kolejności dokonywana jest poprzez sekcję Nagłówek/Stopka w Ustawieniach w obrębie zakładki Wygląd Sklepu. W obrębie danej grupy możesz dodawać konkretne strony poprzez kliknięcie przycisku Dodaj stronę ze sklepu.
Wskazówka
Zmiany kolejności poszczególnych grup odnośników lub zmiany danych odnośników w obrębie danej grupy dokonuje się metodą Przeciągnij i upuść.
    • Element 6: odpowiada za wyświetlanie następujących informacji: e-mail, numer telefonu, telefon2 (jeśli nie istnieje, to zastępowany jest przez telefon komórkowy) fax lub skype. W celu edycji danych kontaktowych należy przejść w panelu e-Sklepu do zakładki Ustawienia/Ustawienia sklepu. Informacje o adresie e-mail pobierane są automatycznie z zakładki Ogólne z sekcji Konto e-mail sklepu. Aby dodać lub zmodyfikować numer telefonu konieczne jest wybranie zakładki Dane sprzedawcy, następnie Infolinia i zweryfikowanie pola Telefon.
      Wskazówka
      W elemencie wyświetlone zostaną maksymalnie 3 informacje (e-mail, telefon i jedno z wybranych pól np. telefon2, telefon komórkowy lub fax.
    • Element 7: odwołania do portali społecznościowych dodajemy bez zmian. Więcej o tym znajdziesz tutaj.
    • Elementy 8-9: aby dodać informację o operatorach płatności oraz partnerach logistycznych należy wybrać zakładkę Wygląd Sklepu/Ustawienia/Bannery. Edycji dokonasz z perspektywy kafelków: Logistics Partners oraz Payment Operators poprzez dodanie właściwych grafik operatorów i partnerów. Aby zmienić nazwę „Operator płatności” i/lub „Partnerzy logistyczni” wybierz zamiast zakładki Bannery, zakładkę Tłumaczenia. Wyszukaj po parametrze „ID” LogisticsPartnersBannerTitle oraz PaymentOperatorsBannerTitle i zmodyfikuj pole „Tekst” według własnych preferencji. Zmiany zapisz i opublikuj. W przypadku chęci usunięcia któregoś z elementów, zaznacz go i naciśnij przycisk „Usuń”.
Wskazówka
Zalecane parametry grafik dla operatorów płatności i partnerów logistycznych: szerokość maksymalna 64 px, rozszerzenie PNG.
    • Element 10: dane dotyczące firmy pobierane są z zakładki Ustawienia/Ustawienia sklepu/Dane sprzedawcy/Dane identyfikacyjne, a także z obszaru: Ustawienia/ Ustawienia sklepu/ Informacja o działalności gospodarczej/ Numer KRS lub CEIDG, Organ rejestrujący oraz Ustawienia/ Ustawienia sklepu/ Adres pocztowy, gdzie uwzględnione zostaną wszystkie pola za wyjątkiem Szerokości i długości geograficznej.

Jak zaprezentować rabaty progowe w szablonie B2B (Szafir)?

Wstęp

W Comarch e-Sklepie połączonym z Comarch ERP XL możesz przygotować dla swoich Klientów rabaty, które będą obowiązywać w e-Sklepie dla określonych progów ilościowych. Opcja dotyczy rabatów procentowych i typu stała cena, które naliczają się w e-Sklepie.

Jak skonfigurować ceny per próg ilościowy w Comarch ERP XL?

Aby w swoim e-Sklepie skorzystać z możliwości prezentacji cen dla określonych progów ilościowych ustalonych w systemie Comarch ERP XL należy:
  • mieć Comarch ERP XL w wersji min. 2023.1,
  • mieć Comarch e-Sklep w wersji min. 2023.5,
  • mieć szablon Szafir w wersji 2023.5,
  • aktywować prezentację rabatów progowych w e-Sklepie przy pomocy Asysty technicznej Comarch e-Sklep.
Wskazówka
Jeżeli chcesz prezentować rabaty progowe w swoim szablonie skontaktuj się z Asystą techniczną Comarch e-Sklep poprzez formularz celem aktywowania nowej funkcji.
W celu utworzenia promocji należy w Comarch ERP XL przejść do zakładki Zestawienia/ Promocje i rabaty i dodać rabat pozycji. Następnie można określić szczegóły rabatu na poszczególnych zakładkach:

Nagłówek:

  • Wskazać nazwę rabatu.
  • Określić priorytet.
  • Pomiń następne promocje.
  • Określić sposób łączenie z poprzednią.
  • Rodzaj dokumentu (musi być wskazany rozchodowy lub wszystkie).
  • Wskazać okres działania.
Wskazówka
Priorytet ma znaczenie przy opcji "Pomiń następne promocje". Zaznaczenie tego parametru spowoduje, że żadna promocja o niższym priorytecie (wyższej liczbie) nie powinna się już naliczyć. Jeżeli priorytet będzie wyższy (niższa liczba) to rabat się naliczy.

Kontrahenci:

W Comarch e-Sklep na promocji można wskazać:
  • Pojedynczego kontrahenta – wtedy promocja dotyczy jednego kontrahenta.
  • Grupę kontrahentów (grupa musi być wysyłana do e-Sklepu) – wtedy promocja dotyczy kontrahentów przypisanych do tej grupy.
  • Grupę główną kontrahentów – wtedy promocja dotyczy wszystkich kontrahentów w e-Sklepie: zalogowanych (którzy posiadają konta w e-Sklepie), niezalogowanych i jednorazowych (niezarejestrowanych). Przykład promocji został opisany w sekcji Przykłady użycia rabatów w e-Sklepie

Towary:

  • Wybrać towar albo grupę towarów.
  • Wskazać rodzaj rabatu (rabat od ceny sprzedaży lub stała cena).
  • Wybrać typ rabatu (procentowy lub wartościowy).
  • Wskazać progi ilościowe.
Aby tak skonfigurowane rabaty mogły być widoczne w e-Sklepie należy:
  • przesłać ustalone promocje do e-Sklepu za pomocą ręcznej synchronizacji różnicowej lub automatu synchronizacji,
  • w panelu administracyjnym e-Sklepu włączyć naliczanie rabatów przesyłanych z systemu ERP w menu Ustawienia/ Ustawienia sklepu/ Ceny i rabaty/ sekcja Rabaty  parametr Sposób liczenia rabatów z Comarch e-Sklep i systemu ERP.
  • w panelu administracyjnym e-Sklepu włączyć prezentację ceny dla ilościowych progów rabatowych w menu Wygląd sklepu/ Ustawienia/ Ustawienia szablonu/ sekcja Ogólne/ parametr Cena per próg ilościowy.
Wskazówka
Prezentacja ceny dla ilościowych progów rabatowy pochodzących z Comarch ERP XL jest możliwa tylko przy drugim widoku listy towarów w szablonie Szafir

Gdzie widać ceny dla progów ilościowych?

Prawidłowo udostępnione z systemu Comarch ERP XL rabaty z progiem ilościowym są widoczne w e-Sklepie w kilku miejscach:
  • w sekcji Polecane na stronie głównej e-Sklepu pod warunkiem, że:
    • na towarach w Comarch ERP XL ustawisz odpowiednią etykietę, która będzie tożsama z typem wybranej sekcji w szablonie Szafir. Na towarze zostanie wyświetlony zakres cen, od najniższej do najwyższej. Po kliknięciu w „Zobacz szczegóły cen” zostanie wyświetlony pop-up ze szczegółami pozostałych cen dla progów ilościowych.
  • na szczegółach towaru zostaną wyświetlone trzy pierwsze ceny dla określonych progów. Po kliknięciu w „Zobacz szczegóły cen” zostanie wyświetlony pop-up ze szczegółami pozostałych cen dla progów ilościowych.
  • na liście zakupów w Twoim e-Sklepie. Po kliknięciu w „Zobacz szczegóły cen” zostanie wyświetlony pop-up ze szczegółami pozostałych cen dla progów ilościowych.
  • w panelu administracyjnym w menu Marketing/ Rabaty (widok dla administratora sklepu):
    • na zakładce Z Comarch ERP XL dostępne są rabaty progowe
  • w profilu zalogowanego Klienta w Twoim e-Sklepie, pod warunkiem, że:
    • włączyłeś naliczanie rabatów przesyłanych z systemu ERP (parametr w panelu w menu Ustawienia/ Ustawienia sklepu/ Ceny i rabaty/ sekcja Rabaty > parametr Sposób liczenia rabatów z Comarch e-Sklep i z systemu ERP),
    • włączyłeś wyświetlanie informacji o rabatach w profilu klienta (parametr w panelu w menu Ustawienia/ Ustawienia sklepu/ Ceny i rabaty/ sekcja Rabaty),
    • włączyłeś prezentację rabatów w profilu klienta na zakładce Promocje jest ustawiona na Tak (parametr w panelu w menu Wygląd sklepu/  Ustawienia/  Ustawienia szablonu/ Ogólne > sekcja Prezentacja rabatów).
  • na składanym zamówieniu w Twoim e-Sklepie, pod warunkiem, że:
    • włączyłeś prezentację rabatów na pozycjach zamówienia i w podsumowaniu (parametr w panelu w menu Wygląd sklepu/ Ustawienia/ Ustawienia szablonu/ Ogólne/ sekcja Prezentacja rabatów).

Więcej informacji

Więcej informacji można znaleźć w artykułach:

Jak zarządzać strefą klienta w Comarch e-Sklep?

Wstęp

Od wersji 2023.5 w ustawieniach szablonów Szafir, Topaz i One Page Shop został dodany nowy obszar, który dotyczy edycji Strefy klienta. W tym artykule dowiesz się jak skonfigurować widok strefy klienta dla swoich klientów.

Konfiguracja

Ustawienia znajdują się w panelu administracyjnym: Wygląd sklepu/ Ustawienia/ Ustawienia szablonu/ Zarządzanie strefą klienta. Na stronie widoczna jest lista elementów widocznych w e-Sklepie. Lista składa się z następujących elementów:
  • Dostępne – ustawienie wyświetlania danego elementu na stronie. Jeżeli parametr jest zaznaczony (podświetla się na szaro) wówczas element będzie widoczny na stronie.
  • Kolejność – w tym polu za pomocą strzałek możemy zmienić kolejność przycisków.
  • Ikona – w tym miejscu należy wprowadzić nazwę ikony, która powinna się wyświetlać na dodanym elemencie. Więcej informacji o ikonach znajdziesz w dalszej części artykułu.
  • Etykieta PL – nazwa elementu, która będzie się wyświetlała w polskiej wersji strony.
  • Id tłumaczenia – id z którego będą pobierane tłumaczenia elementu wyświetlane w obcojęzycznej wersji strony. Tłumaczenia dodawane są w panelu administracyjnym: Wygląd sklepu/ Ustawienia/ Tłumaczenia.
  • Wyróżnij – jeżeli parametr jest zaznaczony, to powoduje zmianę koloru przycisku na inny
[/su_list

Dodawanie ikony

Ikony które są dostępne w szablonie Szafir pochodzą ze strony Themify. Na stronie dostępna jest lista możliwych do wykorzystania ikon. Jeżeli chcesz w swoim elemencie w e-Sklepie dodać ikonę wystarczy, że w polu Ikona wpiszesz nazwę ikony, na przykład: ti-link. Jeżeli jednak chcesz skorzystać z własnych ikon wówczas należy je we własnym zakresie zaimplementować w kodzie szablonu w pliku _layout.html.

Dodawanie tłumaczeń

Nazwę etykiety można wprowadzić na dwa sposoby:
  • w polu Etykieta PL - jeśli w e-Sklepie jest włączony tylko język polski,
  • w polu Id tłumaczenia - dodając id do wybranego tłumaczenia jeśli w e-Sklepie są włączone języki obce.
Jeżeli jest uzupełnione tylko pole Etykieta PL wówczas etykieta będzie miała taką nazwę jaka została wprowadzona w tym polu. Jeżeli jest uzupełnione tylko pole Id tłumaczenia wówczas etykieta będzie miała taką nazwę jaka została przypisana do danego id w sekcji Wygląd sklepu/ Ustawienia/ Tłumaczenia. Jeżeli zostały uzupełnione jednocześnie pola Etykieta PL oraz Id tłumaczenia wówczas etykieta będzie miała taką nazwę jaka została przypisana do danego id w sekcji Wygląd sklepu/ Ustawienia/ Tłumaczenia.
Wskazówka
Pamiętaj, aby elementy były widoczne w e-Sklepie wprowadzone zmiany należy zapisać i opublikować.

Wyróżnienie elementu w Strefie klienta

W szablonie Szafir możliwe jest ustawienie wyróżnienia czcionki wyróżnionego elementu w Strefie klienta. Aby to zrobić zaloguj się do panelu administracyjnego, a następnie do obszaru Wygląd sklepu/Ustawienia. W zakładce Ogólne odnajdź sekcję „Kolor czcionki wyróżnionego elementu w menu profilu klienta”. Wybierz kolory, którymi chcesz wyróżnić element w profilu klienta. Następnie zapisz zmiany. Teraz przejdź do zakładki Strefa klienta. Zaznacz te elementy, które mają zostać wyróżnione. Wystarczy, że wybierzesz odpowiedni checkbox pod Wyróżnij. W efekcie klienci odwiedzający stronę Twojego sklepu po zalogowaniu zobaczą wyróżnione elementy.

Jak prezentować wiele kodów EAN na szczegółach towaru?

Wstęp

W Comarch e-Sklep połączonym z Comarch ERP XL możesz zaprezentować wiele kodów EAN (European Article Number) na szczegółach towaru. Znajdą one zastosowanie w wyszukiwarce, a także w przygotowaniu pliku csv za pomocą którego Twoi klienci dodadzą towary do koszyka. Wiele kodów EAN możesz dodać zarówno na podstawowej jak i dodatkowej jednostce miary towaru. W tym artykule dowiesz się jak dodać dodatkowe kody na towarze, jakie korzyści przynosi ich użycie oraz jak ich używać.

Aby w swoim e-Sklepie skorzystać z możliwości prezentacji wielu kodów EAN na towarze należy:
  • mieć Comarch ERP XL w wersji min. 2024.1,
  • mieć Comarch e-Sklep w wersji min. 2024.3,
  • mieć szablon Szafir, Topaz lub One Page Shop w wersji 2024.3,
  • aktywować prezentację dodatkowych kodów EAN w panelu administracyjnym e-Sklepu.

Jakie są korzyści z używania dodatkowych kodów EAN?

  • Łatwiejsze wyszukiwanie produktów: Dodatkowe kody EAN umożliwiają szybkie i precyzyjne wyszukiwanie towarów w Comarch e-Sklep. Wpisując jeden lub wiele kodów EAN (oddzielone przecinkami) w wyszukiwarkę Twoi klienci natychmiast otrzymają wyniki zawierające dokładnie ten produkt, którego szukają. Identyfikacja produktu odbywa się także zgodnie z przypisanym kodem EAN dla podstawowej (np. szt.) i dodatkowej (np. kartonu) jednostki miary.
  • Szybszy import produktów do koszyka: Funkcjonalność importu produktów za pomocą pliku CSV z wykorzystaniem różnych kodów EAN pozwoli na dywersyfikację towarów w koszyku w zależności od przypisanej jednostki miary do właściwego kodu EAN..

Jakie są sposoby prezentacji kodów EAN na towarach w e-Sklepie?

W Comarch e-Sklep istnieją 4 możliwości prezentacji kodów EAN na szczegółach towaru. Sposób 1. Standardowo prezentowany jest domyślny kod EAN, który został przyporządkowany dla głównej jednostki miary (np. szt) w systemie Comarch ERP XL na zakładce Ogólne. Sposób 2. W e-Sklepie możesz także prezentować kod EAN wskazywany na dodatkowej jednostce miary. Aby go dodać przejdź na zakładkę Jednostki, kody, VAT i wprowadź dedykowany kod EAN na jednostce, w której chcesz także sprzedawać w e-Sklepie. Tak wprowadzony kod EAN pojawi się na szczegółach towaru po wybraniu dedykowanej jednostki przez klienta Twojego e-Sklepu. Sposób 3. Wiele kodów EAN na podstawowej jednostce miary np. szt. Aby je dodać przejdź do zakładki Jednostki, kody, VAT > Kody kreskowe w systemie Comarch ERP XL. Krok 1. Za pomocą zielonego plusa dodaj nowy kod, Krok 2. Wybierz jego typ, Krok 3. Wprowadź kod, Krok 4. Wybierz jednostkę np. szt. Tak wprowadzony kod EAN pojawi się na szczegółach towaru po wybraniu dedykowanej jednostki przez klienta Twojego e-Sklepu. Sposób 4. Wiele kodów EAN na podstawowej i dodatkowej jednostce miary. Aby je dodać przejdź do zakładki Jednostki, kody, VAT > Kody kreskowe w systemie Comarch ERP XL. Krok 1. Za pomocą zielonego plusa dodaj nowy kod, Krok 2. Wybierz jego typ, Krok 3. Wprowadź kod, Krok 4. Wybierz jednostkę np. karton. Tak wprowadzony kod EAN pojawi się na szczegółach towaru po wybraniu dedykowanej jednostki przez klienta Twojego e-Sklepu. Aby tak skonfigurowane kody EAN mogły być widoczne w e-Sklepie należy w panelu administracyjnym e-Sklepu włączyć prezentację dodatkowych kodów EAN na szczegółach towaru w menu Wygląd sklepu > Ustawienia > Ustawienia szablonu > sekcja Ogólne.
Wskazówka
W sekcji Cechy towaru zostanie jako pierwszy zaprezentowany kod EAN dla domyślnej jednostki towaru. Pod warunkiem, że został dodana w systemie Comarch ERP.

Jak korzystać z dodatkowych kodów EAN w wyszukiwarce?

Aby skorzystać z dodatkowych kodów EAN w wyszukiwarce, wystarczy: Krok 1. W panelu administracyjnym e-Sklepu włączyć wyszukiwanie towarów po polu: Kod EAN w menu Ustawienia > Ustawienia sklepu > Wyszukiwanie. Krok 2. Wejść na stronę główną e-Sklepu. Krok 3. W polu wyszukiwania wpisać kod lub wiele kodów EAN oddzielonych przecinkami. Efekt:  Wyniki wyszukiwania będą zawierały produkty odpowiadające wprowadzonym kodom EAN.

Jak importować towary do koszyka za pomocą pliku CSV?(tylko dla wersji B2B)

Krok 1. Przygotowanie pliku CSV: Upewnij się, że plik CSV zawiera kolumnę z kodami EAN i przyporządkowanymi do nich jednostkami miary produktów. Krok 2. Import pliku CSV: Na stronie koszyka znajdziesz opcję importu pliku CSV. Kliknij przycisk "Importuj z pliku CSV", a następnie wybierz przygotowany plik ze swojego komputera. Efekt: Po zaimportowaniu pliku, system automatycznie doda do koszyka produkty odpowiadające kodom EAN i jednostkom miary z pliku CSV w podanej ilości.

Więcej informacji

Więcej informacji można znaleźć w artykułach: Jak skonfigurować jednostki pomocnicze oraz w Przeliczanie jednostki miary według wagi produktu.

Jak zaprezentować kupony rabatowe w szablonach?

Wstęp

Kupon rabatowy jest wygenerowanym kodem, który umożliwia klientom otrzymanie obniżki procentowej lub wartościowej, a także opcji darmowej dostawy. Jeżeli klient zastosuje kupon rabatowy w koszyku, odpowiednia promocja zostanie naliczona w ramach zamówienia. Kupon rabatowy może być szansą na zwiększenie sprzedaży –  klienci widząc możliwość obniżki chętniej skorzystają z zakupu towarów. W tym momencie możliwe jest zaprezentowanie kuponów w ramach szablonów Topaz, Rubin oraz Szafir.
Wskazówka
Sposób konfiguracji kuponów rabatowych został opisany w artykule Kupony rabatowe.

Jak pokazać klientom dostępne kupony rabatowe?

W sklepie istnieje możliwość pokazania kuponów rabatowych ustawionych dla wszystkich bądź zalogowanych kontrahentów. Prezentacja kuponów odbywa się w ramach wskazanej dla kuponu grupy towarowej.
Wskazówka
W przypadku, gdy kupon rabatowy jest ustawiony na wszystkie kategorie, jest on wyłączony z prezentacji. Dzięki temu możliwe jest ustawienie takiego kuponu bez automatycznego prezentowania promocji na wszystkich towarach w sklepie. Z prezentacji są także wyłączone kupony rabatowe dla newslettera i wybranych subskrybentów, dzięki czemu są one unikalne w ramach mailingu.
Wskazane kody rabatowe można pokazać klientom zarówno na dedykowanej stronie, jak i w ramach towarów, na które obowiązują. Aby włączyć prezentację kuponów rabatowych, wykonaj następujące kroki: Krok 1. Przejdź do obszaru Ustawienia > Ustawienia sklepu > Towary > Etykiety towaru. Z tego poziomu włącz flagę Promocja dla towarów, dla których dodano kupony rabatowe: Na wskazanych towarach pojawi się flaga „Promocja”. Umożliwi to zaprezentowanie kuponów rabatowych w szablonie.
Wskazówka
Tak ustawiona flaga promocja umożliwia prezentację kuponów rabatowych - nie jest uwzględniana w przypadku filtrów na liście towarów. W tym celu należy użyć oznaczenia flagi promocja pochodzącej z systemu ERP lub ceny poprzedniej.
Krok 2. W obszarze Wygląd sklepu > Ustawienia > Prezentacja rabatów włącz prezentację kuponów rabatowych na dedykowanej stronie w strefie klienta: W wyniku wskazanych ustawień, dostępna dla klientów stanie się strona, na której będzie widoczna lista kodów, wraz ze szczegółami obniżki:

Strona w profilu klienta ze szczegółami kodów rabatowych w szablonie Szafir

Kupony rabatowe w profilu klienta w szablonie Topaz

Krok 3. Po włączeniu prezentacji kuponów rabatowych na liście, szczegółach towaru i w koszyku, na towarach będzie dostępny przycisk "Pokaż promocje":

Prezentacja kodów rabatowych na listach w szablonie Szafir

Kupony rabatowe na listach w szablonie Topaz

Kliknięcie na niego dostarczy klientom informację o kuponach obowiązujących na danym towarze: Na szczegółach towaru widoczne są etykiety, które informują o dostępnych kodach, które Klient może wykorzystać:

Prezentacja kuponów rabatowych na szczegołach towaru w szablonie Szafir

             

Prezentacja kuponów rabatowych na szczegołach towaru w szablonie Topaz

Prezentacja kuponów rabatowych na szczegołach towaru w szablonie Rubin
Prezentacja kuponów rabatowych na szczegołach towaru w szablonie Rubin
Dzięki wskazanej prezentacji kuponów, klient w prosty sposób będzie mógł skopiować kod rabatowy z poziomu popupu i zastosować go w ramach zamówienia. Tę funkcję można wykorzystać w celach marketingowych - poprzez zaprezentowaną obniżkę zwiększyć zainteresowanie ofertą sklepu.

Instrukcja: Jak dostosować samodzielnie szablon do szybkich zwrotów w e-Commerce?

Wskazówka
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ć.
W szablonach Rubin, Szafir oraz Topaz / One Page Shop wprowadziliśmy funkcjonalność szybkich zwrotów. Umożliwia ona klientom składanie wniosku o zwrot produktu zarówno z poziomu profilu klienta (zalogowani), jak i z dedykowanej strony zwrotów po podaniu numeru zamówienia oraz adresu e-mail (niezalogowani). Wraz z funkcjonalnością pojawił się m.in. rozbudowany ekran potwierdzenia zwrotu zawierający kolejne kroki i adres do wysyłki, banery informacyjne dotyczące terminu zwrotu oraz przyciski akcji w tabeli produktów wyświetlane bezpośrednio (zamiast w rozwijanym menu). Aby Twój szablon poprawnie obsługiwał te zmiany, wprowadź modyfikacje opisane poniżej w sekcji dotyczącej Twojego szablonu. Po zakończeniu edycji pamiętaj o kompilacji i minifikacji plików js oraz scss.
Wskazówka
Potrzebujesz pomocy lub masz pytania? Skontaktuj się z nami poprzez portal asysta.comarch.pl.
Wskazówka
Obecnie w Polsce trwają prace nad ostatecznym kształtem przepisów oraz nowelizacją Ustawy o prawach konsumenta, która wdroży unijną dyrektywę UE 2023/2673 i wprowadzi nowe wymagania techniczne dla platform e-commerce (m.in. ułatwienia w procedurze zwrotów). W momencie publikacji ostatecznej wersji polskich przepisów, wdrożone funkcjonalności mogą wymagać dostosowania do ostatecznych wytycznych ustawodawcy. Prezentowane informacje mają charakter poglądowy a instrukcja dotyczy zmian, jakie wprowadziliśmy w szablonach Comarch – w przypadku pytań dotyczących indywidualnej sytuacji prawnej Twojego sklepu, zalecamy konsultację z radcą prawnym lub adwokatem.

Szablon Rubin

1. Tłumaczenia

W panelu administracyjnym sklepu zaktualizuj dwa istniejące tłumaczenia oraz dodaj nowe klucze wymagane przez funkcjonalność szybkich zwrotów. Zaktualizuj poniższe istniejące tłumaczenia: ReturnSuccessTitle - PL: Gotowe! Przyjęliśmy Twoje zgłoszenie - EN: Done! We have received your request - DE: Fertig! Wir haben Ihre Anfrage erhalten - FR: C'est fait ! Nous avons reçu votre demande ReturnSuccessInfo - PL: Przygotuj paczkę i odeślij ją do nas – wszystkie szczegóły znajdziesz poniżej. - EN: Prepare the package and send it back to us – all the details are below. - DE: Bereiten Sie das Paket vor und senden Sie es an uns zurück – alle Details finden Sie unten. - FR: Préparez le colis et renvoyez-le nous – tous les détails sont ci-dessous. Dodaj nowe tłumaczenia: OrderReturnDeadlineInfo - PL: Zamówienie można zwrócić w ciągu {0} dni od daty dostawy. - EN: You can return the order within {0} days from the delivery date. - DE: Sie können die Bestellung innerhalb von {0} Tagen ab Lieferdatum zurückgeben. - FR: Vous pouvez retourner la commande dans les {0} jours suivant la date de livraison. RestrictedOrderViewInfo - PL: Wybrane dane zamówienia zostały ukryte ze względów bezpieczeństwa. Możesz zwrócić produkty bez logowania. - EN: Some order details are hidden for security reasons. You can return products without signing in. - DE: Einige Bestelldetails wurden aus Sicherheitsgründen ausgeblendet. Sie können Produkte ohne Anmeldung zurücksenden. - FR: Certaines informations de la commande ont été masquées pour des raisons de sécurité. Vous pouvez retourner des produits sans vous connecter. Com_RefundMethod - PL: Sposób zwrotu środków - EN: Refund method - DE: Rückerstattungsmethode - FR: Mode de remboursement Com_RefundAsPaid - PL: Tak jak zapłacono - EN: Same as paid - DE: Wie bezahlt - FR: Comme payé Com_RefundToBankAccount - PL: Na konto bankowe - EN: To bank account - DE: Auf Bankkonto - FR: Sur le compte bancaire Com_BankAccountNumber - PL: Nr konta bankowego - EN: Bank account number - DE: Bankkontonummer - FR: Numéro de compte bancaire Com_ConfirmationSentTo - PL: Potwierdzenie zwrotu zostało wysłane na adres: - EN: Return confirmation has been sent to: - DE: Bestätigung wurde gesendet an: - FR: La confirmation de retour a été envoyée à: Com_NextSteps - PL: Następne kroki - EN: Next Steps - DE: Nächste Schritte - FR: Prochaines étapes Com_PackProducts - PL: Bezpiecznie zapakuj swoje produkty - EN: Safely pack your products - DE: Verpacken Sie Ihre Produkte sicher - FR: Emballez vos produits en toute sécurité Com_SendPackageTo - PL: Wyślij paczkę na adres: - EN: Send the package to the address: - DE: Senden Sie das Paket an die Adresse: - FR: Envoyez le colis à l'adresse: Com_RefundAfterReceive - PL: Pieniądze zwrócimy niezwłocznie po otrzymaniu i sprawdzeniu Twojej paczki - EN: We will refund the money promptly upon receipt and inspection of your package - DE: Wir erstatten das Geld umgehend nach Erhalt und Überprüfung Ihres Pakets zurück - FR: Nous rembourserons l'argent rapidement après réception et inspection de votre colis ReturnInThreeSteps - PL: Zwróć towar w 3 krokach - EN: Return the product in 3 steps - DE: Geben Sie die Ware in 3 Schritten zurück - FR: Retournez la marchandise en 3 étapes ReturnWithoutReasonInfo - PL: Przysługuje Ci możliwość zwrotu bez podania przyczyny. - EN: You have the right to return the product without giving a reason. - DE: Sie haben das Recht, die Ware ohne Angabe von Gründen zurückzugeben. - FR: Vous avez le droit de retourner la marchandise sans donner de motif. OrEnterOrderNumber - PL: lub podaj numer zamówienia - EN: or enter the order number - DE: oder geben Sie die Bestellnummer ein - FR: ou saisissez le numéro de commande FillForm - PL: Wypełnij formularz - EN: Fill out the form - DE: Füllen Sie das Formular aus - FR: Remplissez le formulaire ReturnOnline - PL: zwrotu online - EN: online return - DE: Online-Rückgabe - FR: retour en ligne SendParcel - PL: Wyślij paczkę - EN: Send the parcel - DE: Paket senden - FR: Envoyer le colis SendParcelToOurAddress - PL: nadaj przesyłkę na nasz adres - EN: send the parcel to our address - DE: senden Sie das Paket an unsere Adresse - FR: envoyez le colis à notre adresse FastSimpleSecure - PL: Szybko, prosto i bezpiecznie. - EN: Fast, simple and secure. - DE: Schnell, einfach und sicher. - FR: Rapide, simple et sécurisé. ReadyForReturn - PL: Gotowy do zwrotu? - EN: Ready for return? - DE: Bereit zur Rückgabe? - FR: Prêt pour le retour ? ReadyForReturnInfo - PL: Kliknij poniżej, aby rozpocząć proces zwrotu. Przeprowadzimy Cię przez wszystkie kroki. - EN: Click below to start the return process. We will guide you through all the steps. - DE: Klicken Sie unten, um den Rückgabeprozess zu starten. Wir führen Sie durch alle Schritte. - FR: Cliquez ci-dessous pour lancer le processus de retour. Nous vous guiderons à travers toutes les étapes. StartReturn - PL: Rozpocznij zwrot - EN: Start return - DE: Rückgabe starten - FR: Commencer le retour ProductReturn - PL: Zwrot produktu - EN: Product return - DE: Produktrückgabe - FR: Retour du produit ProductReturnFormInfo - PL: Wprowadź dane zamówienia, aby zlokalizować zakup i rozpocząć proces zwrotu. Numer zamówienia znajdziesz w e-mailu z potwierdzeniem. - EN: Enter the order details to locate the purchase and start the return process. You will find the order number in the confirmation e-mail. - DE: Geben Sie die Bestelldaten ein, um den Kauf zu lokalisieren und den Rückgabeprozess zu starten. Die Bestellnummer finden Sie in der Bestätigungs-E-Mail. - FR: Saisissez les données de la commande pour localiser l'achat et démarrer le processus de retour. Vous trouverez le numéro de commande dans l'e-mail de confirmation. InvalidOrderNumber - PL: Błędny numer zamówienia. Spróbuj ponownie. - EN: Invalid order number. Please try again. - DE: Ungültige Bestellnummer. Bitte versuchen Sie es erneut. - FR: Numéro de commande incorrect. Veuillez réessayer. FindOrder - PL: Znajdź zamówienie - EN: Find order - DE: Bestellung finden - FR: Trouver la commande LoginToSeeOrdersAndReturn - PL: aby zobaczyć swoje zamówienia i dokonać zwrotu. - EN: to see your orders and make a return. - DE: um Ihre Bestellungen einzusehen und eine Rückgabe vorzunehmen. - FR: pour voir vos commandes et effectuer un retour. RefundLookupError - PL: Wystąpił błąd podczas wyszukiwania zamówienia. Spróbuj ponownie później. - EN: An error occurred while searching for the order. Please try again later. - DE: Bei der Suche nach der Bestellung ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut. - FR: Une erreur s'est produite lors de la recherche de la commande. Veuillez réessayer plus tard.

2. Nowa strona Szybkie zwroty

Funkcjonalność szybkich zwrotów wymaga utworzenia dodatkowej strony statycznej refunds. Wykonaj poniższe kroki.

Plik refunds.html w katalogu głównym szablonu

W katalogu głównym szablonu utwórz nowy plik refunds.html z następującą zawartością: {% extends '_layout.html' -%} {% block pageContent -%} {% include 'static-elements/refunds/refunds.html' -%} {% endblock -%}

Plik static-elements/refunds/refunds.html

W folderze static-elements utwórz nowy katalog refunds, a w nim plik refunds.html: <div class="page-padding"> <h1 class="refunds-page-title refunds-initial-view-js">{{ translations.Returns }}</h1> <section class="refunds-container refunds-initial-view-js"> <div class="refunds-info"> <h2 class="refunds-info__title">{{ translations.ReturnInThreeSteps }}</h2> <p class="refunds-info__intro">{{ translations.ReturnWithoutReasonInfo }}</p> <ol class="refunds-steps"> <li class="refunds-steps__item"> <span class="refunds-steps__number">1</span> <p class="refunds-steps__text"><strong>{{ translations.LogIn }}</strong> {{ translations.OrEnterOrderNumber }}</p> </li> <li class="refunds-steps__item"> <span class="refunds-steps__number">2</span> <p class="refunds-steps__text"><strong>{{ translations.FillForm }}</strong> {{ translations.ReturnOnline }}</p> </li> <li class="refunds-steps__item"> <span class="refunds-steps__number">3</span> <p class="refunds-steps__text"><strong>{{ translations.SendParcel }}</strong> - {{ translations.SendParcelToOurAddress }}</p> </li> </ol> <p class="refunds-info__hint">{{ translations.FastSimpleSecure }}</p> </div> <div class="refunds-cta"> <div class="refunds-cta__header"> <h3 class="refunds-cta__title">{{ translations.ReadyForReturn }}</h3> <p class="refunds-cta__description">{{ translations.ReadyForReturnInfo }}</p> </div> {% if usr.Authenticated -%} <a href="{{ config.DefinedPages.CustomerProfile.Url }}?tab=orders" class="primary-action-button refunds-cta__button" rel="nofollow" aria-label="{{ translations.StartReturn }}"> {{ translations.StartReturn }} </a> {% else -%} <button type="button" class="primary-action-button refunds-cta__button start-refund-js" aria-label="{{ translations.StartReturn }}"> {{ translations.StartReturn }} </button> {% endif -%} </div> </section> {% unless usr.Authenticated -%} <section class="refund-form-section refund-form-view-js hidden-js"> <div class="refund-form"> <div class="refund-form__header"> <h2 class="refund-form__title">{{ translations.ProductReturn }}</h2> <p class="refund-form__description">{{ translations.ProductReturnFormInfo }}</p> </div> <form class="refund-form__body refund-form-js" novalidate data-error="{{ translations.RefundLookupError }}"> <div class="form__input-wrapper"> <label for="refundOrderNumber" class="form__input-info input-info-js">{{ translations.NumberOfOrder }}</label> <input id="refundOrderNumber" type="text" name="orderNumber" class="form__input-value input-value-js" maxlength="50" required> <div class="form__invalid-input validation-info-js validation-required-js hidden-js">{{ translations.FieldRequired }}</div> </div> <div class="form__input-wrapper"> <label for="refundEmail" data-type="email" class="form__input-info input-info-js">{{ translations.EmailAddress }}</label> <input id="refundEmail" type="email" name="email" class="form__input-value input-value-js" maxlength="192" autocomplete="email" required> <div class="form__invalid-input validation-info-js validation-required-js hidden-js">{{ translations.FieldRequired }}</div> <div class="form__invalid-input validation-info-js email-valid-js hidden-js">{{ translations.FillProperEmail }}</div> </div> <button type="button" class="primary-action-button refund-form__submit refund-form-submit-js"> {{ translations.FindOrder }} </button> <p class="refund-form__login-info"> {{ translations.HaveAccount }} <a class="refund-form__login-link show-login-popup-js" rel="nofollow" tabindex="0" role="button">{{ translations.LogIn }},</a> {{ translations.LoginToSeeOrdersAndReturn }} </p> </form> </div> </section> {% endunless -%} </div>

Plik js/layout1.js – kod obsługi formularza zwrotu

Na końcu pliku js/layout1.js dodaj poniższy kod odpowiedzialny za walidację i wysłanie formularza wyszukiwania zamówienia (dla niezalogowanego klienta): $(document).ready(function() { refunds.init(); }); const refunds = { init: function() { this.events(); }, findRefundOrder: function(orderNumber, email) { return $.post('', { __action: 'Order/QuickReturnLookup', orderId: orderNumber, email: email, __csrf: __CSRF }); }, showForm: function() { $('.refunds-initial-view-js').addClass('hidden-js'); $('.refund-form-view-js').removeClass('hidden-js'); $('.refund-form-js input[name="orderNumber"]').trigger('focus'); }, clearFieldError: function(input) { const $input = $(input); $input.removeClass('validation-error-js'); $input.closest('.form__input-wrapper').find('.form__invalid-input').addClass('hidden-js'); }, submitForm: async function(button) { const $submit = $(button); const $form = $submit.closest('.refund-form-js'); const $orderInput = $form.find('input[name="orderNumber"]'); const $emailInput = $form.find('input[name="email"]'); const orderValue = $orderInput.val().trim(); const emailValue = $emailInput.val().trim(); const emailRegex = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w{2,}$/; let hasError = false; $form.find('.form__invalid-input').addClass('hidden-js'); $form.find('.form__input-value').removeClass('validation-error-js'); if (orderValue.length === 0) { $orderInput.addClass('validation-error-js'); $orderInput.closest('.form__input-wrapper').find('.validation-required-js').removeClass('hidden-js'); hasError = true; } if (emailValue.length === 0) { $emailInput.addClass('validation-error-js'); $emailInput.closest('.form__input-wrapper').find('.validation-required-js').removeClass('hidden-js'); hasError = true; } else if (!emailRegex.test(emailValue)) { $emailInput.addClass('validation-error-js'); $emailInput.closest('.form__input-wrapper').find('.email-valid-js').removeClass('hidden-js'); hasError = true; } if (hasError) { return; } $submit.prop('disabled', true); try { const result = await refunds.findRefundOrder(orderValue, emailValue); const action = result.action; if (action.Redirect302) { globalThis.location.href = action.Redirect302; return; } app.temporaryPopupMessage(undefined, action.Message, action.Type); } catch (error) { console.error('Refund order lookup failed.', error); app.temporaryPopupMessage(undefined, $form.data('error'), 'err'); } finally { $submit.prop('disabled', false); } }, events: function() { $('body').on('click', '.start-refund-js', function() { refunds.showForm(); }); $('body').on('input', '.refund-form-js .form__input-value', function() { refunds.clearFieldError(this); }); $('body').on('click', '.refund-form-submit-js', function(e) { e.preventDefault(); refunds.submitForm(this); }); $('body').on('keydown', '.refund-form-js input', function(e) { if (e.key === 'Enter') { e.preventDefault(); $(this).closest('.refund-form-js').find('.refund-form-submit-js').trigger('click'); } }); } };

Pliki SCSS strony Szybkie zwroty

W folderze scss/static-elements utwórz nowy katalog refunds i umieść w nim trzy pliki: refunds-g.scss, refunds-m.scss i refunds-t.scss. scss/static-elements/refunds/refunds-g.scss: .refunds-page-title { margin: 0 0 24px; font-size: 24px; font-weight: 500; line-height: 32px; color: $primaryColorFont; } .refunds-container { display: flex; flex-direction: column; align-items: center; gap: 32px; margin-bottom: 32px; color: $primaryColorFont; } .refunds-info { align-self: flex-start; display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 800px; &__title { margin: 0; font-size: 20px; font-weight: 600; line-height: 28px; color: $primaryColorFont; } &__intro { margin: 0; font-size: 16px; font-weight: 500; line-height: 24px; color: $primaryColorFont; } &__hint { margin: 4px 0 0; font-size: 12px; font-weight: 400; line-height: 20px; color: $breadcrumbs; } } .refunds-steps { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; &__item { display: flex; align-items: center; gap: 12px; } &__number { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background-color: #f3f3f3; color: $primaryColor; font-size: 14px; font-weight: 600; line-height: 20px; } &__text { margin: 0; font-size: 14px; font-weight: 400; line-height: 22px; color: $primaryColorFont; strong { font-weight: 600; } } } .refunds-cta { box-sizing: border-box; width: 100%; max-width: 578px; padding: 40px; display: flex; flex-direction: column; align-items: center; gap: 24px; background-color: $bgColor; border: 1px solid $lightBorderColor; border-radius: 8px; &__header { display: flex; flex-direction: column; align-items: center; gap: 12px; width: 100%; } &__title { margin: 0; font-size: 24px; font-weight: 600; line-height: 32px; text-align: center; color: $primaryColorFont; } &__description { margin: 0; font-size: 16px; font-weight: 400; line-height: 24px; text-align: center; color: $breadcrumbs; } &__button { width: 100%; text-decoration: none; } } .refund-form-section { display: flex; justify-content: center; width: 100%; margin-bottom: 32px; } .refund-form { box-sizing: border-box; width: 100%; max-width: 578px; padding: 40px; background-color: $bgColor; border: 1px solid $lightBorderColor; border-radius: 8px; &__header { display: flex; flex-direction: column; gap: 12px; margin-bottom: 32px; } &__title { margin: 0; font-size: 24px; font-weight: 600; line-height: 32px; color: $primaryColorFont; } &__description { margin: 0; font-size: 14px; font-weight: 400; line-height: 20px; color: $breadcrumbs; } &__body { display: flex; flex-direction: column; } .form__input-wrapper { .form__input-value { &.validation-error-js { border-color: $dangerColor; color: $dangerColor; } } .form__invalid-input { position: static; display: block; margin: 4px 0 0; padding-left: 16px; text-align: left; color: $dangerColor; font-size: 12px; font-weight: 400; line-height: 16px; } } &__submit { width: 100%; margin-top: 4px; } &__login-info { margin: 16px 0 0; text-align: center; font-size: 14px; font-weight: 400; line-height: 20px; color: $primaryColorFont; } &__login-link { color: $primaryColor; font-weight: 600; cursor: pointer; text-decoration: none; &:hover { text-decoration: underline; } } } scss/static-elements/refunds/refunds-m.scss: @media screen and (max-width: 767px) { .refunds-page-title { font-size: 20px; line-height: 28px; margin-bottom: 16px; } .refunds-container { gap: 24px; margin-bottom: 24px; } .refunds-info { max-width: 100%; gap: 12px; &__title { font-size: 18px; line-height: 24px; } &__intro { font-size: 14px; line-height: 22px; } } .refunds-steps { &__item { align-items: flex-start; gap: 10px; } &__number { width: 28px; height: 28px; font-size: 13px; } &__text { font-size: 13px; line-height: 20px; padding-top: 3px; } } .refunds-cta { padding: 24px; &__title { font-size: 20px; line-height: 28px; } &__description { font-size: 14px; line-height: 22px; } } .refund-form { padding: 24px; &__header { margin-bottom: 24px; } &__title { font-size: 20px; line-height: 28px; } &__description { font-size: 13px; line-height: 18px; } &__login-info { font-size: 13px; } } } scss/static-elements/refunds/refunds-t.scss: @media screen and (min-width: 768px) and (max-width: 1439px) { .refunds-info { max-width: 100%; } }

3. Modyfikacje istniejących plików HTML

partials/cart/thx.html

W pliku partials/cart/thx.html znajdź fragment generujący etykietę zgody w sekcji zgód marketingowych: <label tabindex="0" role="checkbox" aria-checked="false" for="{{ consent.FieldValue }}" class="cart__tos-label tos-js"> Usuń z niego atrybut for="{{ consent.FieldValue }}". Po zmianie linia powinna wyglądać tak: <label tabindex="0" role="checkbox" aria-checked="false" class="cart__tos-label tos-js">

partials/common/login.html

1. W pliku partials/common/login.html w kontenerze opakowującym formularz dodaj klasę login-container-popup. Zamień linię: <div class="{% if page.PageId != config.DefinedPages.Login.Id and page.PageId != config.DefinedPages.CustomerProfile.Id and page.PageId != config.DefinedPages.Order.Id -%} login-popup-js{% else -%} login-container {% endif -%}"> na: <div class="{% if page.PageId != config.DefinedPages.Login.Id and page.PageId != config.DefinedPages.CustomerProfile.Id and page.PageId != config.DefinedPages.Order.Id -%} login-popup-js login-container-popup{% else -%} login-container {% endif -%}"> 2. W warunku otaczającym przycisk zamykający popup logowania usuń warunek and settings.signIn == "popup". Zamień linię: {% if page.PageId != config.DefinedPages.Login.Id and page.PageId != config.DefinedPages.CustomerProfile.Id and page.PageId != config.DefinedPages.Order.Id and settings.signIn == "popup" -%} na: {% if page.PageId != config.DefinedPages.Login.Id and page.PageId != config.DefinedPages.CustomerProfile.Id and page.PageId != config.DefinedPages.Order.Id -%}

partials/product/amount-stepper.html

W pliku partials/product/amount-stepper.html znajdź blok ustawiający zmienne dla typów complaint i return: {% if type == 'complaint' or type == 'return' -%} {% assign reloadClass = '' -%} {% assign complaintOrReturn = true -%} {% endif -%} i dodaj w jego wnętrzu nową linię ustawiającą maksymalną ilość produktu: {% if type == 'complaint' or type == 'return' -%} {% assign reloadClass = '' -%} {% assign complaintOrReturn = true -%} {% assign max = product.Quantity | Normalize -%} {% endif -%}

partials/order/products-partials/product-table-header.html

Na samym początku pliku partials/order/products-partials/product-table-header.html dodaj blok przypisujący flagę complaintsOrReturnsEnabled: {% if config.Complaints.ComplaintsEnabled or config.Complaints.ReturnsEnabled -%} {% assign complaintsOrReturnsEnabled = true -%} {% endif -%}

partials/product/products-table.html

Przyciski reklamacji i zwrotu są wyświetlane bezpośrednio w tabeli, zamiast w rozwijanym menu. W pliku partials/product/products-table.html znajdź komórkę z opcjami: <td class="options drop"> {% if order.Status == 3 and complaintsOrReturnsEnabled and product.CanComplain == true -%} <button aria-label="{{translations.Options}}" class="pure-button open-dropdown-js"> <svg width="24" height="24" style="transform: rotate(90deg);"> <use href="css/img/fontawesome-icons.svg?v={{lqTS}}#ellipsis-h-{{settings.iconStyle}}"></use> </svg> </button> <div class="dropdown dropdown-js hidden-js" {% if productsSize == 1 -%} style="bottom: auto !important;" {% endif -%}> {% if config.Complaints.ComplaintsEnabled == true and product.CanComplain == true -%} <button class="pure-button open-complaint-form-js" data-type="complaint" data-product="{{ product.Url }}"> {{ translations.Complain }} </button> {% endif -%} {% if config.Complaints.ReturnsEnabled == true and product.CanReturn == true and product.IsSetElement == false -%} <button class="pure-button open-complaint-form-js" data-type="return" data-product="{{ product.Url }}"> {{ translations.Return }} </button> {% endif -%} </div> {% endif -%} </td> i zastąp ją następującym fragmentem: <td class="options complaint-actions-inline"> {% if order.Status == 3 and complaintsOrReturnsEnabled -%} {% if config.Complaints.ComplaintsEnabled == true and product.CanComplain == true -%} <button class="pure-button open-complaint-form-js complaint-action-btn" data-type="complaint" data-product="{{ product.Url }}"> {{ translations.Complain }} </button> {% endif -%} {% if config.Complaints.ReturnsEnabled == true and product.CanReturn == true and product.IsSetElement == false -%} <button class="pure-button open-complaint-form-js complaint-action-btn" data-type="return" data-product="{{ product.Url }}"> {{ translations.Return }} </button> {% endif -%} {% endif -%} </td> Następnie w tym samym pliku znajdź zewnętrzny warunek otaczający kontenery formularzy reklamacji i zwrotu i poluzuj go (usuń warunek product.CanComplain == true). Zamień: {% if order.Status == 3 and complaintsOrReturnsEnabled and product.CanComplain == true -%} na: {% if order.Status == 3 and complaintsOrReturnsEnabled -%}

partials/customer-profile/orders/order-details.html

W pliku partials/customer-profile/orders/order-details.html wykonaj poniższe zmiany. a) Bloki Opcje (zawierające klasę options submenu z przyciskami akcji zamówienia) opakuj warunkiem {% unless order.Restricted -%} ... {% endunless -%}, aby ukryć je dla niezalogowanych klientów dokonujących szybkiego zwrotu. Wstaw {% unless order.Restricted -%} bezpośrednio przed otwierającym <div class="options submenu"> i zamykającym {% endunless -%} po jego zamykającym </div>: {% unless order.Restricted -%} <div class="options submenu"> ... </div> {% endunless -%} b) W kontenerze messages-container-js na początku (przed istniejącymi komunikatami typu OrderEditable) dodaj baner informujący o ukrytych danych zamówienia oraz, na końcu kontenera, baner informujący o terminie zwrotu: {% if order.Restricted -%} <div class="return-info-banner"> <svg width="20" height="20" class="icon" aria-hidden="true"> <use href="css/img/fontawesome-icons.svg?v={{lqTS}}#info-circle-{{settings.iconStyle}}"></use> </svg> <span>{{ translations.RestrictedOrderViewInfo }}</span> </div> {% endif -%} {% if order.Status == 3 and config.Complaints.ReturnsEnabled -%} <div class="return-info-banner"> <svg width="20" height="20" class="icon" aria-hidden="true"> <use href="css/img/fontawesome-icons.svg?v={{lqTS}}#info-circle-{{settings.iconStyle}}"></use> </svg> <span>{{ translations.OrderReturnDeadlineInfo | Format: config.Complaints.DaysToReturn }}</span> </div> {% endif -%} c) We wszystkich miejscach wyświetlających numer telefonu klienta zamień warunki sprawdzające jedynie pusty ciąg na warunki sprawdzające również wartość null. Zamień każde wystąpienie: {% if order.Customer.DeliveryAddress.PhoneNo != '' -%} na: {% if order.Customer.DeliveryAddress.PhoneNo != null and order.Customer.DeliveryAddress.PhoneNo != '' -%} d) Analogicznie zaktualizuj warunek wyświetlania NIP-u/TIN-u w sekcji danych do faktury. Zamień: {% elseif order.Customer.TIN != '' -%} na: {% elseif order.Customer.TIN != null and order.Customer.TIN != '' -%} e) W sekcji adresu dostawy zamień otwierający warunek alternatywny {% else -%} (bezpośrednio przed kontenerem z translations.DelivAddress) na bardziej restrykcyjny: {% elseif order.Customer.DeliveryAddress != null -%} f) Sekcję wybranego dokumentu (ChoosenDocument) opakuj warunkiem ukrywającym ją dla zamówień typu restricted. Wstaw {% unless order.Restricted -%} przed kontenerem order-details-item z tytułem translations.ChoosenDocument oraz {% endunless -%} za jego zamykającym </div>. g) W sekcji dokumentów zamówienia dodaj do warunku sprawdzenie order.Restricted == false. Zamień: {% if orderDocumentsSize > 0 -%} na: {% if orderDocumentsSize > 0 and order.Restricted == false -%} h) W sekcji załączników zamówienia dodaj analogiczne sprawdzenie. Zamień: {% if config.Orders.AttachmentsEnabled and orderAttachmentsSize > 0 -%} na: {% if config.Orders.AttachmentsEnabled and orderAttachmentsSize > 0 and order.Restricted == false -%} i) W sekcji zamówień powiązanych zamień: {% if order.RelatedOrders[0] -%} na: {% if order.RelatedOrders[0] and order.Restricted == false -%}

partials/customer-profile/complaints/complaint-form.html

W pliku partials/customer-profile/complaints/complaint-form.html wprowadź poniższe zmiany. a) W kontenerze amount-stepper-container (wewnątrz warunku {% if type != 'set-return' -%}) na samym początku dodaj nową etykietę: <span class="amount-stepper-container__label">{{ translations.Quantity }}:</span> b) Pod sekcją z komentarzem reklamacji/zwrotu (po zamykającym {% endif -%} z sekcji complaint-message-textarea) wewnątrz warunku {% if type != 'set-return' -%} dodaj sekcję wyboru sposobu zwrotu środków: <div class="refund-method-wrapper"> <div class="refund-method-wrapper__title">{{ translations.Com_RefundMethod }} <span class="required">*</span></div> <label class="refund-method-option"> <input type="radio" name="refundMethod" class="refund-method-option__radio refund-method-radio-js" value="asPaid" checked> <span class="refund-method-option__box" aria-hidden="true"></span> <span class="refund-method-option__label"> {{ translations.Com_RefundAsPaid }} {% if order.Payment.Name and order.Payment.Name != '' -%} ({{ order.Payment.Name }}){% endif -%} </span> </label> <label class="refund-method-option"> <input type="radio" name="refundMethod" class="refund-method-option__radio refund-method-radio-js" value="bankAccount"> <span class="refund-method-option__box" aria-hidden="true"></span> <span class="refund-method-option__label">{{ translations.Com_RefundToBankAccount }}</span> </label> </div> <div class="form__input-wrapper account-number-wrapper-js hidden-js"> <label for="accountNumber" class="form__input-info input-info-js">{{ translations.Com_BankAccountNumber }} <span class="required">*</span></label> <input id="accountNumber" class="form__input-value input-value-js" type="text" name="accountNumber" maxlength="50" disabled> <div class="form__invalid-input validation-info-js validation-required-js hidden-js">{{ translations.FieldRequired }}</div> </div> c) W sekcji załączników plików zamień prosty licznik file-{{forloop.index}} na prefiks zawierający identyfikator zamówienia, numer produktu i typ formularza. Tuż przed pętlą {% for i in (1..config.Complaints.AttachmentsMaxCount) -%} dodaj nową linię: {% capture filePrefix -%}file-{{ order.Id }}-{{ product.No }}-{{ type }}{% endcapture -%} Następnie wewnątrz pętli zamień: <label for="file-{{forloop.index}}" class="file"> <input id="file-{{forloop.index}}" class="add-attachement-in-complaint-js hidden-js" type="file" ... > na: <label for="{{ filePrefix }}-{{ forloop.index }}" class="file"> <input id="{{ filePrefix }}-{{ forloop.index }}" class="add-attachement-in-complaint-js hidden-js" type="file" ... > d) Komunikat sukcesu wyświetlany po wysłaniu formularza ma być inny dla reklamacji, a inny dla zwrotu. Znajdź kontener <div class="success-message-js hidden-js"> i jego dotychczasową zawartość: <div class="success-message-js hidden-js"> <span> {{ successInfo }} </span> </div> zastąp: <div class="success-message-js hidden-js"> {% if complaint -%} <span>{{ successInfo }}</span> {% else -%} <div class="data-form__header return-success__header"> <span></span> <button class="close-button hide-container-js closing-modal-js" aria-label="{{ translations.Close }}"> <svg aria-hidden="true" width="24" height="24" class="svgIcon"><use xlink:href="css/img/fontawesome-icons.svg?v={{lqTS}}#times-{{settings.iconStyle}}"></use></svg> </button> </div> <div class="return-success__icon"> <svg aria-hidden="true" width="48" height="48" class="svgIcon"><use xlink:href="css/img/fontawesome-icons.svg?v={{lqTS}}#check-circle-{{settings.iconStyle}}"></use></svg> </div> <div class="return-success__title">{{ successTitle }}</div> <div class="return-success__subtitle">{{ successInfo }}</div> {% if order.Customer.Email and order.Customer.Email != '' -%} <div class="return-success__email-box"> <svg aria-hidden="true" width="20" height="20" class="svgIcon"><use xlink:href="css/img/fontawesome-icons.svg?v={{lqTS}}#info-circle-{{settings.iconStyle}}"></use></svg> <div> <div>{{ translations.Com_ConfirmationSentTo }}</div> <strong>{{ order.Customer.Email }}</strong> </div> </div> {% endif -%} <div class="return-success__steps-title">{{ translations.Com_NextSteps }}</div> <ol class="return-success__steps"> <li>{{ translations.Com_PackProducts }}</li> <li> {{ translations.Com_SendPackageTo }} {% if config.Shop.Address.Street != '' -%} {{ config.Shop.Address.Street }}{% endif -%} {% if config.Shop.Address.StreetNo != '' -%} {{ config.Shop.Address.StreetNo }}{% endif -%} {% if config.Shop.Address.UnitNo != '' -%}/{{ config.Shop.Address.UnitNo }}{% endif -%} {% if config.Shop.Address.ZipCode != '' or config.Shop.Address.City != '' -%}, {{ config.Shop.Address.ZipCode }} {{ config.Shop.Address.City }}{% endif -%} </li> <li>{{ translations.Com_RefundAfterReceive }}</li> </ol> <div class="buttons-container short"> <button class="primary-action-button hide-container-js closing-modal-js"> {{ translations.Close }} </button> </div> {% endif -%} </div>

4. Modyfikacje pliku js/layout-customerprofile0.js

Poniższe zmiany dotyczą pliku js/layout-customerprofile0.js (jego nieskompresowanej wersji, którą po edycji należy ponownie zminifikować do layout-customerprofile0.min.js). a) W funkcji openComplaintForm wewnątrz warunku if (hiddenContainer) – tuż po linii hiddenContainer.querySelector('.success-message-js').classList.add('hidden-js'); – dodaj kod ujawniający nagłówek formularza oraz resetujący wybór sposobu zwrotu do opcji domyślnej: const mainHeader = hiddenContainer.querySelector(':scope > .data-form__header'); if (mainHeader) { mainHeader.classList.remove('hidden-js'); } hiddenContainer.classList.remove('hidden-js'); const asPaidRadio = hiddenContainer.querySelector('.refund-method-radio-js[value="asPaid"]'); if (asPaidRadio) { asPaidRadio.checked = true; $(asPaidRadio).trigger('change'); } (Linia hiddenContainer.classList.remove('hidden-js'); już istnieje – pokazana jest dla kontekstu. Nowe są bloki const mainHeader i const asPaidRadio.) b) W funkcji sendComplaint, w bloku przygotowującym dane formularza – po pętli for (const item of dataFromHTML) dokładającej pola do fd – dodaj fragment dołączający parametr qrHash z adresu URL przy wysyłaniu zwrotu: if (form.find('input[name="__action"]').val() === 'Order/ReturnAdd') { const qrHash = new URLSearchParams(globalThis.location.search).get('qrHash'); if (qrHash) { fd.append('qrHash', qrHash); } } c) W tej samej funkcji sendComplaint, w callbacku success po pomyślnym wysłaniu, zamień fragment: form.parents('.add-complaint-form-js').find('.success-message-js').removeClass('hidden-js'); na: const container = form.parents('.add-complaint-form-js'); container.find('.success-message-js').removeClass('hidden-js'); container.children('.data-form__header').addClass('hidden-js'); d) Na samym końcu pliku js/layout-customerprofile0.js dodaj nowy handler obsługujący przełączanie sposobu zwrotu środków. Pokazuje on / ukrywa pole na numer konta bankowego: $('body').on('change', '.refund-method-radio-js', function(e) { const radio = $(e.currentTarget); const form = radio.closest('.form-js'); const wrapper = form.find('.account-number-wrapper-js'); const input = wrapper.find('input[name="accountNumber"]'); if (radio.val() === 'bankAccount' && radio.prop('checked')) { wrapper.removeClass('hidden-js'); input.prop('disabled', false); input.prop('required', true); } else { wrapper.addClass('hidden-js'); wrapper.find('.validation-required-js').addClass('hidden-js'); wrapper.find('.form__input-info').removeClass('validation-error'); input.removeClass('validation-error-js'); input.prop('required', false); input.prop('disabled', true); input.val(''); } });

5. Modyfikacje plików SCSS profilu klienta

Poniższe style należy dodać do plików scss/static-elements/customer-profile/customer-profile-g.scss (wersja desktop) oraz scss/static-elements/customer-profile/customer-profile-m.scss (wersja mobile). Po zmianach pamiętaj o kompilacji – nie edytuj ręcznie wynikowych plików CSS.

Plik scss/static-elements/customer-profile/customer-profile-g.scss

a) Dla "przyklejonej" kolumny z akcjami reklamacji/zwrotu w tabeli produktów dodaj reguły wewnątrz selektora otaczającego tabelę zamówienia (tam, gdzie znajdują się style dla thead tr th): thead tr th.open-complaint-form-container-js { position: sticky; right: 0; background: transparent; z-index: 5; &::before { content: ''; position: absolute; top: 0; bottom: 0; right: 0; width: 200px; background: linear-gradient(to right, rgba(255, 255, 255, 0), #fff 50%); pointer-events: none; } } td.complaint-actions-inline { white-space: nowrap; text-align: right; padding-left: 16px; padding-right: 8px; position: sticky; right: 0; background: #fff; z-index: 2; &::before { content: ''; position: absolute; top: 0; bottom: 0; left: -32px; width: 32px; background: linear-gradient(to right, rgba(255, 255, 255, 0), #fff 100%); pointer-events: none; } .pure-button.complaint-action-btn { display: inline-flex; background: transparent; border: 0; border-radius: 4px; color: $primaryColor; padding: 4px 8px; cursor: pointer; font-size: 14px; font-weight: 600; line-height: 20px; transition: background-color 150ms ease; &:hover, &:focus-visible { color: $primaryColor; background: rgba(0, 0, 0, 0.06); } & + .complaint-action-btn { margin-left: 8px; } } } b) Style hovera dla wiersza tabeli z akcjami reklamacji/zwrotu (umieść w głównym zakresie pliku): .row { &:hover, &:focus-within, &.hovered-js { td.complaint-actions-inline { background: #F1DDDE; &::before { background: linear-gradient(to right, rgba(241, 221, 222, 0), #F1DDDE 100%); } } } &.set td.complaint-actions-inline { background: #ececec; &::before { background: linear-gradient(to right, rgba(236, 236, 236, 0), #ececec 100%); } } } c) Style banera informacyjnego (termin zwrotu / ukryte dane zamówienia): .return-info-banner { display: flex; align-items: center; gap: 12px; padding: 12px 16px; margin: 0 0 24px 0; background: #E8F1F8; border: 1px solid #C7DCEE; border-radius: 5px; font-size: 14px; line-height: 20px; color: #2C3E50; .icon { flex-shrink: 0; fill: #4A90BC; } strong { font-weight: 600; } } d) Style sekcji wyboru sposobu zwrotu środków oraz radio-buttonów: .refund-method-wrapper { text-align: left; display: flex; flex-direction: column; gap: 16px; margin-bottom: 24px; &__title { color: $primaryColorFont; font-size: 14px; font-weight: 500; line-height: 20px; } } .refund-method-option { display: flex; align-items: center; gap: 8px; cursor: pointer; &__radio { display: none; &:checked ~ .refund-method-option__box { border-color: $primaryColor; &::after { content: ""; display: block; width: 8px; height: 8px; border-radius: 50%; background: $primaryColor; } } } &__box { display: flex; justify-content: center; align-items: center; width: 16px; height: 16px; border: 1px solid #767676; border-radius: 50%; flex-shrink: 0; } &__label { font-size: 14px; line-height: 20px; } } e) Style ekranu sukcesu po wysłaniu zwrotu (m.in. ikona, tytuł, lista kolejnych kroków): .add-complaint-form-js .characters-length { margin-top: 0; bottom: 23px; } .add-complaint-form-js { .quantity__plus.disabled-js, .quantity__minus.disabled-js { opacity: 0.4; } } .add-complaint-form-js .data-form__header.return-success__header { margin-bottom: 8px; } .return-success { &__icon { display: flex; justify-content: center; margin-bottom: 16px; svg { fill: #2E7D32; } } &__title { text-align: center; font-size: 20px; font-weight: 600; line-height: 28px; color: $primaryColorFont; margin-bottom: 8px; } &__subtitle { text-align: center; font-size: 14px; line-height: 20px; color: $labelsColor; padding-bottom: 24px; border-bottom: 1px solid #E7E7E7; margin-bottom: 24px; } &__email-box { display: flex; align-items: flex-start; gap: 12px; padding: 12px 16px; background: #F0F6FC; border: 1px solid #D6E4F0; border-radius: 6px; margin-bottom: 24px; font-size: 14px; line-height: 20px; color: $primaryColorFont; svg { fill: #1976D2; flex-shrink: 0; margin-top: 2px; } } &__steps-title { font-size: 14px; font-weight: 600; line-height: 20px; color: $primaryColorFont; margin-bottom: 12px; } &__steps { margin: 0 0 24px; padding-left: 20px; font-size: 14px; line-height: 20px; color: $primaryColorFont; li { margin-bottom: 8px; &:last-child { margin-bottom: 0; } } } } f) Uzupełnij wnętrze selektora .file-attachements-wrapper o style dla kontenera pojedynczego załącznika: .file-container { .file { align-items: center; font-weight: 400; color: $primaryColorFont; justify-content: flex-start; } .icon-js svg { fill: $primaryColorFont; } .clear-file-input-js { flex-shrink: 0; display: flex; align-items: center; svg { width: 14px; height: 14px; fill: $primaryColor; } } }

Plik scss/static-elements/customer-profile/customer-profile-m.scss

Na końcu pliku customer-profile-m.scss dodaj reguły dopasowujące przyciski reklamacji/zwrotu do widoku mobilnego (układają się one wówczas jeden pod drugim): .eshop__table-container { thead tr th.open-complaint-form-container-js::before { width: 100px; } td.complaint-actions-inline { .pure-button.complaint-action-btn { display: block; width: max-content; margin-left: auto; text-align: right; & + .complaint-action-btn { margin-left: auto; margin-top: 4px; } } } } Po wprowadzeniu wszystkich powyższych zmian w szablonie Rubin pamiętaj o kompilacji oraz minifikacji plików js i scss, zgodnie z artykułem dostępnym pod linkiem https://pomoc.comarchesklep.pl/artykul/kompilacja-i-minifikacja-plikow/.

Szablon Szafir

1. Tłumaczenia

W panelu administracyjnym sklepu dodaj nowe klucze tłumaczeń wymagane przez funkcjonalność szybkich zwrotów w szablonie Szafir. OrderReturnDeadlineInfo - PL: Zamówienie można zwrócić w ciągu {0} dni od daty dostawy. - EN: You can return the order within {0} days from the delivery date. - DE: Sie können die Bestellung innerhalb von {0} Tagen ab Lieferdatum zurückgeben. - FR: Vous pouvez retourner la commande dans les {0} jours suivant la date de livraison. RestrictedOrderViewInfo - PL: Wybrane dane zamówienia zostały ukryte ze względów bezpieczeństwa. Możesz zwrócić produkty bez logowania. - EN: Some order details are hidden for security reasons. You can return products without signing in. - DE: Einige Bestelldetails wurden aus Sicherheitsgründen ausgeblendet. Sie können Produkte ohne Anmeldung zurücksenden. - FR: Certaines informations de la commande ont été masquées pour des raisons de sécurité. Vous pouvez retourner des produits sans vous connecter. RefundMethod - PL: Sposób zwrotu środków - EN: Refund method - DE: Rückerstattungsmethode - FR: Méthode de remboursement RefundAsPaid - PL: Tak jak zapłacono - EN: As paid - DE: Wie bezahlt - FR: Comme payé RefundToBankAccount - PL: Na konto bankowe - EN: To bank account - DE: Auf Bankkonto - FR: Sur le compte bancaire BankAccountNumber - PL: Nr konta bankowego - EN: Bank account number - DE: Bankkontonummer - FR: Numéro de compte bancaire ReturnConfirmationTitle - PL: Gotowe! Przyjęliśmy Twoje zgłoszenie - EN: Done! We have received your request - DE: Fertig! Wir haben Ihre Anfrage erhalten - FR: Terminé! Nous avons reçu votre demande ReturnConfirmationInfo - PL: Przygotuj paczkę i odeślij ją do nas – wszystkie szczegóły znajdziesz poniżej. - EN: Prepare the package and send it back to us – all details can be found below. - DE: Bereiten Sie das Paket vor und senden Sie es an uns zurück – alle Details finden Sie unten. - FR: Préparez le colis et renvoyez-le nous – tous les détails se trouvent ci-dessous. ReturnConfirmationEmailSent - PL: Potwierdzenie zwrotu zostało wysłane na adres: - EN: Return confirmation has been sent to: - DE: Die Rücksendebestätigung wurde gesendet an: - FR: La confirmation de retour a été envoyée à: NextSteps - PL: Następne kroki - EN: Next steps - DE: Nächste Schritte - FR: Prochaines étapes ReturnStep1 - PL: Bezpiecznie zapakuj swoje produkty - EN: Safely pack your products - DE: Verpacken Sie Ihre Produkte sicher - FR: Emballez vos produits en toute sécurité ReturnStep2Address - PL: Wyślij paczkę na adres - EN: Send the package to - DE: Senden Sie das Paket an - FR: Envoyez le colis à ReturnStep3 - PL: Pieniądze zwrócimy niezwłocznie po otrzymaniu i sprawdzeniu Twojej paczki. - EN: We will refund the money promptly after receiving and checking your package. - DE: Wir erstatten das Geld umgehend nach Erhalt und Überprüfung Ihres Pakets. - FR: Nous rembourserons l'argent rapidement après réception et vérification de votre colis. ReturnInThreeSteps - PL: Zwróć towar w 3 krokach - EN: Return the product in 3 steps - DE: Geben Sie die Ware in 3 Schritten zurück - FR: Retournez la marchandise en 3 étapes ReturnWithoutReasonInfo - PL: Przysługuje Ci możliwość zwrotu bez podania przyczyny. - EN: You have the right to return the product without giving a reason. - DE: Sie haben das Recht, die Ware ohne Angabe von Gründen zurückzugeben. - FR: Vous avez le droit de retourner la marchandise sans donner de motif. OrEnterOrderNumber - PL: lub podaj numer zamówienia - EN: or enter the order number - DE: oder geben Sie die Bestellnummer ein - FR: ou saisissez le numéro de commande FillForm - PL: Wypełnij formularz - EN: Fill out the form - DE: Füllen Sie das Formular aus - FR: Remplissez le formulaire ReturnOnline - PL: zwrotu online - EN: online return - DE: Online-Rückgabe - FR: retour en ligne SendParcel - PL: Wyślij paczkę - EN: Send the parcel - DE: Paket senden - FR: Envoyer le colis SendParcelToOurAddress - PL: nadaj przesyłkę na nasz adres - EN: send the parcel to our address - DE: senden Sie das Paket an unsere Adresse - FR: envoyez le colis à notre adresse FastSimpleSecure - PL: Szybko, prosto i bezpiecznie. - EN: Fast, simple and secure. - DE: Schnell, einfach und sicher. - FR: Rapide, simple et sécurisé. ReadyForReturn - PL: Gotowy do zwrotu? - EN: Ready for return? - DE: Bereit zur Rückgabe? - FR: Prêt pour le retour ? ReadyForReturnInfo - PL: Kliknij poniżej, aby rozpocząć proces zwrotu. Przeprowadzimy Cię przez wszystkie kroki. - EN: Click below to start the return process. We will guide you through all the steps. - DE: Klicken Sie unten, um den Rückgabeprozess zu starten. Wir führen Sie durch alle Schritte. - FR: Cliquez ci-dessous pour lancer le processus de retour. Nous vous guiderons à travers toutes les étapes. StartReturn - PL: Rozpocznij zwrot - EN: Start return - DE: Rückgabe starten - FR: Commencer le retour ProductReturn - PL: Zwrot produktu - EN: Product return - DE: Produktrückgabe - FR: Retour du produit ProductReturnFormInfo - PL: Wprowadź dane zamówienia, aby zlokalizować zakup i rozpocząć proces zwrotu. Numer zamówienia znajdziesz w e-mailu z potwierdzeniem. - EN: Enter the order details to locate the purchase and start the return process. You will find the order number in the confirmation e-mail. - DE: Geben Sie die Bestelldaten ein, um den Kauf zu lokalisieren und den Rückgabeprozess zu starten. Die Bestellnummer finden Sie in der Bestätigungs-E-Mail. - FR: Saisissez les données de la commande pour localiser l'achat et démarrer le processus de retour. Vous trouverez le numéro de commande dans l'e-mail de confirmation. InvalidOrderNumber - PL: Błędny numer zamówienia. Spróbuj ponownie. - EN: Invalid order number. Please try again. - DE: Ungültige Bestellnummer. Bitte versuchen Sie es erneut. - FR: Numéro de commande incorrect. Veuillez réessayer. FindOrder - PL: Znajdź zamówienie - EN: Find order - DE: Bestellung finden - FR: Trouver la commande LoginToSeeOrdersAndReturn - PL: aby zobaczyć swoje zamówienia i dokonać zwrotu. - EN: to see your orders and make a return. - DE: um Ihre Bestellungen einzusehen und eine Rückgabe vorzunehmen. - FR: pour voir vos commandes et effectuer un retour. RefundLookupError - PL: Wystąpił błąd podczas wyszukiwania zamówienia. Spróbuj ponownie później. - EN: An error occurred while searching for the order. Please try again later. - DE: Bei der Suche nach der Bestellung ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut. - FR: Une erreur s'est produite lors de la recherche de la commande. Veuillez réessayer plus tard.

2. Nowa strona Szybkie zwroty

Funkcjonalność szybkich zwrotów wymaga utworzenia dodatkowej strony statycznej refunds. Wykonaj poniższe kroki.

Plik refunds.html w katalogu głównym szablonu

W katalogu głównym szablonu utwórz nowy plik refunds.html z następującą zawartością: {% extends '_layout.html' %} {% block content %} {% include 'static-elements/refunds/refunds.html' %} {% endblock %}

Plik static-elements/refunds/refunds.html

W folderze static-elements utwórz nowy katalog refunds, a w nim plik refunds.html: <section class="refunds refunds-js form-js"> <p class="refunds-header refunds-initial-view-js">{{ translations.Returns }}</p> <div class="refunds-intro refunds-initial-view-js"> <div class="refunds-info"> <p class="refunds-info-title">{{ translations.ReturnInThreeSteps }}</p> <p class="refunds-info-desc">{{ translations.ReturnWithoutReasonInfo }}</p> <ol class="refunds-steps"> <li class="refunds-steps-item"> <span class="refunds-steps-number">1</span> <p class="refunds-steps-text"><strong>{{ translations.SignIn }}</strong> {{ translations.OrEnterOrderNumber }}</p> </li> <li class="refunds-steps-item"> <span class="refunds-steps-number">2</span> <p class="refunds-steps-text"><strong>{{ translations.FillForm }}</strong> {{ translations.ReturnOnline }}</p> </li> <li class="refunds-steps-item"> <span class="refunds-steps-number">3</span> <p class="refunds-steps-text"><strong>{{ translations.SendParcel }}</strong> - {{ translations.SendParcelToOurAddress }}</p> </li> </ol> <p class="refunds-info-hint">{{ translations.FastSimpleSecure }}</p> </div> <div class="refunds-cta"> <p class="refunds-cta-title">{{ translations.ReadyForReturn }}</p> <p class="refunds-cta-desc">{{ translations.ReadyForReturnInfo }}</p> {% if usr.Authenticated -%} <a aria-label="{{ translations.StartReturn }}" href="{{ config.DefinedPages.CustomerProfile.Url }}?tab=orders" class="refunds-cta-button" rel="nofollow"> {{ translations.StartReturn }} </a> {% else -%} <button aria-label="{{ translations.StartReturn }}" type="button" class="refunds-cta-button start-refund-js"> {{ translations.StartReturn }} </button> {% endif -%} </div> </div> {% unless usr.Authenticated -%} <div class="refunds-form-section refund-form-view-js hidden-js"> <div class="refunds-form"> <p class="refunds-form-title">{{ translations.ProductReturn }}</p> <p class="refunds-form-desc">{{ translations.ProductReturnFormInfo }}</p> <div class="refund-form-js" data-cp-url="{{ config.DefinedPages.CustomerProfile.Url }}"> <div class="message-bar-ui warning-bar-ui refund-lookup-error-js hidden-js">{{ translations.RefundLookupError }}</div> <div class="form__input-wrapper"> <label for="refundOrderNumber" data-type="text" class="form__input-info form__input-info-js">{{ translations.OrderNumber }} <span class="required-ui">*</span></label> <input id="refundOrderNumber" aria-label="{{ translations.OrderNumber }}" type="text" name="orderNumber" class="form__input-value form__input-value-js" maxlength="50" required /> <span class="error-ui validation-info-js validation-required-js hidden-js">{{ translations.RequiredField }}</span> </div> <div class="form__input-wrapper"> <label for="refundEmail" data-type="email" class="form__input-info form__input-info-js">{{ translations.EmailAddress }} <span class="required-ui">*</span></label> <input id="refundEmail" aria-label="{{ translations.EmailAddress }}" type="email" name="email" class="form__input-value form__input-value-js" maxlength="192" autocomplete="email" required /> <span class="error-ui validation-info-js validation-required-js hidden-js">{{ translations.RequiredField }}</span> <span class="error-ui validation-info-js email-valid-js hidden-js">{{ translations.FillProperEmail }}</span> </div> <div class="required-fields-info-ui form__required-fields-info"><span class="required-ui">* </span>{{ translations.RequiredFields }}</div> <button aria-label="{{ translations.FindOrder }}" type="button" class="refunds-form-submit refund-form-submit-js"> {{ translations.FindOrder }} </button> <p class="refunds-form-login-info"> {{ translations.HaveAccount }} <a href="{{ config.DefinedPages.Login.Url }}" class="refunds-form-login-link" rel="nofollow">{{ translations.SignIn }}</a>, {{ translations.LoginToSeeOrdersAndReturn }} </p> </div> </div> </div> {% endunless -%} </section>

Plik js/layout0.js – kod obsługi formularza zwrotu

Na końcu pliku js/layout0.js dodaj poniższy kod odpowiedzialny za walidację i wysłanie formularza wyszukiwania zamówienia (dla niezalogowanego klienta). Po zapisaniu pliku pamiętaj o zminifikowaniu go do layout0.min.js. const refunds = { findRefundOrder(orderNumber, email) { return $.post('', { __action: 'Order/QuickReturnLookup', orderId: orderNumber, email: email, __csrf: __CSRF }); }, showForm() { document.querySelectorAll('.refunds-initial-view-js').forEach((el) => el.classList.add('hidden-js')); document.querySelectorAll('.refund-form-view-js').forEach((el) => el.classList.remove('hidden-js')); document.querySelector('.refund-form-js input[name="orderNumber"]')?.focus(); }, clearFieldError(input) { input.classList.remove('validation-error-ui', 'validation-error-lq'); input.closest('.form__input-wrapper') ?.querySelectorAll('.error-ui') .forEach((el) => el.classList.add('hidden-js')); }, markFieldError(input, errorSelector) { input.classList.add('validation-error-ui', 'validation-error-lq'); input.closest('.form__input-wrapper')?.querySelector(errorSelector)?.classList.remove('hidden-js'); }, validateForm({ orderInput, emailInput }) { const orderValue = orderInput.value.trim(); const emailValue = emailInput.value.trim(); const emailRegex = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w{2,}$/; let hasError = false; if (orderValue.length === 0) { refunds.markFieldError(orderInput, '.validation-required-js'); hasError = true; } if (emailValue.length === 0) { refunds.markFieldError(emailInput, '.validation-required-js'); hasError = true; } else if (!emailRegex.test(emailValue)) { refunds.markFieldError(emailInput, '.email-valid-js'); hasError = true; } return { hasError, orderValue }; }, async submitForm(button) { const form = button.closest('.refund-form-js'); if (!form) { return; } const orderInput = form.querySelector('input[name="orderNumber"]'); const emailInput = form.querySelector('input[name="email"]'); form.querySelectorAll('.error-ui').forEach((el) => el.classList.add('hidden-js')); form.querySelector('.refund-lookup-error-js')?.classList.add('hidden-js'); form.querySelectorAll('.form__input-value').forEach((el) => { el.classList.remove('validation-error-ui', 'validation-error-lq'); }); const { hasError, orderValue } = refunds.validateForm({ orderInput, emailInput }); if (hasError) { return; } button.disabled = true; const errorBar = form.querySelector('.refund-lookup-error-js'); try { const result = await refunds.findRefundOrder(orderValue, emailInput.value.trim()); const action = result.action; if (action.Redirect302) { globalThis.location.href = action.Redirect302; return; } if (errorBar) { errorBar.textContent = action.Message; errorBar.classList.remove('hidden-js'); } } catch (error) { console.error('Refund order lookup failed.', error); errorBar?.classList.remove('hidden-js'); } finally { button.disabled = false; } } }; js.delegate(document.body, 'click', '.start-refund-js', () => refunds.showForm()); js.delegate(document.body, 'input', '.refund-form-js .form__input-value', function () { refunds.clearFieldError(this); }); js.delegate(document.body, 'click', '.refund-form-submit-js', function (event) { event.preventDefault(); refunds.submitForm(this); }); js.delegate(document.body, 'keydown', '.refund-form-js input', function (event) { if (event.key !== 'Enter') { return; } event.preventDefault(); this.closest('.refund-form-js')?.querySelector('.refund-form-submit-js')?.click(); });

Pliki SCSS strony Szybkie zwroty

W folderze scss/static-elements utwórz nowy katalog refunds i umieść w nim dwa pliki: refunds-g.scss (style desktop) oraz refunds-m.scss (style mobilne). scss/static-elements/refunds/refunds-g.scss: .refunds { padding: 40px 110px; background-color: $bgColor; color: $primaryColorFont; .refunds-header { display: block; width: 100%; margin: 0 0 24px; color: $primaryColorFont; text-align: left; font-size: 32px; font-weight: 500; line-height: 42px; letter-spacing: 0.8px; } .refunds-intro { display: flex; flex-direction: column; align-items: center; gap: 32px; margin-bottom: 40px; } .refunds-info { align-self: flex-start; width: 100%; max-width: 800px; display: flex; flex-direction: column; gap: 16px; color: $primaryColorFont; @media screen and (min-width: 769px) and (max-width: 1279px) { max-width: 100%; } } .refunds-info-title { margin: 0; font-size: 20px; font-weight: 500; line-height: 28px; letter-spacing: 0.4px; } .refunds-info-desc { margin: 0; font-size: 16px; font-weight: 400; line-height: 24px; letter-spacing: 0.3px; } .refunds-info-hint { margin: 4px 0 0; color: $secondaryColorFont; font-size: 12px; font-weight: 400; line-height: 20px; letter-spacing: 0.2px; } .refunds-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; } .refunds-steps-item { display: flex; align-items: center; gap: 12px; } .refunds-steps-number { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background-color: $formsBgColor; color: $primaryColor; font-size: 14px; font-weight: 600; line-height: 20px; } .refunds-steps-text { margin: 0; font-size: 14px; font-weight: 400; line-height: 22px; letter-spacing: 0.3px; strong { font-weight: 600; } } .refunds-cta { box-sizing: border-box; width: 100%; max-width: 578px; padding: 32px 40px; display: flex; flex-direction: column; align-items: center; gap: 12px; background-color: $bgColor; border: 1px solid $lightBorderColor; } .refunds-cta-title { margin: 0; text-align: center; font-size: 22px; font-weight: 500; line-height: 30px; letter-spacing: 0.5px; color: $primaryColorFont; } .refunds-cta-desc { margin: 0; text-align: center; color: $secondaryColorFont; font-size: 14px; font-weight: 400; line-height: 22px; letter-spacing: 0.3px; } .refunds-cta-button, .refunds-form-submit { display: inline-block; width: 100%; margin-top: 8px; padding: 12px 16px; background: $btnSolidBgColor; color: $btnSolidTextColor; border: 1px solid $btnSolidBgColor; font-size: 14px; font-weight: 500; line-height: 20px; text-align: center; text-decoration: none; cursor: pointer; transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease; &:hover, &:focus { background: $btnSolidHoverBgColor; color: $btnSolidHoverTextColor; border-color: $btnSolidHoverBgColor; } &[disabled], &[disabled]:hover { background: $btnDisabledBg; border-color: $btnDisabledFont; color: $btnDisabledFont; cursor: default; } } .refunds-form-section { display: flex; justify-content: center; width: 100%; margin-top: 32px; margin-bottom: 40px; } .refunds-form { box-sizing: border-box; width: 100%; max-width: 578px; padding: 32px 40px; background-color: $bgColor; border: 1px solid $lightBorderColor; } .refunds-form-title { margin: 0 0 8px; font-size: 22px; font-weight: 500; line-height: 30px; letter-spacing: 0.5px; color: $primaryColorFont; } .refunds-form-desc { margin: 0 0 20px; color: $secondaryColorFont; font-size: 14px; font-weight: 400; line-height: 22px; letter-spacing: 0.3px; } .refund-form-js { display: flex; flex-direction: column; .form__input-wrapper { position: relative; margin-bottom: 16px; .form__input-info { display: block; margin: 0 0 4px; color: $primaryColorFont; font-size: 13px; font-weight: 400; line-height: 18px; } input { width: 100%; height: 36px; margin: 0; padding: 0 10px; box-sizing: border-box; border: 1px solid $lightBorderColor; font-size: 14px; &::placeholder { font-size: 14px; } &.validation-error-ui, &.validation-error-lq { border-color: $dangerColor; color: $dangerColor; } } .error-ui { position: static; display: block; margin: 4px 0 0; padding: 0; text-align: left; color: $dangerColor; font-size: 12px; font-weight: 400; line-height: 16px; } } .form__required-fields-info { margin: 4px 0 16px; color: $primaryColorFont; font-size: 12px; font-weight: 300; line-height: 18px; } } .refunds-form-login-info { margin: 12px 0 0; text-align: center; color: $primaryColorFont; font-size: 14px; font-weight: 400; line-height: 22px; } .refunds-form-login-link { color: $primaryColor; font-weight: 600; cursor: pointer; text-decoration: none; &:hover { text-decoration: underline; } } } scss/static-elements/refunds/refunds-m.scss: @media screen and (max-width: 768px) { .refunds { padding: 20px 16px; .refunds-header { font-size: 24px; line-height: 32px; margin-bottom: 16px; } .refunds-intro { gap: 24px; margin-bottom: 24px; } .refunds-info { max-width: 100%; gap: 12px; } .refunds-info-title { font-size: 18px; line-height: 24px; } .refunds-info-desc { font-size: 14px; line-height: 22px; } .refunds-steps-item { align-items: flex-start; gap: 10px; } .refunds-steps-number { width: 28px; height: 28px; font-size: 13px; } .refunds-steps-text { font-size: 13px; line-height: 20px; padding-top: 3px; } .refunds-cta { padding: 24px; } .refunds-cta-title { font-size: 20px; line-height: 28px; } .refunds-form-section { margin-top: 20px; margin-bottom: 24px; } .refunds-form { padding: 24px; border: none; } .refunds-form-title { font-size: 20px; line-height: 28px; } .refunds-form-desc { margin-bottom: 16px; } .refunds-form-login-info { font-size: 13px; } } }

3. Modyfikacje istniejących plików HTML

partials/customer-profile/orders/order-details.html

W pliku partials/customer-profile/orders/order-details.html wykonaj poniższe zmiany. a) Blok z przyciskami akcji nawigacji zamówienia (zawierający {% if order.EditingAllowed -%}, opcje anulowania, edycji, ponowienia, druku itp.) opakuj warunkiem ukrywającym go dla zamówień typu restricted. Bezpośrednio po linii z elementem <li class="active-ui only-text-ui ...">...</li> wstaw: {% unless order.Restricted -%} a tuż przed zamykającym znacznikiem </ul> tej samej nawigacji dodaj: {% endunless -%} b) Na początku kontenera messages-container-lq messages-container-ui (zaraz po jego otwarciu) dodaj baner informujący o ukrytych danych zamówienia: {% if order.Restricted -%} <div class="message-bar-ui no-margin-ui"> <i class="not-clickable-ui ti-info-alt"></i> {{ translations.RestrictedOrderViewInfo }} </div> {% endif -%} c) Na końcu tego samego kontenera (przed zamykającym </div>) dodaj baner informujący o terminie zwrotu wraz z przypisaniem flagi complaintsOrReturnsEnabled: {% if config.Complaints.ComplaintsEnabled or config.Complaints.ReturnsEnabled -%} {% assign complaintsOrReturnsEnabled = true -%} {% endif -%} {% if order.Status == 3 and complaintsOrReturnsEnabled -%} <div class="message-bar-ui no-margin-ui"> <i class="not-clickable-ui ti-info-alt"></i> {{translations.OrderReturnDeadlineInfo | Format: config.Complaints.DaysToReturn}} </div> {% endif -%} d) Blok z przyciskami akcji wewnątrz info-container-js (zawierający przyciski order.CanCancel, edycji, ponowienia zakupu itd.) opakuj warunkiem {% unless order.Restricted -%} ... {% endunless -%}. Wstaw {% unless order.Restricted -%} bezpośrednio przed pierwszym {% if order.CanCancel -%}, a {% endunless -%} po zamykającym warunku odpowiedzialnym za popup potwierdzenia ({% include 'partials/common/confirmation-popup.html' with 0 -%}). e) W sekcji wyświetlającej dokumenty zamówienia zamień: {% if order.Documents %} na: {% if order.Documents and order.Restricted == false %} f) We wszystkich miejscach wyświetlających numer telefonu klienta zamień warunki sprawdzające jedynie pusty ciąg na warunki sprawdzające również wartość null. Zamień każde wystąpienie: {% if order.Customer.DeliveryAddress.PhoneNo != '' -%} na: {% if order.Customer.DeliveryAddress.PhoneNo != null and order.Customer.DeliveryAddress.PhoneNo != '' -%} g) Każdą sekcję z tytułem translations.ChoosenDocument opakuj warunkiem ukrywającym ją dla zamówień restricted. Wstaw {% unless order.Restricted -%} bezpośrednio przed <div class="name-ui mt20-ui">{{ translations.ChoosenDocument }}</div> oraz {% endunless -%} po jej zamykającym </div>. h) W sekcji wyświetlającej alternatywny adres dostawy zamień warunek {% else -%} (bezpośrednio przed order-info-item-ui z translations.DelivAddress) na bardziej restrykcyjny: {% elseif order.Customer.DeliveryAddress != null -%} i) W sekcji danych do faktury zamień warunek wyświetlania NIP-u/TIN-u. Zamień: {% elseif order.Customer.TIN != '' -%} na: {% elseif order.Customer.TIN != null and order.Customer.TIN != '' -%} j) W sekcji załączników zamówienia (kontener attachements-ui remarks-ui) zamień warunek otaczający. Zamień: {% if config.Orders.AttachmentsEnabled -%} na: {% if config.Orders.AttachmentsEnabled and order.Restricted == false -%} k) W sekcji zamówień powiązanych (kontener remarks-ui eshop-order-ui) zamień: {% if order.RelatedOrders -%} na: {% if order.RelatedOrders and order.Restricted == false -%}

partials/customer-profile/complaints/complaint-form.html

W pliku partials/customer-profile/complaints/complaint-form.html wprowadź poniższe zmiany. a) W kontenerze amount-stepper-container-ui amount-stepper-container-lq (wewnątrz warunku {% if type != 'set-return' -%}) na samym początku dodaj etykietę widoczną tylko dla zwrotów: {% unless complaint -%} <span class="amount-stepper-label-ui">{{ translations.Quantity }}:</span> {% endunless -%} b) Bezpośrednio po zamknięciu kontenera amount-stepper-container-ui (po jego </div> i odpowiadającym {% endif -%}) dodaj sekcję wyboru sposobu zwrotu środków – widoczną tylko w formularzu zwrotu: {% unless complaint -%} <div class="refund-method-container-ui refund-method-container-js"> <label for="refundMethod" class="label-ui">{{ translations.RefundMethod }} <span class="required-ui">*</span></label> <div> <span class="select-background-ui"> <select id="refundMethod" aria-label="{{ translations.RefundMethod }}" name="refundMethod" class="refund-method-select-js"> <option value="original" selected="selected">{{ translations.RefundAsPaid }} {% if order.Payment.Name and order.Payment.Name != "" -%} ({{ order.Payment.Name }}){% endif -%}</option> <option value="bank">{{ translations.RefundToBankAccount }}</option> </select> </span> <i class="ti-angle-down select-arrow-ui"></i> </div> <div class="bank-account-container-ui bank-account-container-js hidden-js"> <label for="bankAccountNumber" class="label-ui">{{ translations.BankAccountNumber }} <span class="required-ui">*</span></label> <div> <input id="bankAccountNumber" aria-label="{{ translations.BankAccountNumber }}" type="text" name="bankAccountNumber" placeholder="{{ translations.BankAccountNumber }}" disabled /> <span class="error-ui validation-info-js validation-required-js hidden-js">{{ translations.RequiredField }}</span> </div> </div> </div> {% endunless -%} c) Komunikat sukcesu wyświetlany po wysłaniu formularza ma być inny dla reklamacji, a inny dla zwrotu. Znajdź kontener <div class="success-message-js hidden-js"> wraz z jego dotychczasową zawartością (komunikat ComplaintAdded + successInfo): <div class="success-message-js hidden-js"> <div class="complaint-added-ui"> <i class="va-mid-ui ti-check"></i> <span class="va-mid-ui line-height-1-ui"> {{ translations.ComplaintAdded }} </span> </div> <div class="info-ui"> {{ successInfo }} </div> </div> i zastąp go nową, rozszerzoną wersją z dwoma wariantami (reklamacja / zwrot): <div class="success-message-js hidden-js"> {% if complaint -%} <div class="complaint-added-ui"> <i class="va-mid-ui ti-check"></i> <span class="va-mid-ui line-height-1-ui"> {{ translations.ComplaintAdded }} </span> </div> <div class="info-ui"> {{ successInfo }} </div> {% else -%} <div class="return-success-popup-ui"> <div class="return-success-icon-ui"> <i class="ti-check"></i> </div> <div class="return-success-title-ui">{{ translations.ReturnConfirmationTitle }}</div> <div class="return-success-desc-ui">{{ translations.ReturnConfirmationInfo }}</div> {% if order.Customer.Email and order.Customer.Email != "" -%} <div class="return-success-email-info-ui"> <i class="ti-info-alt"></i> <span>{{ translations.ReturnConfirmationEmailSent }} <strong>{{ order.Customer.Email }}</strong></span> </div> {% endif -%} <div class="return-success-steps-title-ui">{{ translations.NextSteps }}</div> <ol class="return-success-steps-ui"> <li>{{ translations.ReturnStep1 }}</li> <li>{{ translations.ReturnStep2Address }}: {{ config.Shop.Address.Street }} {{ config.Shop.Address.StreetNo }}{% if config.Shop.Address.UnitNo and config.Shop.Address.UnitNo != "" -%}/{{ config.Shop.Address.UnitNo }}{% endif -%}, {{ config.Shop.Address.ZipCode }} {{ config.Shop.Address.City }}</li> <li>{{ translations.ReturnStep3 }}</li> </ol> <div class="return-success-actions-ui"> <button aria-label="{{ translations.Close }}" class="btn-ui return-success-close-ui hide-container-js closing-modal-js"> {{ translations.Close }} </button> </div> </div> {% endif -%} </div>

4. Modyfikacje pliku js/init-ui2.js

Poniższe zmiany dotyczą pliku js/init-ui2.js (po edycji pamiętaj o zminifikowaniu go do init-ui2.min.js). a) Tuż po linii rejestrującej handler openComplaintForm: js.delegate(document.body, 'click', '.open-complaint-form-js', openComplaintForm); dodaj poniższy handler obsługujący przełączanie sposobu zwrotu środków – pokazuje on / ukrywa pole na numer konta bankowego: js.delegate(document.body, 'change', '.refund-method-select-js', function() { const container = js.parents(this, 'refund-method-container-js'); if(!container) return; const bankWrapper = container.getElementsByClassName('bank-account-container-js')[0]; if(!bankWrapper) return; const bankInput = bankWrapper.querySelector('input[name="bankAccountNumber"]'); const validationMsg = bankWrapper.getElementsByClassName('validation-required-js')[0]; if(this.value === 'bank') { bankWrapper.classList.remove('hidden-js'); if(bankInput) { bankInput.disabled = false; bankInput.required = true; } } else { bankWrapper.classList.add('hidden-js'); if(bankInput) { bankInput.disabled = true; bankInput.required = false; bankInput.value = ''; bankInput.classList.remove('validation-error-lq', 'validation-error-ui'); } if(validationMsg) validationMsg.classList.add('hidden-js'); } }); b) W funkcji sendComplaint, bezpośrednio po pętli for (var i=0; i<dataFromHTML.length; i++) dokładającej pola do fd, dodaj fragment dołączający parametr qrHash z adresu URL przy wysyłaniu zwrotu: if (form.find('input[name="__action"]').val() === 'Order/ReturnAdd') { var qrHash = new URLSearchParams(globalThis.location.search).get('qrHash'); if (qrHash) { fd.append('qrHash', qrHash); } }

5. Modyfikacje pliku scss/globals/_globals2.scss

W pliku scss/globals/_globals2.scss wewnątrz selektora opakowującego (tego, który zawiera m.in. .note-ui) dodaj poniższe reguły. Po zmianach pamiętaj o kompilacji SCSS – nie edytuj ręcznie wynikowych plików CSS. a) Style etykiety ilości oraz kontenera wyboru sposobu zwrotu środków (wstaw w sąsiedztwie istniejących reguł sekcji formularza reklamacji/zwrotu): .amount-stepper-label-ui { margin-right: 10px; vertical-align: middle; } .refund-method-container-ui { margin-bottom: 20px; .bank-account-container-ui { margin-top: 10px; input { width: 100%; } } } b) Style ekranu sukcesu po wysłaniu zwrotu (ikona, tytuł, lista kolejnych kroków, sekcja z e-mailem i przyciskiem zamknięcia): .return-success-popup-ui { padding: 30px 20px 20px; .return-success-icon-ui { width: 60px; height: 60px; border-radius: 50%; border: 2px solid #68a204; margin: 0 auto 20px; display: flex; align-items: center; justify-content: center; i { color: #68a204; font-size: 26px; } } .return-success-title-ui { font-size: 18px; font-weight: 500; text-align: center; margin-bottom: 10px; } .return-success-desc-ui { font-size: 14px; color: $primaryColorFont; text-align: center; margin-bottom: 20px; } .return-success-email-info-ui { background-color: $bgColor; border: 1px solid $lightBorderColor; padding: 12px; border-radius: 4px; font-size: 14px; margin-bottom: 20px; display: flex; align-items: flex-start; i { margin-right: 10px; color: $primaryColorFont; font-size: 16px; line-height: 1.4; } strong { display: block; } } .return-success-steps-title-ui { font-weight: 500; margin-bottom: 10px; } .return-success-steps-ui { padding-left: 20px; margin: 0 0 20px; font-size: 14px; li { margin-bottom: 8px; padding-left: 5px; } } .return-success-actions-ui { border-top: 1px solid $lightBorderColor; padding-top: 15px; text-align: right; .return-success-close-ui { padding: 10px 30px; } } } Po wprowadzeniu wszystkich powyższych zmian w szablonie Szafir pamiętaj o kompilacji oraz minifikacji plików js i scss, zgodnie z artykułem dostępnym pod linkiem https://pomoc.comarchesklep.pl/artykul/kompilacja-i-minifikacja-plikow/.

Szablon Topaz / One Page Shop

Zmiany opisane poniżej dotyczą szablonu Topaz. Szablon One Page Shop ma praktycznie identyczną strukturę – wszystkie modyfikacje dotyczą tych samych plików i mają tę samą zawartość. Wystarczy zastosować poniższą instrukcję w plikach szablonu OPS pod tymi samymi ścieżkami.

1. Tłumaczenia

W panelu administracyjnym sklepu dodaj nowe klucze tłumaczeń wymagane przez funkcjonalność szybkich zwrotów w szablonie Topaz / OPS. RestrictedOrderViewInfo - PL: Wybrane dane zamówienia zostały ukryte ze względów bezpieczeństwa. Możesz zwrócić produkty bez logowania. - EN: Some order details are hidden for security reasons. You can return products without signing in. - DE: Einige Bestelldetails wurden aus Sicherheitsgründen ausgeblendet. Sie können Produkte ohne Anmeldung zurücksenden. - FR: Certaines informations de la commande ont été masquées pour des raisons de sécurité. Vous pouvez retourner des produits sans vous connecter. Prf_ReturnPeriodInfo - PL: Zamówienie można zwrócić w ciągu {0} dni od daty dostawy. - EN: The order can be returned within {0} days from the delivery date. - DE: Die Bestellung kann innerhalb von {0} Tagen ab dem Lieferdatum zurückgegeben werden. - FR: La commande peut être retournée dans un délai de {0} jours à compter de la date de livraison. ComplaintSuccessTitle - PL: Gotowe! Przyjęliśmy Twoje zgłoszenie - EN: Done! We have received your request - DE: Fertig! Wir haben Ihre Meldung erhalten - FR: C'est fait ! Nous avons reçu votre demande ComplaintSuccessDescription - PL: Przygotuj paczkę i odeślij ją do nas – wszystkie szczegóły znajdziesz poniżej. - EN: Prepare the parcel and send it back to us – you will find all the details below. - DE: Bereiten Sie das Paket vor und senden Sie es an uns zurück – alle Details finden Sie unten. - FR: Préparez le colis et renvoyez-le-nous – vous trouverez tous les détails ci-dessous. ComplaintSuccessConfirmationSent - PL: Potwierdzenie zwrotu zostało wysłane na adres: - EN: The return confirmation has been sent to: - DE: Die Rückgabebestätigung wurde an folgende Adresse gesendet: - FR: La confirmation de retour a été envoyée à : ComplaintSuccessStepsTitle - PL: Następne kroki - EN: Next steps - DE: Nächste Schritte - FR: Étapes suivantes ComplaintSuccessStep1 - PL: Bezpiecznie zapakuj swoje produkty - EN: Pack your products securely - DE: Verpacken Sie Ihre Produkte sicher - FR: Emballez vos produits en toute sécurité ComplaintSuccessStep2 - PL: Wyślij paczkę na adres: - EN: Send the parcel to: - DE: Senden Sie das Paket an folgende Adresse: - FR: Envoyez le colis à l'adresse : ComplaintSuccessStep3 - PL: Pieniądze zwrócimy niezwłocznie po otrzymaniu i sprawdzeniu Twojej paczki. - EN: We will refund the money immediately after receiving and inspecting your parcel. - DE: Wir erstatten den Betrag unverzüglich nach Erhalt und Prüfung Ihres Pakets. - FR: Nous rembourserons l'argent immédiatement après réception et vérification de votre colis. ReturnInThreeSteps - PL: Zwróć towar w 3 krokach - EN: Return the product in 3 steps - DE: Geben Sie die Ware in 3 Schritten zurück - FR: Retournez la marchandise en 3 étapes ReturnWithoutReasonInfo - PL: Przysługuje Ci możliwość zwrotu bez podania przyczyny. - EN: You have the right to return the product without giving a reason. - DE: Sie haben das Recht, die Ware ohne Angabe von Gründen zurückzugeben. - FR: Vous avez le droit de retourner la marchandise sans donner de motif. OrEnterOrderNumber - PL: lub podaj numer zamówienia - EN: or enter the order number - DE: oder geben Sie die Bestellnummer ein - FR: ou entrez le numéro de commande FillForm - PL: Wypełnij formularz - EN: Fill out the form - DE: Füllen Sie das Formular aus - FR: Remplissez le formulaire ReturnOnline - PL: zwrotu online - EN: online return - DE: Online-Rückgabe - FR: retour en ligne SendParcel - PL: Wyślij paczkę - EN: Send the parcel - DE: Senden Sie das Paket - FR: Envoyez le colis SendParcelToOurAddress - PL: nadaj przesyłkę na nasz adres - EN: send the parcel to our address - DE: senden Sie das Paket an unsere Adresse - FR: envoyez le colis à notre adresse FastSimpleSecure - PL: Szybko, prosto i bezpiecznie. - EN: Fast, simple and secure. - DE: Schnell, einfach und sicher. - FR: Rapide, simple et sûr. ReadyForReturn - PL: Gotowy do zwrotu? - EN: Ready for return? - DE: Bereit zur Rückgabe? - FR: Prêt pour le retour ? ReadyForReturnInfo - PL: Kliknij poniżej, aby rozpocząć proces zwrotu. Przeprowadzimy Cię przez wszystkie kroki. - EN: Click below to start the return process. We will guide you through all the steps. - DE: Klicken Sie unten, um den Rückgabeprozess zu starten. Wir führen Sie durch alle Schritte. - FR: Cliquez ci-dessous pour commencer le processus de retour. Nous vous guiderons à chaque étape. StartReturn - PL: Rozpocznij zwrot - EN: Start return - DE: Rückgabe starten - FR: Commencer le retour ProductReturn - PL: Zwrot produktu - EN: Product return - DE: Produktrückgabe - FR: Retour du produit ProductReturnFormInfo - PL: Wprowadź dane zamówienia, aby zlokalizować zakup i rozpocząć proces zwrotu. Numer zamówienia znajdziesz w e-mailu z potwierdzeniem. - EN: Enter the order details to locate the purchase and start the return process. You will find the order number in the confirmation e-mail. - DE: Geben Sie die Bestelldaten ein, um den Kauf zu lokalisieren und den Rückgabeprozess zu starten. Die Bestellnummer finden Sie in der Bestätigungs-E-Mail. - FR: Saisissez les données de la commande pour localiser l'achat et démarrer le processus de retour. Vous trouverez le numéro de commande dans l'e-mail de confirmation. FindOrder - PL: Znajdź zamówienie - EN: Find order - DE: Bestellung finden - FR: Trouver la commande RefundLookupError - PL: Wystąpił błąd podczas wyszukiwania zamówienia. Spróbuj ponownie później. - EN: An error occurred while searching for the order. Please try again later. - DE: Fehler beim Suchen nach der Bestellung. Versuchen Sie es später erneut. - FR: Une erreur s'est produite lors de la recherche de la commande. Veuillez réessayer plus tard. LoginToSeeOrdersAndReturn - PL: aby zobaczyć swoje zamówienia i dokonać zwrotu. - EN: to see your orders and make a return. - DE: um Ihre Bestellungen zu sehen und eine Rückgabe zu machen. - FR: pour voir vos commandes et faire un retour. InvalidEmail - PL: Nieprawidłowy adres e-mail - EN: Invalid e-mail address - DE: Ungültige E-Mail-Adresse - FR: L'adresse e-mail est incorrecte. RefundMethod - PL: Sposób zwrotu środków - EN: Refund method - DE: Rückerstattungsmethode - FR: Mode de remboursement RefundAsPaid - PL: Zwrot na sposób płatności - EN: Refund to the original payment method - DE: Rückerstattung auf die ursprüngliche Zahlungsmethode - FR: Remboursement sur le mode de paiement initial RefundToBankAccount - PL: Zwrot na konto bankowe - EN: Refund to bank account - DE: Rückerstattung auf das Bankkonto - FR: Remboursement sur un compte bancaire

2. Nowa strona Szybkie zwroty

Funkcjonalność szybkich zwrotów wymaga utworzenia dodatkowej strony statycznej refunds. Wykonaj poniższe kroki.

Plik refunds.html w katalogu głównym szablonu

W katalogu głównym szablonu utwórz nowy plik refunds.html z następującą zawartością: {% extends '_layout.html' -%} {% block pageContent -%} {% include 'static-elements/refunds/refunds.html' -%} {% endblock -%}

Plik static-elements/refunds/refunds.html

W folderze static-elements utwórz nowy katalog refunds, a w nim plik refunds.html: {% include 'partials/common/breadcrumbs.html' -%} <section class="refunds-container refunds-container-js page-padding"> <div class="refunds-view refunds-view--intro refunds-initial-view-js"> <div class="refunds-intro"> <h2 class="refunds-intro__heading">{{ translations.ReturnInThreeSteps }}</h2> <p class="refunds-intro__lead">{{ translations.ReturnWithoutReasonInfo }}</p> <ol class="refunds-steps"> <li class="refunds-steps__item"> <span class="refunds-steps__text"> <strong>{{ translations.Log_Login }}</strong> {{ translations.OrEnterOrderNumber }} </span> </li> <li class="refunds-steps__item"> <span class="refunds-steps__text"> <strong>{{ translations.FillForm }}</strong> {{ translations.ReturnOnline }} </span> </li> <li class="refunds-steps__item"> <span class="refunds-steps__text"> <strong>{{ translations.SendParcel }}</strong> &ndash; {{ translations.SendParcelToOurAddress }} </span> </li> </ol> <p class="refunds-intro__hint">{{ translations.FastSimpleSecure }}</p> </div> <div class="refunds-card"> <h3 class="refunds-card__heading">{{ translations.ReadyForReturn }}</h3> <p class="refunds-card__text">{{ translations.ReadyForReturnInfo }}</p> {% if customer.Authenticated -%} <a class="primary-action-button refunds-card__btn" href="{{ config.DefinedPages.CustomerProfile.Url }}?tab=orders-active"> {{ translations.StartReturn }} </a> {% else -%} <button type="button" class="primary-action-button refunds-card__btn start-refund-js"> {{ translations.StartReturn }} </button> {% endif -%} </div> </div> <div class="refunds-view refunds-view--lookup refund-form-view-js hidden-js"> <div class="refunds-lookup"> <h2 class="refunds-lookup__heading">{{ translations.ProductReturn }}</h2> <p class="refunds-lookup__lead">{{ translations.ProductReturnFormInfo }}</p> <div class="form inputs-container-js refund-form-js" data-error="{{ translations.RefundLookupError }}"> <div class="form__input-wrapper"> <input id="refunds-orderNumber" class="form__input-value form__input-value-js" type="text" name="orderNumber" autocomplete="off" required /> <label for="refunds-orderNumber" class="form__input-info form__input-info-js">{{ translations.Com_OrderNumber }}</label> <p class="form__invalid-input form__validation-required-js" style="display: none">{{ translations.Com_FieldIsMandatory }}</p> </div> <div class="form__input-wrapper"> <input id="refunds-email" class="form__input-value form__input-value-js" type="email" name="email" autocomplete="email" required /> <label for="refunds-email" data-type="email" class="form__input-info form__input-info-js">{{ translations.EmailAddress }}</label> <p class="form__invalid-input form__validation-required-js" style="display: none">{{ translations.Com_FieldIsMandatory }}</p> <p class="form__invalid-input form__validation-info-js" style="display: none">{{ translations.InvalidEmail }}</p> </div> <button type="button" class="primary-action-button refund-form-submit-js"> {{ translations.FindOrder }} </button> {% if customer.Authenticated == false -%} <p class="refunds-lookup__loginPrompt"> {{ translations.Reg_HaveAcc }} <button type="button" class="refunds-lookup__loginLink refunds-login-trigger-js"> {{ translations.Log_Login }} </button> {{ translations.LoginToSeeOrdersAndReturn }} </p> {% endif -%} </div> </div> </div> </section> {% if customer.Authenticated == false -%} <div class="popup-dialog popup-dialog-js popup-dialog-js--loginRegister loginRegister loginRegister--login-only"> {% include 'partials/common/login-register-popup.html' -%} </div> {% endif -%} {% if settings.newsletterOnFunctionalPages == 'yes' -%} {% include 'partials/newsletter-global.html' -%} {% endif -%}

Plik js/layout1.js – kod obsługi formularza zwrotu

Na końcu pliku js/layout1.js dodaj poniższy kod odpowiedzialny za walidację i wysłanie formularza wyszukiwania zamówienia (dla niezalogowanego klienta). Po zapisaniu pamiętaj o zminifikowaniu pliku do layout1.min.js. $(document).ready(function() { refunds.init(); }); const refunds = { init: function() { this.events(); }, findRefundOrder: function(orderNumber, email) { return $.post('', { __action: 'Order/QuickReturnLookup', orderId: orderNumber, email: email, __csrf: __CSRF }); }, showForm: function() { $('.refunds-initial-view-js').addClass('hidden-js'); $('.refund-form-view-js').removeClass('hidden-js'); $('.refund-form-js input[name="orderNumber"]').trigger('focus'); }, clearFieldError: function(input) { const $input = $(input); const $wrapper = $input.closest('.form__input-wrapper'); $input.removeClass('form__validation-error-js'); $wrapper.removeClass('form__validation-error-js'); $wrapper.find('.form__invalid-input').hide(); }, submitForm: async function(button) { const $submit = $(button); const $form = $submit.closest('.refund-form-js'); const $orderInput = $form.find('input[name="orderNumber"]'); const $emailInput = $form.find('input[name="email"]'); const orderValue = $orderInput.val().trim(); const emailValue = $emailInput.val().trim(); const emailRegex = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w{2,}$/; let hasError = false; $form.find('.form__invalid-input').hide(); $form.find('.form__input-value').removeClass('form__validation-error-js'); $form.find('.form__input-wrapper').removeClass('form__validation-error-js'); if (orderValue.length === 0) { $orderInput.addClass('form__validation-error-js'); $orderInput.closest('.form__input-wrapper').find('.form__validation-required-js').show(); hasError = true; } if (emailValue.length === 0) { $emailInput.addClass('form__validation-error-js'); $emailInput.closest('.form__input-wrapper').find('.form__validation-required-js').show(); hasError = true; } else if (emailRegex.test(emailValue)) { $emailInput.removeClass('form__validation-error-js'); } else { $emailInput.addClass('form__validation-error-js'); $emailInput.closest('.form__input-wrapper').find('.form__validation-info-js').show(); hasError = true; } if (hasError) { return; } $submit.prop('disabled', true); try { const result = await refunds.findRefundOrder(orderValue, emailValue); const action = result.action; if (action.Redirect302) { globalThis.location.href = action.Redirect302; return; } app.showTemporaryPopup(action.Message, 'error', '', 8000); } catch (error) { console.error('Refund order lookup failed.', error); app.showTemporaryPopup($form.data('error'), 'error'); } finally { $submit.prop('disabled', false); } }, events: function() { $('body').on('click', '.start-refund-js', function() { refunds.showForm(); }); $('body').on('click', '.refunds-login-trigger-js', function(e) { const $popup = $('.popup-dialog-js--loginRegister'); if ($popup.find('.reminder-container:visible').length > 0) { $popup.find('.backButton-js').click(); } const loginTabText = $popup.find('.loginRegisterTab-js').first().text(); $popup.find('.loginRegisterHeader').text(loginTabText); globalThis.location.hash = '#login-redirect=' + __CustomerProfileUrl + '?tab=orders-active'; $('body').addClass('modal-opened'); $popup.show(); $popup.find('.login-register-close-button').removeClass('hidden'); if (typeof app !== 'undefined' && typeof app.activateFocusTrap === 'function') { app.activateFocusTrap('.popup-dialog-js--loginRegister', e.currentTarget); } if (typeof ui !== 'undefined' && typeof ui.formLabelAnimationOnCartLoginPage === 'function') { ui.formLabelAnimationOnCartLoginPage(); } }); $('body').on('click', '.login-register-close-button', function() { $('.popup-dialog-js--loginRegister').hide(); $('body').removeClass('modal-opened'); history.replaceState(null, '', globalThis.location.pathname + globalThis.location.search); }); $('body').on('input', '.refund-form-js .form__input-value', function() { refunds.clearFieldError(this); }); $('body').on('click', '.refund-form-submit-js', function(e) { e.preventDefault(); refunds.submitForm(this); }); $('body').on('keydown', '.refund-form-js input', function(e) { if (e.key === 'Enter') { e.preventDefault(); $(this).closest('.refund-form-js').find('.refund-form-submit-js').trigger('click'); } }); } };

Pliki SCSS strony Szybkie zwroty

W folderze scss/static-elements utwórz nowy katalog refunds, a w nim trzy pliki: refunds-g.scss (style ogólne), refunds-d.scss (desktop) oraz refunds-m.scss (mobile). scss/static-elements/refunds/refunds-g.scss: .refunds-container { box-sizing: border-box; margin: 0 auto; color: $primaryFontColor; background-color: $primaryPageBg; .refunds-view { display: flex; flex-direction: column; align-items: center; &.hidden-js { display: none; } } .refunds-intro { width: 100%; align-self: stretch; &__heading { margin: 0 0 16px; font-size: $mediumFontSize; font-weight: 600; line-height: 1.4; color: $primaryFontColor; } &__lead { margin: 0 0 16px; font-size: 16px; font-weight: 500; line-height: 1.5; color: $primaryFontColor; } &__hint { margin: 16px 0 0; font-size: $extraSmallFontSize; color: $secondaryFontColor; } } .refunds-steps { list-style: none; counter-reset: refund-step; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; &__item { counter-increment: refund-step; display: flex; align-items: center; gap: 12px; &::before { content: counter(refund-step); flex: 0 0 auto; width: 32px; height: 32px; border-radius: 50%; background-color: $primaryBtnBg; color: $primaryBtnFontColor; font-weight: 600; font-size: $smallFontSize; line-height: 1; display: inline-flex; align-items: center; justify-content: center; } } &__text { font-size: $smallFontSize; line-height: 1.5; color: $primaryFontColor; strong { font-weight: 600; } } } .refunds-card, .refunds-lookup { box-sizing: border-box; width: 100%; background-color: $primaryPageBg; border: 1px solid $borderColor; border-radius: 8px; display: flex; flex-direction: column; margin-left: auto; margin-right: auto; } .refunds-card { align-items: center; text-align: center; gap: 12px; &__heading { margin: 0; font-size: $bigFontSize; font-weight: 600; line-height: 1.3; color: $primaryFontColor; } &__text { margin: 0; font-size: 16px; line-height: 1.5; color: $secondaryFontColor; } &__btn { width: 100%; margin-top: 12px; } } .refund-form-js { .form__invalid-input { margin: 4px 0 0; font-size: $extraSmallFontSize; line-height: 16px; color: $messageErrorColor; } } .refunds-lookup { gap: 16px; &__heading { margin: 0; font-size: $bigFontSize; font-weight: 600; line-height: 1.3; color: $primaryFontColor; } &__lead { margin: 0; font-size: $smallFontSize; line-height: 1.5; color: $secondaryFontColor; } &__loginPrompt { margin: 8px 0 0; font-size: $smallFontSize; color: $primaryFontColor; } &__loginLink { color: $linkFontColor; font-weight: 600; text-decoration: underline; &:hover, &:focus { text-decoration: none; } } &__error { margin: 0; font-size: $smallFontSize; color: $messageErrorColor; } } } .loginRegister--login-only { .loginRegisterTabsContainer { display: none; } .registrationForm-js { display: none; } } scss/static-elements/refunds/refunds-d.scss: @media screen and (min-width: 769px) { .refunds-container { max-width: 1400px; padding-top: 32px; padding-bottom: 64px; .refunds-view { gap: 32px; } .refunds-intro { &__heading { font-size: 20px; } &__lead { margin-bottom: 24px; } } .refunds-card, .refunds-lookup { max-width: 578px; } .refunds-card { padding: 40px; } .refunds-lookup { padding: 32px; } } } scss/static-elements/refunds/refunds-m.scss: @media screen and (max-width: 768px) { .refunds-container { padding: 16px 3% 40px; .refunds-view { gap: 24px; } .refunds-intro { &__heading { font-size: 18px; } &__lead { font-size: $smallFontSize; margin-bottom: 16px; } } .refunds-steps { &__item { &::before { width: 28px; height: 28px; } } } .refunds-card { padding: 24px 16px; &__heading { font-size: $mediumFontSize; } &__text { font-size: $smallFontSize; } } .refunds-lookup { padding: 20px 16px; &__heading { font-size: $mediumFontSize; } } } }

3. Modyfikacje istniejących plików HTML

partials/customer/order-content.html

W pliku partials/customer/order-content.html wykonaj poniższe zmiany. a) Bezpośrednio po otwarciu kontenera <div class="orderContent order-content-js"> dodaj dwa nowe bannery informacyjne (jeden o ukrytych danych zamówienia, drugi o terminie zwrotu, wraz z wyliczaniem flagi hasReturnableProduct): {% if order.Restricted -%} <div class="orderContent__returnInfo" role="note"> <svg height="20" width="20" class="svgIcon orderContent__returnInfo__icon"> <use xlink:href="css/img/icons-sprite.svg#circle-info-regular"></use> </svg> <span>{{ translations.RestrictedOrderViewInfo }}</span> </div> {% endif -%} {% assign hasReturnableProduct = false -%} {% if config.Complaints.ReturnsEnabled -%} {% for product in order.Products -%} {% if product.CanReturn -%} {% assign hasReturnableProduct = true -%} {% break -%} {% endif -%} {% endfor -%} {% endif -%} {% if hasReturnableProduct -%} <div class="orderContent__returnInfo" role="note"> <svg height="20" width="20" class="svgIcon orderContent__returnInfo__icon"> <use xlink:href="css/img/icons-sprite.svg#circle-info-regular"></use> </svg> <span>{{ translations.Prf_ReturnPeriodInfo | Format: config.Complaints.DaysToReturn }}</span> </div> {% endif -%} b) Kontener <div class="orderContent__docsToPrint">...</div> (z dokumentami do druku) opakuj warunkiem {% unless order.Restricted -%} ... {% endunless -%}, aby ukrywać go dla zamówień typu restricted. c) Cały dotychczasowy blok przycisków reklamacji/zwrotu (oparty na konstrukcji {% if ComplaintsEnabled and ReturnsEnabled %} ... {% elseif ComplaintsEnabled %} ... {% elseif ReturnsEnabled %} ... {% endif %} z jednym wspólnym przyciskiem) zamień na dwa niezależne warunki, dzięki czemu dla produktu uprawnionego zarówno do reklamacji, jak i do zwrotu, oba przyciski wyświetlają się obok siebie. Strukturę wewnątrz warunku {% if product.CanComplain or product.CanReturn -%} zastąp poniższym kodem: {% if config.Complaints.ComplaintsEnabled == true and product.CanComplain -%} <button class="orderContent__complainBtn orderContent__complainBtn--complaint showPopupBtn-js addDataToComplaintForm-js" data-popup-class="popupDialog" data-form-type="complaint" data-product-id="{{product.No}}" data-image-id="{{product.ImageId}}" data-imageext="{{product.Image.ExternalUrl}}" data-product-name="{{product.NameNoHtml | H}}" data-product-complain="{{product.CanComplain}}" data-product-count="{{ newProductQuantity }} {{ product.Unit }}" data-product-unit="{{ product.Unit }}" data-product-description="{{ productAttributes | Join: ' / ' | H }}" data-product-return="false" data-product-quantity="{{newProductQuantity}}" data-order-id="{{order.Id}}" data-order-date="{{order.Date | Date: 'yyyy-MM-dd'}}"> <svg height="16" width="16" class="svgIcon"> <use xlink:href="css/img/icons-sprite.svg#comment-alt-regular"></use> </svg> {{translations.Prf_SendComplaint }} </button> {% endif -%} {% if config.Complaints.ReturnsEnabled == true and product.CanReturn -%} <button class="orderContent__complainBtn orderContent__complainBtn--return showPopupBtn-js addDataToComplaintForm-js" data-popup-class="popupDialog" data-form-type="return" data-product-id="{{product.No}}" data-image-id="{{product.ImageId}}" data-imageext="{{product.Image.ExternalUrl}}" data-product-name="{{product.NameNoHtml | H}}" data-product-complain="false" data-product-count="{{ newProductQuantity }} {{ product.Unit }}" data-product-unit="{{ product.Unit }}" data-product-description="{{ productAttributes | Join: ' / ' | H }}" data-product-return="{{product.CanReturn}}" data-product-quantity="{{newProductQuantity}}" data-order-id="{{order.Id}}" data-order-date="{{order.Date | Date: 'yyyy-MM-dd'}}"> <svg height="16" width="16" class="svgIcon"> <use xlink:href="css/img/icons-sprite.svg#sync-regular"></use> </svg> {{ translations.Prf_OrderReturn }} </button> {% endif -%} Kluczowe różnice w stosunku do poprzedniej wersji: każdy przycisk renderowany jest niezależnie (oba mogą wystąpić równocześnie), przyciski mają modyfikatory orderContent__complainBtn--complaint i orderContent__complainBtn--return, atrybut data-form-type (odczytywany w JS), w przycisku reklamacji wymuszone data-product-return="false", w przycisku zwrotu wymuszone data-product-complain="false", ikony 16x16 (#comment-alt-regular dla reklamacji, #sync-regular dla zwrotu), a etykieta przycisku zawiera już tylko jedno tłumaczenie. d) W pozostałych miejscach z przyciskami reklamacji/zwrotu (sekcje, w których nie zmieniają się klasy i ikony) dodaj jedynie dwa nowe atrybuty: data-product-unit="{{ product.Unit }}" data-product-description="{{ productAttributes | Join: ' / ' | H }}" e) W sekcji załączników zamówienia zamień: {% if order.Attachments[0] -%} na: {% if order.Attachments[0] and order.Restricted == false -%} f) W kontenerze <div class="orderContent__recipient orderContent__infoBox"> zamień wyświetlanie adresu dostawy: <span class="orderContent__lightText">{{ order.Customer.DeliveryAddress.Street }} {{ order.Customer.DeliveryAddress.StreetNo }} / {{ order.Customer.DeliveryAddress.UnitNo }}</span> na: <span class="orderContent__lightText">{{ order.Customer.DeliveryAddress.Street }} {{ order.Customer.DeliveryAddress.StreetNo }}{% if order.Customer.DeliveryAddress.UnitNo != null and order.Customer.DeliveryAddress.UnitNo != '' -%} / {{ order.Customer.DeliveryAddress.UnitNo }}{% endif -%}</span> g) Sekcję z przyciskami akcji zamówienia (orderCancel-js, ponowienie płatności itd.) opakuj warunkiem {% unless order.Restricted -%} ... {% endunless -%}. Wstaw {% unless order.Restricted -%} bezpośrednio przed warunkiem {% if order.CanRestorePayment -%}, a {% endunless -%} po zamykającym {% endif -%} bloku z przyciskiem anulowania zamówienia.

partials/common/complaint-popup.html

Plik partials/common/complaint-popup.html wymaga gruntownej przebudowy. Usuwamy z niego radiowe przyciski wyboru "reklamacja/zwrot", a typ formularza jest teraz przekazywany z zewnątrz przez parametr __include. Sterowanie ilością zostało przeniesione do kafelka z produktem (z etykietą i obsługą wariantu statycznego, gdy zostaje tylko 1 sztuka). W formularzu zwrotu zamiast samego pola numeru konta pojawia się sekcja wyboru sposobu zwrotu środków (refundMethod-js) – z opcją „zwrot na sposób płatności" (domyślna) lub „zwrot na konto bankowe". Pole numeru konta jest domyślnie ukryte i disabled; pokazuje się dopiero po wybraniu opcji „konto bankowe" (obsługa w JS w metodzie changeRefundMethod). Tytuł, kafelek produktu i formularz oznaczone są dodatkową klasą complain-form-view-js, dzięki czemu po pomyślnym wysłaniu formularza można je ukryć i wyświetlić ekran sukcesu (complain-success-view-js). Zastąp całą zawartość pliku poniższym kodem: {% if hideContent -%} {% else -%} {% assign formType = include -%} {% assign isReturn = false -%} {% if formType == 'return' -%} {% assign isReturn = true -%} {% endif -%} <div class="popupDialog popupDialog__complain popupDialog-js"> {% assign order = customer-profile.Order -%} <div class="popupDialog__wrapper"> <button class="popupDialog__closeBtn closePopupBtn-js"> <svg aria-hidden="true" height="20" width="20" class="svgIcon"> <use href="css/img/icons-sprite.svg#times-light"></use> </svg> </button> <span class="popupDialog__title complain-form-view-js"> {% if isReturn -%} {{ translations.Prf_OrderReturn }} {% else -%} {{ translations.Prf_SendComplaint }} {% endif -%} </span> <div class="complain__product complain-form-view-js"> <figure> <img class="productImage-js" src="" alt="product_image"> <svg height="20" width="20" class="svgIcon svgIcon--emptyImage"> <use href="css/img/icons-sprite.svg#image-regular"></use> </svg> </figure> <div class="complain__productInfo"> <span class="complain__productTitle productName-js"></span> <span class="complain__productDescription productDescription-js"></span> <div class="complain__quantity"> <label for="complaint-quantity" class="complain__quantityLabel">{{translations.Com_Quantity}}:</label> <div class="complain__quantityControl min-js quantityControl-js"> <button aria-label="{{translations.ButtonMinusAddProduct}}" type="button" class="button-minus-add-product" data-field="quantity"> <svg aria-hidden="true" height="20" width="20" class="svgIcon"> <use href="css/img/icons-sprite.svg#minus-light"></use> </svg> </button> <input id="complaint-quantity" class="quantity-field quantity__field-js" name="quantity" data-decimal="false" type="number" min="1" max="" value="1"> <button aria-label="{{ translations.ButtonPlusAddProduct }}" type="button" class="button-plus-add-product" data-field="quantity"> <svg aria-hidden="true" height="20" width="20" class="svgIcon"> <use href="css/img/icons-sprite.svg#plus-light"></use> </svg> </button> </div> <span class="complain__quantityStatic quantityStatic-js hidden"></span> <span class="complain__quantityUnit productUnit-js"></span> </div> </div> </div> <div class="form complain__form inputs-container-js complain-form-view-js" data-success-info="{{ translations.ReportSuccessInfo }}"> <input type="hidden" name="__action" value="{% if isReturn -%}Order/ReturnAdd{% else -%}Order/ComplaintAdd{% endif -%}"/> <strong>{{ translations.ReportDetails }}</strong> {% unless isReturn -%} <div class="form__input-wrapper"> {% if config.Complaints.Defects <> null -%} {% assign defectsSize = config.Complaints.Defects | Size -%} <label for="defectId" class="hidden">{{ translations.ChooseComplaintCause }}</label> <select id="defectId" name="defectId" class="form__input-value form__input-value-js {% if settings.themeName == 'Dark' -%} darkTheme {% endif -%}" required> {% if defectsSize > 1 -%} <option value="-1">* {{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"> <span class="form__icons"> <svg aria-hidden="true" height="20" width="20" class="svgIcon"> <use href="css/img/icons-sprite.svg#calendar-regular"></use> </svg> </span> <input type="date" name="defectDate" id="defectDate" class="form__input-value form__icon_padding form__input-value-js orderDate-js" placeholder="yyyy-MM-dd" value="{{config.Now | Date: 'yyyy-MM-dd'}}" max="{{config.Now | Date: 'yyyy-MM-dd'}}" min="{{order.Date | Date: 'yyyy-MM-dd'}}"/> <label for="defectDate" class="form__input-info">* {{translations.Prf_DefectDate}}</label> </div> <div class="form__input-wrapper"> {% if config.Complaints.Requests <> null -%} {% assign requestsSize = config.Complaints.Requests | Size -%} <label for="requestId" class="hidden">{{ translations.Prf_ComplainRequest }}</label> <select id="requestId" name="requestId" class="form__input-value form__input-value-js {% if settings.themeName == 'Dark' -%} darkTheme {% endif -%}" required> {% if requestsSize > 1 -%} <option value="-1">* {{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> {% else -%} {% assign returnTypes = config.Complaints.Returns | Size -%} {% if returnTypes > 1 -%} <div class="form__input-wrapper"> <label for="returnId" class="hidden">{{ translations.ChooseReturn }}</label> <select id="returnId" name="returnId" class="form__input-value form__input-value-js {% if settings.themeName == 'Dark' -%} darkTheme {% endif -%}" required> <option value="-1">* {{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 %} {% endunless -%} <div class="form__input-wrapper returnInputWrapper-js refundMethodWrapper-js"> <label for="refundMethod" class="complain__sectionLabel">{{ translations.RefundMethod }}</label> <select id="refundMethod" name="refundMethod" class="form__input-value form__input-value-js refundMethod-js {% if settings.themeName == 'Dark' -%}darkTheme{% endif -%}"> <option value="payment">{{ translations.RefundAsPaid }}{% if order.Payment.Name != null and order.Payment.Name != '' %} ({{ order.Payment.Name }}){% endif %}</option> <option value="account">{{ translations.RefundToBankAccount }}</option> </select> </div> <div class="form__input-wrapper accountNumberWrapper-js" style="display: none"> <input class="form__input-value form__input-value-js" id="accountNumber" type="text" name="accountNumber" maxlength="50" disabled /> <label for="accountNumber" class="form__input-info form__input-info-js">{{translations.Crt_BankAccountNumber}}</label> <p class="form__invalid-input form__validation-required-js" style="display: none">{{ translations.Com_FieldIsMandatory }}</p> </div> <div class="form__input-wrapper"> {{translations.Prf_AdditionalInfo}} <textarea aria-label="{{ translations.Prf_AdditionalInfo }}" 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}}"> <button class="form__attachementsLabel form__attachementsLabel--btn form__attachementsLabel-js" data-page="complaints"> <svg aria-hidden="true" height="20" width="20" class="svgIcon"> <use href="css/img/icons-sprite.svg#paperclip-{{settings.iconStyle}}"></use> </svg> {{ translations.AddAttachment }} </button> {% capture maxSize -%}{{config.Complaints.AttachmentMaxSize | DividedBy: 1024}}KB{% endcapture -%} {% for i in (1..config.Complaints.AttachmentsMaxCount) -%} <div class="form__attachement-input-container form__attachement-input-container-attachments form__attachement-input-container--hidden"> <svg aria-hidden="true" height="20" width="20" class="svgIcon"> <use class="svgIconType" href="css/img/icons-sprite.svg#file-light"></use> </svg> <span class="form__attachement-input-container-attachments__text"></span> <input aria-label="{{ translations.AddAttachment }}" class="addAttachementInComplaint-js" type="file" name="file" accept="{{ config.Complaints.AttachmentExtensions }}" data-file-size="{{ config.Complaints.AttachmentMaxSize }}" data-size-exceeded="{{ translations.Com_FileSizeExceeded | Format: maxSize }}" data-invalid-file="{{ translations.Com_InvalidFile | Format: config.Complaints.AttachmentExtensions }}" /> <button class="form__clear-attachement-input clearFileInput-js" style="display: none"> <svg aria-hidden="true" height="20" width="20" class="svgIcon"> <use href="css/img/icons-sprite.svg#times-light"></use> </svg> </button> </div> {% endfor -%} </div> {% endif -%} <input type="hidden" class="productId-js" name="no" value=""/> <input type="hidden" class="orderId-js" name="orderId" value="{{order.Id}}"/> <button type="button" data-id="{{order.Id}}" class="primary-action-button orderComplaintOrReturnAdd-js">{{translations.Report}}</button> </div> <div class="complain__successView complain-success-view-js hidden"> <div class="complain__successIcon"> <svg aria-hidden="true" height="64" width="64" class="svgIcon"> <use href="css/img/icons-sprite.svg#check-circle-{{settings.iconStyle}}"></use> </svg> </div> <h2 class="complain__successTitle">{{ translations.ComplaintSuccessTitle }}</h2> <p class="complain__successDescription">{{ translations.ComplaintSuccessDescription }}</p> <div class="complain__successInfoBox"> <svg aria-hidden="true" height="20" width="20" class="svgIcon"> <use href="css/img/icons-sprite.svg#info-circle-{{settings.iconStyle}}"></use> </svg> <span> {{ translations.ComplaintSuccessConfirmationSent }} <strong>{{ order.Customer.Email }}</strong> </span> </div> <h3 class="complain__successStepsTitle">{{ translations.ComplaintSuccessStepsTitle }}</h3> <ol class="complain__successSteps"> <li>{{ translations.ComplaintSuccessStep1 }}</li> <li> {{ translations.ComplaintSuccessStep2 }} {{ config.Shop.Address.Street }} {{ config.Shop.Address.StreetNo }}{% if config.Shop.Address.UnitNo != '' %}/{{ config.Shop.Address.UnitNo }}{% endif %}, {{ config.Shop.Address.ZipCode }} {{ config.Shop.Address.City }} </li> <li>{{ translations.ComplaintSuccessStep3 }}</li> </ol> <div class="complain__successActions"> <button type="button" class="primary-action-button closePopupBtn-js">{{ translations.BtnClose }}</button> </div> </div> </div> </div> {% endif -%}

4. Modyfikacje pliku js/layout1.js

Poniższe zmiany dotyczą pliku js/layout1.js. Po edycji pamiętaj o zminifikowaniu pliku do layout1.min.js. a) W obiekcie customerProfile usuń całą metodę setComplainReturnRadioButtons (radiowy przełącznik reklamacja/zwrot nie jest już potrzebny – typ formularza jest teraz przekazywany przy otwieraniu popupu). b) W tym samym obiekcie zastąp całą metodę addDataToComplaintForm nową implementacją, która odczytuje typ formularza z atrybutu data-form-type przycisku i przekazuje go do szablonu jako parametr __include: addDataToComplaintForm: function (e) { const template = 'partials/common/complaint-popup.html'; const data = e.currentTarget; const formType = data.dataset.formType === 'return' ? 'return' : 'complaint'; $.get('', {__template: template, __include: "'" + formType + "'"}, function(result) { $('.customer__content-js').append(result.template); $('.popupDialog__complain').show(); $('body').addClass('customerProfileNoScroll'); const image = $('.productImage-js'); const productName = $('.productName-js'); const inputId = $('.productId-js'); const productQuantity = $('.quantity__field-js'); const quantityControl = $('.quantityControl-js'); const quantityStatic = $('.quantityStatic-js'); const productUnit = $('.productUnit-js'); const productDescription = $('.productDescription-js'); const orderId = $('.orderId-js'); const buttonOrderId = $('.orderComplaintOrReturnAdd-js'); const orderDate = $('.orderDate-js'); productName.text(data.dataset.productName); productUnit.text(data.dataset.productUnit || ''); productDescription.text(data.dataset.productDescription || ''); inputId.val(data.dataset.productId); productQuantity.attr('max', data.dataset.productQuantity); if (parseInt(data.dataset.productQuantity, 10) <= 1) { quantityControl.addClass('hidden'); quantityStatic.text(data.dataset.productQuantity).removeClass('hidden'); } else { quantityControl.removeClass('hidden'); quantityStatic.addClass('hidden'); } orderId.val(data.dataset.orderId); buttonOrderId.attr('data-id', data.dataset.orderId); orderDate.attr('min', data.dataset.orderDate); if (data.dataset.imageext != null && data.dataset.imageext != "") { image.attr('src', data.dataset.imageext); } else if (data.dataset.imageId != null && data.dataset.imageId != "") { image.attr('src', '/Image/?id=' + data.dataset.imageId); } if (image.attr('src') === '' || image.attr('src') == null) { image.closest('figure').addClass('noImage-js'); } setTimeout(function() { app.activateFocusTrap('.popupDialog__complain', e.target); }, 200); }); }, c) Zastąp metodę changeReturnComplaintForm nową wersją (obsługuje również pole numeru konta i wywołuje changeRefundMethod po przełączeniu na zwrot): changeReturnComplaintForm: function (e) { const form = $(e.currentTarget).closest('.inputs-container-js'); const complaintInputs = form.find('.complaintInputWrapper-js'); const returnInputs = form.find('.returnInputWrapper-js'); const accountWrapper = form.find('.accountNumberWrapper-js'); const accountInput = accountWrapper.find('input[name="accountNumber"]'); if ($(e.currentTarget).is(':checked') && e.currentTarget.dataset.name === 'complaint' ) { $.each(complaintInputs, function (index, value) { $(value).find('input, select').first().removeAttr('disabled'); $(value).show(300); }); returnInputs.each(function () { $(this).find('input, select').first().attr('disabled', 'true'); }); returnInputs.hide(300); accountInput.removeAttr('disabled').removeAttr('required').removeClass('form__validation-error-js'); accountWrapper.find('.form__validation-required-js').hide(); accountWrapper.show(300); } if ($(e.currentTarget).is(':checked') && e.currentTarget.dataset.name === 'return' ) { $.each(complaintInputs, function (index, value) { $(value).find('input, select').first().attr('disabled', 'true'); $(value).hide(300); }); returnInputs.each(function () { $(this).find('input, select').first().removeAttr('disabled'); }); returnInputs.show(300); customerProfile.changeRefundMethod(form); } }, d) Tuż za metodą changeReturnComplaintForm dodaj nową metodę changeRefundMethod (pokazuje / ukrywa pole numeru konta bankowego na podstawie wybranego sposobu zwrotu): changeRefundMethod: function (formOrEvent) { const form = formOrEvent && formOrEvent.currentTarget ? $(formOrEvent.currentTarget).closest('.inputs-container-js') : $(formOrEvent); const select = form.find('.refundMethod-js'); const accountWrapper = form.find('.accountNumberWrapper-js'); const accountInput = accountWrapper.find('input[name="accountNumber"]'); if (select.val() === 'account') { accountInput.removeAttr('disabled').attr('required', 'required'); accountWrapper.show(300); } else { accountInput.attr('disabled', 'true').removeAttr('required').removeClass('form__validation-error-js'); accountWrapper.find('.form__validation-required-js').hide(); accountWrapper.hide(300); } }, e) W metodzie wysyłającej formularz reklamacji/zwrotu (gdzie znajduje się pętla for (let i=0; i<dataFromHTML.length; i++)) zaraz po pętli dodaj fragment dołączający parametr qrHash z URL-a przy wysyłaniu zwrotu: if (form.find('input[name="__action"]').val() === 'Order/ReturnAdd') { const qrHash = new URLSearchParams(globalThis.location.search).get('qrHash'); if (qrHash) { fd.append('qrHash', qrHash); } } f) W tej samej metodzie, w callbacku success po pomyślnym wysłaniu, zamień fragment: if (data.action.Result) { const 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(); $('.popupDialog__complain').remove(); $('body').removeClass('customerProfileNoScroll'); customerProfile.showOrderDetails(e); } na: if (data.action.Result) { $('.clearFileInput-js').hide(); sessionStorage.removeItem('complains'); sessionStorage.removeItem('order' + (e.currentTarget).dataset.id); const popup = $(e.currentTarget).closest('.popupDialog__complain'); popup.find('.complain-form-view-js').addClass('hidden'); popup.find('.complain-success-view-js').removeClass('hidden'); popup.find('.popupDialog__wrapper').scrollTop(0); } g) W sekcji rejestracji zdarzeń profilu klienta (tam, gdzie znajdują się m.in. mainSection.on('change', '.radioComplaint-js, .radioReturn-js', ...)) tuż za istniejącym handlerem zmiany typu formularza dopisz nowy handler: mainSection.on('change', '.refundMethod-js', function (e) { customerProfile.changeRefundMethod(e); }); h) Znajdź globalny handler obsługujący keydown z selektorem zaczynającym się od .radioReturn-js label, .radioComplaint-js label, ... i usuń z niego dwa pierwsze selektory (po zmianie zaczyna się od .showHideSection-js): // PRZED $('body').on('keydown', '.radioReturn-js label, .radioComplaint-js label, .showHideSection-js, .checkBoxes__container label, .customerPagination__form-js, .discountsDetails-js, .customer-focus, .customer__menuActivate-js', function(e) { ... }); // PO $('body').on('keydown', '.showHideSection-js, .checkBoxes__container label, .customerPagination__form-js, .discountsDetails-js, .customer-focus, .customer__menuActivate-js', function(e) { ... }); i) W obiekcie app w pliku js/layout1.js obsłuż przekierowanie po zalogowaniu z parametrem #login-redirect= (używanym przez stronę szybkich zwrotów). W bloku rejestracji/logowania, w miejscu gdzie obsługiwany jest hash #back-to-cart, tuż po nim dodaj: if (globalThis.location.hash.startsWith('#login-redirect=')) { url = globalThis.location.hash.slice('#login-redirect='.length); } Następnie w handlerze sukcesu logowania, gdzie znajduje się sprawdzenie window.location.hash.includes('#back-to-cart') wraz z przekierowaniem, tuż za nim dodaj analogiczny blok przekierowujący w przypadku #login-redirect=: if (globalThis.location.hash.startsWith('#login-redirect=')) { globalThis.location.assign(globalThis.location.hash.slice('#login-redirect='.length)); return; }

5. Modyfikacje plików SCSS profilu klienta

Poniższe style należy dodać do plików scss/static-elements/customer-profile/customer-profile-g.scss (style ogólne), customer-profile-d.scss (desktop) i customer-profile-m.scss (mobile). Po zmianach pamiętaj o kompilacji SCSS – nie edytuj ręcznie wynikowych plików CSS.

Plik customer-profile-g.scss

a) Wewnątrz selektora opakowującego (tam, gdzie znajdują się istniejące reguły .orderContent__complainBtn) dodaj nowe reguły dla banera informacyjnego oraz kontenera grupującego przyciski reklamacji/zwrotu: .orderContent__returnInfo { display: flex; align-items: center; gap: 10px; margin: 16px 0 24px; padding: 12px 16px; background-color: color-mix(in srgb, #{$linkFontColor} 4%, transparent); border: 1px solid color-mix(in srgb, #{$linkFontColor} 20%, transparent); border-radius: 4px; font-size: 13px; line-height: 1.4; color: $primaryFontColor; &__icon { flex-shrink: 0; fill: $linkFontColor; } } .orderContent__complainBtns { display: flex; flex-wrap: wrap; gap: 16px; margin: 16px 0 4px; } b) Następnie zastąp istniejące style .orderContent__complainBtn nowym wyglądem przycisku (z tłem, obramowaniem i hoverem): .orderContent__complainBtn { display: inline-flex; align-items: center; gap: 6px; margin: 16px 0 4px; padding: 6px 12px; background: transparent; border: 1px solid transparent; border-radius: 4px; color: $linkFontColor; fill: $linkFontColor; font-size: 13px; font-weight: 500; line-height: 1.2; white-space: nowrap; cursor: pointer; transition: background-color 180ms, border-color 180ms; .orderContent__complainBtns & { margin: 0; } svg { margin: 0; flex-shrink: 0; } &:hover, &:focus-visible { background-color: color-mix(in srgb, #{$linkFontColor} 6%, transparent); border-color: color-mix(in srgb, #{$linkFontColor} 25%, transparent); outline: none; } } c) Dodaj style etykiety sekcji oraz kontenera sposobu zwrotu w popupie reklamacji: .complain__sectionLabel { display: block; margin-bottom: 8px; font-size: 14px; font-weight: 500; color: $primaryFontColor; } .refundMethodWrapper-js { margin-bottom: 18px; } d) Dodaj kompletne style ekranu sukcesu zwrotu (m.in. ikona, tytuł, lista kolejnych kroków, sekcja z e-mailem i przyciskiem zamknięcia): .complain__successView { text-align: center; padding: 16px 0 0; } .complain__successIcon { display: flex; justify-content: center; margin: 8px 0 20px; .svgIcon { width: 64px; height: 64px; fill: #2d8c4a; } } .complain__successTitle { font-size: 20px; font-weight: 600; line-height: 1.3; margin: 0 0 12px; color: $primaryFontColor; } .complain__successDescription { font-size: 14px; line-height: 1.5; color: $primaryFontColor; margin: 0 0 20px; } .complain__successInfoBox { display: flex; align-items: flex-start; gap: 10px; padding: 12px 14px; margin: 0 0 24px; border: 1px solid #cfdcef; background-color: #f0f5fc; border-radius: 6px; text-align: left; .svgIcon { flex-shrink: 0; margin-top: 2px; fill: #3a6ea5; } span { font-size: 13px; line-height: 1.5; color: $primaryFontColor; } strong { display: block; font-weight: 600; margin-top: 2px; word-break: break-all; } } .complain__successStepsTitle { font-size: 15px; font-weight: 600; margin: 0 0 12px; text-align: left; color: $primaryFontColor; } .complain__successSteps { margin: 0 0 24px; padding-left: 22px; text-align: left; li { font-size: 14px; line-height: 1.6; margin-bottom: 6px; color: $primaryFontColor; } } .complain__successActions { display: flex; justify-content: flex-end; padding-top: 16px; padding-bottom: 30px; margin-top: 8px; border-top: 1px solid $borderColor; .primary-action-button { min-width: 140px; margin: 0; } @media screen and (max-width: 599px) { .primary-action-button { width: 100%; min-width: 0; } } } e) Zaktualizuj style załączników – znajdź istniejące reguły .form__clear-attachement-input oraz reguły z position: absolute; top: 11px; i zastąp je nowymi (które ustawiają załączniki w układzie statycznym z poprawnym overflowem i przyciskiem usuwania): .form__attachement-input-container-attachments__text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .form__attachement-input-container--hidden, .form__attachement-input-container-attachments.form__attachement-input-container--hidden { display: none; } .form__clear-attachement-input { position: static; background: transparent; border: none; padding: 2px 4px; margin: 0 0 0 4px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; svg { fill: $linkFontColor; transition: opacity 200ms; } &:hover svg { opacity: 0.7; } }

Plik customer-profile-d.scss

Wewnątrz wrappera @media screen and (min-width: 769px) zamień istniejące style przycisku reklamacji: .customer .orderContent__complainBtn { position: absolute; bottom: 8px; } na poniższy fragment (dodaje pozycjonowanie kontenera grupującego przyciski oraz osobny selektor dla pojedynczych przycisków w wierszu produktu): .customer .orderContent__complainBtns { position: absolute; bottom: 8px; right: 0; margin: 0; } .customer .orderContent__productDetails > .orderContent__complainBtn { position: absolute; bottom: 8px; right: 0; margin: 0; }

Plik customer-profile-m.scss

W obrębie selektora .customer zamień istniejący media-query @media screen and (max-width: 359px) wraz z jego wnętrzem na rozszerzoną wersję (układająca przyciski reklamacji/zwrotu pionowo na szerokościach do 450px): @media screen and (max-width: 450px) { .customer__orderContent .orderContent__productsRow { max-height: none; } .customer__orderContent .orderContent__productDetails { display: flex; flex-direction: column; align-items: stretch; } .customer__orderContent .orderContent__complainBtns { display: flex; flex-direction: column; align-items: stretch; gap: 8px; width: 100%; margin: 12px 0 4px; padding-top: 12px; border-top: 1px solid $borderColor; } .customer__orderContent .orderContent__complainBtn { display: flex; width: 100%; justify-content: center; padding: 10px 12px; margin: 12px 0 4px; border-color: color-mix(in srgb, #{$linkFontColor} 25%, transparent); } .customer__orderContent .orderContent__complainBtns .orderContent__complainBtn { margin: 0; } }

6. Szablon One Page Shop

Szablon One Page Shop ma praktycznie identyczną strukturę plików jak Topaz. Wszystkie powyższe modyfikacje (tłumaczenia, nowa strona zwrotów, edycje plików partials/common/complaint-popup.html oraz partials/customer/order-content.html, zmiany w js/layout1.js oraz w plikach SCSS profilu klienta i strony zwrotów) wykonaj 1:1 w plikach szablonu OPS pod tymi samymi ścieżkami. Treść kodu jest taka sama, ścieżki plików są takie same – instrukcja dla Topaza w pełni stosuje się również do One Page Shopa. Po wprowadzeniu wszystkich powyższych zmian w szablonie Topaz (oraz One Page Shop) pamiętaj o kompilacji oraz minifikacji plików js i scss, zgodnie z artykułem dostępnym pod linkiem https://pomoc.comarchesklep.pl/artykul/kompilacja-i-minifikacja-plikow/.
Wskazówka
Potrzebujesz pomocy lub masz pytania? Skontaktuj się z nami poprzez portal asysta.comarch.pl.