Error executing template "Designs/Keflico/eCom/Productlist/products.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_a70e42c38304420385aeaa43a6625237.FindTopGroup(Group group) in D:\dynamicweb.net\Solutions\keflico.live\Files\Templates\Designs\Keflico\eCom\Productlist\products.cshtml:line 1550
   at CompiledRazorTemplates.Dynamic.RazorEngine_a70e42c38304420385aeaa43a6625237.Execute() in D:\dynamicweb.net\Solutions\keflico.live\Files\Templates\Designs\Keflico\eCom\Productlist\products.cshtml:line 210
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()
  1     @using System.Web;
  2     @using Dynamicweb.Ecommerce;
  3     @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites
  4     @using Dynamicweb.Ecommerce.Variants;
  5     @using Keflico.Website.Custom
  6     @{
  7         string AllProductsPage = Pageview.Area.Item["AllProductsPage"].ToString();
  8         string VariantsLookup = "/" + Pageview.Area.Item["Variantslookup"].ToString();
  9         string BundleLookup = "/" + Pageview.Area.Item["Bundlelookup"].ToString();
 10         bool isloggedin = false;
 11     
 12     
 13         var allProductsList = new List<string>(); // Use a List to build your JSON string.
 14     
 15         foreach (var product in GetLoop("Products"))
 16         {
 17             var primaryGroup = product.GetString("Ecom:Product.PrimaryGroupID").ToLower();
 18             var productId = product.GetString("Ecom:Product.ID");
 19             var totalStock = product.GetDouble("Ecom:Product.Stock");
 20             totalStock += product.GetDouble("Ecom:Product:Field.StockSea.Value");
 21             var totalStockWareHouse = product.GetInteger("Ecom:Product.Stock");
 22             var totalStockSea = product.GetInteger("Ecom:Product:Field.StockSea.Value");
 23     
 24             if (primaryGroup != "group32")
 25             {
 26                 string additionalStockString = product.GetString("Ecom:Product:Field.ProductPieceOnPurchase.Value");
 27                 if (int.TryParse(additionalStockString, out int additionalStock))
 28                 {
 29                     totalStock += additionalStock;
 30                     totalStockSea += additionalStock;
 31                 }
 32             }
 33     
 34             var productObject = $@"{{
 35     ""id"": ""{productId}"",
 36     ""primaryGroup"": ""{primaryGroup}"",
 37     ""totalStock"": {totalStock},
 38     ""totalStockWareHouse"": {totalStockWareHouse},
 39     ""totalStockSea"": {totalStockSea}
 40     }}";
 41             allProductsList.Add(productObject);
 42         }
 43     
 44         // Join all the product objects with commas and wrap them in brackets to form a JSON array.
 45         var allProductNumbersString = "[" + string.Join(",", allProductsList) + "]";
 46     
 47     
 48     
 49         string sortBy = GetString("Ecom:ProductList.SortBy");
 50         string sortOrder = GetString("Ecom:ProductList.SortOrder");
 51     
 52         bool isStandardSorting = string.IsNullOrWhiteSpace(HttpContext.Current.Request.QueryString["Sortby"]);
 53     
 54     
 55         if (!string.IsNullOrWhiteSpace(GetGlobalValue("Global:Extranet.UserName").ToString()))
 56         {
 57             isloggedin = true;
 58         }
 59     
 60     
 61         FavoriteList favoriteList = null;
 62         var service = new FavoriteProductService();
 63         if (isloggedin)
 64         {
 65             var user = Dynamicweb.Security.UserManagement.User.GetCurrentFrontendUser();
 66             favoriteList = user.GetFavoriteLists().FirstOrDefault();
 67     
 68         }
 69     
 70         var shouldRefreshFavorite = (isloggedin && !Dynamicweb.Security.UserManagement.User.GetCurrentFrontendUser().UserHasFavoriteList()).ToString().ToLower();
 71     
 72     }
 73     
 74     <header class="header header-overview module module-sand-light">
 75         <div class="breadcrumbs">
 76             @RenderNavigation(new
 77             {
 78                 StartLevel = 1,
 79                 EndLevel = 4,
 80                 ExpandMode = "Pathonly",
 81                 Template = "breadcrumbs.xslt"
 82             })
 83         </div>
 84         @if (String.IsNullOrWhiteSpace(GetString("Ecom:ProductList:Page.GroupID")))
 85         {
 86             <h1 class="header__title">@Translate("Translate_TopLevelGroup_Headline")</h1>
 87             <p>@Translate("Translate_TopLevelGroup_Text")</p>
 88         }
 89         else
 90         {
 91             <h1 class="header__title">@GetString("Ecom:Group.Name")</h1>
 92     
 93             if (!String.IsNullOrWhiteSpace(GetString("Ecom:Group.Description")))
 94             {
 95                 @GetString("Ecom:Group.Description")
 96             }
 97         }
 98     
 99     </header>
100     
101     @if (GetLoop("ProductGroups").Count() > 0 && String.IsNullOrWhiteSpace(GetString("Ecom:ProductList:Page.GroupID")))
102     {
103         <section class="category-page module module-sand">
104             <article class="card-list__wrapper">
105                 <a href="@AllProductsPage" class="all-link">
106                     @Translate("Translate_ProductList_SeeAllProducts")
107                     <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.006 11.031">
108                         <g class="arrow" transform="translate(441 901.014) rotate(180)">
109                             <path class="angle" d="M-17182.074-20447.988l4.809-4.809,4.809,4.809" transform="translate(20875.289 -16281.768) rotate(-90)" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" />
110                             <line class="line" x2="17" transform="translate(423.5 895.5)" stroke-linecap="round" stroke-width="1" />
111                         </g>
112                     </svg>
113                 </a>
114                 <ul class="card-list">
115                     @foreach (var group in GetLoop("ProductGroups"))
116                     {
117                         bool show = group.GetBoolean("Ecom:Group.ShowInMenu");
118                         if (show)
119                         {
120                             string groupLink = "/Default.aspx?Id=" + Pageview.Page.ID + "&GroupID=" + group.GetString("Ecom:Group.ID");
121                             string groupName = group.GetString("Ecom:Group.Name");
122                             string groupImage = group.GetString("Ecom:Group.LargeImage");
123                             string groupImageLg = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(groupImage) + "&Width=665&Height=665&Crop=0&Compression=100";
124                             string groupImageMd = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(groupImage) + "&Width=512&Height=512&Crop=0&Compression=100";
125                             string groupImageSm = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(groupImage) + "&Width=415&Height=415&Crop=0&Compression=100";
126                             string groupImageDefault = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(groupImage) + "&Width=620&Height=620&Crop=0&Compression=100";
127     
128                             <li class="card-list__card">
129                                 <a href="@groupLink" class="card__wrapper">
130                                     <picture class="card__picture">
131                                         <source srcset="@groupImageLg" media="(min-width: 1536px)">
132                                         <source srcset="@groupImageMd" media="(min-width: 992px)">
133                                         <source srcset="@groupImageSm" media="(min-width: 768px)">
134                                         <img src="@groupImageDefault" alt="@groupName">
135                                     </picture>
136                                     <ul class="card__info-list">
137                                         <li class="card__info-list__item">
138                                             <span class="info info--large">@groupName</span>
139                                         </li>
140                                     </ul>
141                                 </a>
142                             </li>
143                         }
144     
145                     }
146                 </ul>
147             </article>
148         </section>
149     }
150     else if (GetLoop("Subgroups").Count() > 0)
151     {
152         var currentGroup = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(GetString("Ecom:Group.ID"));
153         var currentToplevel = FindTopGroup(currentGroup);
154         string allLink = AllProductsPage + "&Group=" + currentGroup.Name;
155     
156         <section class="category-page module module-sand">
157             <article class="card-list__wrapper">
158                 <a href="@allLink" class="all-link">
159                     @Translate("Translate_ProductList_SeeAllProducts")
160                     <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.006 11.031">
161                         <g class="arrow" transform="translate(441 901.014) rotate(180)">
162                             <path class="angle" d="M-17182.074-20447.988l4.809-4.809,4.809,4.809" transform="translate(20875.289 -16281.768) rotate(-90)" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" />
163                             <line class="line" x2="17" transform="translate(423.5 895.5)" stroke-linecap="round" stroke-width="1" />
164                         </g>
165                     </svg>
166                 </a>
167                 <ul class="card-list">
168                     @foreach (var group in GetLoop("Subgroups"))
169                     {
170                         bool show = group.GetBoolean("Ecom:Group.ShowInMenu");
171                         if (show)
172                         {
173                             string groupLink = "/Default.aspx?Id=" + Pageview.Page.ID + "&GroupID=" + group.GetString("Ecom:Group.ID");
174                             string groupName = group.GetString("Ecom:Group.Name");
175                             string groupImage = group.GetString("Ecom:Group.LargeImage");
176                             string groupImageLg = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(groupImage) + "&Width=665&Height=665&Crop=0&Compression=100";
177                             string groupImageMd = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(groupImage) + "&Width=512&Height=512&Crop=0&Compression=100";
178                             string groupImageSm = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(groupImage) + "&Width=415&Height=415&Crop=0&Compression=100";
179                             string groupImageDefault = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(groupImage) + "&Width=620&Height=620&Crop=0&Compression=100";
180     
181                             if (groupName.ToLower().Contains("alle") || groupName.ToLower().Contains("all"))
182                             {
183                                 groupLink = allLink;
184                             }
185     
186                             <li class="card-list__card">
187                                 <a href="@groupLink" class="card__wrapper">
188                                     <picture class="card__picture">
189                                         <source srcset="@groupImageLg" media="(min-width: 1536px)">
190                                         <source srcset="@groupImageMd" media="(min-width: 992px)">
191                                         <source srcset="@groupImageSm" media="(min-width: 768px)">
192                                         <img src="@groupImageDefault" alt="@groupName">
193                                     </picture>
194                                     <ul class="card__info-list">
195                                         <li class="card__info-list__item">
196                                             <span class="info info--large">@groupName</span>
197                                         </li>
198                                     </ul>
199                                 </a>
200                             </li>
201                         }
202                     }
203                 </ul>
204             </article>
205         </section>
206     }
207     else
208     {
209         var currentGroup = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(GetString("Ecom:Group.ID"));
210         var currentToplevel = FindTopGroup(currentGroup);
211     
212         <section class="module module-sand product-overview" id="productsFlowApp">
213             <aside id="productFilters" class="filter-section" data-group="@currentGroup.Id" data-top-group="@currentToplevel.Name" data-all="false">
214                 <div class="filter-section__header">
215                     <h3>@Translate("Translate_ProductList_FilterHeadline")</h3>
216     
217                     <div class="filter-section__exit" id="filter-article-close">
218                         <svg xmlns="http://www.w3.org/2000/svg" width="10.819" height="10.819" viewBox="0 0 10.819 10.819">
219                             <g id="Group_1017" data-name="Group 1017" transform="translate(-1.591 2.559)">
220                                 <rect id="Rectangle_702" data-name="Rectangle 702" width="14" height="1.3" rx="0.65" transform="translate(1.591 7.341) rotate(-45)" />
221                                 <rect id="Rectangle_703" data-name="Rectangle 703" width="14" height="1.3" rx="0.65" transform="translate(2.51 -2.559) rotate(45)" />
222                             </g>
223                         </svg>
224                     </div>
225                 </div>
226                 <ul v-if="showFilters" class="filter-section__filter-list">
227                     <li v-for="facet in facets" v-if="((facet.QueryParameter != 'Group' && currentGroup && !allProducts) || allProducts) && sortedOptions(facet.Options).length > 1" :class="'filter-item filter-item--' + facet.RenderType.toLowerCase()">
228                         <template v-if="facet.RenderType.toLowerCase() != 'range'">
229                             <input class="filter-item__checkbox-toggle" type="checkbox" :id="facet.QueryParameter" aria-checked="true">
230                             <label class="filter-item__header" :for="facet.QueryParameter">
231                                 <span class="filter-item__header__title">{{ translation('Translate_ProductList_Filter_' + facet.QueryParameter) }}</span>
232                             </label>
233                         </template>
234     
235                         <div class="filter-item__body">
236                             <ul class="filter-item__input-list">
237                                 <template v-if="facet.RenderType.toLowerCase() == 'range'">
238                                     <li class="input-list__filter">
239                                         <label for="filter-length">{{ translation('Translate_ProductList_Filter_' + facet.QueryParameter.replace('Start', '')) }}</label>
240                                         <div class="duo-range-slider">
241                                             <input class="duo-range-slider__range filter-option" :data-name="facet.QueryParameter" :name="facet.QueryParameter" :value="getLowestValue(facet.Options)" :min="getLowestValue(facet.Options)" :max="getHighestValue(facet.Options)" step="1" type="range">
242                                             <input class="duo-range-slider__range filter-option" :data-name="facet.QueryParameter.replace('Start', 'End')" :name="facet.QueryParameter.replace('Start', 'End')" :value="getHighestValue(facet.Options)" :min="getLowestValue(facet.Options)" :max="getHighestValue(facet.Options)" step="1" type="range">
243                                             <div class="duo-range-slider__values">
244                                                 <div class="duo-range-slider__values-min"><span>{{ facet.Options[0].Value }}</span> mm</div>
245                                                 <div class="duo-range-slider__values-max"><span>{{ facet.Options.at(-1).Value }}</span> mm</div>
246                                             </div>
247                                         </div>
248                                     </li>
249                                 </template>
250                                 <template v-else-if="facet.RenderType.toLowerCase() == 'select'">
251                                     <li class="input-list__filter">
252                                         <div class="search-select">
253                                             <div class="search-select__search">
254                                                 <input class="search-select__search-input" type="search" :placeholder="translation('Translate_General_SearchFor')">
255                                                 <button class="search-select__search-clear" type="button">@Translate("Translate_Close")</button>
256                                             </div>
257                                             <div class="search-select__dropdown">
258                                                 <label v-for="option in sortedOptions(facet.Options)" class="search-select__dropdown-item" :for="facet.QueryParameter + '_' + option.Value">{{ option.Value }}</label>
259                                             </div>
260                                             <div class="search-select__tags">
261                                                 <template v-for="option in sortedOptions(facet.Options)">
262                                                     <input type="checkbox" class="filter-option" :data-name="facet.QueryParameter" :data-parameter-type="facet.QueryParameterType" :name="facet.QueryParameter + addition(facet.QueryParameterType)" :value="option.Value" :id="facet.QueryParameter + '_' + option.Value">
263                                                     <label :for="facet.QueryParameter + '_' + option.Value">{{ option.Label }}</label>
264                                                 </template>
265                                             </div>
266                                         </div>
267                                     </li>
268                                 </template>
269                                 <template v-else>
270                                     <li class="input-list__filter">
271                                         <template v-for="option in sortedOptions(facet.Options)">
272                                             <div class="form__fieldset" v-if="facet.QueryParameter != 'Type'">
273                                                 <div class="form__field-wrap">
274                                                     <input type="checkbox" class="form__field form__field--checkbox filter-option" :data-name="facet.QueryParameter" :name="facet.QueryParameter + addition(facet.QueryParameterType)" :value="option.Value" :id="facet.QueryParameter + '_' + option.Value">
275                                                     <label :for="facet.QueryParameter + '_' + option.Value">{{ option.Label }}</label>
276                                                 </div>
277                                             </div>
278                                             <div class="form__fieldset" v-if="facet.QueryParameter == 'Type' && !(option.Label == 'kunde' || option.Label == 'relatordre')">
279                                                 <div class="form__field-wrap">
280                                                     <input type="checkbox" class="form__field form__field--checkbox filter-option" :data-name="facet.QueryParameter" :name="facet.QueryParameter + addition(facet.QueryParameterType)" :value="option.Value" :id="facet.QueryParameter + '_' + option.Value">
281                                                     <label :for="facet.QueryParameter + '_' + option.Value">{{ translation(`Translate_Filter_ProductType_${option.Label}`) }}</label>
282                                                 </div>
283                                             </div>
284                                         </template>
285                                     </li>
286                                 </template>
287                             </ul>
288                         </div>
289                     </li>
290                 </ul>
291                 <div class="filter-section__footer">
292                     <div class="footer-controls">
293                         <button id="clearFilterButton" class="btn btn-link">@Translate("Translate_ProductList_ResetFilters")</button>
294                         <button id="applyFilterButton" class="btn btn-secondary btn--small">@Translate("Translate_ProductList_ApplyFilters")</button>
295                     </div>
296                 </div>
297             </aside>
298             <article class="card-list__wrapper">
299                 <div class="card-list__header">
300                     <div class="form__fieldset mobile-only">
301                         <button class="btn btn-secondary" id="toggle-filter-btn">
302                             @Translate("Translate_Filter")
303                         </button>
304                     </div>
305                     <div class="form__fieldset">
306                         <label for="sorting" class="select-label">@Translate("Translate_ProductList_SortBy")</label>
307                         <select class="form__field--narrow form__field-wrap--select" id="sorting">
308                             <option value="default">@Translate("Translate_ProductList_SortOption_Highlighted")</option>
309                             <option value="asc" @(!isStandardSorting && sortOrder.ToLower() == "asc" ? "selected" : "")>@Translate("Translate_ProductList_SortOption_Alphabetic")</option>
310                             <option value="desc" @(!isStandardSorting && sortOrder.ToLower() == "desc" ? "selected" : "")>@Translate("Translate_ProductList_SortOption_ReverseAlpabetic")</option>
311                         </select>
312                     </div>
313                 </div>
314                 <ul class="card-list">
315                     @foreach (var product in GetLoop("Products"))
316                     {
317                         string link = product.GetString("Ecom:Product.Link.Clean");
318                         string name = product.GetString("Ecom:Product.Name");
319                         string teaser = product.GetString("Ecom:Product:Field.Name2.Value.Clean");
320                         string productNumber = product.GetString("Ecom:Product.Number");
321                         string dbNumberLabel = product.GetString("Ecom:Product:Field.ProductDbNumber.Name");
322                         string dbNumber = product.GetString("Ecom:Product:Field.ProductDbNumber.Value");
323                         string primaryGroup = product.GetString("Ecom:Product.PrimaryGroupID").ToLower();
324                         var productLayout = !string.IsNullOrWhiteSpace(product.GetString("Ecom:Product:Field.ProductLayout")) ? product.GetString("Ecom:Product:Field.ProductLayout") : primaryGroup;
325     
326                         var combination = new VariantCombination(product.GetString("Ecom:Product.ID"));
327                         IList<VariantCombination> productVariants = new List<VariantCombination>();
328                         var singleVariant = new VariantCombination();
329     
330                         if (combination != null && combination.Product != null)
331                         {
332                             productVariants = combination.Product.VariantCombinations;
333                         }
334     
335                         if (productVariants.Count == 1)
336                         {
337                             singleVariant = productVariants.FirstOrDefault();
338                         }
339     
340                         string labelCode = product.GetString("Ecom:Product:Field.ProductPurchaseCode");
341     
342                         bool isBundleOnly = System.Convert.ToBoolean(product.GetString("Ecom:Product:Field.BundleOnly"));
343                         bool usePriceExplanation = false;
344     
345                         string primaryPrice = product.GetString("Ecom:Product.Price.PriceWithoutVAT");
346                         string secondaryPrice = "";
347                         string tertiaryPrice = "";
348                         string primaryPriceDescription = "";
349                         string secondaryPriceDescription = "";
350                         string tertiaryPriceDescription = "";
351     
352                         //product.GetRawTags();
353     
354                         var totalStock = product.GetDouble("Ecom:Product.Stock");
355                         totalStock += product.GetDouble("Ecom:Product:Field.StockSea.Value");
356     
357                         int totalStockWareHouse = product.GetInteger("Ecom:Product.Stock");
358                         int totalStockSea = product.GetInteger("Ecom:Product:Field.StockSea.Value");
359     
360     
361                         if (primaryGroup != "group32")
362                         {
363                             totalStock += product.GetDouble("Ecom:Product:Field.ProductPieceOnPurchase.Value");
364                             totalStockSea += product.GetInteger("Ecom:Product:Field.ProductPieceOnPurchase.Value");
365                         }
366     
367                         bool isSingleVariant = product.GetInteger("Ecom:Product.VariantCount") == 1 ? true : false;
368     
369                         double singleVariantStock = 0;
370                         int singleVariantStockSea = 0;
371     
372                         if (isSingleVariant)
373                         {
374                             singleVariantStock = singleVariant.Product.UnitStock;
375                             singleVariantStockSea = !String.IsNullOrWhiteSpace(singleVariant.Product.ProductFieldValues.GetProductFieldValue("ProductPieceOnPurchase").Value.ToString()) ? Convert.ToInt32(singleVariant.Product.ProductFieldValues.GetProductFieldValue("ProductPieceOnPurchase").Value.ToString().Replace(",", "")) : 0;
376                             totalStockWareHouse = (int)singleVariantStock;
377                             totalStock += singleVariantStock;
378                             totalStock += singleVariantStockSea;
379                         }
380     
381                         string priceCurrencySymbol = product.GetString("Ecom:Product.Currency.Symbol");
382                         string salesUnit = product.GetString("Ecom:Product.DefaultUnitName").ToLower();
383                         string salesUnitCode = product.GetString("Ecom:Product:Field.ProductLengthUnitCode");
384     
385                         string productImage = product.GetString("Ecom:Product.ImageDefault.Clean");
386                         string productImageLg = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(productImage) + "&Width=416&Height=371&Crop=0&Compression=100";
387                         string productImageMd = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(productImage) + "&Width=310&Height=277&Crop=0&Compression=100";
388                         string productImageSm = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(productImage) + "&Width=230&Height=205&Crop=0&Compression=100";
389                         string productImageDefault = "/admin/public/getimage.ashx?Image=" + HttpUtility.UrlEncode(productImage) + "&Width=340&Height=304&Crop=0&Compression=100";
390                         bool isFavorite = favoriteList != null && service.GetFavoriteProducts(favoriteList.ListId).Any(x => x.ProductId == productNumber);
391                         switch (productLayout)
392                         {
393                             case "group1":
394     
395                                 secondaryPrice = product.GetString("Ecom:Product:Field.ProductPriceBundle.Value").Replace(",", "-").Replace(".", ",").Replace("-", ".");
396                                 if (salesUnit == "m³")
397                                 {
398                                     primaryPriceDescription = Translate("Translate_ProductPage_SalesUnit_m3_Under");
399                                     secondaryPriceDescription = Translate("Translate_ProductPage_SalesUnit_m3_Over");
400                                 }
401                                 else if (salesUnit == "kbf")
402                                 {
403                                     primaryPriceDescription = Translate("Translate_ProductPage_SalesUnit_kbf_Under");
404                                     secondaryPriceDescription = Translate("Translate_ProductPage_SalesUnit_kbf_Over");
405                                 }
406                                 else
407                                 {
408                                     primaryPriceDescription = Translate("Translate_ProductDecription_Hardwood_Anbrud");
409                                     secondaryPriceDescription = Translate("Translate_ProductDecription_Hardwood_Bundt");
410                                 }
411     
412                                 if (secondaryPrice.Contains(","))
413                                 {
414                                     string[] sp = secondaryPrice.Split(',');
415     
416                                     if (sp[1].Length == 1)
417                                     {
418                                         secondaryPrice += "0";
419                                     }
420                                 }
421                                 else
422                                 {
423                                     secondaryPrice += ",00";
424                                 }
425     
426                                 break;
427     
428                             case "group32":
429                                 secondaryPrice = product.GetString("Ecom:Product:Field.ProductPriceAbove100SQM");
430     
431                                 primaryPriceDescription = Translate("Translate_ProductDecription_Terrase_Anbrud");
432                                 secondaryPriceDescription = Translate("Translate_ProductDecription_Terrase_Above");
433     
434                                 usePriceExplanation = true;
435     
436                                 if (secondaryPrice.Contains(","))
437                                 {
438                                     string[] sp = secondaryPrice.Split(',');
439     
440                                     if (sp[1].Length == 1)
441                                     {
442                                         secondaryPrice += "0";
443                                     }
444                                 }
445                                 else
446                                 {
447                                     secondaryPrice += ",00";
448                                 }
449     
450                                 break;
451     
452                             case "group73":
453                                 secondaryPrice = product.GetString("Ecom:Product:Field.ProductPriceAbove100SQM");
454                                 tertiaryPrice = product.GetString("Ecom:Product:Field.ProductPriceAbove3000M.Value").Replace(".", ",");
455     
456                                 primaryPriceDescription = Translate("Translate_ProductDecription_Facade_Under1000");
457                                 secondaryPriceDescription = Translate("Translate_ProductDecription_Facade_Between1000and3000");
458                                 tertiaryPriceDescription = Translate("Translate_ProductDecription_Facade_Above3000");
459     
460                                 usePriceExplanation = true;
461     
462                                 if (secondaryPrice.Contains(","))
463                                 {
464                                     string[] sp = secondaryPrice.Split(',');
465     
466                                     if (sp[1].Length == 1)
467                                     {
468                                         secondaryPrice += "0";
469                                     }
470                                 }
471                                 else
472                                 {
473                                     secondaryPrice += ",00";
474                                 }
475     
476                                 if (tertiaryPrice.Contains(","))
477                                 {
478                                     string[] tp = tertiaryPrice.Split(',');
479     
480                                     if (tp[1].Length == 1)
481                                     {
482                                         tertiaryPrice += "0";
483                                     }
484                                 }
485                                 else
486                                 {
487                                     tertiaryPrice += ",00";
488                                 }
489     
490                                 break;
491     
492                             case "group52":
493                             case "group66":
494                                 secondaryPrice = product.GetString("Ecom:Product:Field.ProductPriceHalfParcel").Replace(",", "-").Replace(".", ",").Replace("-", ".");
495                                 tertiaryPrice = product.GetString("Ecom:Product:Field.ProductPriceCompleteParcel").Replace(",", "-").Replace(".", ",").Replace("-", ".");
496     
497                                 primaryPriceDescription = Translate("Translate_ProductDecription_Plader_Anbrud");
498                                 secondaryPriceDescription = Translate("Translate_ProductDecription_Plader_Half");
499                                 tertiaryPriceDescription = Translate("Translate_ProductDecription_Plader_Full");
500     
501                                 if (secondaryPrice.Contains(","))
502                                 {
503                                     string[] sp = secondaryPrice.Split(',');
504     
505                                     if (sp[1].Length == 1)
506                                     {
507                                         secondaryPrice += "0";
508                                     }
509                                 }
510                                 else
511                                 {
512                                     secondaryPrice += ",00";
513                                 }
514     
515                                 if (tertiaryPrice.Contains(","))
516                                 {
517                                     string[] tp = tertiaryPrice.Split(',');
518     
519                                     if (tp[1].Length == 1)
520                                     {
521                                         tertiaryPrice += "0";
522                                     }
523                                 }
524                                 else
525                                 {
526                                     tertiaryPrice += ",00";
527                                 }
528     
529                                 break;
530     
531                             case "group126":
532                                 primaryPriceDescription = Translate("Translate_ProductDecription_Accessories");
533                                 break;
534     
535                             default:
536                                 primaryPriceDescription = Translate("Translate_ProductDecription_General_Description");
537                                 break;
538                         }
539     
540                         <li class="card-list__card product-card">
541     
542                             <a href="@link" class="card__wrapper">
543     
544                                 @switch (labelCode.ToLower())
545                                 {
546                                     case "prøve":
547                                         <div class="card-list__label-code label-code label-code--samples">@Translate("LabelCode_" + labelCode.ToLower().Replace("ø", "oe"))</div>
548                                         break;
549                                     case "skaffe":
550                                     case "relatordre":
551                                         <div class="card-list__label-code label-code">@Translate("LabelCode_" + labelCode.ToLower())</div>
552                                         break;
553                                     default:
554                                         break;
555                                 }
556     
557                                 @if (!String.IsNullOrWhiteSpace(productImage))
558                                 {
559                                     <picture class="card__picture">
560                                         <source srcset="@productImageLg" media="(min-width: 1536px)">
561                                         <source srcset="@productImageMd" media="(min-width: 992px)">
562                                         <source srcset="@productImageSm" media="(min-width: 768px)">
563                                         <img src="@productImageDefault" alt="@name.Replace("\"", """)">
564                                         @if (isloggedin)
565                                         {
566                                             <button id="favoriteAdd-@productNumber" style="@(isFavorite ? "display:none" : "")" class="card-favorite" @@click.prevent="favoriteAdd('@productNumber')">
567                                                 <svg id="Lag_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100">
568                                                     <!-- Generator: Adobe Illustrator 29.5.0, SVG Export Plug-In . SVG Version: 2.1.0 Build 137)  -->
569                                                     <path d="M50,92.9c-.2,0-.5,0-.6-.3L10,53.2c-4.9-4.9-7.5-11.3-7.5-18.2s2.7-13.4,7.5-18.2c4.9-4.9,11.3-7.5,18.2-7.5s13.4,2.7,18.2,7.5l3.5,3.5,3.5-3.5c4.9-4.9,11.3-7.5,18.2-7.5s13.4,2.7,18.2,7.5c4.9,4.9,7.5,11.3,7.5,18.2s-2.7,13.4-7.5,18.2l-39.4,39.4c-.2.2-.4.3-.6.3ZM50.1,90.8l38.8-38.8c4.5-4.5,7-10.6,7-17s-2.5-12.5-7-17c-4.5-4.5-10.6-7-17-7s-12.5,2.5-17,7l-4.2,4.2c-.2.2-.4.3-.6.3s-.5,0-.6-.3l-4.2-4.2c-4.5-4.5-10.6-7-17-7s-12.5,2.5-17,7c-4.5,4.5-7,10.6-7,17s2.5,12.5,7,17l.4.4,38.4,38.4Z" />
570                                                 </svg>
571     
572                                             </button>
573                                             <button id="favoriteRemove-@productNumber" style="@(isFavorite ? "" : "display:none")" class="card-favorite card-favorite--filled" @@click.prevent="favoriteRemove('@productNumber')">
574                                                 <svg id="Lag_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100">
575                                                     <!-- Generator: Adobe Illustrator 29.5.0, SVG Export Plug-In . SVG Version: 2.1.0 Build 137)  -->
576                                                     <path d="M50,92.9c-.2,0-.5,0-.6-.3L10,53.2c-4.9-4.9-7.5-11.3-7.5-18.2s2.7-13.4,7.5-18.2c4.9-4.9,11.3-7.5,18.2-7.5s13.4,2.7,18.2,7.5l3.5,3.5,3.5-3.5c4.9-4.9,11.3-7.5,18.2-7.5s13.4,2.7,18.2,7.5c4.9,4.9,7.5,11.3,7.5,18.2s-2.7,13.4-7.5,18.2l-39.4,39.4c-.2.2-.4.3-.6.3Z" />
577                                                 </svg>
578                                             </button>
579                                         }
580     
581                                     </picture>
582                                 }
583                                 else
584                                 {
585                                     <div class="card__picture card__picture--dummie">
586                                         @if (isloggedin)
587                                         {
588                                             <button id="favoriteAdd-@productNumber" style="@(isFavorite ? "display:none" : "")" class="card-favorite" @@click.prevent="favoriteAdd('@productNumber')">
589                                                 <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 109.57" style="enable-background:new 0 0 122.88 109.57" xml:space="preserve">
590                                                 <g fill="gray">
591                                                 <path d="M65.46,19.57c-0.68,0.72-1.36,1.45-2.2,2.32l-2.31,2.41l-2.4-2.33c-0.71-0.69-1.43-1.4-2.13-2.09 c-7.42-7.3-13.01-12.8-24.52-12.95c-0.45-0.01-0.93,0-1.43,0.02c-6.44,0.23-12.38,2.6-16.72,6.65c-4.28,4-7.01,9.67-7.1,16.57 c-0.01,0.43,0,0.88,0.02,1.37c0.69,19.27,19.13,36.08,34.42,50.01c2.95,2.69,5.78,5.27,8.49,7.88l11.26,10.85l14.15-14.04 c2.28-2.26,4.86-4.73,7.62-7.37c4.69-4.5,9.91-9.49,14.77-14.52c3.49-3.61,6.8-7.24,9.61-10.73c2.76-3.42,5.02-6.67,6.47-9.57 c2.38-4.76,3.13-9.52,2.62-13.97c-0.5-4.39-2.23-8.49-4.82-11.99c-2.63-3.55-6.13-6.49-10.14-8.5C96.5,7.29,91.21,6.2,85.8,6.82 C76.47,7.9,71.5,13.17,65.46,19.57L65.46,19.57z M60.77,14.85C67.67,7.54,73.4,1.55,85.04,0.22c6.72-0.77,13.3,0.57,19.03,3.45 c4.95,2.48,9.27,6.1,12.51,10.47c3.27,4.42,5.46,9.61,6.1,15.19c0.65,5.66-0.29,11.69-3.3,17.69c-1.7,3.39-4.22,7.03-7.23,10.76 c-2.95,3.66-6.39,7.44-10,11.17C97.2,74.08,91.94,79.12,87.2,83.66c-2.77,2.65-5.36,5.13-7.54,7.29L63.2,107.28l-2.31,2.29 l-2.34-2.25l-13.6-13.1c-2.49-2.39-5.37-5.02-8.36-7.75C20.38,71.68,0.81,53.85,0.02,31.77C0,31.23,0,30.67,0,30.09 c0.12-8.86,3.66-16.18,9.21-21.36c5.5-5.13,12.97-8.13,21.01-8.42c0.55-0.02,1.13-0.03,1.74-0.02C46,0.48,52.42,6.63,60.77,14.85 L60.77,14.85z" />
592                                         </g>
593                                         </svg>
594                                             </button>
595                                             <button id="favoriteRemove-@productNumber" style="@(isFavorite ? "" : "display:none")" class="card-favorite card-favorite--filled" @@click.prevent="favoriteRemove('@productNumber')">
596                                                 <svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 107.39">
597                                                     <defs>
598     
599                                                     </defs>
600                                                     <title>red-heart</title>
601                                                     <path class="cls-1" d="M60.83,17.18c8-8.35,13.62-15.57,26-17C110-2.46,131.27,21.26,119.57,44.61c-3.33,6.65-10.11,14.56-17.61,22.32-8.23,8.52-17.34,16.87-23.72,23.2l-17.4,17.26L46.46,93.55C29.16,76.89,1,55.92,0,29.94-.63,11.74,13.73.08,30.25.29c14.76.2,21,7.54,30.58,16.89Z" />
602                                                 </svg>
603                                             </button>
604                                         }
605     
606     
607                                     </div>
608                                 }
609     
610                                 <div class="card__info-list__item">
611                                     <span class="info info--small info--header">@Translate("Translate_Product_Page_ProductNumber"): @productNumber</span>
612                                     @if (!string.IsNullOrWhiteSpace(dbNumber))
613                                     {
614                                         <span class="info info--small info--header">@dbNumberLabel: @dbNumber</span>
615                                     }
616                                 </div>
617                                 <div class="card__info-list__item ">
618                                     <span class="info info--title">@name</span>
619                                 </div>
620                                 <div class="card__info-list__item">
621                                     <span class="info info--small product-teaser">@teaser</span>
622                                 </div>
623                                 @if (isloggedin && ((!String.IsNullOrWhiteSpace(primaryPrice) && primaryPrice != "0,00" && primaryPrice != ",00") || (!String.IsNullOrWhiteSpace(secondaryPrice) && secondaryPrice != "0,00" && secondaryPrice != ",00") || (!String.IsNullOrWhiteSpace(tertiaryPrice) && tertiaryPrice != "0,00" && tertiaryPrice != ",00")))
624                                 {
625                                     <div class="card__info-list__item column">
626                                         @if (usePriceExplanation)
627                                         {
628                                             <div class="price-line price-line--explanation">
629                                                 @Translate("Translate_PriceExplanation_" + primaryGroup)
630                                             </div>
631                                         }
632                                         @if (!String.IsNullOrWhiteSpace(primaryPrice) && primaryPrice != "0,00" && primaryPrice != ",00")
633                                         {
634                                             <div class="price-line">
635                                                 <div class="definition">@primaryPriceDescription</div>
636                                                 <div class="price">@primaryPrice <text> </text> @priceCurrencySymbol <text>pr.</text> @salesUnit</div>
637                                             </div>
638                                         }
639     
640                                         @if (!String.IsNullOrWhiteSpace(secondaryPrice) && secondaryPrice != "0,00" && secondaryPrice != ",00")
641                                         {
642                                             <div class="price-line">
643                                                 <div class="definition">@secondaryPriceDescription</div>
644                                                 <div class="price">@secondaryPrice <text> </text> @priceCurrencySymbol <text>pr.</text> @salesUnit</div>
645                                             </div>
646                                         }
647     
648                                         @if (!String.IsNullOrWhiteSpace(tertiaryPrice) && tertiaryPrice != "0,00" && tertiaryPrice != ",00")
649                                         {
650                                             <div class="price-line">
651                                                 <div class="definition">@tertiaryPriceDescription</div>
652                                                 <div class="price">@tertiaryPrice <text> </text> @priceCurrencySymbol <text>pr.</text> @salesUnit</div>
653                                             </div>
654                                         }
655                                     </div>
656                                 }
657                                 @*@if (isSingleVariant)
658                                     {
659                                         <div class="card__info-list__item">
660                                             <span class="info info--small" style="font-size: 15px;margin-top:15px;">
661                                                 @if (totalStockWareHouse > 0)
662                                                 {
663                                                     <text>
664                                                         <svg width="30px" height="30px" viewBox="0 -0.5 25 25" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#12ca30">
665                                                             <g id="SVGRepo_bgCarrier" stroke-width="0" />
666                                                             <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />
667                                                             <g id="SVGRepo_iconCarrier"> <path d="M4 12.6111L8.92308 17.5L20 6.5" stroke="#24a84b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> </g>
668                                                         </svg>
669                                                         @Translate("Translate_ProductPage_StockOnWarehouse_OnStock")
670                                                     </text>
671                                                 }
672                                                 else
673                                                 {
674                                                     <text>
675                                                         <svg width="30px" height="30px" viewBox="0 -0.5 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
676                                                             <g id="SVGRepo_bgCarrier" stroke-width="0" />
677                                                             <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />
678                                                             <g id="SVGRepo_iconCarrier"> <path d="M6.96967 16.4697C6.67678 16.7626 6.67678 17.2374 6.96967 17.5303C7.26256 17.8232 7.73744 17.8232 8.03033 17.5303L6.96967 16.4697ZM13.0303 12.5303C13.3232 12.2374 13.3232 11.7626 13.0303 11.4697C12.7374 11.1768 12.2626 11.1768 11.9697 11.4697L13.0303 12.5303ZM11.9697 11.4697C11.6768 11.7626 11.6768 12.2374 11.9697 12.5303C12.2626 12.8232 12.7374 12.8232 13.0303 12.5303L11.9697 11.4697ZM18.0303 7.53033C18.3232 7.23744 18.3232 6.76256 18.0303 6.46967C17.7374 6.17678 17.2626 6.17678 16.9697 6.46967L18.0303 7.53033ZM13.0303 11.4697C12.7374 11.1768 12.2626 11.1768 11.9697 11.4697C11.6768 11.7626 11.6768 12.2374 11.9697 12.5303L13.0303 11.4697ZM16.9697 17.5303C17.2626 17.8232 17.7374 17.8232 18.0303 17.5303C18.3232 17.2374 18.3232 16.7626 18.0303 16.4697L16.9697 17.5303ZM11.9697 12.5303C12.2626 12.8232 12.7374 12.8232 13.0303 12.5303C13.3232 12.2374 13.3232 11.7626 13.0303 11.4697L11.9697 12.5303ZM8.03033 6.46967C7.73744 6.17678 7.26256 6.17678 6.96967 6.46967C6.67678 6.76256 6.67678 7.23744 6.96967 7.53033L8.03033 6.46967ZM8.03033 17.5303L13.0303 12.5303L11.9697 11.4697L6.96967 16.4697L8.03033 17.5303ZM13.0303 12.5303L18.0303 7.53033L16.9697 6.46967L11.9697 11.4697L13.0303 12.5303ZM11.9697 12.5303L16.9697 17.5303L18.0303 16.4697L13.0303 11.4697L11.9697 12.5303ZM13.0303 11.4697L8.03033 6.46967L6.96967 7.53033L11.9697 12.5303L13.0303 11.4697Z" fill="#c81919" /> </g>
679                                                         </svg>
680                                                         @Translate("Translate_ProductPage_StockOnWarehouse_NotOnStock")
681                                                     </text>
682                                                 }
683     
684                                                 @if (totalStockSea > 0)
685                                                 {
686                                                     <text>
687                                                         <svg width="30px" height="30px" viewBox="0 -0.5 25 25" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#12ca30">
688                                                             <g id="SVGRepo_bgCarrier" stroke-width="0" />
689                                                             <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />
690                                                             <g id="SVGRepo_iconCarrier"> <path d="M4 12.6111L8.92308 17.5L20 6.5" stroke="#24a84b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> </g>
691                                                         </svg>
692                                                         @Translate("Translate_ProductPage_StockOnSea_InRoute")
693                                                     </text>
694                                                 }
695                                                 else
696                                                 {
697                                                     <text>
698                                                         <svg width="30px" height="30px" viewBox="0 -0.5 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
699                                                             <g id="SVGRepo_bgCarrier" stroke-width="0" />
700                                                             <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />
701                                                             <g id="SVGRepo_iconCarrier"> <path d="M6.96967 16.4697C6.67678 16.7626 6.67678 17.2374 6.96967 17.5303C7.26256 17.8232 7.73744 17.8232 8.03033 17.5303L6.96967 16.4697ZM13.0303 12.5303C13.3232 12.2374 13.3232 11.7626 13.0303 11.4697C12.7374 11.1768 12.2626 11.1768 11.9697 11.4697L13.0303 12.5303ZM11.9697 11.4697C11.6768 11.7626 11.6768 12.2374 11.9697 12.5303C12.2626 12.8232 12.7374 12.8232 13.0303 12.5303L11.9697 11.4697ZM18.0303 7.53033C18.3232 7.23744 18.3232 6.76256 18.0303 6.46967C17.7374 6.17678 17.2626 6.17678 16.9697 6.46967L18.0303 7.53033ZM13.0303 11.4697C12.7374 11.1768 12.2626 11.1768 11.9697 11.4697C11.6768 11.7626 11.6768 12.2374 11.9697 12.5303L13.0303 11.4697ZM16.9697 17.5303C17.2626 17.8232 17.7374 17.8232 18.0303 17.5303C18.3232 17.2374 18.3232 16.7626 18.0303 16.4697L16.9697 17.5303ZM11.9697 12.5303C12.2626 12.8232 12.7374 12.8232 13.0303 12.5303C13.3232 12.2374 13.3232 11.7626 13.0303 11.4697L11.9697 12.5303ZM8.03033 6.46967C7.73744 6.17678 7.26256 6.17678 6.96967 6.46967C6.67678 6.76256 6.67678 7.23744 6.96967 7.53033L8.03033 6.46967ZM8.03033 17.5303L13.0303 12.5303L11.9697 11.4697L6.96967 16.4697L8.03033 17.5303ZM13.0303 12.5303L18.0303 7.53033L16.9697 6.46967L11.9697 11.4697L13.0303 12.5303ZM11.9697 12.5303L16.9697 17.5303L18.0303 16.4697L13.0303 11.4697L11.9697 12.5303ZM13.0303 11.4697L8.03033 6.46967L6.96967 7.53033L11.9697 12.5303L13.0303 11.4697Z" fill="#c81919" /> </g>
702                                                         </svg>
703                                                         @Translate("Translate_ProductPage_StockOnSea_NotInRoute")
704                                                     </text>
705                                                 }
706     
707                                             </span>
708                                         </div>
709                                     }
710                                     else
711                                     {*@
712                                 <div class="card__info-list__item">
713                                     <span class="info info--small info--flex-wrap">
714                                         <div style="display:none;" id="RequestProductText_@(product.GetString("Ecom:Product.ID"))">
715                                             <div style="display: inline-flex; align-items: center;">
716                                                 <span style="width: 8px; height: 8px; background-color: orange; border-radius: 50%; margin-right: 8px;"></span>
717                                                 @Translate("Translate_ProductPage_RequestProduct")
718                                             </div>
719                                         </div>
720                                         <div id="StockOnWarehouse_OnStock_@(product.GetString("Ecom:Product.ID"))" class="info--stock" style="display:none;">
721                                             <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="#24a84b" d="m9.55 18l-5.7-5.7l1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z" /></svg>
722                                             <span class="info--stock--text">@Translate("Translate_ProductPage_StockOnWarehouse_OnStock")</span>
723                                         </div>
724                                         <div id="StockOnWarehouse_NotOnStock_@(product.GetString("Ecom:Product.ID"))" class="info--stock" style="display:none;">
725                                             <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="#c81919" d="M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6z" /></svg>
726                                             <span class="info--stock--text">@Translate("Translate_ProductPage_StockOnWarehouse_NotOnStock")</span>
727                                         </div>
728                                         <div id="StockOnSea_InRoute_@(product.GetString("Ecom:Product.ID"))" class="info--stock" style="display:none;">
729                                             <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="#24a84b" d="m9.55 18l-5.7-5.7l1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z" /></svg>
730                                             <span class="info--stock--text">@Translate("Translate_ProductPage_StockOnSea_InRoute")</span>
731                                         </div>
732                                         <div id="StockOnSea_NotInRoute_@(product.GetString("Ecom:Product.ID"))" class="info--stock" style="display:none;">
733                                             <svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="#c81919" d="M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6z" /></svg>
734                                             <span class="info--stock--text">@Translate("Translate_ProductPage_StockOnSea_NotInRoute")</span>
735                                         </div>
736                                         <div class="info--stock skeleton_@(product.GetString("Ecom:Product.ID"))" v-if="isLoading">
737                                             <div class="info--stock--skeleton--icon"></div>
738                                             <div class="info--stock--skeleton--text"></div>
739                                         </div>
740                                         <div class="info--stock skeleton_@(product.GetString("Ecom:Product.ID"))" v-if="isLoading">
741                                             <div class="info--stock--skeleton--icon"></div>
742                                             <div class="info--stock--skeleton--text"></div>
743                                         </div>
744                                     </span>
745                                 </div>
746                                 @*}*@
747     
748                             </a>
749                         </li>
750                     }
751                 </ul>
752                 @if (GetInteger("Ecom:ProductList.TotalPages") > 1)
753                 {
754                     string prevClass = "";
755                     string nextClass = "";
756                     string extraPaginationClass = "";
757     
758                     if (String.IsNullOrWhiteSpace(GetString("Ecom:ProductList.PrevPage.Clean")))
759                     {
760                         prevClass = " disabled";
761                     }
762     
763                     if (String.IsNullOrWhiteSpace(GetString("Ecom:ProductList.NextPage.Clean")))
764                     {
765                         nextClass = " disabled";
766                     }
767     
768                     if (GetInteger("Ecom:ProductList.TotalPages") > 5)
769                     {
770                         extraPaginationClass = "navigation__pagination--overload";
771                     }
772     
773                     <div class="card-list__footer">
774                         <div class="card-list__navigation">
775                             <a href="@GetString("Ecom:ProductList.PrevPage.Clean")" class="navigation__arrow back@(prevClass)">
776                                 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 6.503"><path d="M10.94,6.5,14,3.249,10.94,0H9.693L12.4,2.8H0v.9H12.4L9.693,6.5Z" /></svg>
777                             </a>
778     
779                             <div class="navigation__pagination @extraPaginationClass">
780                                 <span class="navigation__pagination__title"> @Translate("Translate_ProductList_BreadCrumbPage") </span>
781                                 @for (int i = 0; i < GetInteger("Ecom:ProductList.TotalPages"); i++)
782                                 {
783                                     string currentClass = (i + 1) == GetInteger("Ecom:ProductList.CurrentPage") ? " active" : "";
784                                     string url = GetString("Ecom:Group.Link.Clean") + "&PageNum=" + (i + 1);
785     
786                                     foreach (var query in GetLoop("Query.Parameters"))
787                                     {
788                                         if (!String.IsNullOrWhiteSpace(query.GetString("Parameter.Value")))
789                                         {
790                                             url += "&" + query.GetString("Parameter.Name") + "=" + query.GetString("Parameter.Value");
791                                         }
792                                     }
793     
794                                     if (!isStandardSorting)
795                                     {
796                                         url += "&Sortby=NameForSort" + "&SortOrder=" + sortOrder;
797                                     }
798     
799                                     <a href="@url" class="navigation__pagination__link@(currentClass)">@(i + 1)</a>
800                                 }
801                             </div>
802                             <a href="@GetString("Ecom:ProductList.NextPage.Clean")" class="navigation__arrow forward@(nextClass)">
803                                 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 6.503"><path d="M10.94,6.5,14,3.249,10.94,0H9.693L12.4,2.8H0v.9H12.4L9.693,6.5Z" /></svg>
804                             </a>
805                         </div>
806                     </div>
807                 }
808             </article>
809     
810         </section>
811         <script async type="module">
812             let activeFilters = {};
813             let delay;
814             var allProductNumbers = @allProductNumbersString;
815     
816     
817           new Vue({
818               el: '#productsFlowApp',
819           name: 'Products Flow',
820           components: {
821     
822           },
823           computed: {
824     
825           },
826           mounted() {
827               var allProductNumbers = @allProductNumbersString;
828               const fetchPromises = [];
829     
830               for (const productNumberObjects of allProductNumbers) {
831                   fetchPromises.push(this.getVariants(productNumberObjects.id));
832               }
833     
834               // Wait for all fetch operations to complete
835               Promise.all(fetchPromises)
836                   .then(() => {
837                       // This block will run after all variants have been successfully fetched
838                       this.isLoading = false;
839                       this.afterFetchingVariants();
840                   });
841     
842               @*@if(primaryGroup == "group1") {
843                   <text>
844                       this.lookUpBundle(@(productNumber));
845                   </text>
846               }
847     
848               this.getAccessories();
849     
850               if(!this.enquireProduct && this.isLoggedIn) {
851                   this.orderButtonSpinner = setTimeout(() => {
852                       document.querySelector('#product-order-button .loader').style.display = "inline-block";
853                   }, 3000);
854               } else {
855                   document.querySelector('#product-order-button .product-order-form-button').style.display = "block";
856               }*@
857           },
858           data() {
859               return {
860                   isLoggedIn: @isloggedin.ToString().ToLower(),
861                   variants: [],
862                   isLoading: true,
863                   ongoingOperations: {},
864     
865                   showFilters: false,
866                   facets: null,
867                   translations: null,
868                   allProducts: false,
869                   currentGroup: '',
870               }
871               },
872               created() {
873                   this.allProducts = (document.getElementById('productFilters').getAttribute('data-all')
874                       .toLowerCase() === 'true');
875     
876                   if (!this.allProducts) {
877                       this.currentGroup = `&Group=${document.getElementById('productFilters').getAttribute('data-top-group')}&GroupID=${document.getElementById('productFilters').getAttribute('data-group')}`;
878                   } else {
879                       const urlSearchParams = new URLSearchParams(window.location.search);
880                       const params = Object.fromEntries(urlSearchParams.entries());
881     
882                       let group = '';
883     
884                       if (params.Group) {
885                           group = `&Group=${params.Group}`;
886                       }
887     
888                       this.currentGroup = group;
889                   }
890     
891                   fetch('/dwapi/translations/area/1')
892                       .then(response => response.json())
893                       .then(response => {
894                           this.translations = response;
895                       })
896                       .catch(error => {
897                           console.log(error);
898                       });
899     
900                   fetch(`/dwapi/ecommerce/products/search?RepositoryName=Products&QueryName=FilterQuery&ProductSettings.FilledProperties=Name&FilledProperties=Products,FacetGroups,TotalProductsCount${this.currentGroup}`)
901                       .then(response => response.json())
902                       .then(response => {
903                           this.facets = response.FacetGroups[0].Facets;
904                           this.showFilters = true;
905     
906                           this.$nextTick(() => {
907                               setUpFilters();
908                               setupSearchSelects();
909                               setupDuoRangeSliders();
910                           });
911                       });
912               },
913               methods: {
914                   addition(paramType) {
915                       let addition = '';
916     
917                       if (paramType == 'System.String[]' || paramType == 'System.Double[]') {
918                           addition = '[]';
919                       }
920     
921                       return addition;
922                   },
923                   translation(key) {
924                       return this.translations.find(x => x.Key == key) ? this.translations.find(x => x.Key == key).Value : key;
925                   },
926                   sortedOptions(options) {
927                       const newList = options.filter(x => x.Count && x.Count > 0 && parseInt(x.Value) != -1);
928                       return newList;
929                   },
930                   getLowestValue(options) {
931                       const newList = options.filter(x => x.Count && x.Count > 0);
932                       newList.sort((a, b) => a - b);
933     
934                       return newList[0].Value;
935     
936                   },
937                   getHighestValue(options) {
938                       const newList = options.filter(x => x.Count && x.Count > 0);
939                       newList.sort((a, b) => a - b);
940     
941                       return newList.at(-1).Value;
942                   },
943                   favoriteRemove(productNumber) {
944                         if (this.ongoingOperations[productNumber]) {
945                         return;
946                         }
947                         this.ongoingOperations[productNumber] = true;
948                                   let url = location.protocol + '//' + location.host; //RemoveProductFromFavoriteList
949                       url += "?FavoriteCmd=RemoveProductFromFavoriteList&ProductId=" + productNumber;
950                 fetch(url).then(response => {
951                     document.getElementById("favoriteRemove-" + productNumber).style = "display:none"
952                     document.getElementById("favoriteAdd-" + productNumber).style = "";
953     
954                     let favorite = document.getElementsByClassName("favorite-qty");
955                     if (favorite.length == 1) {
956                         let qty = favorite[0];
957                         let currentCount = Number(qty.getAttribute("data-count"));
958                         let count = currentCount === 0 ? currentCount : currentCount - 1;
959                         qty.setAttribute("data-count", count)
960                         qty.innerHTML = count;
961     
962                     }
963                     }).finally(() => {
964                     this.ongoingOperations[productNumber] = false; // Reset the flag when the operation is complete
965             });
966                   },
967                   favoriteAdd(productNumber) {
968                         if (this.ongoingOperations[productNumber]) {
969                         return;
970                         }
971                         this.ongoingOperations[productNumber] = true;
972                     let url = location.protocol + '//' + location.host;
973                       url += "?FavoriteCmd=addproducttofavoritelist&ProductId=" + productNumber;
974                       let loc = location;
975                       fetch(url).then(response => {
976                           if ('@shouldRefreshFavorite' === 'true') {
977                               loc.reload()
978                           } else {
979                               document.getElementById("favoriteAdd-" + productNumber).style = "display:none"
980                               document.getElementById("favoriteRemove-" + productNumber).style = "";
981                               //simple just assume it went ok
982                               let favorite = document.getElementsByClassName("favorite-qty");
983                               if (favorite.length == 1) {
984                                   let qty = favorite[0];
985                                   let count = Number(qty.getAttribute("data-count")) + 1;
986                                   qty.setAttribute("data-count", count)
987                                   qty.innerHTML = count;
988     
989                               }
990                           }
991                     }).finally(() => {
992                 this.ongoingOperations[productNumber] = false; // Reset the flag when the operation is complete
993             });
994                   },
995                   setElementDisplay(elementId, display = "") {
996                         const element = document.getElementById(elementId);
997                         if (element) {
998                             element.style.display = display;
999                         }
1000                     },
1001                   doesCookieExist(cookieName) {
1002                       const cookies = document.cookie.split('; ');
1003                       const cookieExists = cookies.some(cookie => cookie.startsWith(cookieName + '='));
1004                       return cookieExists;
1005                   },
1006                   setCookie(name, value, days) {
1007                       let expires = "";
1008                       if (days) {
1009                           const date = new Date();
1010                           date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
1011                           expires = "; expires=" + date.toUTCString();
1012                       }
1013                       document.cookie = name + "=" + (value || "") + expires + "; path=/";
1014                   },
1015                   deleteCookie(name) {
1016                       document.cookie = name + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;';
1017                   },
1018                   getVariants: async function(productNo, url) {
1019                         this.loadingVariants = true;
1020                         return new Promise((resolve, reject) => {
1021                             let requestUrl = `@(VariantsLookup)&ICC_itemId=${productNo}`;
1022                             if (url && url != "") {
1023                                 requestUrl = url;
1024                             }
1025                             if (!this.isLoggedIn) {
1026                                 if (requestUrl.indexOf('username') < 0) {
1027                                     requestUrl += '&username=marketing@keflico.com&password=Keflico100%';
1028                                 }
1029                                 if (!this.doesCookieExist('tempLogin')) {
1030                                     this.setCookie('tempLogin', true, 1);
1031                                 }
1032                             }
1033                             var credentials = this.isLoggedIn ? "same-origin" : "omit"
1034                             fetch(requestUrl, {
1035                                 credentials: credentials
1036                             })
1037                             .then(response => response.json())
1038                             .then(response => {
1039                                 if (response.variants && response.variants.length > 0) { // Check if there are any variants
1040                                     this.variants = this.variants.concat(response.variants);
1041                                     if (response.nextPage != "") {
1042                                         this.getVariants(productNo, response.nextPage).then(resolve);
1043                                     } else {
1044                                         resolve(); // Resolve the promise when the last page is fetched
1045                                     }
1046                                 } else { // There are no variants, so we check the stock directly
1047                                     var product = allProductNumbers.find(p => p.id === productNo);
1048                                     if (product) {
1049                                         // Product found, so we'll continue checking
1050                                         if (product.totalStockWareHouse == 0 && product.totalStockSea == 0) {
1051                                             // If both stocks are 0, we show the request product text
1052                                             this.setElementDisplay(`RequestProductText_${productNo}`);
1053                                         } else {
1054                                             // We have at least SOME stock, so we check which stock is available and show the appropriate texts
1055                                             if (product.totalStockWareHouse > 0) {
1056                                                 this.setElementDisplay(`StockOnWarehouse_OnStock_${productNo}`);
1057                                             } else {
1058                                                 this.setElementDisplay(`StockOnWarehouse_NotOnStock_${productNo}`);
1059                                             }
1060                                             if (product.totalStockSea > 0) {
1061                                                 this.setElementDisplay(`StockOnSea_InRoute_${productNo}`);
1062                                             } else {
1063                                                 this.setElementDisplay(`StockOnSea_NotInRoute_${productNo}`);
1064                                             }
1065                                         }
1066                                     }
1067                                     else {
1068                                         // Product not found, so we show the request product text
1069                                         this.setElementDisplay(`RequestProductText_${productNo}`);
1070     
1071                                     }
1072                                     // Hide the loading skeletons for the element (we do it here, so it doesnt stay visible, before isLoading is set to false in afterFetchingVariants)
1073                                     Array.from(document.getElementsByClassName(`skeleton_${productNo}`) || []).forEach(el => el && (el.style.display = "none"));
1074                                     resolve();
1075                                 }
1076                             })
1077                             .catch(error => {
1078                                 console.error("Error fetching variants:", error);
1079                                 reject(error); // Reject the promise if there's an error
1080                             });
1081                         });
1082                     },
1083                   afterFetchingVariants() {
1084                       var allProductNumbers = @allProductNumbersString;
1085                       // Code to execute after all variants have been fetched
1086                       //console.debug("All variants have been fetched!");
1087     
1088                       // Example: Group by id prefix or perform other operations
1089                       const groupedStock = {};
1090                       this.variants.forEach(variant => {
1091                           const idPrefix = variant.id.split('_')[0];
1092                           if (!groupedStock[idPrefix]) {
1093                               groupedStock[idPrefix] = [];
1094                           }
1095                           groupedStock[idPrefix].push({
1096                               warehouse: variant.stock.warehouse,
1097                               sea: variant.stock.sea,
1098                               purchase: variant.stock.purchase
1099                           });
1100                       });
1101     
1102                       for (const idPrefix in groupedStock) {
1103                           if (groupedStock.hasOwnProperty(idPrefix)) {
1104                               //console.log("ID Prefix:", idPrefix);
1105                               var totalWarehouse = 0;
1106                               var totalSea = 0;
1107     
1108                               // Access the array of stock objects for this group
1109                               const stocks = groupedStock[idPrefix];
1110     
1111                               const productObject = allProductNumbers.find(product => product.id === idPrefix);
1112                               const group = productObject.primaryGroup;
1113     
1114                               // Loop through the array of stock entries for this idPrefix
1115                               stocks.forEach(stock => {
1116                                   //console.log("Warehouse:", stock.warehouse, "Sea:", stock.sea);
1117                                   totalSea += stock.sea
1118                                   totalWarehouse += stock.warehouse
1119     
1120                                   if (group != "group32") {
1121                                       totalSea += Number(stock.purchase)
1122                                       totalWarehouse += Number(stock.purchase)
1123                                   }
1124     
1125                               });
1126     
1127                               if (totalSea == 0 && totalWarehouse == 0) {
1128                                   document.getElementById(`RequestProductText_${idPrefix}`).style.display = "";
1129                               }
1130                               else {
1131     
1132                                   if (totalWarehouse > 0) {
1133                                       if (document.getElementById(`StockOnWarehouse_OnStock_${idPrefix}`) != null)
1134                                           document.getElementById(`StockOnWarehouse_OnStock_${idPrefix}`).style.display = "";
1135     
1136                                   } else {
1137                                       if (document.getElementById(`StockOnWarehouse_NotOnStock_${idPrefix}`) != null)
1138                                           document.getElementById(`StockOnWarehouse_NotOnStock_${idPrefix}`).style.display = "";
1139     
1140     
1141                                   }
1142     
1143                                   if (totalSea > 0) {
1144                                       if (document.getElementById(`StockOnSea_InRoute_${idPrefix}`) != null)
1145                                           document.getElementById(`StockOnSea_InRoute_${idPrefix}`).style.display = "";
1146     
1147     
1148                                   } else {
1149                                       if (document.getElementById(`StockOnSea_NotInRoute_${idPrefix}`) != null)
1150                                           document.getElementById(`StockOnSea_NotInRoute_${idPrefix}`).style.display = "";
1151     
1152                                   }
1153                               }
1154     
1155     
1156                           }
1157                       }
1158                   }
1159           },
1160           watch: {
1161     
1162           }
1163           });
1164     
1165     
1166             function setUpFilters() {
1167                 const filterSection = document.querySelector('.filter-section');
1168                 const sorting = document.getElementById('sorting');
1169     
1170                 if (filterSection) {
1171                     const applyBtn = document.getElementById('applyFilterButton');
1172                     const clearBtn = document.getElementById('clearFilterButton');
1173     
1174                     const filters = filterSection.querySelectorAll('.filter-option');
1175                     setupFromQueryString();
1176     
1177                     Array.from(filters).forEach(filter => {
1178                         const filterName = filter.getAttribute('data-name');
1179     
1180                         filter.addEventListener('input', event => {
1181                             clearTimeout(delay);
1182     
1183                             switch (filter.type) {
1184                                 case 'range':
1185                                     activeFilters[filterName] = filter.value;
1186                                     break;
1187     
1188                                 case 'checkbox':
1189                                 default:
1190                                     if (filter.checked) {
1191                                         if (activeFilters[filterName]) {
1192                                             activeFilters[filterName].push(filter.value);
1193                                         } else {
1194                                             activeFilters[filterName] = [filter.value];
1195                                         }
1196                                     } else {
1197                                         if (activeFilters[filterName]) {
1198                                             const valueIndex = activeFilters[filterName].indexOf(filter.value);
1199                                             activeFilters[filterName].splice(valueIndex, 1);
1200     
1201                                             if (activeFilters[filterName].length == 0) {
1202                                                 delete activeFilters[filterName];
1203                                             }
1204                                         }
1205                                     }
1206                                     break;
1207                             }
1208                         });
1209                     });
1210     
1211                     clearBtn.addEventListener('click', event => {
1212                         if (activeFilters['Sortby']) {
1213                             const tempBy = activeFilters['Sortby'];
1214                             const tempOrder = activeFilters['SortOrder'];
1215     
1216                             activeFilters = {};
1217     
1218                             activeFilters['Sortby'] = tempBy;
1219                             activeFilters['SortOrder'] = tempOrder;
1220                         } else {
1221                             activeFilters = {};
1222                         }
1223     
1224                         buildQueryString();
1225                     });
1226     
1227                     applyBtn.addEventListener('click', buildQueryString);
1228                 }
1229     
1230                 if (sorting) {
1231                     sorting.addEventListener('change', event => {
1232                         const sortingValue = sorting.value;
1233     
1234                         if (sortingValue == 'default') {
1235                             delete activeFilters['Sortby'];
1236                             delete activeFilters['SortOrder'];
1237                         } else {
1238                             activeFilters['Sortby'] = 'NameForSort';
1239                             activeFilters['SortOrder'] = sortingValue.toUpperCase();
1240                         }
1241     
1242                         buildQueryString();
1243                     });
1244                 }
1245             }
1246     
1247             function buildQueryString() {
1248                 // Create a new object to hold the processed filters
1249                 const processedFilters = {};
1250     
1251                 // Process each filter
1252                 Object.keys(activeFilters).forEach(key => {
1253                     if (activeFilters[key] && activeFilters[key].length > 0) {
1254     
1255                         // Get the first input with this key to check if it's a numeric filter
1256                         const input = document.querySelector(`[data-name="${key}"]`);
1257                         const isNumeric = input && input.getAttribute('data-parameter-type') === 'System.Double[]';
1258                      if (isNumeric) {
1259                         // Map over each element in the array and replace commas with dots
1260                         activeFilters[key] = activeFilters[key].map(item => {
1261                         // Ensure each item is a string before replacing
1262                     return typeof item === 'string' ? item.replace(',', '.') : item.toString().replace(',', '.');
1263                         });
1264             }
1265     
1266     
1267                         processedFilters[key] = activeFilters[key].join(',');
1268                     }
1269                 });
1270     
1271                 // Build the query string
1272                 const queryString = new URLSearchParams(processedFilters).toString();
1273                 const currentPath = window.location.pathname;
1274     
1275                 if (queryString.length > 0) {
1276                     window.location.href = `${currentPath}?${queryString.replace(/PageNum=\d+&/g, '')}`;
1277                 } else {
1278                     window.location.href = currentPath;
1279                 }
1280             }
1281     
1282             function setupFromQueryString() {
1283     
1284                 const urlSearchParams = new URLSearchParams(window.location.search);
1285                 const params = Object.fromEntries(urlSearchParams.entries());
1286     
1287     
1288                 Object.keys(params).forEach(key => {
1289                     const value = params[key];
1290                     if (!activeFilters[key]) {
1291                         activeFilters[key] = [];
1292                     }
1293     
1294                     // Get all inputs with this key to check if it's a numeric filter
1295                     const inputsWithKey = document.querySelectorAll(`[data-name="${key}"]`);
1296                     const isNumeric = inputsWithKey.length > 0 &&
1297                         inputsWithKey[0].getAttribute('data-parameter-type') === 'System.Double[]';
1298     
1299                     // Get all possible numeric values from the filter options
1300                     const allNumericOptions = Array.from(inputsWithKey).map(input => input.value);
1301     
1302                     // For numeric filters, we need special handling
1303                     if (isNumeric) {
1304                         // First, split the value into parts
1305                         const parts = value.split(',');
1306                         const processedIndices = new Set(); // Track which parts we've processed
1307     
1308                         // Check for decimal values first (values that contain a comma in our options)
1309                         for (let i = 0; i < parts.length; i++) {
1310                             if (processedIndices.has(i)) continue; // Skip if already processed
1311     
1312                             const splitPart = parts[i].split('.')
1313                             const potentialDecimal = splitPart[0] + ',' + splitPart[1];
1314     
1315                             // If this combination exists as an option, add it
1316                             if (allNumericOptions.includes(potentialDecimal)) {
1317                                 activeFilters[key].push(potentialDecimal);
1318                                 processedIndices.add(i);
1319                             }
1320                         }
1321     
1322                         // Now add any remaining individual values
1323                         parts.forEach((part, index) => {
1324                             if (!processedIndices.has(index) && allNumericOptions.includes(part)) {
1325                                 activeFilters[key].push(part);
1326                             }
1327                         });
1328                     } else {
1329                         // For non-numeric values, keep the original behavior
1330                         if (value.indexOf(',') > -1) {
1331                             value.split(',').forEach(entry => {
1332                                 activeFilters[key].push(entry);
1333                             });
1334                         } else {
1335                             activeFilters[key].push(value);
1336                         }
1337                     }
1338     
1339                     // Update the checkboxes based on active filters
1340                     if (key != 'GroupID' && key != 'PageNum' && key != 'Sortby' && key != 'SortOrder') {
1341                         Array.from(inputsWithKey).forEach(input => {
1342                             switch (input.type) {
1343                                 case 'range':
1344                                     input.value = value;
1345                                     break;
1346                                 case 'checkbox':
1347                                 default:
1348                                     // Check if the input value is in the active filters
1349                                     if (activeFilters[key].includes(input.value)) {
1350                                         input.checked = true;
1351                                         const toggle = input.closest('.filter-item')?.querySelector('.filter-item__checkbox-toggle');
1352                                         if (toggle) toggle.checked = true;
1353                                     }
1354                                     break;
1355                             }
1356                         });
1357                     }
1358                 });
1359             }
1360     
1361             function setupSearchSelects() {
1362                 if (document.querySelector('.search-select')) {
1363                     // Add CSS for hiding partial matches
1364                     const style = document.createElement('style');
1365                     style.textContent = `
1366                 .search-select__tags label.partial-match {
1367                     display: none !important;
1368                 }
1369             `;
1370                     document.head.appendChild(style);
1371     
1372                     Array.from(document.querySelectorAll('.search-select')).forEach(select => {
1373                         const input = select.querySelector('.search-select__search-input');
1374                         const dropdown = select.querySelector('.search-select__dropdown');
1375                         const options = select.querySelectorAll('.search-select__tags input');
1376                         const optionsLabels = select.querySelectorAll('.search-select__dropdown-item');
1377                         const clearBtn = select.querySelector('.search-select__search-clear');
1378     
1379                         // Determine if this is a numeric filter based only on parameter type
1380                         const isNumeric = options.length > 0 &&
1381                             options[0].getAttribute('data-parameter-type') == 'System.Double[]';
1382     
1383                         input.addEventListener('focus', open);
1384                         clearBtn.addEventListener('click', clear);
1385                         input.addEventListener('input', search);
1386     
1387                         // Fix the visual representation of selected tags
1388                         function updateVisualTags() {
1389                             // Only process numeric filters
1390                             if (!isNumeric) return;
1391     
1392                             // Get the actual selected values from the URL
1393                             const urlSearchParams = new URLSearchParams(window.location.search);
1394                             const params = Object.fromEntries(urlSearchParams.entries());
1395     
1396                             if (options.length > 0) {
1397                                 const filterName = options[0].getAttribute('data-name');
1398     
1399                                 // Only process if this filter is in the URL
1400                                 if (params[filterName]) {
1401                                     // For numeric filters, don't split the value
1402                                     const selectedValues = [params[filterName]];
1403     
1404                                     // Remove partial-match class from all labels
1405                                     select.querySelectorAll('.search-select__tags label').forEach(label => {
1406                                         label.classList.remove('partial-match');
1407                                     });
1408     
1409                                     // For each selected value, mark exact matches
1410                                     selectedValues.forEach(selectedValue => {
1411                                         Array.from(options).forEach(option => {
1412                                             // For numeric filters, we want exact matches only
1413                                             if (option.value === selectedValue) {
1414                                                 const label = select.querySelector(`label[for="${option.id}"]`);
1415                                                 if (label) {
1416                                                     label.classList.add('selected');
1417                                                 }
1418                                             }
1419                                         });
1420                                     });
1421                                 }
1422                             }
1423                         }
1424     
1425                         // Call this when page loads
1426                         updateVisualTags();
1427     
1428                         Array.from(options).forEach(option => {
1429                             option.addEventListener('change', e => {
1430                                 if (isNumeric && option.checked) {
1431                                     // When a numeric option is checked, hide partial matches
1432                                     const selectedValue = option.value;
1433                                     Array.from(options).forEach(otherOption => {
1434                                         if (otherOption !== option) {
1435                                             const isPartialMatch = selectedValue.includes(otherOption.value) &&
1436                                                 selectedValue !== otherOption.value;
1437     
1438                                             if (isPartialMatch) {
1439                                                 const label = select.querySelector(`label[for="${otherOption.id}"]`);
1440                                                 if (label) {
1441                                                     label.classList.add('partial-match');
1442                                                 }
1443                                             }
1444                                         }
1445                                     });
1446                                 }
1447                             });
1448                         });
1449     
1450                         function clear() {
1451                             dropdown.classList.remove('search-select__dropdown--active');
1452                             clearBtn.classList.remove('search-select__search-clear--active');
1453                             input.value = '';
1454                             resetList();
1455                         }
1456     
1457                         function open() {
1458                             dropdown.classList.add('search-select__dropdown--active');
1459                             clearBtn.classList.add('search-select__search-clear--active');
1460                         }
1461     
1462                         function search() {
1463                             const searchTerm = input.value;
1464     
1465                             if (searchTerm.length > 0) {
1466                                 const searchWords = searchTerm.trim().split(' ');
1467     
1468                                 Array.from(optionsLabels).forEach(option => {
1469                                     let hasMatch = false;
1470     
1471                                     searchWords.forEach(word => {
1472                                         const searchRegEx = new RegExp(word, 'ig');
1473                                         const matches = option.innerText.match(searchRegEx);
1474     
1475                                         if (matches && matches.length > 0) {
1476                                             hasMatch = true;
1477                                             option.innerHTML = option.innerHTML.replaceAll(/\<span class\=\"js-highlight\"\>(.*?)\<\/span\>/gi, '$1').replaceAll(searchRegEx, `<span class="js-highlight">${matches[0]}</span>`);
1478                                         }
1479                                     });
1480     
1481                                     if (!hasMatch) {
1482                                         option.classList.add('js-no-match');
1483                                     } else {
1484                                         option.classList.remove('js-no-match');
1485                                     }
1486                                 });
1487                             } else {
1488                                 resetList();
1489                             }
1490                         }
1491     
1492                         function resetList() {
1493                             Array.from(optionsLabels).forEach(option => {
1494                                 option.classList.remove('js-no-match');
1495                                 option.innerHTML = option.innerText;
1496                             });
1497                         }
1498                     });
1499                 }
1500             }
1501     
1502             function setupDuoRangeSliders() {
1503                 if (document.querySelector('.duo-range-slider')) {
1504                     Array.from(document.querySelectorAll('.duo-range-slider')).forEach(rangeSlider => {
1505                         Array.from(rangeSlider.querySelectorAll('.duo-range-slider__range')).forEach(slider => {
1506                             slider.oninput = updateValues;
1507                             slider.oninput();
1508                         });
1509     
1510                         function updateValues() {
1511                             const parent = this.parentNode;
1512                             const slides = parent.getElementsByTagName('input');
1513                             let slide1 = parseFloat(slides[0].value);
1514                             let slide2 = parseFloat(slides[1].value);
1515                             const displayMin = parent.querySelector('.duo-range-slider__values-min span');
1516                             const displayMax = parent.querySelector('.duo-range-slider__values-max span');
1517     
1518                             if (slide1 > slide2) {
1519                                 const temp = slide2;
1520     
1521                                 slide2 = slide1;
1522                                 slide1 = temp;
1523                             }
1524     
1525                             displayMin.innerText = slide1;
1526                             displayMax.innerText = slide2;
1527                         }
1528                     });
1529                 }
1530             }
1531         </script>
1532     }
1533     
1534     @{
1535         string groupSeoText = GetString("Ecom:Group:Field.SEOText");
1536     
1537         if (!string.IsNullOrWhiteSpace(groupSeoText))
1538         {
1539             <article class="module module-sand-light">
1540                 <div class="rich-text">
1541                     @groupSeoText
1542                 </div>
1543             </article>
1544         }
1545     }
1546     
1547     @functions {
1548         Dynamicweb.Ecommerce.Products.Group FindTopGroup(Dynamicweb.Ecommerce.Products.Group group)
1549         {
1550             if (group.IsTopGroup)
1551             {
1552                 return group;
1553             }
1554             else if (group.ParentGroups != null && group.ParentGroups.Count > 0)
1555             {
1556                 foreach (var parentGroup in group.ParentGroups)
1557                 {
1558                     Dynamicweb.Ecommerce.Products.Group topLevelGroup = FindTopGroup(parentGroup);
1559                     if (topLevelGroup != null)
1560                     {
1561                         return topLevelGroup;
1562                     }
1563                 }
1564             }
1565             return null;
1566         }
1567     }
| Template:BaseUrl | System.String | /Files/Templates/Designs/Keflico/QueryPublisher/ | 
| Template:DesignBaseUrl | System.String | /Files/Templates/Designs/Keflico/ | 
| Loops |  | 
    Facet Groups
	Parameters
	
	QueryResult
	
 
    
        Page  of 
    
    
    
TemplateTags() in code (Designs\Keflico\QueryPublisher/List.cshtml). Remove before going live...