Wstęp
W szablonie Topaz, przy włączonej kontroli stanów magazynowych, w przypadku braku produktu towaru zgrupowanego wyświetlany jest przycisk „Powiadom mnie o dostępności”. Z tego artykułu dowiesz się jak zmodyfikować swój szablon, aby dodatkowo wyświetlać przekreślenie na niedostępnych atrybutach.

Zmiany w plikach szablonu
Przedstawione w artykule modyfikacje są kompatybilne ze wszystkimi czterema widokami szczegółów towaru w szablonie Topaz. Kod odpowiedzialny za przekreślenie na atrybutach, będzie prawie identyczny dla każdego z widoków. Minimalne różnice zostały odpowiednio opisane.
Edycję szablonu rozpocznij od pliku js/layout2.js. Struktura części kodu, którą należy zmienić może się różnić, w zależności czy masz zaimplementowaną graficzną prezentację atrybutów. Dlatego przedstawione zostaną dwa przypadki edycji omawianego pliku.
W przypadku braku implementacji graficznej prezentacji atrybutów, odnajdź fragment:
function SetSuplFirst(drop, s, lvl, valId) {
    var form = drop.parent().parent().parent();
    var fDrop = form.find('.attributes-select');
    drop.empty();
    for (var el in s) {
        drop.append($('').data('el', JSON.stringify(s[el])));
    }
Kod znajdujący się w pętli for zamień na:
var isUnavailable = false;
var unavailableClassName = "";
    if (s[el].Product) {
        if (s[el].Product.StockLevel && s[el].Product.StockLevel.Value === 0) {
            isUnavailable = true;
        }
        if (s[el].StockLevel && s[el].StockLevel.Value === 0) {
            isUnavailable = true;
        }
    }
    else {
        var checkStockLevelLoopObj = s[el];
        var checkSuppliesStockLevelLoopObj = s[el];
        while (typeof checkStockLevelLoopObj.Product === 'undefined') {
            checkSuppliesStockLevelLoopObj = checkStockLevelLoopObj.Supplies;
            checkStockLevelLoopObj = checkStockLevelLoopObj.Supplies[0];
        }
        if (checkSuppliesStockLevelLoopObj.length > 1) {
            for (var supply in checkSuppliesStockLevelLoopObj) {
              if (checkSuppliesStockLevelLoopObj[supply].Product && checkSuppliesStockLevelLoopObj[supply].Product.StockLevel
                 && checkSuppliesStockLevelLoopObj[supply].Product.StockLevel !== 0) {
                  break;
              }
              else {
                  if (checkSuppliesStockLevelLoopObj[supply].Product && !checkSuppliesStockLevelLoopObj[supply].Product.StockLevel) {
                      break;
                  } else {
                      isUnavailable = true;
                  }
              }
              if (checkSuppliesStockLevelLoopObj[supply].StockLevel && checkSuppliesStockLevelLoopObj[supply].StockLevel.Value !== 0) {
                  break;
              }
              else {
                  if (!checkSuppliesStockLevelLoopObj[supply].StockLevel) {
                      break;
                  } else {
                      isUnavailable = true;
                  }
              }
          }
      }
      else {
          if (checkStockLevelLoopObj.Product && checkStockLevelLoopObj.Product.StockLevel && checkStockLevelLoopObj.Product.StockLevel === 0) {
              isUnavailable = true;
          }
          if (checkStockLevelLoopObj.StockLevel && checkStockLevelLoopObj.StockLevel.Value === 0) {
              isUnavailable = true;
          }
      }
}
if (isUnavailable) {
    unavailableClassName = " button-option-unavailable";
}
drop.append($('').data('el', JSON.stringify(s[el])));
Następnie wyszukaj fragment:
function SetSupl(drop, s) {
    var form = drop.closest('#AddToCartForm');
    var fDrop = form.find('.attributes-select');
    drop.empty();
    for (var el in s) {
        drop.append($('').data('el', JSON.stringify(s[el])));
    }
I instrukcje w pętli for zastąp tym samym kodem co w przypadku przedstawionej wyżej modyfikacji funkcji SetSuplFirst.
Dla przypadku zaimplementowanej graficznej prezentacji atrybutów, zlokalizuj fragment:
function SetSuplFirst(drop, s, lvl, valId) {
    var form = drop.parent().parent().parent();
    var fDrop = form.find('.attributes-select'); //pobieram wszystkie kontenery dla poziomów
    drop.empty();
	var attributesColorsData = drop.data('variants-colors');
    var pageLanguage = drop.data('page-language');
    var attributesColors = [];
    if (attributesColorsData) {
        attributesColorsData = attributesColorsData.split('|||');
        for (var i = 0; i < attributesColorsData.length; i++) {
            var attributesColor = attributesColorsData[i].split('||');
            var attributeColorName = attributesColor[0].split('|');
            switch (pageLanguage) {
                case 1:
                    attributeColorName = attributeColorName[0];
                    break;
                case 2:
                    attributeColorName = attributeColorName[1];
                    break;
                case 3:
                    attributeColorName = attributeColorName[2];
                    break;
                case 5:
                    attributeColorName = attributeColorName[3];
                    break;
                default:
                    break;
            }
            attributesColors.push([attributeColorName,attributesColor[1]]);
        }
    }
    for (var el in s) {
        if (drop.data('variants-colors')) {
            if ((drop.data('colors-option') === 'Grouping_First' && drop.data('attribute-id') === drop.data('first-attribute-id'))
                || (drop.data('colors-option') === 'Grouping_Last' && drop.data('attribute-id') === drop.data('last-attribute-id'))
                || (drop.data('colors-option') === 'Grouping_All')) {
Teraz w pętli for (var el in s), przed pierwszą instrukcją sterującą if wklej:
var isUnavailable = false;
var unavailableClassName = "";
    if (s[el].Product) {
        if (s[el].Product.StockLevel && s[el].Product.StockLevel.Value === 0) {
            isUnavailable = true;
        }
        if (s[el].StockLevel && s[el].StockLevel.Value === 0) {
            isUnavailable = true;
        }
    }
    else {
        var checkStockLevelLoopObj = s[el];
        var checkSuppliesStockLevelLoopObj = s[el];
        while (typeof checkStockLevelLoopObj.Product === 'undefined') {
            checkSuppliesStockLevelLoopObj = checkStockLevelLoopObj.Supplies;
            checkStockLevelLoopObj = checkStockLevelLoopObj.Supplies[0];
        }
        if (checkSuppliesStockLevelLoopObj.length > 1) {
            for (var supply in checkSuppliesStockLevelLoopObj) {
              if (checkSuppliesStockLevelLoopObj[supply].Product && checkSuppliesStockLevelLoopObj[supply].Product.StockLevel
                 && checkSuppliesStockLevelLoopObj[supply].Product.StockLevel !== 0) {
                  break;
              }
              else {
                  if (checkSuppliesStockLevelLoopObj[supply].Product && !checkSuppliesStockLevelLoopObj[supply].Product.StockLevel) {
                      break;
                  } else {
                      isUnavailable = true;
                  }
              }
              if (checkSuppliesStockLevelLoopObj[supply].StockLevel && checkSuppliesStockLevelLoopObj[supply].StockLevel.Value !== 0) {
                  break;
              }
              else {
                  if (!checkSuppliesStockLevelLoopObj[supply].StockLevel) {
                      break;
                  } else {
                      isUnavailable = true;
                  }
              }
          }
      }
      else {
          if (checkStockLevelLoopObj.Product && checkStockLevelLoopObj.Product.StockLevel && checkStockLevelLoopObj.Product.StockLevel === 0) {
              isUnavailable = true;
          }
          if (checkStockLevelLoopObj.StockLevel && checkStockLevelLoopObj.StockLevel.Value === 0) {
              isUnavailable = true;
          }
      }
}
if (isUnavailable) {
    unavailableClassName = " button-option-unavailable";
}
Następnie, w obrębie wspomnianej wyżej pętli for, wyszukaj wszystkie wystąpienia kodu zawierające fragment:
drop.append($('
I dla wszystkich znalezionych przypadków, na końcu fragmentów takich jak przedstawiony powyżej, dodaj kod:
' + unavailableClassName + '
Na koniec zmian w pliku, odnajdź poniższe instrukcje:
function SetSupl(drop, s) {
    var form = drop.closest('#AddToCartForm');
    var fDrop = form.find('.attributes-select'); //pobieram wszystkie kontenery dla poziomów
    drop.empty();
	var attributesColorsData = drop.data('variants-colors');
    var pageLanguage = drop.data('page-language');
    var attributesColors = [];
    if (attributesColorsData) {
        attributesColorsData = attributesColorsData.split('|||');
        for (var i = 0; i < attributesColorsData.length; i++) {
            var attributesColor = attributesColorsData[i].split('||');
            var attributeColorName = attributesColor[0].split('|');
            switch (pageLanguage) {
                case 1:
                    attributeColorName = attributeColorName[0];
                    break;
                case 2:
                    attributeColorName = attributeColorName[1];
                    break;
                case 3:
                    attributeColorName = attributeColorName[2];
                    break;
                case 5:
                    attributeColorName = attributeColorName[3];
                    break;
                default:
                    attributeColorName = attributeColorName[0];
                    break;
            }
            attributesColors.push([attributeColorName,attributesColor[1]]);
        }
    }
    for (var el in s) {
        if (drop.data('variants-colors')) {
            if ((drop.data('colors-option') === 'Grouping_First' && drop.data('attribute-id') === drop.data('first-attribute-id'))
                || (drop.data('colors-option') === 'Grouping_Last' && drop.data('attribute-id') === drop.data('last-attribute-id'))
                || (drop.data('colors-option') === 'Grouping_All')) {
Teraz dokonaj analogicznych modyfikacji jak w obrębie funkcji SetSuplFirst. Pamiętaj, że tym razem, zmiany we wszystkich wystąpieniach kodu:
drop.append($('
Musisz dokonać w obrębie funkcji SetSupl.
Do dokończenia zmian w szablonie pozostało zmodyfikowanie pliku ze stylami. W zależności od wygenerowanego w kreatorze szablonu widoku szczegółów towaru, nazwa edytowanego pliku będzie się różnić. Zmiany stylistyki w szablonie są identyczne dla wszystkich widoków, lecz umieszczane w innych fragmentach plików.
Najpierw przedstawiona zostanie modyfikacja przeznaczona dla pierwszego i drugiego widoku szczegółów towaru. Otwórz plik css/layout0.css i wyszukaj fragment:
.product-details .input-group input[type='button'].active,.product-details .input-group .attributes-select .button-option.active,.product-details .set .button-option.active,.product-details .set .button-option-sets.active{border:2px solid {{settings.paginationActiveBorderColor}};background-color:transparent}
Teraz tuż za ostatnim znakiem przedstawionego kodu wklej instrukcje:
.product-details .button-option-unavailable{position:relative;color:{{settings.secondaryFontColor}};transition:300ms}.product-details .button-option-unavailable::before{content:"";position:absolute;top:0;left:0;height:100%;width:100%;transition:300ms;background-color:rgba(226,226,226,0.2)}.product-details .button-option-unavailable::after{content:"";display:block;position:absolute;top:0;left:0;height:100%;width:100%;transition:300ms;cursor:pointer}@keyframes change-gradient-to-inactive{from{background:linear-gradient(to right top, transparent, transparent 48%, {{settings.secondaryFontColor}} 50%, {{settings.secondaryFontColor}} 50%, transparent 52%, transparent)}to{background:linear-gradient(to right top, transparent, transparent 49%, {{settings.secondaryFontColor}} 50%, {{settings.secondaryFontColor}} 50%, transparent 51%, transparent)}}@keyframes change-gradient-to-active{from{background:linear-gradient(to right top, transparent, transparent 49%, {{settings.secondaryFontColor}} 50%, {{settings.secondaryFontColor}} 50%, transparent 51%, transparent)}to{background:linear-gradient(to right top, transparent, transparent 48%, {{settings.secondaryFontColor}} 50%, {{settings.secondaryFontColor}} 50%, transparent 52%, transparent)}}.product-details .button-option-unavailable:not(.active){border:1px solid {{settings.secondaryFontColor}} !important}.product-details .button-option-unavailable:not(.active)::after{background:linear-gradient(to right top, transparent, transparent 49%, {{settings.secondaryFontColor}} 50%, {{settings.secondaryFontColor}} 50%, transparent 51%, transparent);-webkit-animation:change-gradient-to-inactive 300ms;animation:change-gradient-to-inactive 300ms}.product-details .button-option-unavailable.active{border:2px solid {{settings.secondaryFontColor}} !important}.product-details .button-option-unavailable.active::after{background:linear-gradient(to right top, transparent, transparent 48%, {{settings.secondaryFontColor}} 50%, {{settings.secondaryFontColor}} 50%, transparent 52%, transparent);-webkit-animation:change-gradient-to-active 900ms;animation:change-gradient-to-active 900ms}
Poniżej natomiast pokazane są zmiany przeznaczone dla trzeciego i czwartego widoku szczegółów towaru. Otwórz plik css/layout0.css i wyszukaj fragment:
.productDetails-attributes .button-option:not(.dropdown):not(.units-container){display:flex;flex-direction:row;justify-content:flex-start;align-items:center;margin-bottom:10px;flex-wrap:wrap}.productDetails-attributes .button-option:not(.dropdown):not(.units-container).active{border:2px solid {{settings.linkFontColor}};display:inline-flex}.productDetails-attributes .button-option:not(.dropdown):not(.units-container) span{height:50px;padding-left:15px;padding-right:15px;font-size:14px;font-weight:normal;font-stretch:normal;font-style:normal;line-height:1.71;letter-spacing:1.2px;text-align:center;color:{{settings.primaryFontColor}};border:2px solid {{settings.primaryPageBgColor}};cursor:pointer;transition:300ms;margin-right:5px}.productDetails-attributes .button-option:not(.dropdown):not(.units-container) span:not(.active):hover{border-color:{{settings.secondaryFontColor}}}
Następnie zaraz po ostatnim znaku powyższego fragmentu dodaj kod:
.productDetails-attributes .button-option:not(.dropdown):not(.units-container) .button-option-unavailable{position:relative;color:{{settings.secondaryFontColor}};transition:300ms}.productDetails-attributes .button-option:not(.dropdown):not(.units-container) .button-option-unavailable::before{content:"";position:absolute;top:0;left:0;height:100%;width:100%;transition:300ms;background-color:rgba(226,226,226,0.2)}.productDetails-attributes .button-option:not(.dropdown):not(.units-container) .button-option-unavailable::after{content:"";display:block;position:absolute;top:0;left:0;height:100%;width:100%;transition:300ms;cursor:pointer}@keyframes change-gradient-to-inactive{from{background:linear-gradient(to right top, transparent, transparent 48%, {{settings.linkFontColor}} 50%, {{settings.linkFontColor}} 50%, transparent 52%, transparent)}to{background:linear-gradient(to right top, transparent, transparent 49%, {{settings.secondaryFontColor}} 50%, {{settings.secondaryFontColor}} 50%, transparent 51%, transparent)}}@keyframes change-gradient-to-active{from{background:linear-gradient(to right top, transparent, transparent 49%, {{settings.secondaryFontColor}} 50%, {{settings.secondaryFontColor}} 50%, transparent 51%, transparent)}to{background:linear-gradient(to right top, transparent, transparent 48%, {{settings.linkFontColor}} 50%, {{settings.linkFontColor}} 50%, transparent 52%, transparent)}}.productDetails-attributes .button-option:not(.dropdown):not(.units-container) .button-option-unavailable:not(.active){border:1px solid {{settings.secondaryFontColor}}}.productDetails-attributes .button-option:not(.dropdown):not(.units-container) .button-option-unavailable:not(.active)::after{background:linear-gradient(to right top, transparent, transparent 49%, {{settings.secondaryFontColor}} 50%, {{settings.secondaryFontColor}} 50%, transparent 51%, transparent);-webkit-animation:change-gradient-to-inactive 300ms;animation:change-gradient-to-inactive 300ms}.productDetails-attributes .button-option:not(.dropdown):not(.units-container) .button-option-unavailable.active{border:1px solid {{settings.linkFontColor}}}.productDetails-attributes .button-option:not(.dropdown):not(.units-container) .button-option-unavailable.active::after{background:linear-gradient(to right top, transparent, transparent 48%, {{settings.linkFontColor}} 50%, {{settings.linkFontColor}} 50%, transparent 52%, transparent);-webkit-animation:change-gradient-to-active 900ms;animation:change-gradient-to-active 900ms}