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