Error executing template "Designs/Swift/Paragraph/Swift_ProductListGridView.cshtml"
System.InvalidOperationException: ValueFactory attempted to access the Value property of this instance.
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at Dynamicweb.Ecommerce.ProductCatalog.VariantInfoViewModel.CalculatePrice()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at Dynamicweb.Ecommerce.ProductCatalog.VariantInfoViewModel.CalculatePriceMin()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at CompiledRazorTemplates.Dynamic.RazorEngine_65a057f947704223903cd08aff1a62f7.<RenderProductList>b__7_0(TextWriter __razor_helper_writer) in D:\dynamicweb.net\Solutions\Zington\ote.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductListGridView.cshtml:line 349
at CompiledRazorTemplates.Dynamic.RazorEngine_65a057f947704223903cd08aff1a62f7.Execute() in D:\dynamicweb.net\Solutions\Zington\ote.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductListGridView.cshtml:line 58
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 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel>
2 @using Dynamicweb.Ecommerce.ProductCatalog
3 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites
4 @using System.Linq
5 @using Dynamicweb.Core
6 @using Dynamicweb.Environment
7
8 @functions
9 {
10 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]);
11 string liveInfoClass = "";
12 string productInfoFeed = "";
13
14 string showPricesWithVat = "";
15 bool neverShowVat = false;
16
17 bool isDetailPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID"));
18
19 ProductListViewModel productList = new ProductListViewModel();
20 }
21
22 @{
23 if (Dynamicweb.Context.Current.Items.Contains("ProductList"))
24 {
25 productList = (ProductListViewModel)Dynamicweb.Context.Current.Items["ProductList"];
26 }
27
28 showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower();
29 neverShowVat = string.IsNullOrEmpty(showPricesWithVat);
30
31 if (isLazyLoadingForProductInfoEnabled)
32 {
33 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed"))
34 {
35 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString();
36 if (!string.IsNullOrEmpty(productInfoFeed))
37 {
38 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\"";
39 }
40 }
41 liveInfoClass = "js-live-info";
42 }
43
44 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
45 string themePadding = theme != string.Empty ? "p-3" : string.Empty;
46 }
47
48 @if (!isDetailPage) {
49 if (!string.IsNullOrEmpty(theme))
50 {
51 <div class="h-100@(theme) @themePadding item_@Model.Item.SystemName.ToLower()" @productInfoFeed>
52 @{@RenderProductList()}
53 </div>
54 }
55 else
56 {
57 <div class="pt-3 item_@Model.Item.SystemName.ToLower()" @productInfoFeed>
58 @{@RenderProductList()}
59 </div>
60 }
61 }
62
63 @helper RenderProductList()
64 {
65 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
66 bool anonymousUser = Pageview.User == null;
67 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsWebServiceConnectionAvailable"]);
68
69 string productTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ProductTheme")) ? " theme " + Model.Item.GetRawValueString("ProductTheme").Replace(" ", "").Trim().ToLower() : "";
70 string productThemePadding = productTheme != string.Empty ? "p-3" : string.Empty;
71
72 string url = Dynamicweb.Context.Current.Request.RawUrl;
73 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false;
74 string staticVariantsLayout = Model.Item.GetRawValueString("StaticVariantsLayout", "hide");
75
76 string groupId = productList?.Group?.Id != null ? productList.Group.Id : "";
77
78 var badgeParms = new Dictionary<string, object>();
79 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType"));
80 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign"));
81 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign"));
82 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays"));
83 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges"));
84
85 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false;
86 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false;
87
88 string googleTagManagerID = Pageview.AreaSettings.GetString("GoogleTagManagerID");
89 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID");
90 var cookieOptInLevel = CookieManager.GetCookieOptInLevel();
91 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical"));
92
93 var favoriteParameters = new Dictionary<string, object>();
94 if (!anonymousUser && !hideFavoritesSelector)
95 {
96 int defaultFavoriteListId = 0;
97
98 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists();
99 if (favoreiteLists.Count() == 1) {
100 foreach (FavoriteList list in favoreiteLists) {
101 defaultFavoriteListId = list.ListId;
102 }
103 }
104
105 favoriteParameters.Add("ListId", defaultFavoriteListId);
106 }
107
108 if (productList.TotalProductsCount > 0)
109 {
110 int pageSizeSetting = 30;
111 int pageSize = productList.PageSize;
112 pageSize += pageSizeSetting;
113
114 int loadedProducts = productList.PageSize > productList.TotalProductsCount ? productList.TotalProductsCount : productList.PageSize;
115
116 <div class="grid grid-2 grid-lg-3">
117
118 @if ((!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) || !string.IsNullOrWhiteSpace(googleTagManagerID)) && allowTracking)
119 {
120 <script>
121 gtag("event", "view_item_list", {
122 item_list_id: "product_list_gridview",
123 item_list_name: "Product list (Gridview)",
124 items: [
125 @foreach (ProductViewModel product in productList.Products)
126 {
127 <text>{
128 item_id: "@product.Number",
129 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(product.Name)",
130 currency: "@product.Price.CurrencyCode",
131 price: @PriceViewModelExtensions.ToStringInvariant(product.Price)
132 },</text>
133 }
134 ]
135 });
136 </script>
137 }
138
139 @foreach (ProductViewModel product in productList.Products)
140 {
141 string link = product.GetProductLink(GetPageIdByNavigationTag("Shop"), false);
142
143 string imagePath = product?.DefaultImage?.Value ?? "";
144 imagePath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath);
145
146 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", "");
147 ratio = ratio != "0" ? ratio : "";
148 string ratioCssClass = ratio != "" ? " ratio" : "";
149 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
150
151 string imagePathXs = "/Admin/Public/GetImage.ashx?width=" + 480 + "&image=" + imagePath + "&format=webp";
152 string imagePathS = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp";
153 string imagePathFallBack = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp";
154
155 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : "";
156 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
157 string imageOutlineStyle = imageTheme == string.Empty ? "style=\"border: 1px solid transparent\"" : string.Empty;
158
159 string imageId = "ProductImage_" + product.Id + product.VariantId;
160 string priceId = "ProductPrice_" + product.Id + product.VariantId;
161
162 @* Alternative image *@
163 var supportedImageFormats = new string[] { ".jpg", ".webp", ".png", ".gif" };
164 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : "";
165 var selectedAssetCategories = Model.Item.GetRawValueString("AlternativeImageAssets");
166 IEnumerable<MediaViewModel> alternativeImagesList = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets);
167
168 if (alternativeImagesList.FirstOrDefault() != null)
169 {
170 alternativeImagesList = alternativeImagesList.OrderByDescending(x => x.Value.Equals(defaultImage));
171
172 if (alternativeImagesList.First().Value == defaultImage) {
173 alternativeImagesList = alternativeImagesList.Skip(1);
174 }
175 }
176
177 string alternativeImage = alternativeImagesList.FirstOrDefault() != null ? alternativeImagesList.FirstOrDefault().Value : "";
178 alternativeImage = !string.IsNullOrEmpty(alternativeImage) ? "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + alternativeImage + "&format=webp" : "";
179
180 @* Badges *@
181 DateTime createdDate = product.Created.Value;
182 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false;
183 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges;
184 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges;
185
186 @* Main features *@
187 IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList();
188 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>();
189
190 foreach (var selection in selectedDisplayGroups)
191 {
192 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values)
193 {
194 if (selection == group.Id) {
195 mainFeatures.Add(group);
196 }
197 }
198 }
199
200 <article class="position-relative@(productTheme) product-list-item js-product @liveInfoClass" data-product-id="@product.Id" data-variant-id="@product.VariantId" itemscope itemtype="https://schema.org/Product">
201 @if (!anonymousUser) {
202 if (!hideFavoritesSelector)
203 {
204 <div class="position-absolute top-0 end-0 my-3" style="z-index: 2">
205 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters)
206 </div>
207 }
208 }
209
210 @if (showBadges) {
211 <div class="position-absolute top-0 left-0 p-1 p-lg-2 ps-0 ps-lg-0" style="z-index: 2">
212 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)
213 </div>
214 }
215
216 <div class="d-flex flex-column d-block h-100">
217 @{
218 string clickProductLink = string.Empty;
219 if ((!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) || !string.IsNullOrWhiteSpace(googleTagManagerID)) && allowTracking)
220 {
221 clickProductLink = "onclick=\"return clickProductLink('" + @product.Id + "', '" + @Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(product.Name) + "', '" + @Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(product.VariantName) + "', '" + @product.Price.CurrencyCode + "', '" + @PriceViewModelExtensions.ToStringInvariant(product.Price) + "')\"";
222 }
223 }
224 <a href="/@link" class="text-decoration-none d-flex flex-column" @clickProductLink>
225 @if ((!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) || !string.IsNullOrWhiteSpace(googleTagManagerID)) && allowTracking)
226 {
227 <script>
228 function clickProductLink(productId, productName, productVariant, productCurrency, productPrice) {
229 if (typeof gtag !== "undefined") {
230 gtag("event", "select_item", {
231 item_list_id: "product_list_gridview",
232 item_list_name: "Product list (Gridview)",
233 items: [
234 {
235 item_id: productId,
236 item_name: productName,
237 currency: productCurrency,
238 item_list_id: "product_list_gridview",
239 item_list_name: "Product list (Gridview)",
240 item_variant: productVariant,
241 price: productPrice
242 }
243 ]
244 });
245 }
246 }
247 </script>
248 }
249
250 <div class="@productThemePadding order-2">
251 <div class="flex-grow-1">
252 <h3 class="h6 mb-0 text-break">@product.Name @if (!string.IsNullOrEmpty(product.VariantName)) { <text>(@product.VariantName)</text> }</h3>
253 @if (!Model.Item.GetBoolean("HideProductNumber")) {
254 <p class="fs-7 opacity-85 mb-2">@product.Number</p>
255 }
256 @if (mainFeatures.Count > 0)
257 {
258 <ul class="p-0 lh-sm opacity-75" style="list-style-position: inside">
259 @foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures)
260 {
261 foreach (var fieldViewModel in mainFeatureGroup.Fields)
262 {
263 var field = fieldViewModel.Value;
264 string fieldValue = field?.Value is object ? field.Value.ToString() : "";
265
266 if (fieldValue != "") {
267 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue;
268 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue;
269
270 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) {
271 fieldValue = "";
272
273 foreach (FieldOptionValueViewModel option in field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) {
274 fieldValue = option.Name;
275 }
276 }
277
278 bool isColor = false;
279 if (fieldValue.Contains("#") && (Translate(field.Name) == Translate("Color") || Translate(field.Name) == Translate("Colour"))) {
280 isColor = true;
281 }
282
283 if (!string.IsNullOrEmpty(fieldValue)) {
284 if (!isColor) {
285 <li>@(field.Name): @fieldValue</li>
286 } else {
287 <li class="position-relative">
288 <span class="colorbox-sm" style="background-color: @fieldValue"></span>
289 </li>
290 }
291 }
292 }
293 }
294 }
295 </ul>
296 }
297 </div>
298
299 @if (!hidePrice) {
300 string priceMin = "";
301 string priceMax = "";
302
303 <div>
304 <div>
305 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span>
306
307 @if (showPricesWithVat == "false" && !neverShowVat) {
308 if (isLazyLoadingForProductInfoEnabled)
309 {
310 <span itemprop="price" content="" class="d-none"></span>
311 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span>
312 }
313 else
314 {
315 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted;
316 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span>
317 if (product.Price.Price != product.PriceBeforeDiscount.Price) {
318 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span>
319 }
320 }
321
322 } else {
323
324 if (isLazyLoadingForProductInfoEnabled)
325 {
326 <span itemprop="price" content="" class="d-none"></span>
327 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span>
328 }
329 else
330 {
331 string beforePrice = product.PriceBeforeDiscount.PriceFormatted;
332
333 <span itemprop="price" content="@product.Price.Price" class="d-none"></span>
334 if (product.Price.Price != product.PriceBeforeDiscount.Price) {
335 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span>
336 }
337 }
338 }
339
340 @if (showPricesWithVat == "false" && !neverShowVat) {
341 if (isLazyLoadingForProductInfoEnabled)
342 {
343 <span class="text-price js-text-price"><span class="spinner-border" role="status"></span></span>
344 }
345 else
346 {
347 string price = product.Price.PriceWithoutVatFormatted;
348 if (product?.VariantInfo?.VariantInfo != null) {
349 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : "";
350 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : "";
351 }
352 if (priceMin != priceMax) {
353 price = priceMin + " - " + priceMax;
354 }
355 <span class="text-price">@price</span>
356 }
357 } else {
358 if (isLazyLoadingForProductInfoEnabled)
359 {
360 <span class="text-price js-text-price"><span class="spinner-border" role="status"></span></span>
361 }
362 else
363 {
364 string price = product.Price.PriceFormatted;
365 if (product?.VariantInfo?.VariantInfo != null) {
366 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : "";
367 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : "";
368 }
369 if (priceMin != priceMax) {
370 price = priceMin + " - " + priceMax;
371 }
372 <span class="text-price">@price</span>
373 }
374 }
375 </div>
376 @if (showPricesWithVat == "false" && !neverShowVat) {
377 if (isLazyLoadingForProductInfoEnabled)
378 {
379 <div class="fs-7 opacity-85 text-price js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></div>
380 }
381 else
382 {
383 string price = product.Price.PriceWithVatFormatted;
384 if (product?.VariantInfo?.VariantInfo != null) {
385 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : "";
386 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : "";
387 }
388 if (priceMin != priceMax) {
389 price = priceMin + " - " + priceMax;
390 }
391 <div class="fs-7 opacity-85 text-price">@price @Translate("Incl. VAT")</div>
392 }
393 }
394 </div>
395 }
396
397 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "swatches") {
398 var optionCount = product.VariantInfo.VariantInfo.Count();
399 var showMaxVariants = 5;
400
401 <div class="d-flex flex-row gap-1 align-items-center">
402 @foreach (VariantInfoViewModel variant in product.VariantInfo.VariantInfo.Take(showMaxVariants))
403 {
404 <span class="colorbox colorbox-sm rounded-circle border me-1" style="background-color: @variant.OptionColor"></span>
405 }
406 @if (optionCount > showMaxVariants)
407 {
408 int left = optionCount - showMaxVariants;
409 <span class="ms-2">+@left</span>
410 }
411 </div>
412 }
413 </div>
414
415 <div class="overflow-hidden order-1 @(imageTheme)" @imageOutlineStyle>
416 <div class="ratio" style="@(ratioVariable)">
417 <div class="d-flex justify-content-center align-items-center">
418 @if (string.IsNullOrEmpty(alternativeImage)) {
419 <img
420 id="@imageId"
421 srcset="
422 @imagePathXs 480w,
423 @imagePathS 640w"
424 sizes="(min-width: 992px) 33vw, 50vw"
425 src="@imagePathFallBack"
426 loading="lazy"
427 decoding="async"
428 class="mw-100 mh-100 @imageThemePadding"
429 alt="@product.Name">
430 } else {
431 <img
432 id="@imageId"
433 src="@imagePathFallBack"
434 loading="lazy"
435 decoding="async"
436 class="mw-100 mh-100 @imageThemePadding"
437 alt="@product.Name"
438 onmouseover="this.src='@alternativeImage'"
439 onmouseout="this.src='@imagePathFallBack'">
440 }
441 </div>
442 </div>
443
444 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "images")
445 {
446 int variantGroupCount = 0;
447 int showMaxVariantGroups = 2;
448 int showMaxVariants = 3;
449 var productVariantTheme = productTheme != "" ? productTheme : "bg-white";
450
451 <div class="position-relative">
452 <div id="StaticVariants_@product.Id" class="static-variants w-100 d-none d-lg-block position-absolute left-0 bottom-0 @productTheme" style="pointer-events: none;">
453
454 @foreach (var variantGroup in product.VariantGroups())
455 {
456 int variantsCount = 0;
457
458 <div class="d-flex gap-2 mb-2">
459 @foreach (var variant in variantGroup.Options)
460 {
461 if (variantGroupCount < showMaxVariantGroups)
462 {
463 var optionsCount = variantGroup.Options.Count();
464
465 if (variantsCount < showMaxVariants)
466 {
467 string optionWidth = !string.IsNullOrEmpty(variant.Color) ? "w-25" : "";
468
469 <article class="static-variants-option @optionWidth @(productVariantTheme)" title="@product.Name @variant.Name" style="pointer-events: initial;">
470 @if (!string.IsNullOrEmpty(variant.Color))
471 {
472 string defaultProductImage = Dynamicweb.Context.Current.Server.UrlEncode(product.DefaultImage.Value);
473 string variantImage = Dynamicweb.Context.Current.Server.UrlEncode(variant.Image.Value);
474 string defaultPrice = !hidePrice ? product.Price.PriceFormatted : "0";
475 string variantPrice = !hidePrice ? product.Price.PriceFormatted : "0";
476
477 if (isLazyLoadingForProductInfoEnabled)
478 {
479 <figure class="w-100 d-block m-0" data-price-formatted="" onmouseover="swift.StaticVariants.SwitchProduct(event, '@product.Id', this.getAttribute('data-price-formatted'), '@variant.Image.Value')" onmouseout="swift.StaticVariants.SwitchProduct(event, '@product.Id', this.getAttribute('data-price-formatted'), '@product.DefaultImage.Value)')">
480 <div class="d-flex align-items-center justify-content-center">
481 <img src="/admin/public/GetImage.ashx?image=@variantImage&width=75&height=75&crop=5&FillCanvas=true&format=webp&Quality=70" height="75" width="75" class="p-1 text-small" loading="lazy" decoding="async" alt="@product.Name, @variant.Name">
482 </div>
483 </figure>
484 }
485 else
486 {
487 <figure class="w-100 d-block m-0" onmouseover="swift.StaticVariants.SwitchProduct(event, '@product.Id', '@defaultPrice', '@variant.Image.Value')" onmouseout="swift.StaticVariants.SwitchProduct(event, '@product.Id', '@variantPrice', '@product.DefaultImage.Value')">
488 <div class="d-flex align-items-center justify-content-center">
489 <img src="/admin/public/GetImage.ashx?image=@variantImage&width=75&height=75&crop=5&FillCanvas=true&format=webp&Quality=70" height="75" width="75" class="p-1 text-small" loading="lazy" decoding="async" alt="@product.Name, @variant.Name">
490 </div>
491 </figure>
492 }
493 }
494 else
495 {
496 <div class="d-flex align-items-center justify-content-center">
497 @variant.Name
498 </div>
499 }
500 <div class="visually-hidden">
501 <h4>@product.Name, @variant.Name</h4>
502 @if (!hidePrice) {
503 if (isLazyLoadingForProductInfoEnabled)
504 {
505 <span class="text-price js-text-price"></span>
506 }
507 else
508 {
509 <span class="text-price">@product.Price.PriceFormatted</span>
510 }
511 }
512 </div>
513 </article>
514 }
515
516 variantsCount++;
517
518 if (variantsCount == showMaxVariants && optionsCount != showMaxVariants)
519 {
520 int left = optionsCount - showMaxVariants;
521 <div class="variant-option ms-1 d-flex justify-content-center align-items-center">
522 <span>+@left</span>
523 </div>
524 }
525 }
526 }
527 </div>
528
529 variantGroupCount++;
530 }
531 </div>
532 </div>
533 }
534 </div>
535 </a>
536 </div>
537 </article>
538 }
539 </div>
540
541 <div class="my-3" id="LoadMoreButton">
542 <div class="text-center d-flex flex-column gap-3">
543 <div class="opacity-85">@loadedProducts @Translate("out of") @productList.TotalProductsCount @Translate("products")</div>
544 @if (productList.PageCount != 1) {
545 string sortBySelection = Dynamicweb.Context.Current.Request?.Form["SortBy"] ?? "";
546 sortBySelection = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SortBy")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SortBy") : sortBySelection;
547 string mainProductId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("MainProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("MainProductID") : "";
548
549 string searchQuery = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("q")) ? Dynamicweb.Context.Current.Request.QueryString.Get("q") : "";
550 string searchLayout = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout") : "";
551
552 <form method="get" action="@url" data-response-target-element="content" class="w-100">
553 @foreach (FacetGroupViewModel facetGroup in productList.FacetGroups)
554 {
555 foreach (FacetViewModel facetItem in facetGroup.Facets)
556 {
557 foreach (FacetOptionViewModel facetOption in facetItem.Options)
558 {
559 if (facetOption.Selected)
560 {
561 <input type="hidden" name="@facetItem.QueryParameter" value="[@facetOption.Value]">
562 }
563 }
564 }
565 }
566
567 <input type="hidden" name="PageSize" value="@pageSize">
568 <input type="hidden" name="SortBy" value="@sortBySelection">
569 <input type="hidden" name="RequestType" value="UpdateList">
570
571 @if (!string.IsNullOrEmpty(searchQuery))
572 {
573 <input type="hidden" name="q" value="@searchQuery">
574 <input type="hidden" name="SearchLayout" value="@searchLayout">
575 }
576
577 @if (!string.IsNullOrEmpty(mainProductId))
578 {
579 <input type="hidden" name="MainProductID" value="@mainProductId">
580 }
581
582 @{
583 string nextPageLink = "/Default.aspx?ID=" + Pageview.Page.ID + "&PageSize=" + pageSize + "&SortBy=" + sortBySelection;
584
585 foreach (FacetGroupViewModel facetGroup in productList.FacetGroups)
586 {
587 foreach (FacetViewModel facetItem in facetGroup.Facets)
588 {
589 foreach (FacetOptionViewModel facetOption in facetItem.Options)
590 {
591 if (facetOption.Selected)
592 {
593 nextPageLink += "&" + facetItem.QueryParameter + "=[" + facetOption.Value + "]";
594 }
595 }
596 }
597 }
598
599 nextPageLink += !string.IsNullOrEmpty(searchQuery) ? "&q=" + searchQuery : "";
600 }
601
602 <a href="@nextPageLink" class="btn btn-primary" onclick="swift.ProductList.Update(event)" id="LoadMoreButton_@Model.ID">@Translate("Load more products")</a>
603 </form>
604 }
605 </div>
606 </div>
607 } else {
608 if (!Pageview.IsVisualEditorMode) {
609 <div class="alert alert-dark m-0">
610 @Translate("We did not find anything matching your search result")
611 </div>
612 } else {
613 <div class="alert alert-dark m-0" role="alert">
614 <span>@Translate("Product list: The list will be shown here, if any")</span>
615 </div>
616 }
617 }
618 }
619