Jak wyeksportować i zaimportować swój szablon w e-Sklepie?
Comarch e-Sklep daje możliwość dostosowania wyglądu do preferencji sprzedawcy oraz potrzeb Klientów. Jeśli utworzyłeś już swój autorski projekt, to masz możliwość jego zapisania. Plik będzie potrzebny przy chęci modyfikacji wyglądu. Zapisana wersja szablonu może również stanowić kopię bezpieczeństwa. Warto jest więc wyeksportować szablon do pliku.
Jak wyeksportować swój szablon z Comarch e-Sklep?
Krok 1. W panelu administracyjnym przejdź do sekcji Wygląd Sklepu/ Ustawienia/ Edytuj ustawienia zaawansowane/ zakładka Eksport szablonu.

Krok 2. W tym miejscu możesz opcjonalnie (przed eksportem) określić autora szablonu, podać adres e-mail, telefon oraz adres WWW swojego e-Sklepu.
Krok 3. Jeżeli chcesz wyeksportować szablon do pliku użyj przycisku Eksportuj szablon.
Gotowe! Szablon został wyeksportowany do pliku na Twoim urządzeniu.
Po skomponowaniu swojego własnego szablonu możesz wgrać go do e-Sklepu. Postępuj zgodnie z poniższymi krokami. Krok 1. W panelu administracyjnym przejdź do sekcji Wygląd Sklepu/ Ustawienia/ Edytuj ustawienia zaawansowane/ zakładka Import szablonu. Krok 2. Określ, czy wgrywany szablon ma podmienić istniejące bannery. Gdy parametr zostanie zaznaczony, wówczas wszystkie bannery zapisane na danym slocie zostaną usunięte i zastąpione bannerami z importowanego szablonu. Krok 3. Wybierz plik XML swojego szablonu i naciśnij Importuj szablon. Krok 4. Teraz możesz podejrzeć zmiany, jakie zaszły w wyglądzie Twojego e-Sklepu. W tym celu możesz podejrzeć zmiany w czasie rzeczywistym lub przejść w tryb incognito. Pamiętaj, że wszelkie zmiany zostaną opublikowane po pełnej synchronizacji, która wciągu 10 minut zgodnie
z wyświetlanym komunikatem. Gotowe! Twój szablon został zaimportowany do e-Sklepu. Aby zaktualizować szablon Rubin, Szafir, Topaz lub One Page Shop należy skorzystać z aktualnej wersji Kreatora Wyglądu, do którego odnośnik znajduje się w panelu administracyjnym e-Sklepu w sekcji Wygląd sklepu/ Kreator wyglądu. Istnieją dwie możliwości aktualizacji tych szablonów: Wyeksportowany z Kreatora Wyglądu szablon należy wgrać do e-Sklepu. W panelu administracyjnym e-Sklepu, w sekcji Wygląd sklepu znajduje się bezpośredni odnośnik do importu szablonu z Kreatora wyglądu. Szczegółowa instrukcja dostępna jest w artykule Jak wyeksportować i zaimportować swój szablon w e-Sklepie? Podczas dokonywania modyfikacji w szablonach zachęcamy do skorzystania z naszego Centrum Pomocy. Aby zaktualizować szablon Bursztyn, Agat lub Opal należy w panelu administracyjnym e-Sklepu przejść do sekcji Wygląd Sklepu/ Gotowe szablony Comarch, w której znajdują się najnowsze wersje szablonów dostępne do zainstalowania. Z wyświetlonej listy należy wybrać szablon, który ma zostać wgrany do e-Sklepu. W tym celu wybierz przycisk Chcę pobrać i zainstalować szablon: Następnie wyświetli się nowe okno, w którym należy: Aby nowy szablon pojawił się w e-Sklepie należy upewnić się, czy jest on aktywny oraz ustawiony jako domyślny. W sekcji Wygląd sklepu/Ustawienia/Edytuj ustawienia zaawansowane w zakładce Szablon znajdują się odpowiedzialne za to parametry, które powinny być zaznaczone. Wszelkie zmiany należy zapisać, a następnie opublikować. Comarch e-Sklep umożliwia tworzenie oraz edycję szablonów graficznych opartych na silniku DotLiquid wykonanych w technice Responsive Web Design (RWD). Dzięki tej technologii strona sklepu dostosowuje się automatycznie do rozdzielczości urządzenia, na którym jest wyświetlana i nie ma potrzeby tworzenia osobnych szablonów dla wersji mobilnej. Dodatkowo wszystkie standardowe szablony są dostosowane do wyświetlania w rozdzielczości 4K. Domyślnie użytkownicy Comarch e-Sklep mają dostęp do pięciu darmowych szablonów: Topaz, Dla gastronomii i One Page Shop (generowane przez Kreator wyglądu) oraz Szafir i Rubin(dostępny dla wersji B2B w Kreatorze B2B). Więcej o darmowych szablonach przeczytasz w artykułach Szablon Rubin, Szablon Dla gastronomii, Szablon Szafir, Szablon Topaz i Szablon One Page Shop. Szablony te można poddać edycji w celu spersonalizowania wyglądu swojego sklepu dzięki prostym ustawieniom w panelu administracyjnym, gdzie można zmienić takie parametry jak: Dla bardziej zaawansowanych użytkowników znających technologie takie jak HTML, CSS, JS, DotLiquid udostępniony jest obszar z plikami szablonu, gdzie można dokonywać zmian na poziomie kodu szablonu, co daje praktycznie nieograniczone możliwości edycji i personalizacji wyglądu sklepu. Wymagania: Jednym z kluczowych elementów wpływających na sukces w branży e-commerce jest odpowiednia oprawa wizualna sklepu internetowego. To ona w dużej mierze kształtuje wrażenia użytkowników oraz wpływa na przebieg ich procesu zakupowego. Dzięki narzędziu Kreator Szablonu Rubin (B2B) właściciele sklepów mają możliwość stworzenia unikalnego, dopasowanego szablonu, który będzie obsługiwał zarówno klientów detalicznych, jak i firmy, łącząc modele B2B oraz B2C. Kreator Szablonu Rubin to intuicyjne narzędzie do projektowania wyglądu sklepu. Wszystko odbywa się na poziomie strony internetowej, gdzie przy pomocy metody "przeciągnij i upuść" możesz stworzyć szablon idealnie pasujący do Twojego sklepu. Kreator pozwala na zaprojektowanie wszystkich kluczowych obszarów sklepu, w tym: Aby móc używać tego narzędzia należy posiadać: Aby rozpocząć projektowanie szablonu, należy udać się na stronę Kreatora Wyglądu, gdzie pojawi się powitalny komunikat. Na początku trzeba wybrać, dla jakiego typu sprzedaży ma być stworzony szablon – detalicznej lub hurtowej. Sprzedaż hurtowa jest dedykowana tym, którzy chcą oferować swoje produkty także klientom biznesowym w większych ilościach. Po dokonaniu wyboru modelu sprzedaży, wystarczy kliknąć przycisk „Rozpocznij pracę”. Następnie, użytkownik będzie miał możliwość stworzenia nowego szablonu lub edytowania jednego z dostępnych, gotowych szablonów. Po ustawieniu wersji językowej dla Kreatora, warto nadać nazwę swojemu szablonowi. Można to zrobić klikając „Zmień” w lewym górnym rogu, a następnie wpisując nową nazwę, która zastąpi domyślną nazwę „Rubin”. Ta nazwa będzie widoczna po zaimportowaniu szablonu do panelu administracyjnego oraz po wyeksportowaniu go do pliku XML, wraz z datą i godziną wygenerowania szablonu. W tej sekcji należy zaprojektować wygląd strony głównej naszego Comarch e-Sklepu dla szablonu Rubin. Strona główna może składać się z następujących elementów: Po kliknięciu w strzałkę, sekcje rozwiną się i pokażą dostępne elementy: Metodą "Przeciągnij i upuść" przenosimy wybrane elementy na obszar po prawej stronie: Postępuj analogicznie z pozostałymi elementami. Sekcja „Proponowane” oferuje dodatkowe opcje do konfiguracji. Z menu rozwijanego, dostępnego po kliknięciu przycisku „Zmień typ”, możesz wybrać kategorię produktów, które mają być wyświetlane w tej sekcji (np. nowości, najczęściej oglądane, wyprzedaż, sugerowane). Po dokonaniu zmiany typu, sekcja zostanie odpowiednio nazwana po załadowaniu szablonu do sklepu. Dodatkowo po najechaniu kursorem na tę sekcję, wybierając Opcje, możemy ustawić liczbę towarów, która będzie się wyświetlać (maksymalnie 50). Po wybraniu wszystkich niezbędnych sekcji nasza strona główna powinna być gotowa. Efekt naszej pracy możemy podejrzeć klikając Podgląd w prawym górnym rogu. Dzięki tej opcji możesz zobaczyć, jak będzie prezentował się Twój szablon zarówno w wersji dekstop, jak i mobile. Kolejnym krokiem jest utworzenie listy towarów. Aby to zrobić, należy rozwinąć listę zgodnie z poniższym rysunkiem, a następnie przejść do wskazanej zakładki: Podobnie, jak w przypadku strony głównej konfigurujemy wygląd listy towarów. Dodatkowo możemy wskazać liczbę wyświetlanych towarów, domyślny widok listy towarów, prezentację atrybutów, a także zdecydować czy pozwolisz na zakup towarów wielowariantowych z listy towarów. Następnie możemy przejść do modyfikacji wyglądu naszego szablonu. W tym celu należy przejść do zakładki Style. Po dodaniu elementów strony możemy dodatkowo podejrzeć, jak będzie wyglądał nasz przyszły szablon. W tym celu należy przejść do zakładki Style. W tym momencie dla szablonu Rubin został przygotowany jeden motyw kolorystyczny: Motyw możemy zmieniać według indywidualnych preferencji. Możemy zmodyfikować kolor poszczególnych elementów (m.in.: tło strony, tło nagłówka, nazwę towaru). Zmiany należy zapisać klikając Zapisz. Jeśli wykonane zmiany nie są zadowalające, możemy przywrócić ustawienia domyśle dla danego motywu kolorystycznego, klikając przycisk Przywróć. W tym momencie w ramach szablu Rubin została udostępniona jedna czcionka: Ostatnim elementem, który możemy modyfikować z poziomu zakładki Style jest wygląd ikon. Mamy do wyboru 3 różne warianty wyglądu ikon. Dodatkowo istnieje możliwość wyboru w jakiej postaci wyświetla się ikona koszyka (torba, wózek lub koszyk) oraz loader. Po zaprojektowaniu wyglądu szablonu możemy go wygenerować i pobrać na dysk korzystając
z przycisku Generuj widocznego w prawym górnym rogu: W przypadku prawidłowego wygenerowania szablonu wyświetlony zostanie komunikat informujący o tym fakcie i pobrany plik. Pobrany szablon należy zaimportować w Comarch e-Sklep w zakładce Wygląd sklepu/ Zaimportuj szablon z Kreatora Wyglądu. W przypadku braku obowiązkowych elementów szablonu nie będzie możliwe jego wygenerowanie. przycisk Generuj będzie wyszarzały, a w prawym dolnym rogu pojawi się komunikat „Uzupełnij elementy wymagane w szablonie”. Sekcje, gdzie element nie został wybrany, będą się wyświetlać z odpowiednią ikoną. Więcej informacji można znaleźć w artykułach o wyglądzie sklepu oraz o dostosowaniu wyglądu. Po wybraniu wariantu Kolory, w panelu administracyjnym automatycznie pojawi się nowe okno z nazwami kolorów oraz ich graficznymi odpowiednikami w notacji heksadecymalnej. Jest to część konfiguracji tej funkcji, która umożliwia mapowanie kolorów. Funkcja ta działa na podstawie atrybutów przypisanych do towarów w systemie ERP. Domyślnie kolory takie jak niebieski, żółty czy fioletowy są mapowane automatycznie, więc uzupełnianie tego okna nie jest konieczne. Jednak jeśli atrybut zawiera niestandardowe nazwy kolorów, np. „miętowy”, należy ręcznie przypisać odpowiedni kolor w poniższym polu. Jednym z kluczowych elementów wpływających na sukces w branży e-commerce jest odpowiednia oprawa wizualna sklepu internetowego. To ona w dużej mierze kształtuje wrażenia użytkowników oraz wpływa na przebieg ich procesu zakupowego. Dzięki narzędziu Kreator Szablonu Rubin (B2B) właściciele sklepów mają możliwość stworzenia unikalnego, dopasowanego szablonu, który będzie obsługiwał zarówno klientów detalicznych, jak i firmy, łącząc modele B2B oraz B2C. Kreator Szablonu Rubin to intuicyjne narzędzie do projektowania wyglądu sklepu. Wszystko odbywa się na poziomie strony internetowej, gdzie przy pomocy metody "przeciągnij i upuść" możesz stworzyć szablon idealnie pasujący do Twojego sklepu. Kreator pozwala na zaprojektowanie wszystkich kluczowych obszarów sklepu, w tym: Aby móc używać tego narzędzia należy posiadać: Aby rozpocząć projektowanie szablonu, należy udać się na stronę Kreatora Wyglądu, gdzie pojawi się powitalny komunikat. Na początku trzeba wybrać, dla jakiego typu sprzedaży ma być stworzony szablon – detalicznej lub hurtowej. Sprzedaż hurtowa jest dedykowana tym, którzy chcą oferować swoje produkty także klientom biznesowym w większych ilościach. Po dokonaniu wyboru modelu sprzedaży, wystarczy kliknąć przycisk „Rozpocznij pracę”. Następnie, użytkownik będzie miał możliwość stworzenia nowego szablonu lub edytowania jednego z dostępnych, gotowych szablonów. Po ustawieniu wersji językowej dla Kreatora, warto nadać nazwę swojemu szablonowi. Można to zrobić klikając „Zmień” w lewym górnym rogu, a następnie wpisując nową nazwę, która zastąpi domyślną nazwę „Rubin”. Ta nazwa będzie widoczna po zaimportowaniu szablonu do panelu administracyjnego oraz po wyeksportowaniu go do pliku XML, wraz z datą i godziną wygenerowania szablonu. W tej sekcji należy zaprojektować wygląd strony głównej naszego Comarch e-Sklepu dla szablonu Rubin. Strona główna może składać się z następujących elementów: Po kliknięciu w strzałkę, sekcje rozwiną się i pokażą dostępne elementy: Metodą "Przeciągnij i upuść" przenosimy wybrane elementy na obszar po prawej stronie: Postępuj analogicznie z pozostałymi elementami. Sekcja „Proponowane” oferuje dodatkowe opcje do konfiguracji. Z menu rozwijanego, dostępnego po kliknięciu przycisku „Zmień typ”, możesz wybrać kategorię produktów, które mają być wyświetlane w tej sekcji (np. nowości, najczęściej oglądane, wyprzedaż, sugerowane). Po dokonaniu zmiany typu, sekcja zostanie odpowiednio nazwana po załadowaniu szablonu do sklepu. Dodatkowo po najechaniu kursorem na tę sekcję, wybierając Opcje, możemy ustawić liczbę towarów, która będzie się wyświetlać (maksymalnie 50). Po wybraniu wszystkich niezbędnych sekcji nasza strona główna powinna być gotowa. Efekt naszej pracy możemy podejrzeć klikając Podgląd w prawym górnym rogu. Dzięki tej opcji możesz zobaczyć, jak będzie prezentował się Twój szablon zarówno w wersji dekstop, jak i mobile. Kolejnym krokiem jest utworzenie listy towarów. Aby to zrobić, należy rozwinąć listę zgodnie z poniższym rysunkiem, a następnie przejść do wskazanej zakładki: Podobnie, jak w przypadku strony głównej konfigurujemy wygląd listy towarów. Dodatkowo możemy wskazać liczbę wyświetlanych towarów, domyślny widok listy towarów, prezentację atrybutów, a także zdecydować czy pozwolisz na zakup towarów wielowariantowych z listy towarów. Następnie możemy przejść do modyfikacji wyglądu naszego szablonu. W tym celu należy przejść do zakładki Style. Po dodaniu elementów strony możemy dodatkowo podejrzeć, jak będzie wyglądał nasz przyszły szablon. W tym celu należy przejść do zakładki Style. W tym momencie dla szablonu Rubin został przygotowany jeden motyw kolorystyczny: Motyw możemy zmieniać według indywidualnych preferencji. Możemy zmodyfikować kolor poszczególnych elementów (m.in.: tło strony, tło nagłówka, nazwę towaru). Zmiany należy zapisać klikając Zapisz. Jeśli wykonane zmiany nie są zadowalające, możemy przywrócić ustawienia domyśle dla danego motywu kolorystycznego, klikając przycisk Przywróć. W tym momencie w ramach szablu Rubin została udostępniona jedna czcionka: Ostatnim elementem, który możemy modyfikować z poziomu zakładki Style jest wygląd ikon. Mamy do wyboru 3 różne warianty wyglądu ikon. Dodatkowo istnieje możliwość wyboru w jakiej postaci wyświetla się ikona koszyka (torba, wózek lub koszyk) oraz loader. Po zaprojektowaniu wyglądu szablonu możemy go wygenerować i pobrać na dysk korzystając
z przycisku Generuj widocznego w prawym górnym rogu: W przypadku prawidłowego wygenerowania szablonu wyświetlony zostanie komunikat informujący o tym fakcie i pobrany plik. Pobrany szablon należy zaimportować w Comarch e-Sklep w zakładce Wygląd sklepu/ Zaimportuj szablon z Kreatora Wyglądu. W przypadku braku obowiązkowych elementów szablonu nie będzie możliwe jego wygenerowanie. przycisk Generuj będzie wyszarzały, a w prawym dolnym rogu pojawi się komunikat „Uzupełnij elementy wymagane w szablonie”. Sekcje, gdzie element nie został wybrany, będą się wyświetlać z odpowiednią ikoną. Więcej informacji można znaleźć w artykułach o wyglądzie sklepu oraz o dostosowaniu wyglądu. Z poziomu kafli dostępne są również dodatkowe akcje, takie jak dodanie do zapytania o wycenę, list zakupów czy do porównania. Co więcej, dla wskazanej listy, została dodana możliwość kopiowania kodu towaru. Zgodnie z ustawieniem filtry będą działać dopiero po kliknięciu wskazanego przycisku. Jeżeli parametr jest ustawiony na „Nie”, lista będzie filtrować się dynamicznie po wybraniu filtra. Dzięki temu możliwe będzie wskazanie historii zakupów z ostatnich 6 miesięcy Po wybraniu wariantu Kolory, w panelu administracyjnym automatycznie pojawi się nowe okno z nazwami kolorów oraz ich graficznymi odpowiednikami w notacji heksadecymalnej. Jest to część konfiguracji tej funkcji, która umożliwia mapowanie kolorów. Funkcja ta działa na podstawie atrybutów przypisanych do towarów w systemie ERP. Domyślnie kolory takie jak niebieski, żółty czy fioletowy są mapowane automatycznie, więc uzupełnianie tego okna nie jest konieczne. Jednak jeśli atrybut zawiera niestandardowe nazwy kolorów, np. „miętowy”, należy ręcznie przypisać odpowiedni kolor w poniższym polu. Przykładowy wygląd rozbudowanej stopki w szablonie Topaz 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ć. Strona w profilu klienta ze szczegółami kodów rabatowych w szablonie Szafir Kupony rabatowe w profilu klienta w szablonie Topaz Prezentacja kodów rabatowych na listach w szablonie Szafir Kupony rabatowe na listach w szablonie Topaz Prezentacja kuponów rabatowych na szczegołach towaru w szablonie Szafir Prezentacja kuponów rabatowych na szczegołach towaru w szablonie Topaz Krok 1. Aby dodać zdjęcia kategorii w Comarch ERP Optima przejdź do zakładki Ogólne > Grupy towarowe i wybierz grupę do której ma zostać dodane zdjęcie. Krok 2. Następnie na zakładce Atrybuty za pomocą symbolu Plusa dodaj atrybut binarny. Modyfikacja wszystkich bannerów dostępnych w szablonach Comarch możliwa jest w panelu administracyjnym w obszarze Wygląd sklepu> Ustawienia> Bannery. Informacje odnośnie domyślnej, tradycyjnej modyfikacji bannerów znajdują się w artykule: Bannery
Odnajdź banner na liście nazwany "promotionalBanner", a następnie wybierz go, aby edytować i dodać zdjęcie Comarch e-Sklep daje możliwość dostosowania wyglądu do preferencji sprzedawcy oraz potrzeb Klientów. Jeśli utworzyłeś już swój autorski projekt, to masz możliwość jego zapisania. Plik będzie potrzebny przy chęci modyfikacji wyglądu. Zapisana wersja szablonu może również stanowić kopię bezpieczeństwa. Warto jest więc wyeksportować szablon do pliku. Krok 1. W panelu administracyjnym przejdź do sekcji Wygląd Sklepu/ Ustawienia/ Edytuj ustawienia zaawansowane/ zakładka Eksport szablonu. Krok 2. W tym miejscu możesz opcjonalnie (przed eksportem) określić autora szablonu, podać adres e-mail, telefon oraz adres WWW swojego e-Sklepu. Krok 3. Jeżeli chcesz wyeksportować szablon do pliku użyj przycisku Eksportuj szablon. Gotowe! Szablon został wyeksportowany do pliku na Twoim urządzeniu. Po skomponowaniu swojego własnego szablonu możesz wgrać go do e-Sklepu. Postępuj zgodnie z poniższymi krokami. Krok 1. W panelu administracyjnym przejdź do sekcji Wygląd Sklepu/ Ustawienia/ Edytuj ustawienia zaawansowane/ zakładka Import szablonu. Krok 2. Określ, czy wgrywany szablon ma podmienić istniejące bannery. Gdy parametr zostanie zaznaczony, wówczas wszystkie bannery zapisane na danym slocie zostaną usunięte i zastąpione bannerami z importowanego szablonu. Krok 3. Wybierz plik XML swojego szablonu i naciśnij Importuj szablon. Krok 4. Teraz możesz podejrzeć zmiany, jakie zaszły w wyglądzie Twojego e-Sklepu. W tym celu możesz podejrzeć zmiany w czasie rzeczywistym lub przejść w tryb incognito. Pamiętaj, że wszelkie zmiany zostaną opublikowane po pełnej synchronizacji, która wciągu 10 minut zgodnie
z wyświetlanym komunikatem. Gotowe! Twój szablon został zaimportowany do e-Sklepu. Aby zaktualizować szablon Rubin, Szafir, Topaz lub One Page Shop należy skorzystać z aktualnej wersji Kreatora Wyglądu, do którego odnośnik znajduje się w panelu administracyjnym e-Sklepu w sekcji Wygląd sklepu/ Kreator wyglądu. Istnieją dwie możliwości aktualizacji tych szablonów: Wyeksportowany z Kreatora Wyglądu szablon należy wgrać do e-Sklepu. W panelu administracyjnym e-Sklepu, w sekcji Wygląd sklepu znajduje się bezpośredni odnośnik do importu szablonu z Kreatora wyglądu. Szczegółowa instrukcja dostępna jest w artykule Jak wyeksportować i zaimportować swój szablon w e-Sklepie? Podczas dokonywania modyfikacji w szablonach zachęcamy do skorzystania z naszego Centrum Pomocy. Aby zaktualizować szablon Bursztyn, Agat lub Opal należy w panelu administracyjnym e-Sklepu przejść do sekcji Wygląd Sklepu/ Gotowe szablony Comarch, w której znajdują się najnowsze wersje szablonów dostępne do zainstalowania. Z wyświetlonej listy należy wybrać szablon, który ma zostać wgrany do e-Sklepu. W tym celu wybierz przycisk Chcę pobrać i zainstalować szablon: Następnie wyświetli się nowe okno, w którym należy: Aby nowy szablon pojawił się w e-Sklepie należy upewnić się, czy jest on aktywny oraz ustawiony jako domyślny. W sekcji Wygląd sklepu/Ustawienia/Edytuj ustawienia zaawansowane w zakładce Szablon znajdują się odpowiedzialne za to parametry, które powinny być zaznaczone. Wszelkie zmiany należy zapisać, a następnie opublikować. Modyfikacja wszystkich bannerów dostępnych w szablonach Comarch możliwa jest w panelu administracyjnym w obszarze Wygląd sklepu> Ustawienia> Bannery. Informacje odnośnie domyślnej, tradycyjnej modyfikacji bannerów znajdują się w artykule: Bannery
Odnajdź banner na liście nazwany "promotionalBanner", a następnie wybierz go, aby edytować i dodać zdjęcie Jednym z kluczowych elementów, który przyczynia się do sukcesu w branży e-commerce jest oprawa wizualna sklepu. W dużym stopniu to właśnie ona wpływa na całościowe wrażenia użytkowników i ich proces zakupowy. Dzięki narzędziu - Kreator Wyglądu Comarch e-Sklep, właściciele sklepów mają możliwość stworzyć swój własny, niepowtarzalny szablon, który będzie dostosowany do ich preferencji oraz do potrzeb użytkowników. Kreator jest dostępny pod linkiem: Kreator szablonu Comarch e-Sklep. Odnośnik znajduje się w panelu administracyjnym e-Sklepu Wygląd sklepu > Kreator wyglądu. Kreator jest intuicyjnym narzędziem służącym do tworzenia szablonów. Jego obsługa nie wymaga umiejętności technicznych ani programowania. Wszystko odbywa się na poziomie strony internetowej, gdzie z gotowych elementów za pomocą metody "przeciągnij i upuść" możesz zaprojektować wygląd swojego sklepu. Dodatkowo, stworzony szablon jest dostosowany do wyświetlania na różnych urządzeniach (RWD). Aby móc używać tego narzędzia należy posiadać: Aby rozpocząć tworzenie szablonu należy przejść na stronę Kreatora Wyglądu. Wyświetlony zostanie powitalny komunikat. Na początku należy określić, dla jakiego modelu sprzedaży projektujesz szablon dla swojego e-Sklepu. Do wyboru masz sprzedaż detaliczną lub hurtową. Sprzedaż detaliczna przeznaczona jest dla użytkowników, którzy chcą sprzedawać swoje produkty głównie klientom indywidualnym. Wybierz odpowiedni model, a następnie naciśnij przycisk Rozpocznij pracę. Po jego kliknięciu będzie można stworzyć nowy szablon lub edytować jeden z kilku gotowych. Gdy już wybierzesz wersję językową dla swojego Kreatora warto zacząć od nazwania swojego przyszłego szablonu. Można to zrobić klikając Jak wgrać własny szablon do Comarch e-Sklep?
Własny szablon. Jak go wgrać?


Polityka bezpieczeństwa haseł
Jak wykonać kopię bezpieczeństwa mojego szablonu?
Jak wykonać kopię bezpieczeństwa mojego szablonu?
Oprawa wizualna sklepu jest bardzo ważnym elementem podczas prowadzenia sprzedaży w sieci. Narzędzie Kreator Wyglądu Comarch e-Sklep umożliwia stworzenie własnych, niepowtarzalnych szablonów wedle preferencji i potrzeb użytkowników. W intuicyjnym narzędziu jakim jest Kreator, będziesz w stanie zaprojektować wszelkie niezbędne funkcje, takie jak stronę główną, listę towarów, szczegóły towaru oraz koszyk.
Jeśli Twój projekt szablonu został zaimportowany do e-Sklepu oraz skonfigurowany wedle Twoich preferencji (dodanie grafik do bannerów, modyfikacje kolorystyczne) masz możliwość jego zapisania, co będzie stanowić kopię bezpieczeństwa.
Kopia bezpieczeństwa to dane, które w każdej chwili można odtworzyć w przypadku ich utracenia (np. poprzez przypadkowe usunięcie) lub częstego wprowadzania zmian. Dane, jakie może zawierać kopia bezpieczeństwa to szablon, który wcześniej został przez nas wygenerowany wraz z bannerami. Zaleca się wykonywanie kopii zapasowych oraz zabezpieczenie ich hasłem. Każda wykonana kopia zapasowa powinna zostać zapisana w bezpiecznym i dogodnym dla Użytkownika miejscu.
Jak wykonać kopię bezpieczeństwa?
W Panelu Administracyjnym przejdź do sekcji Wygląd sklepu/ Ustawienia/ Edytuj ustawienia zaawansowane, a następnie przejdź do zakładki Eksport szablonu.
W pustych polach znajdujących się w zakładce Eksport szablonu, opcjonalnie przed eksportem możesz uzupełnić powyższe dane, tj. Autora szablonu, e-mail, telefon i stronę WWW sklepu.
Aby wyeksportować szablon, kliknij Eksportuj szablon.
Wyeksportowany plik jest w formacie XML, a jego nazwa zawiera szablon oraz wersję, co znacznie ułatwia zarządzanie kopiami bezpieczeństwa w przypadku wykonywania ich regularnie.
Jak wyeksportować bannery?
Bannery, które znajdują się w Twoim sklepie możesz również wyeksportować tworząc kopię bezpieczeństwa. W tym celu także musisz przejść do Wygląd sklepu/ Ustawienia/ Edytuj ustawienia zaawansowane, a następnie do zakładki Eksport szablonu.
Obok przycisku Eksportuj szablon, znajduje się przycisk Eksportuj bannery, który musisz kliknąć aby bannery, które posiadasz w e-Sklepie zostały weksportowane.
Kopia bezpieczeństwa jest również zapisywana w formacie XML, a nazwa pliku wskazuje na szablon, wersję i zawartość bannerów.
Jak zabezpieczyć hasłem swój szablon?
Jeśli chcesz, aby Twój szablon został zabezpieczony hasłem, a znajomość hasła była niezbędna podczas zaimportowania tego szablonu do e-Sklepu, musisz w tym celu przejść w Panelu Administracyjnym do sekcji Wygląd sklepu/ Ustawienia/ Ustawienia zaawansowane, następnie do zakładki Szablon.
W tym miejscu nadasz hasło dla swojego szablonu.
Zgodnie z Polityką bezpieczeństwa haseł, hasło powinno:
Jeśli wyeksportujesz szablon, który zabezpieczyłeś hasłem – podczas importu tego szablonu, będziesz musiał podać nadane wcześniej hasło. Jego brak uniemożliwi import szablonu, a podanie błędnego hasła spowoduje nieprawidłowe działanie szablonu po imporcie.
Więcej informacji znajdziesz:
Obsługa Tłumacza Google w szablonach
Obsługa Tłumacza Google w szablonach
W Comarch e-Sklep możesz ułatwić zakupy swoim Klientom udostępniając możliwość tłumaczenia stron z pomocą Tłumacza Google. Opcja dotyczy wszystkich szablonów Comarch.
Jak włączyć Tłumacz Google w szablonie?
Szablon Szafir, Bursztyn, Agat, Opal
W panelu administracyjnym przejdź do Wygląd sklepu/ Ustawienia, a następnie w sekcji Ustawienia Dodatkowe oznacz parametr „Zezwól na tłumaczenie strony przez Tłumacz Google”:
Wprowadzone zmiany zapisz i opublikuj.
Szablon Topaz, Szafir, One Page Shop
W panelu administracyjnym przejdź do Wygląd sklepu/ Ustawienia/ Dodatkowe i oznacz parametr Zezwól na tłumaczenie strony przez Tłumacz Google:
Wprowadzone zmiany zapisz i opublikuj.
Po zapisaniu i publikacji wprowadzonych zmian, na stronie Twojego sklepu u góry wyświetli się Tłumacz Google pozwalający na wybór języka.

Polecane produkty, kategorie oraz bannery w wyszukiwarce
Wstęp
Parametry wyszukiwania stanowią istotny element w funkcjonowaniu sklepu internetowego. Wygodne oraz efektywne przeszukiwanie asortymentu zachęca klientów do kontynuacji zakupów. Aktualnie, po kliknięciu w pole wyszukiwania, klienci natychmiast uzyskują dostęp do polecanych produktów, kategorii i wybranych marek. Nowa funkcja nie tylko ułatwia skuteczne przeglądanie oferty, ale również pełni rolę skutecznego narzędzia do promowania asortymentu. Dzięki łatwemu dostępowi do rekomendacji, zwiększasz atrakcyjność swojego e-Sklepu, umożliwiając klientom bardziej intuicyjne odkrywanie interesujących produktów.
Wyżej opisaną funkcjonalność możesz włączyć w dwóch miejscach:
Jak zaktualizować szablon do najnowszej wersji?
Jak zaktualizować szablon do najnowszej wersji?
Wstęp
W e-Sklepie możesz dowolnie zmieniać szablony. Możesz je także modyfikować oraz aktualizować. Wiąże się to z wgraniem zupełnie nowego szablonu na miejsce starego. Aktualizacja wyglądu powoduje zmianę dotychczasowych ustawień. Przed każdą aktualizacją polecamy utworzyć odpowiednią dla siebie instrukcję, która będzie zawierała Twoje ustawienia kolorów czy modyfikacje w kodzie szablonu. Możesz także wyeksportować dotychczasowy szablon, aby stanowił on kopię bezpieczeństwa.
Jak zaktualizować szablon?
Instrukcja aktualizacji szablonów: Rubin, Szafir, Topaz i One Page Shop (dostępnych w Kreatorze Wyglądu)
Instrukcja aktualizacji szablonów: Bursztyn, Agat i Opal.


Instrukcja aktualizacji innych szablonów (niż szablonów Comarch) oraz aktualizacji szablonów z własnymi modyfikacjami
Jeżeli w swoim Comarch e-Sklepie korzystasz z własnego szablonu, czyli innego niż gotowe szablony Comarch, to powinieneś jego aktualizację przeprowadzić we własnym zakresie lub zlecić ją firmie/ osobie, która dla Ciebie przygotowała ten szablon.
W przypadku, gdy korzystasz z gotowego szablonu Comarch, ale wprowadziłeś do niego indywidualne zmiany (poza Kreatorem Wyglądu), to również we własnym zakresie (lub ze wsparciem autora zmian) musisz zadbać o aktualizację swojej wersji szablonu.Wybór szablonu startowego
Wstęp
W przypadku pierwszej konfiguracji, na stronie sklepu nie ma zaimportowanego żadnego z szablonów. Rozpoczynając pracę z Comarch e-Sklep, przy pierwszym wejściu na adres Twojego sklepu, otworzy się strona z wyborem szablonu graficznego.
First run
Po wejściu na sklep pokaże się widok „First Run”. Umożliwia on pierwsze dostosowanie wyglądu strony.
W widoku "First run" będziesz mógł stworzyć swój własny szablon w Kreatorze wyglądu lub wybrać spośród darmowych wzorów przygotowanych przez Comarch:
Kreator wyglądu
Aby stworzyć nowy szablon w panelu administracyjnym e-Sklep w zakładkę Wygląd sklepu/ Kreator wyglądu, zostaniesz przekierowany na stronę kreatora, następnie wybierz, czy chcesz stworzyć szablon dla sklepu B2B czy B2C.
W kolejnym korku będziesz mógł wybrać, czy opcję Dokończ poprzedni szablon bądź Stwórz nowy projekt
Następnie w przypadku tworzenia nowego projektu w oknie wyboru będziesz mógł wskazać właściwy szablon, który będziesz mógł dostosować do własnych preferencji względem dostępnych opcji.
Następnie należy wgrać pobrany szablon klikając w Wybierz plik XML. Po wgraniu szablonu kliknij Importuj Szablon.
Gotowe szablony Comarch
Gotowe szablony Comarch przy pierwszym uruchomieniu sklepu można wgrać na dwa sposoby:
1. Przy pierwszym wejściu na adres Twojego sklepu
Pojawi się strona z gotowymi szablonami:
2. Wybór szablonu z panelu administracyjnego Comarch e-Sklep
Standardowe szablony dostępne są w panelu Comarch e-Sklep w zakładce Wygląd sklepu/ Gotowe szablony Comarch.
Po kliknięciu w Chcę pobrać i zainstalować szablon, szablon zostanie zapisany. Pojawi się komunikat o pomyślnym przeprowadzeniu zmian.
Wybrany szablon można w każdej chwili zmienić w panelu administracyjnym e-Sklepu. Podstawowe informacje o naszych darmowych szablonach znajdziesz w artykule Gotowe szablony Comarch. Instalacja szablonów z poziomu panelu administracyjnego opisana w artykule Szablony w sklepie.
3. Wgranie własnego szablonu do Comarch e-Sklep
W e-Sklepie możesz także wgrać własny szablon. Jeśli został on już przez Ciebie utworzony
i wygenerowany w Kreatorze Wyglądu, to wystarczy wgrać go do Twojego e-Sklepu, aby móc cieszyć się jego unikalnym wyglądem. Można to zrobić za pomocą opcji Wygląd sklepu/ Zaimportuj szablon z Kreatora wyglądu.
Gotowe szablony Comarch
W obszarze Wygląd sklepu/ Gotowe szablony Comarch znajdziesz listę wszystkich darmowych szablonów Comarch.
Poprzez link Zobacz demo Możesz obejrzeć sklepy demonstracyjne przygotowane dla każdego z szablonów oraz pobrać i zainstalować wybrany przez siebie szablon.
Szczegóły dotyczące edycji szablonów znajdziesz w kategorii Dostosowanie wyglądu.
Wspierane przeglądarki
(minimalna wersja)
Kreator wyglądu szablonu Rubin
Wstęp
Zastosowanie
Kto może używać tej opcji?
Zaczynamy!
Jak zaprojektować układ szablonu?
W sytuacji gdy wcześniej tworzony był już projekt, widoczny będzie przycisk umożliwiający dokończenie szablonu lub rozpoczęcie nowego projektu.
Opcja Nowy szablon umożliwia zbudowanie od początku nowego szablonu Rubin według Twojego indywidualnego pomysłu.
W ramach szablonów B2B do wyboru zostały udostępnione szablon Rubin oraz szablon Szafir w trzech wersjach kolorystycznych. Są to gotowe wersje szablonów z możliwością ich edycji. Dzięki temu nie musisz zaczynać pracy od zera – wystarczy, że wybierzesz jeden z dostępnych szablonów, a następnie łatwo dopasujesz go do swoich wymagań. Podwójne kliknięcie na wybrany kafelek pozwala przejść do kolejnego etapu.
W trzech prostych krokach zapoznasz się z podstawowymi funkcjami kreatora. Po ich przejściu, możesz rozpocząć dalsze dostosowywanie szablonu lub od razu wygenerować przygotowaną wersję szablonu Rubin.


Od czego zacząć?
Wybierz preferowaną wersję językową Kreatora i rozpocznij tworzenie swojego szablonu. Dostępne opcje to: język polski, angielski, niemiecki oraz francuski. Aby zmienić język, kliknij strzałkę obok flagi i nazwy języka w lewym dolnym rogu. Po wybraniu języka, przy podglądzie na pełnym ekranie, szablon będzie wyświetlany w wybranej wersji językowej.
Krok 1 - Strona główna


Nagłówek
Sekcja „Nagłówek” prezentuje jeden widok. Nagłówek dodatkowo można konfigurować w zakresie funkcji związanych ze składaniem Zapytań o wycenę.
Bannery
Sekcja "Bannery" pozwala dodać slider. Informacje dotyczące rekomendowanych rozmiarów obrazków w bannerach opisaliśmy w tym artykule.
Widget
Sekcja "Widget" pozwala dodać jeden element, który pozwoli zaprezentować następujące funkcj:
Banner pop-up
Sekcja „Banner pop-up” pozwala na dodanie bannera w formie popupu. Banner ten jest możliwy do wyświetlenia jedynie na stronie głównej e-Sklepu.
Proponowane


Podczas tworzenia szablonu w Kreatorze wyglądu Rubin po dodaniu sekcji towary polecane można określić typ wyświetlanych produktów. Nazwa sekcji jest zależna oraz adekwatna do określonego typu. Daną nazwę można modyfikować w tłumaczeniach szablonu. Dodatkowo każdy wybrany typ jest podlinkowany do wszystkich produktów wskazanego rodzaju.
W zależności od wybranego typu, edycji parametrów wyświetlania dokonasz w panelu administracyjnym e-Sklepu. Z kolei informację o typie towaru ustawisz w systemie ERP.
Typy towarów edytowane z poziomu panelu administracyjnego Comarch e-Sklep:
Dodawanie informacji o typie produktu w systemie ERP dotyczy:
Newsletter
Dla newslettera możliwe jest ustawienie stałej sekcji na stornie głównej lub newslettera w postaci popupu:
Slider marek
Sekcja "Slider marek" pozwala na dodanie do szablonu widoku slidera z logo marek. W sliderze wyświetlane są logotypy marek, które zostały zaimportowane z systemu ERP. Po kliknięciu na wybrane logo, użytkownik zobaczy listę produktów przypisanych do danej marki. Sekcje te mogą być umieszczone w dowolnym miejscu na stronie głównej szablonu.
Slider producentów
Sekcja "Slider producentów" pozwala na dodanie do szablonu widoku slidera z logo producentów. W sliderze wyświetlane są logotypy producentów zaimportowane z systemu ERP. Po kliknięciu na wybrane logo, użytkownik zobaczy listę produktów przypisanych do danego producenta. Sekcje te mogą być umieszczone w dowolnym miejscu na stronie głównej szablonu.
Aktualności
Sekcja umożliwia zamieszczenie aktualności na stronie głównej w formie listy artykułów z bloga. Tekst wprowadza się w panelu administracyjnym przechodząc do zakładki Marketing > Blog.
Banner infografika
Sekcja "Banner infografika" pozwala na zaprezentowanie korzyści z zakupów w Twoim sklepie. Dotyczą one obszarów: informacji o dostawie, płatnościach czy obowiązujących rabatach rabatach. Banner możesz dodać tylko do strony głównej. Obrazki są możliwe do edycji w ramach elementu homepage-small w panelu administracyjnym e-Sklepu w obszarze: Wygląd sklepu > Ustawienia > Bannery.
Stopka
Sekcja "Stopka" zawiera jeden widok do wyboru.
W widoku Stopki widnieją strony statyczne, którymi można zarządzać w sekcji Wygląd Sklepu/ Ustawienia/ Stopka. Dla każdej z sekcji wyświetla się jej utworzona w panelu nazwa. W stopce znajduje się komunikat @Comarch SA 2025. All rights reserved Powered by Comarch e-Sklep. Dodatkowo istnieje możliwość umieszczenia w stopce odnośników do mediów społecznościowych oraz ich ikony. Możemy dodać ikony naszych dostawców oraz form płatności wykorzystywanych w sklepie w ramach ustawień Wygląd Sklepu > Ustawienia > Bannery w sekcjach LogisticsPartners oraz PaymentOperators.
Strona główna wymaga dwóch podstawowych elementów: nagłówka oraz stopki. Pozostałe elementy nie są obowiązkowe.
Krok 2 - Lista towarów

Banner
Sekcja "Banner" daje możliwość dodania do listy slideru, dzięki czemu masz możliwość wzbogacenia kategorii towarowych o obrazki.
Panel filtra
Sekcja "Panel filtra" umożliwia wybór liczby wyświetlanych wartości filtrów a także o tym czy filtrowanie powinno odbywać się po klinićiu przycisku "Zastosuj".
Z tego poziomu możemy także wybrać elementy prezentowane w panelu filtrów.
Opis kategorii
W sekcji możesz zdecydować, czy opis ma być domyślnie zwinięty lub rozwinięty.

Lista towarów
W Kreatorze Szablonu Rubin dostępny jest jeden widok listy towarów:
Krok 3 - Koszyk
Po utworzeniu szczegółów towaru przechodzimy do ustawień koszyka dostępnego w e-Sklepie.
Dodatkowo na stronie koszyka możesz wskazać sposób prezentacji przeliczników jednostek miary:
Style
Jak zmienić kolory, czcionki oraz ikony?

Motyw kolorystyczny
Czcionki
Ikony
Funkcja cofania zmian
Funkcja odpowiadająca za cofanie oraz powtórzenie wprowadzonych zmian oznaczona jest symbolem strzałek, zapamiętuje ona do 30 ostatnich modyfikacji szablonu. Nieaktywna strzałka oznacza, że aktualnie nie ma dostępnych kroków.
Mamy możliwość przywracania zmian: dodawania/ usuwania elementów oraz edycji ich położenia, a także stylu w szablonie.
Generowanie i wgrywanie szablonu


Aktualizacja szablonu
W Kreatorze szablonu Rubin znajduje się opcja aktualizacji poprzednich wersji szablonu. W celu zaktualizowania całego szablonu należy wgrać plik z poprzednim szablonem do Kreatora szablonu Rubin według tej instrukcji:
Więcej informacji
Graficzna prezentacja atrybutów na karcie produktu oraz na liście towarów w szablonie Rubin
Wstęp
Funkcja umożliwia prezentację atrybutów w formie kolorów, miniatur oraz kafelków z tekstem. Dotyczy ona towarów z wariantami. Chcesz, aby w Twoim e-sklepie atrybuty były prezentowane graficznie? Pokażemy Ci, jak w prosty sposób zmienić ustawienia, aby to umożliwić.
Konfiguracja atrybutów graficznych
Aby skonfigurować atrybuty graficzne, przejdź w Panelu administracyjnym do opcji Wygląd sklepu > Ustawienia, a następnie do sekcji Lista towarów > Strona główna. W tym miejscu możesz wybrać sposób prezentacji atrybutów: jako kolory, miniatury zdjęć lub warianty towarów.
Wariant - Kolory
Przykładowa konfiguracja wariantu - Kolory
W systemie ERP stwórz atrybut o nazwie Kolor w formacie lista, a następnie udostępnij go do Comarch e-Sklep.
Wypełnij pozycje listy, wpisując nazwy kolorów, np. żółty, fioletowy, pomarańczowy itd. Następnie przypisz utworzony atrybut do odpowiedniego towaru, który zostanie przesłany do e-Sklepu. Podczas przypisywania tego atrybutu do towaru pozostaw pole „Wartość” puste, aby umożliwić wybór wariantów kolorystycznych.
Po zakończeniu powyższych kroków przeprowadź synchronizację z e-Sklepem.
Prezentacja wariantu Kolory na stronie e-Sklepu
Na liście towarów:
Na szczegółach towarów - dla wszystkich wariantów
Wariant - Miniatury (tylko dla towarów zgrupowanych - "fantomów")
Wybór wariantu miniatury umożliwia graficzną prezentację wariantów towarów za pomocą pierwszego zdjęcia każdego towaru zgrupowanego (fantoma). Ze względu na specyfikę działania (każdy towar musi posiadać własne zdjęcie), funkcja ta jest dostępna wyłącznie dla towarów zgrupowanych.
Przykładowa konfiguracja wariantu - Miniatury
W systemie Comarch ERP stwórz towar zgrupowany zgodnie z dokumentacją Comarch e-Sklep.
Poniższy wariant opiera się na konfiguracji atrybutu przedstawionej w sekcji dotyczącej graficznej prezentacji kolorów.
Prezentacja wariantu Miniatury na stronie e-Sklepu
Na liście towarów:

Na szczegółach towarów:
Wariant - Tekst
W ramach konfiguracji wariantów z tekstem wystarczy zaznaczyć wskazane ustawienie:
Po zapisaniu i opublikowaniu zmian kafelki z tekstem zostaną wyświetlone w odpowiednich miejscach w ramach e-Sklepu.
Przykładowa konfiguracja wariantu z tekstem
W systemie Comarch ERP stwórz towar zgrupowany zgodnie z dokumentacją Comarch e-Sklep.
Poniższy wariant wykorzystuje konfigurację atrybutu zaprezentowaną w sekcji poświęconej graficznej prezentacji kolorów.
Prezentacja wariantu z tekstem na stronie e-Sklepu
Na liście towarów:

Na szczegółach towarów:
Instrukcja: Jak dostosować samodzielnie szablon do szybkich zwrotów w e-Commerce?
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> – {{ 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/.
Szablon Rubin ( dedykowany dla B2B)
Kreator wyglądu szablonu Rubin
Wstęp
Zastosowanie
Kto może używać tej opcji?
Zaczynamy!
Jak zaprojektować układ szablonu?
W sytuacji gdy wcześniej tworzony był już projekt, widoczny będzie przycisk umożliwiający dokończenie szablonu lub rozpoczęcie nowego projektu.
Opcja Nowy szablon umożliwia zbudowanie od początku nowego szablonu Rubin według Twojego indywidualnego pomysłu.
W ramach szablonów B2B do wyboru zostały udostępnione szablon Rubin oraz szablon Szafir w trzech wersjach kolorystycznych. Są to gotowe wersje szablonów z możliwością ich edycji. Dzięki temu nie musisz zaczynać pracy od zera – wystarczy, że wybierzesz jeden z dostępnych szablonów, a następnie łatwo dopasujesz go do swoich wymagań. Podwójne kliknięcie na wybrany kafelek pozwala przejść do kolejnego etapu.
W trzech prostych krokach zapoznasz się z podstawowymi funkcjami kreatora. Po ich przejściu, możesz rozpocząć dalsze dostosowywanie szablonu lub od razu wygenerować przygotowaną wersję szablonu Rubin.


Od czego zacząć?
Wybierz preferowaną wersję językową Kreatora i rozpocznij tworzenie swojego szablonu. Dostępne opcje to: język polski, angielski, niemiecki oraz francuski. Aby zmienić język, kliknij strzałkę obok flagi i nazwy języka w lewym dolnym rogu. Po wybraniu języka, przy podglądzie na pełnym ekranie, szablon będzie wyświetlany w wybranej wersji językowej.
Krok 1 - Strona główna


Nagłówek
Sekcja „Nagłówek” prezentuje jeden widok. Nagłówek dodatkowo można konfigurować w zakresie funkcji związanych ze składaniem Zapytań o wycenę.
Bannery
Sekcja "Bannery" pozwala dodać slider. Informacje dotyczące rekomendowanych rozmiarów obrazków w bannerach opisaliśmy w tym artykule.
Widget
Sekcja "Widget" pozwala dodać jeden element, który pozwoli zaprezentować następujące funkcj:
Banner pop-up
Sekcja „Banner pop-up” pozwala na dodanie bannera w formie popupu. Banner ten jest możliwy do wyświetlenia jedynie na stronie głównej e-Sklepu.
Proponowane


Podczas tworzenia szablonu w Kreatorze wyglądu Rubin po dodaniu sekcji towary polecane można określić typ wyświetlanych produktów. Nazwa sekcji jest zależna oraz adekwatna do określonego typu. Daną nazwę można modyfikować w tłumaczeniach szablonu. Dodatkowo każdy wybrany typ jest podlinkowany do wszystkich produktów wskazanego rodzaju.
W zależności od wybranego typu, edycji parametrów wyświetlania dokonasz w panelu administracyjnym e-Sklepu. Z kolei informację o typie towaru ustawisz w systemie ERP.
Typy towarów edytowane z poziomu panelu administracyjnego Comarch e-Sklep:
Dodawanie informacji o typie produktu w systemie ERP dotyczy:
Newsletter
Dla newslettera możliwe jest ustawienie stałej sekcji na stornie głównej lub newslettera w postaci popupu:
Slider marek
Sekcja "Slider marek" pozwala na dodanie do szablonu widoku slidera z logo marek. W sliderze wyświetlane są logotypy marek, które zostały zaimportowane z systemu ERP. Po kliknięciu na wybrane logo, użytkownik zobaczy listę produktów przypisanych do danej marki. Sekcje te mogą być umieszczone w dowolnym miejscu na stronie głównej szablonu.
Slider producentów
Sekcja "Slider producentów" pozwala na dodanie do szablonu widoku slidera z logo producentów. W sliderze wyświetlane są logotypy producentów zaimportowane z systemu ERP. Po kliknięciu na wybrane logo, użytkownik zobaczy listę produktów przypisanych do danego producenta. Sekcje te mogą być umieszczone w dowolnym miejscu na stronie głównej szablonu.
Aktualności
Sekcja umożliwia zamieszczenie aktualności na stronie głównej w formie listy artykułów z bloga. Tekst wprowadza się w panelu administracyjnym przechodząc do zakładki Marketing > Blog.
Banner infografika
Sekcja "Banner infografika" pozwala na zaprezentowanie korzyści z zakupów w Twoim sklepie. Dotyczą one obszarów: informacji o dostawie, płatnościach czy obowiązujących rabatach rabatach. Banner możesz dodać tylko do strony głównej. Obrazki są możliwe do edycji w ramach elementu homepage-small w panelu administracyjnym e-Sklepu w obszarze: Wygląd sklepu > Ustawienia > Bannery.
Stopka
Sekcja "Stopka" zawiera jeden widok do wyboru.
W widoku Stopki widnieją strony statyczne, którymi można zarządzać w sekcji Wygląd Sklepu/ Ustawienia/ Stopka. Dla każdej z sekcji wyświetla się jej utworzona w panelu nazwa. W stopce znajduje się komunikat @Comarch SA 2025. All rights reserved Powered by Comarch e-Sklep. Dodatkowo istnieje możliwość umieszczenia w stopce odnośników do mediów społecznościowych oraz ich ikony. Możemy dodać ikony naszych dostawców oraz form płatności wykorzystywanych w sklepie w ramach ustawień Wygląd Sklepu > Ustawienia > Bannery w sekcjach LogisticsPartners oraz PaymentOperators.
Strona główna wymaga dwóch podstawowych elementów: nagłówka oraz stopki. Pozostałe elementy nie są obowiązkowe.
Krok 2 - Lista towarów

Banner
Sekcja "Banner" daje możliwość dodania do listy slideru, dzięki czemu masz możliwość wzbogacenia kategorii towarowych o obrazki.
Panel filtra
Sekcja "Panel filtra" umożliwia wybór liczby wyświetlanych wartości filtrów a także o tym czy filtrowanie powinno odbywać się po klinićiu przycisku "Zastosuj".
Z tego poziomu możemy także wybrać elementy prezentowane w panelu filtrów.
Opis kategorii
W sekcji możesz zdecydować, czy opis ma być domyślnie zwinięty lub rozwinięty.

Lista towarów
W Kreatorze Szablonu Rubin dostępny jest jeden widok listy towarów:
Krok 3 - Koszyk
Po utworzeniu szczegółów towaru przechodzimy do ustawień koszyka dostępnego w e-Sklepie.
Dodatkowo na stronie koszyka możesz wskazać sposób prezentacji przeliczników jednostek miary:
Style
Jak zmienić kolory, czcionki oraz ikony?

Motyw kolorystyczny
Czcionki
Ikony
Funkcja cofania zmian
Funkcja odpowiadająca za cofanie oraz powtórzenie wprowadzonych zmian oznaczona jest symbolem strzałek, zapamiętuje ona do 30 ostatnich modyfikacji szablonu. Nieaktywna strzałka oznacza, że aktualnie nie ma dostępnych kroków.
Mamy możliwość przywracania zmian: dodawania/ usuwania elementów oraz edycji ich położenia, a także stylu w szablonie.
Generowanie i wgrywanie szablonu


Aktualizacja szablonu
W Kreatorze szablonu Rubin znajduje się opcja aktualizacji poprzednich wersji szablonu. W celu zaktualizowania całego szablonu należy wgrać plik z poprzednim szablonem do Kreatora szablonu Rubin według tej instrukcji:
Więcej informacji
Szablon Rubin - podstawowe informacje
Wstęp
Szablon Rubin jest dedykowany dla sklepów w wersji B2B. Wyróżnia się nowoczesnym wyglądem zgodnym z najnowszymi trendami oraz intuicyjną nawigacją, co pozwala na szybkie i komfortowe zakupy. Oferuje funkcje dedykowane dla modelu B2B, w tym umożliwia wykorzystanie Comarch e-Sklep Sync do wyświetlania zamówień, faktur i wydań zewnętrznych z systemu ERP. Przy pomocy Kreatora wyglądu, można dostosować układ elementów szablonu względem indywidualnych oczekiwań. Elastyczność konfiguracji szablonu sprawia, że jest on idealnym rozwiązaniem dla zróżnicowanych potrzeb biznesowych.
Główne wyróżniki szablonu:
Obsługa sprzedaży dla klientów biznesowych
Jako że szablon Rubin został zaprojektowany, aby umożliwić sprzedaż dla klientów biznesowych, obsługuje on funkcje dostosowane do modelu B2B. W ramach przykładowych udogodnień oferowanych przez szablon należy wymienić możliwości, takie jak:
Konfiguracja szablonu Rubin w panelu administracyjnym
Z poziomu panelu administracyjnego możesz dokonywać konfiguracji opcji wyglądu szablonu. Po przejściu do sekcji Wygląd sklepu > Ustawienia dostępne są ustawienia dla czterech zakładek:
Strona główna
Możliwe jest także zaprezentowania zapisu do newslettera w formie banneru.
Lista towarów
1. Widoki list towarów
Lista towarów zawiera trzy różne widoki. W zakładce Wygląd sklepu > Ustawienia > Lista towarów jest możliwość wybrania domyślnego widoku:
Każdy z nich ma inne zastosowanie i cechy charakterystyczne:
W przypadku ustawień dla widoków szybkiej listy i rozbudowanych kafli, istnieje możliwość ustalenia ilości wyświetlanych atrybutów:
2. Graficzna prezentacja wariantów
W ramach kafli możesz ustawić graficzną prezentację wariantów – po najechaniu na kafel zostaną zaprezentowane atrybuty w postaci:
3. Opis kategorii
Na liście towarów możesz wyświetlić także opis danej kategorii dodany po stronie systemu Comarch ERP. Jest on zaprezentowany w szablonie w następującej formie:
Jeżeli został on wypełniony, możesz zdecydować czy ma być on domyślnie zwinięty czy rozwinięty:
4. Panel filtra
W ramach ustawień listy towarów do dostosowania możliwe są też filtry. Z tego poziomu dostępne do skonfigurowania są następujące opcje:
W przypadku takiego ustawienia przed wskazanym przyciskiem będzie prezentowane pięć wartości filtra – reszta zostanie zaprezentowana po rozwinięciu:

Wskazane ustawienie wpływa na to, czy po wejściu na listę towarów będą wskazane wartości filtrów czy zostaną one zaprezentowane dopiero po rozwinięciu:
Szczegóły towaru
1. Sekcja z towarem
W ramach szczegółów towaru możemy wyróżnić sekcje z towarem.
W jej ramach prezentowane są zdjęcia towarów oraz informacje towarze, takie jak:
2. Opis i specyfikacja
W pierwszej kolejności wskazane są opis i specyfikacja danego towaru. Wskazane elementy takie jak opis czy atrybuty towaru są możliwe do dodania po stronie systemów Comarch ERP.
3. Podmiot odpowiedzialny
Zgodnie z rozporządzeniem GPSR w szablonie możliwe jest zaprezentowanie podmiotów odpowiedzialnych. Konfiguracja w ramach wyświetlenia wskazanych informacji w szablonie została opisana w artykule: Jak dostosować e-Sklep do wymogów Rozporządzenia GPSR?
4. Opinie o towarze
Na szczegółach towaru możliwe do wyświetlenia są opinie o towarze. Sekcja jest wzbogacona o paski informujące o ilości poszczególnych ocen. Obsłużone zostało również zgłaszanie opinii zgodnie z Aktem o usługach cyfrowych DSA.
5. Pliki do pobrania
Sekcja z plikami do pobrania zwiera pliki załączone na karcie towaru po stronie systemu Comarch ERP:
6. Zestawy
W przypadku korzystania z systemu Comarch ERP Optima, do zaprezentowania na stronie szczegółów towaru możliwe są zestawy:
Dzięki wskazanej prezentacji, klient może dowiedzieć się o promocyjnej ofercie zakupu kilku towarów jednocześnie w tańszej cenie.
7. Towary podobne i akcesoria
Dla towarów skonfigurowanych w systemie Comarch ERP XL istnieje możliwość zdefiniowania Towarów podobnych lub Akcesoriów. Aby wskazana sekcja była dostępna w e-Sklepie, należy ją skonfigurować po stronie Comarch ERP XL zgodnie z fragmentem instrukcji, w którym opisano obsługę towarów podobnych lub akcesoriów dla towaru. W przypadku zamienników, możliwe jest także ich skonfigurowanie w Comarch ERP Optima.
Koszyk
Po przejściu na stronę koszyka wyświetla się podsumowanie dodanych do niego towarów. Wskazana sekcja ma na celu prezentację towarów w ramach rekomendacji zakupowych, aby zachęcić klienta do dalszych zakupów.
W szablonie Rubin składanie zamówienia zostało zaprojektowane tak, aby użytkownik mógł w jednym kroku wypełnić niezbędne dane:
Po uzupełnieniu powyższych informacji i zakupie towaru można przejść do podsumowania, w którym prezentowane są m.in. wcześniej wprowadzone dane klienta.
W ramach koszyka istnieje możliwość włączenia prezentacji wartości rabatów dla danego zamówienia, zgodnie z ustawieniem:

Dodatkowo, w przypadku dostaw, możliwe jest także włączenie edycji daty dostawy:

Zapytanie o wycenę
W modelu sprzedaży B2B często spotyka się spersonalizowane wyceny dla różnych kontrahentów. Na wysokość cen wpływają takie czynniki jak zmieniające się ceny towarów, ilość zamówionych produktów, sytuacja rynkowa oraz szczególne warunki ustalone w umowach między sprzedawcą a klientem. Z tego względu w szablonie Rubin istnieje możliwość dodawania towarów do zapytania o wycenę zgodnie z ustawieniem:
Do zapytania o wycenę możesz przejść z poziomu nagłówka, klikając ikonę "Zapytanie o wycenę". Po kliknięciu w szablonie prezentowany jest panel boczny z towarami dodanymi do zapytania:
Po przejściu do szczegółów, dostępny jest formularz:
Obsługa zapytań jest w dalszej kolejności procesowana po stronie panelu administracyjnego. W dalszej kolejności zarówno zapytanie o wycenę, jak i utworzona z niego oferta sprzedaży jest dostępna dla klienta z poziomu Profilu klienta.
Profil klienta
1. Ustawienia profilu klienta
W przypadku ustawień profilu klienta w ramach: Wygląd sklepu > Ustawienia > Zarządzanie strefą klienta widoczna jest lista elementów zamieszczonych w e-Sklepie. Lista składa się z następujących ustawień:
2. Dokumenty dostępne w profilu klienta
W ramach profilu klienta dostępne są dokumenty związane z procesem zamówienia w e-Sklepie.
Z tego poziomu klient ma dostęp do zakładek prezentujących:
3. Zapytania o wycenę i oferty sprzedaży
Z tego poziomu klient ma dostęp również do listy z wykonanymi zapytaniami o wycenę.
Po stworzeniu wyceny na dane zapytanie, w zakładce Oferta sprzedaży pojawi się oferta do zaakceptowania przez klienta:
Więcej informacji na temat zapytań i ofert sprzedaży można znaleźć w artykułach: Zapytaj o cenę oraz Jak stworzyć ofertę sprzedaży i zapytanie ofertowe?
4. Reklamacje/zwroty
Klient ma możliwość zareklamowania towaru po przejściu na szczegóły zamówienia. Z tego poziomu jest możliwość wybrania opcji reklamacji dla towaru:
Informacje o reklamacjach znajdą się w zakładce Reklamacje/ Zwroty:
5. Moje dane
W zakładce Moje dane klient ma możliwość weryfikacji wprowadzonych danych jako firma bądź osoba fizyczna. W tym miejscu wyświetlone są także uzupełnione adresy dostawy – wskazany jest także adres domyślny.
6. Pracownicy
We wskazanej zakładce istnieje możliwość dodania pracownika, jak również jego zablokowania.
Dodatkowo jeżeli pracownik jest przypisany do kilku firm istnieje możliwość zmiany firmy z poziomu nagłówka sklepu:
Więcej na temat pracowników w e-Sklepie możesz dowiedzieć się w artykule: Wielu pracowników kontrahenta.
7. Twoje konto
We wskazanej zakładce znajdują się dane kontaktowe klienta, jak i dane do logowania – e-mail i hasło. Z tego poziomu dostępna jest również historia konta
8. Zgody
W tym miejscu klientowi prezentowana jest lista zaakceptowanych zgód:
9. Do pobrania
Poprzez dodanie załączników do karty kontrahenta w systemie Comarch ERP XL w szablonie Rubin jest możliwe wyświetlanie listy plików do pobrania dla zalogowanych użytkowników. Są one widoczne i dostępne do pobrania w Profilu Klienta we wskazanej zakładce.
Sposób konfiguracji wskazanych plików został opisany w artykule: Lista plików Klienta – Do pobrania
10. Promocje
Dzięki wskazanej zakładce klienci mogą sprawdzić, jakie rabaty aktualnie posiadają. Widoczne w tej zakładce rabaty są przesyłane z systemu ERP oraz ustalane w Panelu Administracyjnym Comarch e-Sklep. Obsługiwane rabaty:
Aby wskazane promocje wyświetlały się w profilu klienta należy włączyć ustawienie w Wygląd sklepu > Ustawienia > Ogólne:
Informacje o konfiguracji rabatów można znaleźć w ramach artykułu: Rabaty – rodzaje i zasady wyliczania.
Listy zakupów
W szablonie Rubin dla wersji sklepu B2B możliwe jest posiadanie wielu list zakupów. Funkcja dostępna jest tylko dla zalogowanych klientów. Umożliwia ona zaplanowanie zakupów z wyprzedzeniem w uporządkowany sposób poprzez tworzenie list na różne okazje i ustawianie dla nich daty przypomnienia:
Po przejściu do list istnieje możliwość utworzenia nowej listy:

W ramach listy jest możliwa do ustawienia data przypomnienia:
Dodatkowo z tego poziomu klient ma możliwość nadania liście nazwy, przeniesienia jej do zapytania o wycenę, jak również dodania towarów z listy do koszyka.
Lookbook
Lookbook oferuje możliwość przedstawienia towarów bezpośrednio na zdjęciu w formie zaznaczonych pinezek. W szablonie Rubin możliwe jest stworzenie wielu lookbooków. Będą one w takim wypadku prezentowane w formie listy:
Szczegóły lookbooka charakteryzują się precyzyjnym oznaczeniem towarów na zdjęciu. Dzięki znacznikom umieszczonym na grafice Twoi klienci będą mogli podejrzeć towar i dodać go bezpośrednio do koszyka lub listy zakupów. Po kliknięciu na przycisk z plusem wyświetli się popup ze szczegółami towaru oraz opcją zakupu.
Blog
Szczegółowe informacje na temat konfiguracji bloga w e-Sklepie znajdują się w artykule: Jak skonfigurować blog?Graficzna prezentacja atrybutów na karcie produktu oraz na liście towarów w szablonie Rubin
Wstęp
Funkcja umożliwia prezentację atrybutów w formie kolorów, miniatur oraz kafelków z tekstem. Dotyczy ona towarów z wariantami. Chcesz, aby w Twoim e-sklepie atrybuty były prezentowane graficznie? Pokażemy Ci, jak w prosty sposób zmienić ustawienia, aby to umożliwić.
Konfiguracja atrybutów graficznych
Aby skonfigurować atrybuty graficzne, przejdź w Panelu administracyjnym do opcji Wygląd sklepu > Ustawienia, a następnie do sekcji Lista towarów > Strona główna. W tym miejscu możesz wybrać sposób prezentacji atrybutów: jako kolory, miniatury zdjęć lub warianty towarów.
Wariant - Kolory
Przykładowa konfiguracja wariantu - Kolory
W systemie ERP stwórz atrybut o nazwie Kolor w formacie lista, a następnie udostępnij go do Comarch e-Sklep.
Wypełnij pozycje listy, wpisując nazwy kolorów, np. żółty, fioletowy, pomarańczowy itd. Następnie przypisz utworzony atrybut do odpowiedniego towaru, który zostanie przesłany do e-Sklepu. Podczas przypisywania tego atrybutu do towaru pozostaw pole „Wartość” puste, aby umożliwić wybór wariantów kolorystycznych.
Po zakończeniu powyższych kroków przeprowadź synchronizację z e-Sklepem.
Prezentacja wariantu Kolory na stronie e-Sklepu
Na liście towarów:
Na szczegółach towarów - dla wszystkich wariantów
Wariant - Miniatury (tylko dla towarów zgrupowanych - "fantomów")
Wybór wariantu miniatury umożliwia graficzną prezentację wariantów towarów za pomocą pierwszego zdjęcia każdego towaru zgrupowanego (fantoma). Ze względu na specyfikę działania (każdy towar musi posiadać własne zdjęcie), funkcja ta jest dostępna wyłącznie dla towarów zgrupowanych.
Przykładowa konfiguracja wariantu - Miniatury
W systemie Comarch ERP stwórz towar zgrupowany zgodnie z dokumentacją Comarch e-Sklep.
Poniższy wariant opiera się na konfiguracji atrybutu przedstawionej w sekcji dotyczącej graficznej prezentacji kolorów.
Prezentacja wariantu Miniatury na stronie e-Sklepu
Na liście towarów:

Na szczegółach towarów:
Wariant - Tekst
W ramach konfiguracji wariantów z tekstem wystarczy zaznaczyć wskazane ustawienie:
Po zapisaniu i opublikowaniu zmian kafelki z tekstem zostaną wyświetlone w odpowiednich miejscach w ramach e-Sklepu.
Przykładowa konfiguracja wariantu z tekstem
W systemie Comarch ERP stwórz towar zgrupowany zgodnie z dokumentacją Comarch e-Sklep.
Poniższy wariant wykorzystuje konfigurację atrybutu zaprezentowaną w sekcji poświęconej graficznej prezentacji kolorów.
Prezentacja wariantu z tekstem na stronie e-Sklepu
Na liście towarów:

Na szczegółach towarów:
Instrukcja: Jak dostosować samodzielnie szablon do szybkich zwrotów w e-Commerce?
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> – {{ 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/.
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 SzafirJak skonfigurować poszczególne elementy?


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:
Nagłówek:
Kontrahenci:
W Comarch e-Sklep na promocji można wskazać:
Towary:
Aby tak skonfigurowane rabaty mogły być widoczne w e-Sklepie należy:
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:
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:
[/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:
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
Jakie są korzyści z używania dodatkowych kodów 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.
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.
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.
Na wskazanych towarach pojawi się flaga „Promocja”. Umożliwi to zaprezentowanie kuponów rabatowych w szablonie.
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:
Na szczegółach towaru widoczne są etykiety, które informują o dostępnych kodach, które Klient może wykorzystać:

Instrukcja: Jak dostosować samodzielnie szablon do szybkich zwrotów w e-Commerce?
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> – {{ 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/.
Szablon Topaz
Modyfikacja informacji wyświetlanych na kaflu towaru
oraz mozaika.
Domyślnie zaznaczona jest lista.
Aby dokonać modyfikacji informacji na kaflu, musisz przejść do zakładki „Kafle”. Jeśli korzystasz z list towarów 3 lub 4, masz możliwość edycji następujących informacji:
W przypadku list towarów 1 i 2, modyfikacja informacji jest uproszczona, ogranicza się jedynie do:

Jak dodać obrazek do nazwy kategorii i podkategorii w Comarch e-Sklep?
Wstęp
W Comarch e-Sklep możesz dodawać obrazki zarówno dla kategorii jak i dla podkategorii towarów. Odpowiednio dobrane grafiki zwiększają atrakcyjność wizualną sklepu, przyciągają wzrok klientów i zachęcają do dalszego przeglądania ofert w Twoim sklepie. Dodatkowo ułatwiają one nawigację i skracają czas poszukiwań Twoich klientów.
W tym artykule dowiesz się, w jaki sposób skonfigurować funkcję dodawania zdjęć do kategorii i podkategorii w Twoim sklepie.
Jak dodać zdjęcie do nazwy kategorii w Comarch e-Sklep?
Comarch ERP Optima



Comarch ERP XL
Wyświetlanie zdjęć kategorii w Comarch ERP XL możesz uzyskać poprzez dodanie odpowiedniego Załącznika.
Krok 1. Aby wprowadzić zdjęcie w formie załącznika przejdź w module Sprzedaż do zakładki Ogólne > Towary i wejdź w edycję wybranej grupy.
Krok 2. W zakładce Załączniki dodaj nowy załącznik ikoną Plusa. W polu Typ wskaż typ załącznika Obraz oraz zaznacz przy parametrze "Dostępność w aplikacji" e-Sklep. Następnie zaimportuj obraz ikoną z czerwoną strzałką.
3. Zmiany zapisz ikoną dyskietki oraz wykonaj synchronizację.
Comarch Betterfly
Krok 1. Zdjęcia kategorii w Comarch Betterfly możesz dodać po wybraniu Więcej funkcji > Comarch e-Sklep > Grupy w Comarch e-Sklep.
Krok 2. Aby wprowadzić obrazek dla kategorii wybierz grupę do której chcesz dodać zdjęcie. Następnie wczytaj je wybierając jedną z 2 opcji: Z dysku lub Z internetu.
Krok 3. Zapisz wprowadzone zmiany i wykonaj synchronizację.
Jak ustawić dodane zdjęcie jako ikonkę w nagłówku szablonu Topaz?
W szablonie Topaz istnieje możliwość ustawienia dodanych zdjęć kategorii jako ikonek przy nazwach kategorii w Twoim sklepie. Ta funkcja obsługuje nagłówki: header-1, header-2, header-3 oraz header-5, które są dostępne w Kreatorze Wyglądu. Zdjęcia w postaci ikon wyświetlają się w następujący sposób:
Widok nagłówka header-2 ze sklepu demonstracyjnego - https://demo.comarchesklep.pl/
Krok 1. Skonfiguruj funkcję wyświetlania się ikon nagłówka. Istnieją dwie możliwości konfiguracji tej funkcji:
Zapisz zmiany, a następnie wygeneruj szablon.
Wprowadzone zmiany zapisz i opublikuj.
Jak mogę dodać zdjęcie do nazwy podkategorii?
W takich szablonach jak Topaz i dla Gastronomii istnieje możliwość dodania obrazków, które będą wyświetlały się przy nazwach podkategorii w Twoim sklepie. Ta funkcja obsługuje wszystkie układy list towarów: products-1, products-2, products-3, products-4, które są dostępne w Kreatorze Wyglądu. Obrazki do nazw podkategorii wyświetlają się w następujący sposób:
Widok zdjęć podkategorii na liście towarów ze sklepu demonstracyjnego - https://demo.comarchesklep.pl/
Aby dodać zdjęcia do podkategorii postępuj według poniższych kroków:
Krok 1. Skonfiguruj funkcję wyświetlania podkategorii. Istnieją dwie możliwości konfiguracji tej funkcji:
Zapisz zmiany, a następnie wygeneruj szablon.
Zapisz i opublikuj wprowadzone zmiany.
Krok 2. Dodaj obrazki do podkategorii w systemie ERP. Skonfigurujesz je w podobny sposób jak obrazki dla kategorii, które zostały opisane w pierwszej części artykułu.
Więcej informacji
Więcej na temat dodawania obrazków w e-Sklepie można przeczytać w artykułach:
Banner promocyjny w szablonie Topaz i One Page Shop
Banner promocyjny - jak to działa?
Banner promocyjny wyświetli się na stronie głównej Twojego e-Sklepu i będzie prezentował typ towaru, który wskażesz. Taki rodzaj banneru pomoże Ci w zwróceniu uwagi Klientów na towary, które zostały objęte na przykład ofertą specjalną.
Dodanie banneru
Banner możesz dodać z poziomu Kreatora Wyglądu B2C (zamiennie: panel administracyjny e-Sklepu: Wygląd sklepu/ Kreator wyglądu).
Aby dodać banner promocyjny do swojego szablonu, przejdź na stronę "Strona główna", po czym wybierz Banner promocyjny i przeciągnij go w wybrane przez Ciebie miejsce.
W podglądzie układu szablonu, po najechaniu kursorem na obszar z bannerem promocyjnym, zobaczysz możliwość jego edycji.
Wybierz ikonę "zębatki", aby móc edytować i określić liczbę wyświetlanych towarów oraz typ promowanych towarów.
Zapisz wprowadzone zmiany. Jeśli pozostałe strony szablonu są gotowe i skonfigurowane, to możesz wygenerować szablon. Więcej informacji na temat Kreatora Wyglądu oraz poszczególnych sekcji znajdziesz w artykule: Kreator wyglądu Topaz (B2C)
Po zaimportowaniu szablonu do panelu administracyjnego możesz przejść do edycji banneru. Postępuj zgodnie z poniższą instrukcją.
Edycja banneru
Po dokonaniu modyfikacji należy zapisać i opublikować zmiany.
Gotowe! Teraz Twój banner promocyjny wyświetli się na stronie głównej.

Jak dodać wiele linków do banneru top?
Wstęp
Banner top jest bannerem, który wyświetla się na samej górze witryny. Ma on na celu wyróżnić informacje, które chcesz przekazać swoim klientom. Z tego względu w ramach banneru top możesz dodać zarówno treść, jak również linki, które będą prowadzić do istotnych obszarów. Dzięki temu możesz zaprezentować klientom informacje o ofercie w Twoim sklepie. Wskazana prezentacja wielu linków możliwa jest w szablonach Topaz oraz Dla gastronomii.
Jak zaprezentować wiele linków w bannerze top?
Krok 1. Aby ustawić wskazany banner według własnych potrzeb przejdź do sekcji Wygląd sklepu > Ustawienia > Bannery. W tym miejscu powinien znajdować się banner o nazwie top:
Krok 2. W ramach edycji banneru przejdź do sekcji Widok zaawansowany:
Krok 3. Po otwarciu wskazanego widoku masz możliwość kilku opcji konfiguracji banneru top - w ramach domyślnych ustawień dla banneru wypełniona jest sama treść. Do banneru można dodać także link.
W takim wypadku całość banneru top będzie klikalna, a wypełniony tekst zostanie zaprezentowany na środku banneru:
Krok 4. Do banneru top można dodać dodatkowe linki. Należy to zrobić wchodząc do edycji banneru lub poprzez opcję Dodaj więcej linków:
Krok 5. Z poziomu wskazanej opcji w zakładce Linki za pomocą przycisku plusa masz możliwość dodania linku:
Następnie możesz wprowadzić nazwę, dodać link oraz zadecydować czy ma się on otwierać w nowym oknie:

Jak zaprezentować banner w liście towarów dla szablonu Topaz?
Wstęp
Banner prezentowany na listach towarowych Twojego e-Sklepu przyciągnie uwagę klientów i pomoże zwiększyć sprzedaż. Dzięki wyrazistym grafikom i atrakcyjnym komunikatom można promować konkretne produkty, sezonowe promocje lub nowości, wyróżniając je spośród innych ofert. Sprawdź jak łatwo można dodać i skonfigurować banner, który będzie wyświetlany na listach towarowych w Twoim sklepie.
Jak dodać banner do listy towarów?
Krok 1. Banner możesz dodać z poziomu Kreatora Wyglądu B2C (zamiennie: panel administracyjny e-Sklepu: Wygląd sklepu > Kreator wyglądu).W Kreatorze banner znajduje się w sekcji Lista towarów > Banner w liście towarów. Aby dodać banner wystarczy w niego kliknąć:
Krok 3. Szablon z tak przygotowanym bannerem należy wygenerować. Po zaimportowaniu szablonu do panelu administracyjnego możesz przejść do dalszej konfiguracji.
Krok 4. Przejdź do ustawień Wygląd sklepu > Ustawienia > Bannery. W tej sekcji znajdź banner o nazwie banner-in-list-1 lub jeśli wybrałeś drugą opcję to banner-in-list-2, a następnie przejdź do jego edycji.
Krok 5. Za pomocą ikony plusa dodaj obrazek w rozmiarze 720x300 dla banner-in-list-1 lub 1400x300 dla banneru banner-in-list-2. Po przesłaniu właściwego obrazka kliknij na Widok zaawansowany, aby przejść do edycji pozostałych elementów.
Posiadam gotowy szablon Comarch. Jak mogę go dostosować do kryteriów WCAG 2.1/2.2?
Wstęp
Z dniem 28 czerwca 2025 roku w życie wejdą przepisy związane z implementacją ustawy z dnia 26 kwietnia 2024 r. o zapewnianiu spełniania wymagań dostępności niektórych produktów i usług. Wdraża ona do polskiego prawa tzw. Europejski Akt o Dostępności (EAA) – unijną dyrektywę 2019/882. Celem ustawy jest zapewnienie, że kluczowe produkty i usługi będą dostępne dla wszystkich użytkowników, w tym osób z niepełnosprawnościami.
Przedsiębiorcy mają czas do 28 czerwca 2025 roku, aby dostosować swoje produkty i usługi do nowych wymogów.
Sprawdź, jaki szablon posiadasz
Nie wiesz, który szablon posiadasz? Możesz to sprawdzić w panelu administracyjnym Comarch e-Sklep. Aby to zrobić, z poziomu panelu administracyjnego przejdź do zakładki Wygląd sklepu/ Ustawienia a następnie, pod przyciskiem "Więcej" wybierz Edytuj ustawienia zaawansowane.
W obszarze, który został wyświetlony, pojawi się kilka zakładek, a wśród nich zakładka "Szablon". Wybierz ją, po czym sprawdź szablon:
Jeśli Twój szablon to Rubin, Szafir, Topaz, One Page Shop lub Dla Gastronomii, to możesz przejść do kolejnej części artykułu.
Jeśli posiadasz własny szablon z indywidualnymi modyfikacjami, to przejdź do artykułu: Posiadam własny szablon, jak mogę go dostosować do kryteriów WCAG 2.1/2.2?
Jeśli posiadasz szablon Bursztyn, Agat lub Opal, to przejdź do artykułu: Posiadam szablon Bursztyn, Opal lub Agat, jak mogę go dostosować do kryteriów WCAG 2.1/2.2?
Co zrobić jako właściciel e-Sklepu lub Partner, jeśli używam gotowych szablonów Comarch?
Jak zacząć?
Jak zaktualizować szablon?
Aby zaktualizować szablon i przejść na Topaz, One Page Shop, Dla Gastronomii, Rubin lub Szafir, należy skorzystać z aktualnej wersji Kreatora Wyglądu. Pamiętaj, że szablony Rubin i Szafir przeznaczone są tylko dla sklepów Enterprise i B2B. Odnośnik znajduje się również w panelu administracyjnym e-Sklepu w sekcji Wygląd sklepu > Kreator wyglądu. Istnieją trzy możliwości aktualizacji tych szablonów:
Odpowiedni kontrast kolorów w e-Sklepie – wymóg WCAG na poziomie AA
Jednym z głównych wymogów WCAG jest stosowanie odpowiednich kontrastów na stronach internetowych.
Jeśli w wybranym szablonie graficznym wykorzystujesz któryś ze standardowych motywów kolorystycznych dostępnych w kreatorach, to zostaną one dostosowane do wymagań wynikających z przepisów.
Jeśli natomiast masz przygotowany własny motyw kolorystyczny lub chcesz zmienić część kolorów zastosowanych w szablonie, to pamiętaj, że muszą one posiadać odpowiednie kontrasty. Wszelkich zmian kolorystycznych możesz dokonać w Kreatorze Wyglądu.
Odpowiedni kontrast między tekstem a tłem jest kluczowy dla osób z dysfunkcjami wzroku. WCAG 2.1 określa minimalny współczynnik kontrastu (4.5:1 dla tekstu zwykłego i 3:1 dla tekstu dużego). Ważne jest również unikanie łączenia kolorów, które mogą być trudne do rozróżnienia, takich jak czerwony i zielony.
Kontrasty po modyfikacji kolorów możesz sprawdzić np. narzędziem WAVE. WAVE to popularny, dostępny dla wszystkich zestaw narzędzi do oceny dostępności stron internetowych. Rozszerzenia WAVE dla Chrome, Firefox i Edge, umożliwiają testowanie bezpośrednio w tych przeglądarkach, bez przesyłania danych na serwer WAVE.
WAVE znajdziesz pod linkiem: WAVE Web Accessibility Evaluation Tools.
Jak sprawdzić kontrast?
Przejdź na stronę swojego e-Sklepu. Włącz wtyczkę WAVE. Po prawej stronie ekranu wyświetli się panel boczny, na pierwszej zakładce widać podsumowanie całej strony:
Już na tym etapie możesz przejść do szczegółów, klikając "View details". Możesz w tym miejscu przejść także do zakładki "Contrast".
Czerwone kwadraty oznaczają obszar do poprawy. Przejdź przez stronę, aby zobaczyć szczegóły każdego z nich. Po kliknięciu na jeden z kwadratów pokaże się komunikat dotyczący kontrastu:
W panelu bocznym pojawi się dokładna informacja na temat koloru.
W tym miejscu, korzystając z suwaków możesz od razu dostosować kolor tak, aby był odpowiedni i sprawdzić, czy spełnia wymagania WCAG.
Gdzie dostosować kolory?
Kolory w szablonie możesz zmienić korzystając z Kreatora Wyglądu. Po imporcie szablonu, edycji lub stworzeniu nowego projektu, przejdź do zakładki "Style" i edytuj motyw kolorystyczny:
Następnie możesz zmieniać swobodnie kolor na wybranych elementach:
Zmian możesz dokonać także z poziomu panelu administracyjnego, przechodząc do zakładki Wygląd Sklepu > Ustawienia.
Pozostałe zmiany
Poza zmianami w szablonie graficznym sklepu należy uzupełnić szereg danych. Znaczącą część tych elementów możesz wykonać już teraz, nie czekając na aktualizację Comarch e-Sklep. Poniżej znajduje się lista zmian, które należy wdrożyć w swoim e-Sklepie.
Teksty alternatywne ("Alt", alty) w Comarch e-Sklep
Twój szablon musi obsługiwać parametr "alt" w obrazkach. Jeśli wykorzystasz aktualny szablon standardowy od Comarch to obsługę zapewnia Comarch e-Sklep, jeśli zdecydowałeś się na indywidualny szablon lub samodzielną aktualizację starszej wersji to upewnij się, że w szablonie są obsługiwane parametry "alt" we wskazanych elementach.
Teksty alternatywne („alt”) to krótkie opisy obrazków, które pozwalają osobom korzystającym z czytników ekranu zrozumieć, co znajduje się na zdjęciu. Atrybut „alt” jest szczególnie ważny dla zdjęć produktów, ponieważ umożliwia przekazanie najważniejszych informacji o produktach. Dlaczego tekst alternatywny jest taki ważny?
Gdzie należy uzupełnić teksty alternatywne (alt)?
1. Dla zdjęć towarów w systemach Comarch ERP
Korzystam z Comarch ERP Optima
Po uruchomieniu Comarch ERP Optima przejdź na pozycję cennika, a następnie edytuj wybrany towar. Następnie przejdź na zakładkę "Atrybuty", a następnie do sekcji "Zdjęcia i załączniki".
W tym miejscu zobaczysz kolumnę "Atrybut Alt".
Po dokonaniu zmian należy je zapisać. Po wykonaniu wszystkich modyfikacji należy wykonać synchronizację.
Korzystam z Comarch ERP XL
Po uruchomieniu ERP XL przejdź do Sprzedaż > Towary , a następnie na zakładkę "Załączniki". Opis alternatywny dla towaru stanowi Kod:
Korzystam z Comarch ERP XT
Przejdź na kartę produktu, a następnie do zakładki "Zdjęcia". Wśród pól do uzupełnienia znajdziesz "Opis alternatywny (Alt)".
W tym miejscu wpisz odpowiednią treść, a następnie zapisz wprowadzone zmiany.
2. Dla zdjęć w bannerach – w panelu administracyjnym Comarch e-Sklep
Pamiętaj, alby alty (opisy alternatywne) były uzupełnione także w przypadku bannerów. Aby to zrobić, przejdź z poziomu panelu administracyjnego do Wygląd sklepu > Ustawienia > Bannery do edycji swoich bannerów. Klikając na odpowiedni kafelek pojawią się opcje z uzupełnieniem Alt:
3. Dla zdjęć stosowanych na stronach z treściami formalnymi – w panelu administracyjnym Comarch e-Sklep
Obrazki możesz dodawać także do treści, które udostępniasz na specjalnych stronach, takich jak np. Regulamin sklepu, Polityka prywatności, Wysyłka itp. W obszarze do edycji, który jest dostępny z poziomu panelu administracyjnego w sekcji Ustawienia > Treści formalne edytuj interesującą Cię treść, a dodając obrazek uzupełnij alt:
4. Dla zdjęć wprowadzonych w opisie towaru i opisie kategorii
Dodając opisy produktów i kategorii, warto upewnić się, że linkowane zdjęcia zawierają poprawnie uzupełniony atrybut "alt". To kluczowe dla osób korzystających z czytników ekranu. Dbałość o ten detal poprawia dostępność strony i zwiększa jej widoczność w sieci. Opis towaru i kategorii możesz dodać w systemach Comarch ERP.
Opis kategorii możesz ustawić również z poziomu panelu administracyjnego, bez konieczności korzystania z systemu ERP. Szczegółowe informacje znajdują się w artykule: Jak edytować kategorię w Comarch e-Sklep?
5. Dla zdjęć w Blogu
Obrazki, które dodawane są dla wpisów blogowych od wersji 2025.4 będą posiadały atrybuty ALT oraz Title. Ich uzupełnienie będzie możliwe przy przejściu z poziomu panelu administracyjnego Comarch e-Sklep do sekcji Marketing > Blog przy dodawaniu wpisu:
6. Dla zdjęć w plikach użytkownika w sekcji "Twoje pliki"
Pliki użytkownika w e-Sklepie to dedykowana przestrzeń do przechowywania własnych materiałów, które mogą zostać wykorzystane w sklepie internetowym. Pliki można przesyłać za pośrednictwem panelu administracyjnego, w sekcji Ustawienia > Twoje pliki. Maksymalny rozmiar pojedynczego pliku: 3 MB.
Pliki, które dodawane są we wspomnianym obszarze, od wersji 2025.4 będą posiadały atrybuty ALT oraz Title. Ich uzupełnienie będzie możliwe przy przejściu z poziomu panelu administracyjnego do sekcji Ustawienia > Twoje pliki przy edycji pliku lub jego dodaniu.
Atrybut title - obowiązkowy czy nie?
Atrybut title jest przydatny do przekazywania dodatkowych informacji o elementach HTML, widocznych jako podpowiedź po najechaniu kursorem. Może być używany w ikonach bez podpisu, formularzach czy linkach, aby dostarczyć kontekst. Nie jest jednak obowiązkowy według WCAG, ponieważ czytniki ekranu często go ignorują, a osoby nawigujące klawiaturą mogą nie mieć do niego dostępu.
Zalecenia dla udostępnianych treści - ogranicz migające elementy
Jako właściciel sklepu musisz sam zadbać o to, jakie treści umieszczasz na swojej stronie. Migające elementy, takie jak animacje czy reklamy, mogą wywoływać problemy u osób z padaczką fotogenną. Według kryteriów WCAG 2.1 zalecane jest ograniczenie migających treści do maksymalnie trzech błysków na sekundę lub ich całkowite usunięcie. Jako właściciel sklepu musisz dbać o statyczne elementy lub spokojne animacje. Również treści zawarte w różnego typu prospektach, gazetkach czy folderach informacyjnych w formie dokumentów powinny być zgodne z wytycznymi WCAG.
1.Obrazki i bannery
W szablonach graficznych takie elementy mogą pojawić się na przykład w bannerach i elementach tekstowych ze zdjęciem. Upewnij się, że grafiki tam zamieszczone są odpowiednie, czyli bez intensywnych animacji i nieodpowiednich kolorów. Szczególną uwagę zwróć na pliki w formacie GIF.
Sprawdź, w jaki sposób możesz skonfigurować bannery oraz elementy tekstowe ze zdjęciem.
2.Materiały audio i viedo
Aby sklep internetowy był w pełni dostępny zgodnie z WCAG, właściciele muszą zadbać o dodanie audiodeskrypcji i transkrypcji do materiałów multimedialnych (filmy, nagrania dźwiękowe). Choć oba rozwiązania służą poprawie dostępności, mają różne funkcje.
Dostosuj komunikację mailową - aktualizacja lub zmiana treści e-mail
Dostosowanie treści e-mail zgodnie z wytycznymi WCAG to kluczowy krok w budowaniu dostępnej i klarownej komunikacji. W tej części artykułu przedstawiamy rozwiązania zarówno dla domyślnych wiadomości, jak i tych modyfikowanych, aby zapewnić ich lepszą czytelność i dostępność dla wszystkich odbiorców.
1.Treści mailowe są domyślne, nie zostały sformatowane
Jeśli treści nie zostały przez Ciebie zmienione i stanowią domyślne treści, które są dostępne w Comarch e-Sklep, to wystarczy, że zaktualizujesz je. Aby to zrobić, należy przejść w panelu administracyjnym do zakładki Ustawienia > Treści formalne, a następnie wybrać „Wysyłka e-mail i SMS”. Zakładka ta zawiera treści wiadomości wysyłane do klientów po wykonaniu określonych akcji, np. po zakupie czy rejestracji.
W Comarch e-Sklep istnieje możliwość przywrócenia standardowej treści pojedynczej wiadomości e-mail lub wszystkich jednocześnie. Opcja ta stanowi również aktualizację treści e-mail. "Przywróć domyślną treść" dostępna jest w panelu administracyjnym e-Sklepu w menu Ustawienia/ Treści formalne/ Wysyłka e-mail i SMS na szczegółach każdej wiadomości, po kliknięciu w przycisk Więcej:
2.Treści mailowe są modyfikowane, zostały przeze mnie zmienione
Jeśli posiadasz własne, dostosowane do Twojej komunikacji marketingowej treści e-mail, to sprawdź, czy są one odpowiednio napisane i sformatowane. Zastosuj się do poniższych wskazówek.
W jaki sposób poprawnie napisać i sformatować treści?
3.Załączniki (m.in. instrukcje, katalogi)
Należy zadbać o to, aby wszystkie udostępniane w sklepie załączniki, takie jak instrukcje czy katalogi, były zgodne z wytycznymi WCAG, zapewniając tym samym ich pełną dostępność dla wszystkich użytkowników.
Dodatkowe narzędzia pomocne przy wdrażaniu WCAG
Przystosowanie e-Sklepu do wymogów nie musi wiązać się z dużymi wydatkami na zmiany w kodzie. Kluczowe aspekty dostępności można wdrożyć samodzielnie, wykorzystując opcje dostępne w systemach Comarch ERP, a także Kreatorze Wyglądu czy Panelu Administracyjnym. Warto pamiętać, że poprawa dostępności to nie tylko obowiązek wynikający z przepisów, ale także realna szansa na zwiększenie grona klientów oraz podniesienie komfortu ich zakupów.
Istnieje kilka ogólnodostępnych narzędzi, które pomogą Ci dostosować sklep do wymogów. Obszerna lista narzędzi do oceny dostępności stron internetowych, zgodnych z WCAG znajduje się na stronie: Web Accessibility Evaluation Tools List .
Podsumowanie
Dostępność cyfrowa to nie jednorazowe przedsięwzięcie, lecz ciągły rozwój i udoskonalanie. Regularne sprawdzanie funkcjonalności, optymalizacja elementów wizualnych i dostosowywanie sposobów interakcji pozwalają na stałe podnoszenie jakości doświadczeń użytkowników. Dążenie do pełnej zgodności ze standardami WCAG 2.1/2.2 oraz wymogami Europejskiego Aktu o Dostępności (EAA) nie tylko ułatwia nawigację wszystkim odwiedzającym, ale także buduje pozytywny wizerunek i wzmacnia pozycję na rynku. Wdrażanie kolejnych usprawnień to inwestycja w przyszłość, która przynosi korzyści zarówno właścicielom, jak i ich klientom.Instrukcja: Jak dostosować samodzielnie szablon do szybkich zwrotów w e-Commerce?
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> – {{ 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/.
One Page Shop - cały sklep na jednej stronie
Jak wyeksportować i zaimportować swój szablon w e-Sklepie?
Jak wyeksportować swój szablon z Comarch e-Sklep?

Jak wgrać własny szablon do Comarch e-Sklep?
Własny szablon. Jak go wgrać?


Jak dodać wideo w sekcji z filmami?
Konfiguracja sekcji z filmami
Z poziomu panelu administracyjnego możesz w łatwy i szybki sposób dodać wideo. Postępuj zgodnie z poniższą instrukcją.
Krok 1. Zaloguj się do panelu administracyjnego.
Krok 2. Przejdź do zakładki Wygląd sklepu/ Ustawienia/ Strona główna. Na dole strony znajdź Ustawienia sekcji z filmami.
Krok 3. Uzupełnij informacje o filmie: Etykietę PL oraz wstaw Link do filmu.
W sekcji możesz zaprezentować filmy:
W polu tekst wpisz odpowiednią nazwę filmu i zapisz zmiany.
Krok 5. Zapisz i opublikuj zmiany.
Gotowe! Od teraz Klienci zobaczą w szablonie skonfigurowaną sekcję z opublikowanymi filmami.
Przejdź do naszego sklepu demonstracyjnego i zobacz, jak wygląda skonfigurowana sekcja.Jak zmienić kolejność wyświetlanych elementów na stronie głównej w One Page Shop?
Konfiguracja strony głównej
Aby dokonać zmian w kolejności wyświetlanych elementów na stronie głównej wystarczy, że zalogujesz się do panelu administracyjnego, a następnie przejdziesz do sekcji Wygląd sklepu/ Ustawienia/ Strona Główna. Wyświetli się wówczas panel, który zawiera w sobie ustawienia nagłówka strony, treści strony oraz sekcji z filmami.
Ustawienia nagłówka strony
W nagłówku znajdują się następujące elementy:
Lista elementów w nagłówku strony
W tym miejscu możesz nie tylko dodać link do nagłówka, ale także ustalić, czy odnośniki do stron mają być widoczne. Możliwa jest też zmiana kolejności wyświetlania poszczególnych elementów. Wystarczy, że przesuniesz wybrany element za pomocą strzałki w kolumnie Kolejność.
Możesz także określić, który odnośnik powinien być wyróżniony, a dokładniej, który link w nagłówku będzie posiadała inny kolor. Zmiany koloru czcionki możesz dokonać w sekcji Wygląd sklepu/ Ustawienia/ Ogólne. Odpowiadają za to sekcje:
Ustawienia treści strony
Pozostając w obszarze Wygląd sklepu/ Ustawienia/ Strona główna możesz zmienić ustawienia treści strony.
W tym miejscu również istnieje możliwość zmiany kolejności wyświetlania poszczególnych elementów na stronie głównej sklepu oraz ukrycia elementu. Wystarczy, że odznaczysz checkbox w kolumnie Dostępne.
Ustawienia sekcji z filmami
W szablonie One Page Shop dostępna jest sekcja, w której możesz publikować filmy, które pomogą Ci wypromować oferowany przez Ciebie towar. Film możesz dodać w prosty i szybki sposób z poziomu panelu administracyjnego. Szczegóły konfiguracji znajdują się w artykule: Jak dodać wideo w sekcji z filmami w szablonie One Page Shop?Jak wykonać kopię bezpieczeństwa mojego szablonu?
Jak wykonać kopię bezpieczeństwa mojego szablonu?
Oprawa wizualna sklepu jest bardzo ważnym elementem podczas prowadzenia sprzedaży w sieci. Narzędzie Kreator Wyglądu Comarch e-Sklep umożliwia stworzenie własnych, niepowtarzalnych szablonów wedle preferencji i potrzeb użytkowników. W intuicyjnym narzędziu jakim jest Kreator, będziesz w stanie zaprojektować wszelkie niezbędne funkcje, takie jak stronę główną, listę towarów, szczegóły towaru oraz koszyk.
Jeśli Twój projekt szablonu został zaimportowany do e-Sklepu oraz skonfigurowany wedle Twoich preferencji (dodanie grafik do bannerów, modyfikacje kolorystyczne) masz możliwość jego zapisania, co będzie stanowić kopię bezpieczeństwa.
Kopia bezpieczeństwa to dane, które w każdej chwili można odtworzyć w przypadku ich utracenia (np. poprzez przypadkowe usunięcie) lub częstego wprowadzania zmian. Dane, jakie może zawierać kopia bezpieczeństwa to szablon, który wcześniej został przez nas wygenerowany wraz z bannerami. Zaleca się wykonywanie kopii zapasowych oraz zabezpieczenie ich hasłem. Każda wykonana kopia zapasowa powinna zostać zapisana w bezpiecznym i dogodnym dla Użytkownika miejscu.
Jak wykonać kopię bezpieczeństwa?
W Panelu Administracyjnym przejdź do sekcji Wygląd sklepu/ Ustawienia/ Edytuj ustawienia zaawansowane, a następnie przejdź do zakładki Eksport szablonu.
W pustych polach znajdujących się w zakładce Eksport szablonu, opcjonalnie przed eksportem możesz uzupełnić powyższe dane, tj. Autora szablonu, e-mail, telefon i stronę WWW sklepu.
Aby wyeksportować szablon, kliknij Eksportuj szablon.
Wyeksportowany plik jest w formacie XML, a jego nazwa zawiera szablon oraz wersję, co znacznie ułatwia zarządzanie kopiami bezpieczeństwa w przypadku wykonywania ich regularnie.
Jak wyeksportować bannery?
Bannery, które znajdują się w Twoim sklepie możesz również wyeksportować tworząc kopię bezpieczeństwa. W tym celu także musisz przejść do Wygląd sklepu/ Ustawienia/ Edytuj ustawienia zaawansowane, a następnie do zakładki Eksport szablonu.
Obok przycisku Eksportuj szablon, znajduje się przycisk Eksportuj bannery, który musisz kliknąć aby bannery, które posiadasz w e-Sklepie zostały weksportowane.
Kopia bezpieczeństwa jest również zapisywana w formacie XML, a nazwa pliku wskazuje na szablon, wersję i zawartość bannerów.
Jak zabezpieczyć hasłem swój szablon?
Jeśli chcesz, aby Twój szablon został zabezpieczony hasłem, a znajomość hasła była niezbędna podczas zaimportowania tego szablonu do e-Sklepu, musisz w tym celu przejść w Panelu Administracyjnym do sekcji Wygląd sklepu/ Ustawienia/ Ustawienia zaawansowane, następnie do zakładki Szablon.
W tym miejscu nadasz hasło dla swojego szablonu.
Zgodnie z Polityką bezpieczeństwa haseł, hasło powinno:
Jeśli wyeksportujesz szablon, który zabezpieczyłeś hasłem – podczas importu tego szablonu, będziesz musiał podać nadane wcześniej hasło. Jego brak uniemożliwi import szablonu, a podanie błędnego hasła spowoduje nieprawidłowe działanie szablonu po imporcie.
Więcej informacji znajdziesz:
One Page Shop - podstawowe informacje
Wstęp
One Page Shop to szablon zaprojektowany z myślą o użytkownikach, którzy w swojej ofercie posiadają jedynie kilka towarów. Dzięki niemu zaprezentujesz na głównej stronie wyjątkowe produkty, które posiadasz w swojej ofercie. Struktura szablonu skupiona jest na zaprezentowaniu asortymentu tak, aby decyzje zakupowe Twoich Klientów zapadały szybciej. Potencjalny klient może odnaleźć najważniejsze informacje na temat Twojej oferty przeglądając jedną stronę witryny.
Szablon One Page Shop:
W tym artykule zawarliśmy podstawowe informacje na temat funkcji dostępnych w szablonie. Dzięki udogodnieniom i możliwości edycji niektórych elementów możesz łatwo spersonalizować swój e-Sklep.
Konfiguracja One Page Shop w panelu administracyjnym
Z poziomu panelu administracyjnego możesz dokonywać edycji każdego elementu znajdującego się na stronie. Możesz także ustalać sposoby dostaw oraz płatności.
Po przejściu do sekcji Wygląd sklepu/ Ustawienia dostępne są ustawienia dla trzech zakładek: Ogólne, Strona główna, Dodatkowe.
Ustawienia Strony Głównej
Ustawienia zawarte w tym miejscu odpowiadają za elementy, które wyświetlane są na stronie głównej. Możesz zmieniać ich kolejność, nazwę, ukryć lub wyróżnić. W tym miejscu wyróżnia się następujące sekcje:
Ustawienia nagłówka strony
W nagłówku znajdują się następujące elementy:
Ustawienia treści strony
Pozostając w obszarze Wygląd sklepu/ Ustawienia/ Strona główna możesz zmienić ustawienia treści strony. W tym miejscu również istnieje możliwość zmiany kolejności wyświetlania poszczególnych elementów na stronie głównej sklepu oraz ukrycia elementu.
Szczegółowe informacje na temat konfiguracji znajdują się w artykule: Jak zmienić kolejność wyświetlanych elementów na stronie głównej w One Page Shop?
Elementy w szablonie
1. Sekcja z filmami
W One Page Shop masz możliwość dodania filmów promujących nie tylko produkty, ale także Twoją firmę, dostępne marki czy producentów. Film możesz dodać w prosty i szybki sposób z poziomu panelu administracyjnego. W sekcji możesz zaprezentować filmy:
Szczegóły konfiguracji znajdują się w artykule: Jak dodać wideo w sekcji z filmami w szablonie One Page Shop?
2. Banner
Aby dodać zdjęcia do banneru przejdź do zakładki Wygląd sklepu/ Ustawienia/ Bannery, a następnie klikając na kafelek o nazwie mainBanner wybierz odpowiednie zdjęcie.
3. Lookbook
Widok charakteryzuje się prezentacją zdjęć w formie listy i precyzyjnym oznaczeniem towarów na zdjęciu. Dzięki znacznikom umieszczonym na grafice Twoi klienci będą mogli podejrzeć towar i dodać go bezpośrednio do koszyka. Po kliknięciu na przycisk z plusem wyświetli się popup ze szczegółami towaru oraz opcją zakupu.
4. Lista towarów
Szablon One Page Shop został stworzony z myślą o użytkownikach, którzy w swojej ofercie nie posiadają wielu produktów. Z tego powodu, na stronie głównej wyświetlanych jest 20 towarów prostych, które zostały przypisane do pierwszej kategorii w systemie Comarch ERP. Z tego powodu zalecamy utworzenie jednej kategorii, która ma być przesyłana do One Page Shop'a.

5. Szczegóły towaru – jakie informacje widnieją na pop-upie?
W szablonie One Page Shop Klient będzie mógł zobaczyć szczegóły danego towaru nie musząc przechodzić na osobną stronę. Wystarczy, że kliknie na kafelek z interesującym go artykułem lub naciśnie Kup teraz. Wyświetli się wówczas pop up, który zawiera w sobie szereg informacji na temat towaru, między innymi:

6. Koszyk
W szablonie One Page Shop składanie zamówienia jest szybkie i wygodne dla użytkownika. Koszyk został zaprojektowany tak, aby użytkownik mógł w jednym kroku wypełnić niezbędne dane:
Od wersji 2024.1.1 w szablonie One Page Shop dodano możliwość wyświetlania w koszyku informacji jakiej kwoty brakuje do darmowej dostawy. Funkcja ta możliwa jest do uruchomienia z poziomu Panelu Administracyjnego w sekcji Wygląd sklepu/ Ustawienia/ Ustawienia szablonu/ Ogólne:
Ustawienia te można zmienić według własnego uznania, a następnie należy użyć opcji Zapisz i Publikuj.
7. Blog
Prowadzenie bloga jest istotnym elementem sklepu internetowego. Dzięki niemu sprzedawca może dzielić się swoją wiedzą i spostrzeżeniami na temat reklamowanych produktów. W ten sposób sprzedawca może podpowiedzieć klientom w jaki sposób mogą oni wykorzystać oferowane przez niego artykuły. Prowadzenie bloga ma również dobry wpływ na pozycjonowanie, ze względu na unikalne treści. Dzięki temu Twój e-Sklep może częściej pojawiać się w wynikach wyszukiwania.
W szablonie One Page Shop masz możliwość tworzenia dowolnej ilości wpisów. Na stronie głównej wyświetlone zostaną trzy najnowsze. Użytkownik ma możliwość podglądu całej listy artykułów. Wystarczy, że kliknie w etykietę Najnowsze wpisy.
Szczegółowe informacje na temat konfiguracji bloga w e-Sklepie znajdują się w artykule: Jak skonfigurować blog?
8. Info-banner
Banner informacyjny pozwala na zaprezentowanie w przejrzysty sposób korzyści z zakupach w Twoim e-Sklepie.
Modyfikacji tego obszaru dokonasz z poziomu panelu administracyjnego, przechodząc do sekcji Wygląd sklepu/ Ustawienia/ Bannery. Następnie przechodząc do szczegółów banneru o nazwie info-banner dostosujesz element do swoich preferencji. Możesz skorzystać z domyślnych grafik lub dodać własne.
9. Element tekstowy ze zdjęciem
Element tekstowy ze zdjęciem charakteryzuje się możliwością wprowadzenia statycznego opisu na stronie głównej w twoim e-Sklepie. Możesz zawrzeć w nim np. podstawowe informacje na temat Twojej firmy, asortymentu sklepu lub inne wartościowe dla Ciebie treści.
Element tekstowy możesz odpowiednio skonfigurować przechodząc do sekcji Wygląd sklepu/ Ustawienia/ Edytuj ustawienia zaawansowane, a następnie do zakładki Obiekty, gdzie skonfigurujesz zawartość elementu.
10. Stopka
Dzięki modyfikacji obszaru możesz jeszcze bardziej spersonalizować swój sklep poprzez uzupełnienie stopki w szereg przydatnych informacji. Elementem tym możesz zarządzać z poziomu sekcji Wygląd Sklepu/ Ustawienia/ Nagłówek i Stopka.
Szczegółowe informacje na temat konfiguracji stopki znajdują się w artykule: Rozbudowana stopka w szablonach – jak ją skonfigurować?
11. Strefa Klienta
Do Strefy Klienta w szablonie One Page Shop masz możliwość zalogowania się poprzez Facebooka oraz przez Google.
Więcej na temat konfiguracji logowania przez Facebooka i Google znajdziesz tutaj.
Masz także możliwość całkowitego ukrycia Strefy Klienta na stronie sklepu internetowego. Zdecydujesz o tym zaznaczając właściwą opcję w zakładce Wygląd sklepu/ Ustawienia/ Ustawienia szablonu/ Ogólne:
Wprowadzone zmiany standardowo należy zapisać i opublikować.Jak zaktualizować szablon do najnowszej wersji?
Jak zaktualizować szablon do najnowszej wersji?
Wstęp
W e-Sklepie możesz dowolnie zmieniać szablony. Możesz je także modyfikować oraz aktualizować. Wiąże się to z wgraniem zupełnie nowego szablonu na miejsce starego. Aktualizacja wyglądu powoduje zmianę dotychczasowych ustawień. Przed każdą aktualizacją polecamy utworzyć odpowiednią dla siebie instrukcję, która będzie zawierała Twoje ustawienia kolorów czy modyfikacje w kodzie szablonu. Możesz także wyeksportować dotychczasowy szablon, aby stanowił on kopię bezpieczeństwa.
Jak zaktualizować szablon?
Instrukcja aktualizacji szablonów: Rubin, Szafir, Topaz i One Page Shop (dostępnych w Kreatorze Wyglądu)
Instrukcja aktualizacji szablonów: Bursztyn, Agat i Opal.


Instrukcja aktualizacji innych szablonów (niż szablonów Comarch) oraz aktualizacji szablonów z własnymi modyfikacjami
Jeżeli w swoim Comarch e-Sklepie korzystasz z własnego szablonu, czyli innego niż gotowe szablony Comarch, to powinieneś jego aktualizację przeprowadzić we własnym zakresie lub zlecić ją firmie/ osobie, która dla Ciebie przygotowała ten szablon.
W przypadku, gdy korzystasz z gotowego szablonu Comarch, ale wprowadziłeś do niego indywidualne zmiany (poza Kreatorem Wyglądu), to również we własnym zakresie (lub ze wsparciem autora zmian) musisz zadbać o aktualizację swojej wersji szablonu.Banner promocyjny w szablonie Topaz i One Page Shop
Banner promocyjny - jak to działa?
Banner promocyjny wyświetli się na stronie głównej Twojego e-Sklepu i będzie prezentował typ towaru, który wskażesz. Taki rodzaj banneru pomoże Ci w zwróceniu uwagi Klientów na towary, które zostały objęte na przykład ofertą specjalną.
Dodanie banneru
Banner możesz dodać z poziomu Kreatora Wyglądu B2C (zamiennie: panel administracyjny e-Sklepu: Wygląd sklepu/ Kreator wyglądu).
Aby dodać banner promocyjny do swojego szablonu, przejdź na stronę "Strona główna", po czym wybierz Banner promocyjny i przeciągnij go w wybrane przez Ciebie miejsce.
W podglądzie układu szablonu, po najechaniu kursorem na obszar z bannerem promocyjnym, zobaczysz możliwość jego edycji.
Wybierz ikonę "zębatki", aby móc edytować i określić liczbę wyświetlanych towarów oraz typ promowanych towarów.
Zapisz wprowadzone zmiany. Jeśli pozostałe strony szablonu są gotowe i skonfigurowane, to możesz wygenerować szablon. Więcej informacji na temat Kreatora Wyglądu oraz poszczególnych sekcji znajdziesz w artykule: Kreator wyglądu Topaz (B2C)
Po zaimportowaniu szablonu do panelu administracyjnego możesz przejść do edycji banneru. Postępuj zgodnie z poniższą instrukcją.
Edycja banneru
Po dokonaniu modyfikacji należy zapisać i opublikować zmiany.
Gotowe! Teraz Twój banner promocyjny wyświetli się na stronie głównej.

Kreator Wyglądu One Page Shop (B2C)
Wstęp
Zastosowanie
Kto może używać tej opcji?
Tworzymy!
Jak zaprojektować układ szablonu?
W sytuacji gdy wcześniej tworzony był już projekt, widoczny będzie przycisk umożliwiający dokończenie szablonu lub rozpoczęcie nowego projektu.
Opcja Nowy szablon umożliwia zbudowanie od początku nowego szablonu Topazu według Twojego indywidualnego pomysłu.


Od czego zacząć?
Wybierz wersję językową Kreatora i rozpocznij pracę nad swoim szablonem. Wersje do wyboru to: język polski, angielski, niemiecki oraz francuski. Język wybieramy klikając strzałkę obok flagi i nazwy języka w lewym dolnym rogu. Przy podglądzie prac na pełnym ekranie, wybrany język zostaje zachowany i szablon będzie wyświetlał się w wybranej wersji językowej.