Error executing template "/Designs/Swift/Paragraph/Swift_ProductListGridView_EA_JGN.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
at CompiledRazorTemplates.Dynamic.RazorEngine_43ece7421eae41a6b1b0862220d0087d.Execute() in D:\dynamicweb.net\Solutions\ContextAnd\Plus_Prod\Files\Templates\Designs\Swift\Paragraph\Swift_ProductListGridView_EA_JGN.cshtml:line 2159
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 System
3 @using System.Collections.Generic
4 @using Dynamicweb.Ecommerce.ProductCatalog
5 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites
6 @using System.Linq
7 @using Dynamicweb.Core
8 @using Dynamicweb.Ecommerce
9 @using Dynamicweb.Environment
10 @using Plus.CustomModules.ExcelGenerator.Operations
11 @using Plus.CustomModules.Helpers
12 @using System.Globalization
13
14 @functions
15 {
16 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsLazyLoadingForProductInfoEnabled;
17 string liveInfoClass = "";
18 string productInfoFeed = "";
19
20 string showPricesWithVat = "";
21 bool neverShowVat = false;
22
23 bool isDetailPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID"));
24
25 ProductListViewModel productList = new ProductListViewModel();
26
27 private bool IsChecked(FieldValueViewModel field)
28 {
29 if (field == null || field.Value == null) return false;
30
31 // Hvis den faktisk kommer som bool
32 if (field.Value is bool b) return b;
33
34 // Hvis den kommer som liste/collection (forekommer for "Liste")
35 if (field.Value is System.Collections.IEnumerable enumerable && !(field.Value is string))
36 {
37 foreach (var v in enumerable)
38 {
39 if (v == null) continue;
40 var s = v.ToString()?.Trim();
41 if (!string.IsNullOrEmpty(s) &&
42 !s.Equals("0") &&
43 !s.Equals("false", StringComparison.OrdinalIgnoreCase))
44 {
45 return true;
46 }
47 }
48 return false;
49 }
50
51 // Fallback: string
52 var str = field.Value.ToString()?.Trim();
53 if (string.IsNullOrEmpty(str)) return false;
54
55 return str.Equals("true", StringComparison.OrdinalIgnoreCase)
56 || str.Equals("1")
57 || str.Equals("yes", StringComparison.OrdinalIgnoreCase)
58 || str.Equals("on", StringComparison.OrdinalIgnoreCase)
59 // typisk for Liste-felt: alt der ikke er tomt/0/false => "checked"
60 || (!str.Equals("0") && !str.Equals("false", StringComparison.OrdinalIgnoreCase));
61 }
62
63
64 private string FindBadgeImage(IEnumerable<Dynamicweb.Frontend.ItemViewModel> badgeItems, string titleBase, string languageId)
65 {
66 if (badgeItems == null) return null;
67 foreach (var badge in badgeItems)
68 {
69 var title = badge.GetString("Title");
70 if (string.IsNullOrEmpty(title)) continue;
71 bool match = false;
72 switch (languageId)
73 {
74 case "LANG3": // DE
75 match = title.StartsWith(titleBase, StringComparison.OrdinalIgnoreCase) && title.EndsWith("_DE", StringComparison.OrdinalIgnoreCase);
76 break;
77 case "LANG4": // SE
78 match = title.StartsWith(titleBase, StringComparison.OrdinalIgnoreCase) && title.EndsWith("_SE", StringComparison.OrdinalIgnoreCase);
79 break;
80 case "LANG5": // NO
81 match = title.StartsWith(titleBase, StringComparison.OrdinalIgnoreCase) && title.EndsWith("_NO", StringComparison.OrdinalIgnoreCase);
82 break;
83 default:
84 match = title.StartsWith(titleBase, StringComparison.OrdinalIgnoreCase)
85 && !title.EndsWith("_DE", StringComparison.OrdinalIgnoreCase)
86 && !title.EndsWith("_SE", StringComparison.OrdinalIgnoreCase)
87 && !title.EndsWith("_NO", StringComparison.OrdinalIgnoreCase);
88 break;
89 }
90 if (match)
91 {
92 var img = badge.GetString("Image");
93 return string.IsNullOrEmpty(img) ? null : System.Web.HttpUtility.UrlPathEncode(img);
94 }
95 }
96 return null;
97 }
98 }
99
100 @{
101 if (Dynamicweb.Context.Current.Items.Contains("ProductList"))
102 {
103 productList = (ProductListViewModel)Dynamicweb.Context.Current.Items["ProductList"];
104 }
105
106 showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower();
107 neverShowVat = string.IsNullOrEmpty(showPricesWithVat);
108
109 if (isLazyLoadingForProductInfoEnabled)
110 {
111 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed"))
112 {
113 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString();
114 if (!string.IsNullOrEmpty(productInfoFeed))
115 {
116 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\"";
117 }
118 }
119 liveInfoClass = "js-live-info";
120 }
121
122 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
123 string themePadding = theme != string.Empty ? "px-0 py-3" : string.Empty;
124 var CartId = Dynamicweb.Ecommerce.Common.Context.Cart == null ? "" : Dynamicweb.Ecommerce.Common.Context.Cart.Id;
125 var CartValue = "";
126 var CartUrl = "";
127 var order = Dynamicweb.Ecommerce.Services.Orders.GetById(CartId);
128
129 }
130
131 @if (!isDetailPage)
132 {
133 var productCount = productList.Group != null ? Dynamicweb.Ecommerce.Services.ProductGroups?.GetGroup(productList.Group.Id).Products.Count : 1;
134
135 FacetsInfo.GetFacetsInfo(productList, out var facetsFound, out var selectedFacetsCount);
136
137 //var group = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(productList.Group.Id);
138 //var productCount = group.Products.Count;
139
140 if (!string.IsNullOrEmpty(theme))
141 {
142 if (productCount > 0 || selectedFacetsCount > 0)
143 {
144 <div class="h-100@(theme) @themePadding item_@Model.Item.SystemName.ToLower()" @productInfoFeed>
145 @{@RenderProductList()}
146 </div>
147 }
148
149 }
150 else
151 {
152 if (productCount > 0)
153 {
154 <div class="@themePadding item_@Model.Item.SystemName.ToLower() @productCount" @productInfoFeed>
155 @{@RenderProductList()}
156 </div>
157 }
158
159 }
160 }
161
162 @helper RenderProductList()
163 {
164 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
165 bool anonymousUser = Pageview.User == null;
166 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable();
167
168 string productTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ProductTheme")) ? " theme " + Model.Item.GetRawValueString("ProductTheme").Replace(" ", "").Trim().ToLower() : "";
169 string productThemePadding = productTheme != string.Empty ? "p-3" : string.Empty;
170
171 string url = Dynamicweb.Context.Current.Request.RawUrl;
172 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false;
173 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Model.Item.GetString("ShowFavoritesSelectorMasterProduct")) ? Model.Item.GetBoolean("ShowFavoritesSelectorMasterProduct") : false;
174 string staticVariantsLayout = Model.Item.GetRawValueString("StaticVariantsLayout", "hide");
175
176 string groupId = productList?.Group?.Id != null ? productList.Group.Id : "";
177
178 var badgeParms = new Dictionary<string, object>();
179 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType"));
180 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign"));
181 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign"));
182 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays"));
183 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges"));
184
185 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false;
186 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false;
187
188 string googleAnalyticsTrackingID = Pageview.AreaSettings.GetString("GoogleAnalyticsTrackingID");
189 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID");
190 var cookieOptInLevel = CookieManager.GetCookieOptInLevel();
191 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical"));
192 var bagdeItems = Model.Item?.GetItems("Bagdes") ?? Enumerable.Empty<Dynamicweb.Frontend.ItemViewModel>().ToList();
193 var mobileGrid = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GridLayoutMobile", "grid-1")) ? Model.Item.GetRawValueString("GridLayoutMobile", "grid-1") : "grid-1";
194
195 var favoriteParameters = new Dictionary<string, object>();
196 if (!anonymousUser && !hideFavoritesSelector)
197 {
198 int defaultFavoriteListId = 0;
199
200 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists();
201 if (favoreiteLists.Count() == 1)
202 {
203 foreach (FavoriteList list in favoreiteLists)
204 {
205 defaultFavoriteListId = list.ListId;
206 }
207 }
208
209 favoriteParameters.Add("ListId", defaultFavoriteListId);
210 }
211
212 if (productList.TotalProductsCount > 0)
213 {
214 if (Pageview.IsVisualEditorMode)
215 {
216 <div class="alert alert-dark m-0" role="alert">
217 <span>@Translate("Product list: The list will be shown here, if any")</span>
218 </div>
219 }
220 else
221 {
222
223 int pageSizeSetting = 30;
224 int pageSize = productList.PageSize;
225 pageSize += pageSizeSetting;
226
227 int loadedProducts = productList.PageSize > productList.TotalProductsCount ? productList.TotalProductsCount : productList.PageSize;
228
229 //indsæt check på Grid colums felt på mobile når itemtype er opdateret.
230
231 <div class="grid @mobileGrid grid-md-2 grid-lg-3 grid-xl-4">
232
233 @foreach (ProductViewModel product in productList.Products)
234 {
235 string variantIdForLink = !string.IsNullOrEmpty(product.VariantId) ? $"&VariantID={product.VariantId}" : "";
236 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(product.DefaultVariantId) ? $"&VariantID={product.DefaultVariantId}" : variantIdForLink;
237
238 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop");
239 link += $"&GroupID={product.PrimaryOrDefaultGroup.Id}";
240 link += $"&ProductID={product.Id}";
241 link += variantIdForLink;
242 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link);
243
244 string imagePath = product?.DefaultImage?.Value ?? "";
245 imagePath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath);
246
247 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", "");
248 ratio = ratio != "0" ? ratio : "";
249 string ratioCssClass = ratio != "" ? " ratio" : "";
250 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
251
252 string imagePathXs = "/Admin/Public/GetImage.ashx?width=" + 480 + "&image=" + imagePath + "&format=webp";
253 string imagePathS = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp";
254 string imagePathFallBack = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp";
255
256 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : "";
257 string imageThemePadding = imageTheme != string.Empty ? "px-0 py-3" : string.Empty;
258 string imageOutlineStyle = imageTheme == string.Empty ? "style=\"border: 1px solid transparent\"" : string.Empty;
259
260 string imageId = "ProductImage_" + product.Id + product.VariantId;
261 string priceId = "ProductPrice_" + product.Id + product.VariantId;@* Alternative image *@ var supportedImageFormats = new string[] { ".jpg", ".webp", ".png", ".gif" };
262 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : "";
263 var selectedAssetCategories = Model.Item.GetRawValueString("AlternativeImageAssets");
264 IEnumerable<MediaViewModel> alternativeImagesList = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets);
265
266 if (alternativeImagesList.FirstOrDefault() != null)
267 {
268 alternativeImagesList = alternativeImagesList.OrderByDescending(x => x.Value.Equals(defaultImage));
269
270 if (alternativeImagesList.First().Value == defaultImage)
271 {
272 alternativeImagesList = alternativeImagesList.Skip(1);
273 }
274 }
275
276 string alternativeImage = alternativeImagesList.FirstOrDefault() != null ? alternativeImagesList.FirstOrDefault().Value : "";
277 alternativeImage = !string.IsNullOrEmpty(alternativeImage) ? "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + alternativeImage + "&format=webp" : ""; @* Badges *@ DateTime createdDate = product.Created.Value;
278 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false;
279 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges;
280 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; @* Main features *@ IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList();
281 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>();
282
283 foreach (var selection in selectedDisplayGroups)
284 {
285 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values)
286 {
287 if (selection == group.Id)
288 {
289 mainFeatures.Add(group);
290 }
291 }
292 }
293
294 FieldValueViewModel pageContent;
295 product.ProductFields.TryGetValue("EFA_PageContentID", out pageContent);
296 var pageContentID = !string.IsNullOrEmpty(pageContent.Value.ToString()) ? Convert.ToInt32(pageContent.Value) : 0;
297
298 if (pageContentID != 0)
299 {
300 var paragraphs = Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(pageContentID);
301
302 if (paragraphs.Count() <= 1)
303 {
304 <article class="position-relative@(productTheme) product-list-item js-product @liveInfoClass" data-product-id="@product.Id">
305 @{
306 foreach (var paragraph in paragraphs)
307 {
308 <div class="d-block h-100 align-items-stretch px-0 py-3">
309 @RenderParagraphContent(paragraph.ID)
310 </div>
311 }
312 ;
313 }
314 </article>
315 }
316 }
317 else
318 {
319 <article class="position-relative@(productTheme) product-list-item js-product @liveInfoClass" data-product-id="@product.Id" itemscope itemtype="https://schema.org/Product">
320 @if (!anonymousUser)
321 {
322 if (!hideFavoritesSelector && product.VariantInfo.VariantInfo == null)
323 {
324 <div class="position-absolute top-0 end-0 my-3" style="z-index: 2">
325 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters)
326 </div>
327 }
328 else if (showFavoritesSelectorMasterProduct)
329 {
330 <div class="position-absolute top-0 end-0 my-3" style="z-index: 2">
331 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters)
332 </div>
333 }
334 }
335
336 @if (showBadges)
337 {
338 <div class="position-absolute top-0 left-0 p-1 p-lg-2 ps-0 ps-lg-0" style="z-index: 2">
339 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)
340 </div>
341 }
342
343 <div class="d-flex flex-column d-block h-100 align-items-stretch">
344 @{
345 string clickProductLink = string.Empty;
346 if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking)
347 {
348 clickProductLink = "onclick=\"return clickProductLink('" + product.Id + "', '" + product.Name + "', '" + product.VariantName + "', '" + product.Price.CurrencyCode + "', '" + product.Price.Price + "')\"";
349 }
350 }
351 <a href="@link" class="text-decoration-none d-flex flex-column j h-100" @clickProductLink>
352 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking)
353 {
354 <script>
355 function clickProductLink(productId, productName, productVariant, productCurrency, productPrice) {
356 if (typeof gtag !== "undefined") {
357 gtag("event", "select_item", {
358 item_list_id: "product_list_gridview",
359 item_list_name: "Product list (Gridview)",
360 items: [
361 {
362 item_id: productId,
363 item_name: productName,
364 currency: productCurrency,
365 item_list_id: "product_list_gridview",
366 item_list_name: "Product list (Gridview)",
367 item_variant: productVariant,
368 price: productPrice
369 }
370 ]
371 });
372 }
373 }
374 </script>
375 }
376
377 <div class="px-0 order-2">
378 <div class="flex-grow-1">
379 <h3 class="h6 mb-0 text-break" itemprop="name">
380 @product.Name
381 @if (!string.IsNullOrEmpty(product.VariantName))
382 {<text>(@product.VariantName)</text>}
383 </h3>
384 @if (!Model.Item.GetBoolean("HideProductNumber"))
385 {
386 <p class="fs-7 opacity-85 mb-2">@product.Number</p>
387 }
388 @if (mainFeatures.Count > 0)
389 {
390 <ul class="p-0 lh-sm opacity-75" style="list-style-position: inside">
391 @foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures)
392 {
393 foreach (var fieldViewModel in mainFeatureGroup.Fields)
394 {
395 var field = fieldViewModel.Value;
396 string fieldValue = field?.Value is object ? field.Value.ToString() : "";
397
398 if (fieldValue != "")
399 {
400 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue;
401 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue;
402
403 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
404 {
405 fieldValue = "";
406
407 foreach (FieldOptionValueViewModel option in field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>)
408 {
409 fieldValue = option.Name;
410 }
411 }
412
413 bool isColor = false;
414 if (fieldValue.Contains("#") && (Translate(field.Name) == Translate("Color") || Translate(field.Name) == Translate("Colour")))
415 {
416 isColor = true;
417 }
418
419 if (!string.IsNullOrEmpty(fieldValue))
420 {
421 if (!isColor)
422 {
423 <li>@(field.Name): @fieldValue</li>
424 }
425 else
426 {
427 <li class="position-relative">
428 <span class="colorbox-sm" style="background-color: @fieldValue"></span>
429 </li>
430 }
431 }
432 }
433 }
434 }
435 </ul>
436
437 }
438 </div>
439
440 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "swatches")
441 {
442 var optionCount = product.VariantInfo.VariantInfo.Count();
443 var showMaxVariants = 5;
444
445 <div class="d-flex flex-row gap-1 align-items-center">
446 @foreach (VariantInfoViewModel variant in product.VariantInfo.VariantInfo.Take(showMaxVariants))
447 {
448 <span class="colorbox colorbox-sm rounded-circle border me-1" style="background-color: @variant.OptionColor"></span>
449
450 }
451 @if (optionCount > showMaxVariants)
452 {
453 int left = optionCount - showMaxVariants;
454 <span class="ms-2">+@left</span>
455
456 }
457 </div>
458 }
459 </div>
460
461 <div class="overflow-hidden order-1 @(imageTheme) pb-2">
462 <div class="d-flex justify-content-center align-items-center">
463 @{
464 FieldValueViewModel plusDesignerIndikator;
465 product.ProductFields.TryGetValue("PlusDesignerLinkEA", out plusDesignerIndikator);
466 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "target=\"_blank\"" : string.Empty;
467 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "rel=\"noopener\"" : string.Empty;
468
469 if (!String.IsNullOrEmpty(plusDesignerIndikator.Value.ToString()))
470 {
471 <div class="plus-designer__container" style="position: absolute; left: 0; margin-bottom: 55%; background: #fff; border: 1px solid #228B64; border-left: none; z-index: 100; width: 75%; max-width: 200px; ">
472 <object>
473 <a href="@plusDesignerIndikator.Value" @target @rel class="text-decoration-none">
474 <div class="plus-designer__inner p-1 ps-3">
475 <p class="m-0 opacity-75 fs-7">Try me in</p>
476 <div class="plus-designer__image-container d-flex w-100">
477 <img class="" src="/Files/Templates/Designs/Swift/Assets/Images/plusdesignerlogo.svg" alt="Alternate Text" style="width:90%" />
478 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right" style="width:10%"><polyline points="9 18 15 12 9 6"></polyline></svg>
479 </div>
480 </div>
481 </a>
482 </object>
483 </div>
484
485 }
486 FieldValueViewModel plusBadge;
487 product.ProductFields.TryGetValue("Art", out plusBadge);
488 FieldValueViewModel fscBadge;
489 product.ProductFields.TryGetValue("FSC", out fscBadge);
490 FieldValueViewModel extraBadgeListObject;
491 product.ProductFields.TryGetValue("ExtraPlusBadges", out extraBadgeListObject);
492 FieldValueViewModel fscBadgeLink;
493 product.ProductFields.TryGetValue("FSC_link", out fscBadgeLink);
494 FieldValueViewModel pefcBadgeLink;
495 product.ProductFields.TryGetValue("PEFC_link", out pefcBadgeLink);
496 <div></div>
497 if (!String.IsNullOrEmpty(plusBadge.Value.ToString()) || !String.IsNullOrEmpty(fscBadge.Value.ToString()) || extraBadgeListObject.Value != null)
498 {
499 foreach (var badge in bagdeItems)
500 {
501 if (fscBadge.Value.ToString().Contains(badge.GetString("Title")))
502 {
503
504 <div class="plus-badge d-flex justify-content-center w-100 fsc-badge-list efa-mod ">
505 <object class="w-100">
506 <div class="d-flex justify-content-start w-75 h-75 efa-mod m-auto">
507 @if (fscBadge.Value.ToString().Contains("FSC"))
508 {
509 <a href="@fscBadgeLink.Value.ToString()" target="_blank" class="w-25"><img class="plus-badge_img fsc-badge_img w-100 efa-mod" srcset="@badge.GetString("Image")" src="" alt="@fscBadge.Value.ToString()" /></a>
510 }
511 @if (fscBadge.Value.ToString().Contains("PEFC"))
512 {
513 <a href="@pefcBadgeLink.Value.ToString()" target="_blank" class="w-25"><img class="plus-badge_img fsc-badge_img w-100 efa-mod" srcset="@badge.GetString("Image")" src="" alt="@fscBadge.Value.ToString()" /></a>
514 }
515 </div>
516 </object>
517 </div>
518 }
519 var badgeTitle = badge.GetString("Title");
520
521 switch (product.LanguageId)
522 {
523 case "LANG3":
524 {
525 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && badgeTitle.EndsWith("_DE"))
526 {
527 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString())
528 }
529 break;
530 }
531 case "LANG4":
532 {
533 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && badgeTitle.EndsWith("_SE"))
534 {
535 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString())
536 }
537 break;
538 }
539 case "LANG5":
540 {
541 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && badgeTitle.EndsWith("_NO"))
542 {
543 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString())
544 }
545 break;
546 }
547 default:
548 {
549 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && !badgeTitle.EndsWith("_NO") && !badgeTitle.EndsWith("_SE") && !badgeTitle.EndsWith("_DE"))
550 {
551 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString())
552 }
553 break;
554 }
555 }
556
557 if (extraBadgeListObject.Value is List<FieldOptionValueViewModel>)
558 {
559 var extraBadgeList = (List<FieldOptionValueViewModel>)extraBadgeListObject.Value;
560 foreach (var extraBadge in extraBadgeList)
561 {
562 var extraBadgeTitle = extraBadge.Value;
563
564 switch (product.LanguageId)
565 {
566 case "LANG3":
567 {
568 if (badgeTitle.StartsWith(extraBadgeTitle) && badgeTitle.EndsWith("_DE"))
569 {
570 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString())
571 }
572 break;
573 }
574 case "LANG4":
575 {
576 if (badgeTitle.StartsWith(extraBadgeTitle) && badgeTitle.EndsWith("_SE"))
577 {
578 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString())
579 }
580 break;
581 }
582 case "LANG5":
583 {
584 if (badgeTitle.StartsWith(extraBadgeTitle) && badgeTitle.EndsWith("_NO"))
585 {
586 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString())
587 }
588 break;
589 }
590 default:
591 {
592 if (badgeTitle.StartsWith(extraBadgeTitle) && !badgeTitle.EndsWith("_NO") && !badgeTitle.EndsWith("_SE") && !badgeTitle.EndsWith("_DE"))
593 {
594 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString())
595 }
596 break;
597 }
598 }
599 }
600 }
601 }
602 }
603
604 }
605 <div class="position-relative w-75">
606 @RenderCornerBadges(product, bagdeItems)
607 @if (string.IsNullOrEmpty(alternativeImage))
608 {
609 <img id="@imageId" itemprop="image"
610 srcset="
611 @imagePathXs 480w,
612 @imagePathS 640w"
613 sizes="(min-width: 992px) 33vw, 50vw"
614 src="@imagePathFallBack"
615 loading="lazy"
616 decoding="async"
617 class="w-100 mw-100 mh-100 @imageThemePadding"
618 alt="@product.Name">
619 }
620 else
621 {
622 <img id="@imageId" itemprop="image"
623 src="@imagePathFallBack"
624 loading="lazy"
625 decoding="async"
626 class="w-100 mw-100 mh-100 @imageThemePadding"
627 alt="@product.Name"
628 onmouseover="this.src='@alternativeImage'"
629 onmouseout="this.src='@imagePathFallBack'">
630
631 }
632 </div>
633 </div>
634
635 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "images")
636 {
637 int variantGroupCount = 0;
638 int showMaxVariantGroups = 2;
639 int showMaxVariants = 3;
640 var productVariantTheme = productTheme != "" ? productTheme : "bg-white";
641
642 <div class="position-relative">
643 <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;">
644
645 @foreach (var variantGroup in product.VariantGroups())
646 {
647 int variantsCount = 0;
648
649 <div class="d-flex gap-2 mb-2">
650 @foreach (var variant in variantGroup.Options)
651 {
652 if (variantGroupCount < showMaxVariantGroups)
653 {
654 var optionsCount = variantGroup.Options.Count();
655
656 if (variantsCount < showMaxVariants)
657 {
658 string optionWidth = !string.IsNullOrEmpty(variant.Color) ? "w-25" : "";
659
660 <article class="static-variants-option @optionWidth @(productVariantTheme)" title="@product.Name @variant.Name" style="pointer-events: initial;">
661 @if (!string.IsNullOrEmpty(variant.Color))
662 {
663 string defaultProductImage = Dynamicweb.Context.Current.Server.UrlEncode(product.DefaultImage.Value);
664 string variantImage = Dynamicweb.Context.Current.Server.UrlEncode(variant.Image.Value);
665 string defaultPrice = !hidePrice ? product.Price.PriceFormatted : "0";
666 string variantPrice = !hidePrice ? product.Price.PriceFormatted : "0";
667
668 if (isLazyLoadingForProductInfoEnabled)
669 {
670 <figure class="w-100 d-block m-0" data-price-formatted="" onmouseover="swift.StaticVariants.SwitchProduct(event, '@product.Id', this.getAttribute('data-price-formatted'), '@variantImage')" onmouseout="swift.StaticVariants.SwitchProduct(event, '@product.Id', this.getAttribute('data-price-formatted'), '@defaultProductImage')">
671 <div class="d-flex align-items-center justify-content-center">
672 <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">
673 </div>
674 </figure>
675 }
676 else
677 {
678 <figure class="w-100 d-block m-0" onmouseover="swift.StaticVariants.SwitchProduct(event, '@product.Id', '@defaultPrice', '@variantImage')" onmouseout="swift.StaticVariants.SwitchProduct(event, '@product.Id', '@variantPrice', '@defaultProductImage')">
679 <div class="d-flex align-items-center justify-content-center">
680 <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">
681 </div>
682 </figure>
683 }
684 }
685 else
686 {
687 <div class="d-flex align-items-center justify-content-center">
688 @variant.Name
689 </div>
690 }
691 <div class="visually-hidden">
692 <h4>@product.Name, @variant.Name</h4>
693 @if (!hidePrice)
694 {
695 if (isLazyLoadingForProductInfoEnabled)
696 {
697 <span class="text-price js-text-price"></span>
698 }
699 else
700 {
701 <span class="text-price">@product.Price.PriceFormatted</span>
702
703 }
704 }
705 </div>
706 </article>
707 }
708
709 variantsCount++;
710
711 if (variantsCount == showMaxVariants && optionsCount != showMaxVariants)
712 {
713 int left = optionsCount - showMaxVariants;
714 <div class="variant-option ms-1 d-flex justify-content-center align-items-center">
715 <span>+@left</span>
716 </div>
717 }
718 }
719 }
720 </div>
721 variantGroupCount++;
722 }
723 </div>
724 </div>
725 }
726 </div>
727 </a>
728 @if (!hidePrice)
729 {
730 string priceMin = "";
731 string priceMax = "";
732 string availability = product.StockLevel > 0 ? "https://schema.org/InStock" : "https://schema.org/OutOfStock";
733 <div itemprop="offers" itemscope itemtype="https://schema.org/Offer">
734 <link itemprop="availability" href="@availability" />
735 <div class="px-0">
736 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span>
737
738 @if (showPricesWithVat == "false" && !neverShowVat)
739 {
740 if (isLazyLoadingForProductInfoEnabled)
741 {
742 <span itemprop="price" content="" class="d-none"></span>
743 <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> }
744 else
745 {
746 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted;
747 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span>
748 if (product.Price.Price != product.PriceBeforeDiscount.Price)
749 {
750 <span class="text-decoration-line-through opacity-75 me-3 text-price campaign-price-efa">@beforePrice</span> }
751 }
752
753 }
754 else
755 {
756
757 if (isLazyLoadingForProductInfoEnabled)
758 {
759 <span itemprop="price" content="" class="d-none"></span>
760 <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>
761 }
762 else
763 {
764 string beforePrice = product.PriceBeforeDiscount.PriceFormatted;
765 string googlepriceraw = product.Price.PriceWithVat.ToString();
766 string googlepriceformatted = googlepriceraw.Replace(',', '.');
767 var priceStandardFieldValueConverted = CampaignPrice.GetConvertedPriceWithoutVat(product);
768 bool campaignPrice = CampaignPrice.CheckCampaignWithoutVat(product, priceStandardFieldValueConverted);
769
770 <span itemprop="price" content="@googlepriceformatted" class="d-none"></span>
771 if (product.Price.Price != product.PriceBeforeDiscount.Price & campaignPrice == false)
772 {
773 <span class="text-decoration-line-through opacity-75 me-3 text-price campaign-price-efa">@beforePrice</span>
774
775 }
776 }
777 }
778
779 @if (showPricesWithVat == "false" && !neverShowVat)
780 {
781 if (isLazyLoadingForProductInfoEnabled)
782 {
783 <span class="text-price js-text-price"><span class="spinner-border" role="status"></span></span> }
784 else
785 {
786 string price = product.Price.PriceWithoutVatFormatted;
787 if (product?.VariantInfo?.VariantInfo != null)
788 {
789 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : "";
790 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : "";
791 }
792 if (priceMin != priceMax)
793 {
794 price = priceMin + " - " + priceMax;
795 }
796 <span class="text-price">@price</span>
797 }
798 }
799 else
800 {
801 if (isLazyLoadingForProductInfoEnabled)
802 {
803 <span class="text-price js-text-price"><span class="spinner-border" role="status"></span></span>
804 }
805 else
806 {
807 string price = product.Price.PriceFormatted;
808 var priceStandardFieldValueConverted = CampaignPrice.GetConvertedPriceWithoutVat(product);
809 var priceStandardWithoutFieldValueConverted = CampaignPrice.GetConvertedPriceWithVat(product);
810 bool campaignPrice = CampaignPrice.CheckCampaignWithoutVat(product, priceStandardFieldValueConverted);
811 var priceBefore = CampaignPrice.PriceBeforeWithVat(product, priceStandardWithoutFieldValueConverted);
812
813 if (product?.VariantInfo?.VariantInfo != null)
814 {
815 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : "";
816 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : "";
817 }
818 if (priceMin != priceMax)
819 {
820 price = priceMin + " - " + priceMax;
821 }
822
823 <div class="d-flex flex-wrap">
824 @if (campaignPrice)
825 {
826 <span class="text-decoration-line-through opacity-75 me-3 text-price w-100 campaign-price-efa">@priceBefore</span>
827 }
828 <span class="text-price fw-bold w-100">@price</span>
829 </div>
830
831 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : "";
832 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0;
833 var productToShow = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId);
834 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code;
835 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : "";
836 product.ProductFields.TryGetValue("ProductNextBackorderDate", out FieldValueViewModel backInStockDateValue);
837 DateTime backInstockDate = backInStockDateValue.Value != null ? DateTime.Parse(backInStockDateValue.Value.ToString()) : DateTime.Today.AddDays(-1);
838 bool hasBackInstockDate = backInStockDateValue != null && backInstockDate >= DateTime.Today;
839 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery >= DateTime.Today;
840 string expectedDeliveryDate = hasBackInstockDate ? backInstockDate.ToShortDateString() : product.ExpectedDelivery?.ToShortDateString() ?? "";
841
842 <div class="fs-8 mt-1 mb-2 text-start">
843 @if (!string.IsNullOrWhiteSpace(product.StockStatus))
844 {
845 <p class="mb-1">@product.StockStatus</p>
846 if (!string.IsNullOrWhiteSpace(product.StockDeliveryText))
847 {
848 <p class="mb-1">@product.StockDeliveryText</p>
849 }
850 }
851 @if (hasExpectedDelivery || hasBackInstockDate)
852 {
853 <div class="mb-1">
854 <span>@Translate("Expected in stock"): </span>
855 <span>@expectedDeliveryDate</span>
856 </div>
857 }
858 </div>
859
860 @*if (!string.IsNullOrWhiteSpace(product.StockStatus)) {
861 <div class="">@product.StockStatus</div>
862 <div class="">@product.StockDeliveryText</div>
863 {
864 <div>
865 <span>@Translate("Expected in stock"): </span>
866 <span>@expectedDeliveryDate</span>
867 </div>
868 }
869 } else {
870
871 if (hasExpectedDelivery || hasBackInstockDate)
872 {
873 <div>
874 <span>@Translate("Expected in stock"): </span>
875 <span>@expectedDeliveryDate</span>
876 </div>
877 }
878 }*@
879
880 @*if (!string.IsNullOrEmpty(ecomCountryCode) && !hidePrice && product.Id != null && shipId != null)
881 {
882 var shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency);
883
884
885
886 if (shippingFeeAmount != null)
887 {
888 <div class="fs-8 text-black-50 text-start">
889 <p>@Translate("Levering fra") @currency @shippingFeeAmount</p>
890 </div>
891 }
892 }*@
893 }
894 }
895
896 @if (showPricesWithVat == "false" && !neverShowVat)
897 {
898 if (isLazyLoadingForProductInfoEnabled)
899 {
900 <div class="fs-7 opacity-85 text-price js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></div>
901 }
902 else
903 {
904 string price = product.Price.PriceWithVatFormatted;
905 if (product?.VariantInfo?.VariantInfo != null)
906 {
907 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : "";
908 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : "";
909 }
910 if (priceMin != priceMax)
911 {
912 price = priceMin + " - " + priceMax;
913 }
914 <div class="fs-7 opacity-85 text-price">@price @Translate("Incl. VAT")</div>
915
916 }
917 }
918 </div>
919 </div>
920 }
921 @RenderAddToCart(product, link, clickProductLink)
922 </div>
923 </article>
924 }
925
926 }
927 </div>
928
929 <div class="my-3" id="LoadMoreButton">
930 <div class="text-center d-flex flex-column gap-3">
931 <div class="opacity-85">@loadedProducts @Translate("out of") @productList.TotalProductsCount @Translate("products")</div>
932 @if (productList.PageCount != 1)
933 {
934 string sortBySelection = Dynamicweb.Context.Current.Request?.Form["SortBy"] ?? "";
935 sortBySelection = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SortBy")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SortBy") : sortBySelection;
936
937 string searchQuery = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("q")) ? Dynamicweb.Context.Current.Request.QueryString.Get("q") : "";
938 string searchLayout = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout") : "";
939
940 <form method="get" action="@url" data-response-target-element="content" class="w-100">
941 @foreach (FacetGroupViewModel facetGroup in productList.FacetGroups)
942 {
943 foreach (FacetViewModel facetItem in facetGroup.Facets)
944 {
945 foreach (FacetOptionViewModel facetOption in facetItem.Options)
946 {
947 if (facetOption.Selected)
948 {
949 <input type="hidden" name="@facetItem.QueryParameter" value="[@facetOption.Value]">
950 }
951 }
952 }
953 }
954
955 <input type="hidden" name="PageSize" value="@pageSize">
956 <input type="hidden" name="SortBy" value="@sortBySelection">
957 <input type="hidden" name="RequestType" value="UpdateList">
958
959 @if (!string.IsNullOrEmpty(searchQuery))
960 {
961 <input type="hidden" name="q" value="@searchQuery">
962 <input type="hidden" name="SearchLayout" value="@searchLayout">
963 }
964
965 @{
966 string nextPageLink = "/Default.aspx?ID=" + Pageview.Page.ID + "&PageSize=" + pageSize + "&SortBy=" + sortBySelection;
967
968 foreach (FacetGroupViewModel facetGroup in productList.FacetGroups)
969 {
970 foreach (FacetViewModel facetItem in facetGroup.Facets)
971 {
972 foreach (FacetOptionViewModel facetOption in facetItem.Options)
973 {
974 if (facetOption.Selected)
975 {
976 nextPageLink += "&" + facetItem.QueryParameter + "=[" + facetOption.Value + "]";
977 }
978 }
979 }
980 }
981
982 nextPageLink += !string.IsNullOrEmpty(searchQuery) ? "&q=" + searchQuery : "";
983 }
984
985 <a href="@nextPageLink" class="btn btn-primary" onclick="swift.ProductList.Update(event)" id="LoadMoreButton_@Model.ID">@Translate("Load more products")</a>
986 </form>
987
988 }
989 </div>
990 </div>
991 }
992 }
993 else
994 {
995 <div class="alert alert-dark m-0">
996 @Translate("We did not find anything matching your search result")
997 </div>
998 }
999 }
1000
1001 @helper RenderBadge(string badge, string plusBadge)
1002 {
1003 <div class="plus-badge d-flex justify-content-center w-100 efa-mod">
1004 <div class="d-flex justify-content-end w-75 h-75">
1005 <img class="plus-badge_img efa-mod" srcset="@badge" src="" alt="@plusBadge" />
1006 </div>
1007 </div>
1008 }
1009
1010 @helper RenderExtraBadge(string badge, string extraBadge)
1011 {
1012 <div class="plus-badge d-flex justify-content-center w-100 efa-mod">
1013 <div class="d-flex justify-content-start w-75 h-75">
1014 <img class="extra-plus-badge_img efa-mod" srcset="@System.Web.HttpUtility.UrlPathEncode(badge)" src="" alt="@extraBadge" />
1015 </div>
1016 </div>
1017 }
1018
1019 @helper RenderCornerBadges(ProductViewModel product, IEnumerable<Dynamicweb.Frontend.ItemViewModel> badgeItems)
1020 {
1021 FieldValueViewModel plusBadgeLeftButtom;
1022 product.ProductFields.TryGetValue("PlusBadgeLeftButtom", out plusBadgeLeftButtom);
1023 FieldValueViewModel plusBadgeRightTop;
1024 product.ProductFields.TryGetValue("PlusBadgeRightTop", out plusBadgeRightTop);
1025
1026 var rightTopImg = IsChecked(plusBadgeRightTop) ? FindBadgeImage(badgeItems, "Outlet", product.LanguageId) : null;
1027 var leftBottomImg = IsChecked(plusBadgeLeftButtom) ? FindBadgeImage(badgeItems, "2. Sortering", product.LanguageId) : null;
1028
1029 if (!string.IsNullOrEmpty(rightTopImg))
1030 {
1031 <img class="position-absolute top-0 end-0" style="max-width: 40%; height: auto; z-index: 2;" src="@rightTopImg" alt="Outlet" loading="lazy" decoding="async" />
1032 }
1033 if (!string.IsNullOrEmpty(leftBottomImg))
1034 {
1035 <img class="position-absolute bottom-0 start-0" style="max-width: 40%; height: auto; z-index: 2;" src="@leftBottomImg" alt="2. Sortering" loading="lazy" decoding="async" />
1036 }
1037 }
1038
1039
1040
1041 @helper RenderViewMore(string link, string clickProductLink)
1042 {
1043 <a href="@link" class="btn btn-secondary flex-fill" @clickProductLink>@Translate("View more")</a>
1044 }
1045
1046 @helper RenderAddToCart(ProductViewModel product, string link, string clickProductLink)
1047 {
1048 string iconPath = "/Files/icons/";
1049 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
1050 if (!url.Contains("LayoutTemplate"))
1051 {
1052 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml";
1053 }
1054
1055 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
1056 bool anonymousUser = Pageview.User == null;
1057
1058 bool hideAddToCart = !string.IsNullOrEmpty(Model.Item.GetString("HideAddToCart")) ? Model.Item.GetBoolean("HideAddToCart") : false;
1059 hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable() ? true : hideAddToCart;
1060 bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("QuantitySelector")) ? Model.Item.GetBoolean("QuantitySelector") : false;
1061
1062 bool isDiscontinued = product.Discontinued;
1063 bool IsNeverOutOfStock = product.NeverOutOfstock;
1064 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : "";
1065 disableAddToCart = isDiscontinued ? "disabled" : disableAddToCart;
1066 disableAddToCart = IsNeverOutOfStock ? "" : disableAddToCart;
1067 disableAddToCart = isLazyLoadingForProductInfoEnabled ? "disabled" : disableAddToCart;
1068
1069 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : "";
1070 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0;
1071 var productToShow = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId);
1072 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code;
1073 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : "";
1074 var shippingFeeAmount = string.Empty;
1075
1076 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null)
1077 {
1078 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency);
1079 }
1080
1081 var addToCartLabel = Model.Item.GetRawValueString("GridLayoutMobile", "grid-1") == "grid-2" ? "" : Translate("Add to cart");
1082 string hideOnMobile = Model.Item.GetRawValueString("GridLayoutMobile") == "grid-2" ? "hide-on-mobile" : "";
1083 string viewMoreIcon = Model.Item.GetRawValueString("ViewMoreIcon");
1084
1085 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount;
1086 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, product.LanguageId);
1087
1088 if (!hideAddToCart)
1089 {
1090 if (product.VariantInfo.VariantInfo == null)
1091 {
1092 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\"";
1093 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1";
1094 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty;
1095 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : "";
1096 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
1097 string imagePathHidden = product?.DefaultImage.Value.ToString() ?? "";
1098 string availability = product.StockLevel > 0 ? "https://schema.org/InStock" : "https://schema.org/OutOfStock";
1099 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70";
1100 <div class="px-0 pb-3">
1101 <form method="post" action="@url">
1102 <input type="hidden" name="redirect" value="false">
1103 <input type="hidden" name="MainProductId" value="None">
1104 <input type="hidden" name="ProductId" value="@product.Id">
1105 <input type="hidden" name="ProductName" value="@product.Name">
1106 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code">
1107 <input type="hidden" name="ProductReferer" value="product_list_listview">
1108 <input type="hidden" name="ProductPrice" value="@product.Price.PriceFormatted">
1109 <input type="hidden" name="cartcmd" value="add">
1110 @* #38 EA: sbj *@
1111 <input type="hidden" name="ProductPriceFormatted" value="@product.Price.PriceFormatted">
1112 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")">
1113 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")">
1114 <input type="hidden" name="ProductImage" value="@imagePathHidden">
1115 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost">
1116 <input type="hidden" name="ProductDeliveryName" value="@deliveryName">
1117 <input type="hidden" name="cartpage" value="@cartUrl">
1118
1119 @if (!string.IsNullOrEmpty(product.VariantId))
1120 {
1121 <input type="hidden" name="VariantId" value="@product.VariantId">}
1122
1123 @if (quantitySelector)
1124 {
1125 <div class="input-group input-primary-button-group">
1126 <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control" style="max-width: 100px" type="number" onkeydown="swift.Cart.UpdateOnEnterKey(event)" @disableAddToCart>
1127 <div class="d-flex gap-2">
1128 @RenderViewMore(link, clickProductLink)
1129 @*<a href="@link" class="btn btn-secondary flex-fill" @clickProductLink>@Translate("View more")</a>*@
1130 @if (disableAddToCart == "disabled")
1131 {
1132 <button name="notify-cookie" type="button" data-href="@link" onclick="addCookie('ProductNotifier',true,1,'@link');" class="btn btn-primary flex-fill js-add-to-cart-button" title="@Translate("Add to cart")" id="AddCookie@(product.Id)">
1133 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "envelope.svg") @Translate("Produkt notifikations label")</span>
1134 </button>
1135 }
1136 else
1137 {
1138 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)">
1139 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "shopping-cart.svg") @Translate("Add to cart")</span>
1140 </button>
1141 }
1142 </div>
1143 </div>
1144 if (stepQty != "1")
1145 {
1146 <div class="invalid-feedback d-none">
1147 @Translate("Please select a quantity that is dividable by") @stepQty
1148 </div>
1149
1150 }
1151 <label for="Quantity_@(product.Id)_@product.VariantId" class="visually-hidden">@Translate("Quantity")</label>
1152 <link itemprop="availability" href="@availability" />
1153 }
1154 else
1155 {
1156 <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart>
1157 <div class="d-flex gap-2">
1158 <a href="@link" class="btn btn-secondary flex-fill view-more-btn px-0" @clickProductLink><div class="@hideOnMobile">@ReadFile(viewMoreIcon)<span>@Translate("View more")</span></div></a>
1159 @if (disableAddToCart == "disabled")
1160 {
1161 <button name="notify-cookie" type="button" data-href="@link" class="btn btn-primary flex-fill js-add-to-cart-button" title="@Translate("Produkt notifikations label")" id="AddCookie@(product.Id)">
1162 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center @hideOnMobile">@ReadFile(iconPath + "envelope.svg") <span class="add-to-cart-label"> @Translate("Produkt notifikations label")</span></span>
1163 </button>
1164 }
1165 else
1166 {
1167 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button px-0" title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)">
1168 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center @hideOnMobile">@ReadFile(iconPath + "shopping-cart.svg") <span class="add-to-cart-label">@Translate("Add to cart")</span></span>
1169 @*<span class="icon-2 d-flex gap-2 align-items-center justify-content-center d-md-none">@ReadFile(iconPath + "shopping-cart.svg")</span>*@
1170 </button>
1171 }
1172 </div>
1173
1174 }
1175 </form>
1176 </div>
1177 }
1178 else
1179 {
1180 string buttonWidth = quantitySelector ? "calc(var(--swift-button-primary-padding-x) * 2 + 1rem + 7rem)" : "calc(var(--swift-button-primary-padding-x) * 2 + 1rem)"; @* Set the width of the container to: button-primary-padding-x × 2 + 1rem for the icon + 7rem for the quantity input *@ string buttonText = quantitySelector ? Translate("Select") : "<span class=\"icon-2\">" + @ReadFile(iconPath + "shopping-cart.svg") + "</span>";
1181
1182 string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : "";
1183 variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString();
1184
1185 string disableVariantSelector = isLazyLoadingForProductInfoEnabled ? "disabled" : "";
1186 <div class="px-0 pb-3">
1187 <form action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" class="d-inline-block">
1188 <input type="hidden" name="ProductID" value="@product.Id">
1189 <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()">
1190 <input type="hidden" name="HideInventory" value="@Model.Item.GetBoolean("HideInventory").ToString()">
1191 <input type="hidden" name="HideStockState" value="@Model.Item.GetBoolean("HideStockState").ToString()">
1192 <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId">
1193 <input type="hidden" name="ViewType" value="ModalContent">
1194 <button type="button" onclick="swift.PageUpdater.Update(event)" class="btn btn-primary" style="width: @buttonWidth" @disableVariantSelector @disableVariantSelector title="@Translate("Select")" data-bs-toggle="modal" data-bs-target="#DynamicModal" id="OpenVariantSelectorModal@(product.Id)_@Pageview.CurrentParagraph.ID">@buttonText</button>
1195 </form>
1196 </div>
1197
1198 }
1199 }
1200 @RenderModal(product)
1201
1202 }
1203
1204 @helper RenderProduct(ProductInfoViewModel relatedProduct, string productId)
1205 {
1206 var relProduct = relatedProduct.GetProduct();
1207 var mainProductId = productId.IsNotNullOrEmpty() ? productId : "None";
1208 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
1209 bool anonymousUser = Pageview.User == null;
1210 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable();
1211 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) : false;
1212
1213 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : "";
1214 string ratioCssClass = ratio != "" ? "ratio" : "";
1215 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
1216
1217 string theme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("Theme")) ? Dynamicweb.Context.Current.Request.Form.Get("Theme") : "";
1218 string themePadding = theme != string.Empty ? "p-3" : string.Empty;
1219 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : "";
1220 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty;
1221 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
1222 string ContentPadding = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ContentPadding")) ? Dynamicweb.Context.Current.Request.Form.Get("ContentPadding") : "";
1223
1224 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower();
1225 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat);
1226
1227 string variantIdForLink = !string.IsNullOrEmpty(relProduct.VariantId) ? $"&VariantID={relProduct.VariantId}" : "";
1228 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(relProduct.DefaultVariantId) ? $"&VariantID={relProduct.DefaultVariantId}" : variantIdForLink;
1229
1230 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop");
1231 link += $"&GroupID={relProduct.PrimaryOrDefaultGroup.Id}";
1232 link += $"&ProductID={relProduct.Id}";
1233 link += variantIdForLink;
1234 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link);
1235
1236 string imagePath = relProduct?.DefaultImage.Value.ToString() ?? "";
1237 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70";
1238
1239 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : "";
1240 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : "";
1241 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : "";
1242 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0;
1243 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : "";
1244
1245 var badgeParms = new Dictionary<string, object>();
1246 badgeParms.Add("saleBadgeType", saleBadgeType);
1247 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName);
1248 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName);
1249 badgeParms.Add("campaignBadgesValues", campaignBadgesValues);
1250 badgeParms.Add("newPublicationDays", newPublicationDays);
1251
1252 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false;
1253 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false;
1254 DateTime createdDate = relProduct.Created.Value;
1255 bool showBadges = saleBadgeEnabled && relProduct.Discount.Price != 0 ? true : false;
1256 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges;
1257 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges;
1258
1259 string disableAddToCart = (relProduct.StockLevel <= 0) ? "disabled" : "";
1260 bool isNeverOutOfStock = relProduct.NeverOutOfstock;
1261 disableAddToCart = isNeverOutOfStock ? "" : disableAddToCart;
1262
1263 string iconPath = "/Files/icons/";
1264 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg");
1265 string addToCartLabel = !addToCartIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(addToCartIcon) + "</span>" : "";
1266 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : "";
1267 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? Translate("Add to cart") : "";
1268 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : "";
1269 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular");
1270 string inputSize = string.Empty;
1271
1272 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
1273 if (!url.Contains("LayoutTemplate"))
1274 {
1275 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml";
1276 }
1277 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
1278 string imagePathHidden = relProduct?.DefaultImage.Value.ToString() ?? "";
1279 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70";
1280
1281 switch (buttonSize)
1282 {
1283 case "small":
1284 inputSize = " input-group-sm";
1285 buttonSize = " btn-sm";
1286 break;
1287 case "regular":
1288 buttonSize = string.Empty;
1289 break;
1290 case "large":
1291 inputSize = " input-group-lg";
1292 buttonSize = " btn-lg";
1293 break;
1294 }
1295
1296 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : "";
1297 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0;
1298 var productToShow = Services.Products.GetProductById(relProduct.Id, relProduct.VariantId, relProduct.LanguageId);
1299 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code;
1300 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : "";
1301 var shippingFeeAmount = string.Empty;
1302
1303 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null)
1304 {
1305 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency);
1306 }
1307
1308 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount;
1309 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, relProduct.LanguageId);
1310
1311 <div class="text-decoration-none d-block h-100">
1312 <div class="h-100 d-flex flex-column justify-content-between@(theme)">
1313 @{
1314 FieldValueViewModel plusDesignerModalIndikator;
1315 relProduct.ProductFields.TryGetValue("PlusDesignerLinkEA", out plusDesignerModalIndikator);
1316 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "target=\"_blank\"" : string.Empty;
1317 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "rel=\"noopener\"" : string.Empty;
1318
1319 if (!string.IsNullOrEmpty(plusDesignerModalIndikator.Value.ToString()))
1320 {
1321 <div class="plus-designer__container" style="position: absolute; left: 0; bottom: 10px; background: #fff; border: 1px solid #228B64; border-left: none; z-index: 100; width: 75%; max-width: 200px; ">
1322 <object>
1323 <a href="@plusDesignerModalIndikator.Value" @target @rel class="text-decoration-none">
1324 <div class="plus-designer__inner p-1 ps-3">
1325 <p class="m-0 opacity-75 fs-7">Try me in</p>
1326 <div class="plus-designer__image-container d-flex w-100">
1327 <img class="" src="/Files/Templates/Designs/Swift/Assets/Images/plusdesignerlogo.svg" alt="Alternate Text" style="width:90%" />
1328 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right" style="width:10%"><polyline points="9 18 15 12 9 6"></polyline></svg>
1329 </div>
1330 </div>
1331 </a>
1332 </object>
1333 </div>
1334
1335 }
1336
1337 }
1338 <div class="@(imageTheme)" style="@imageOutlineStyle">
1339 <div class="@(ratioCssClass) position-relative m-auto mw200 efa-mod" style="@ratioVariable">
1340 @if (showBadges)
1341 {
1342 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2">
1343 @{
1344 @RenderPartial("Components/EcommerceBadge.cshtml", relProduct, badgeParms)
1345 }
1346 </div>
1347 }
1348 <img loading="lazy" decoding="async" src="@imagePath" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="@relProduct.Name">
1349 </div>
1350 </div>
1351 <div class="flex-fill p-3 pb-0 d-flex flex-column justify-content-between @themePadding">
1352 <h3 class="h6 opacity-85">@relProduct.Name @relProduct.VariantName</h3>
1353
1354 @if (!hidePrice)
1355 {
1356 <div>
1357 <p class="h6 m-0">
1358 @if (showPricesWithVat == "false" && !neverShowVat)
1359 {
1360 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price)
1361 {
1362 <span class="text-decoration-line-through opacity-75 me-1">
1363 @relProduct.PriceBeforeDiscount.PriceWithoutVatFormatted
1364 </span>
1365 }
1366 }
1367 else
1368 {
1369 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price)
1370 {
1371 <span class="text-decoration-line-through opacity-75 me-1">
1372 @relProduct.PriceBeforeDiscount.PriceFormatted
1373 </span>
1374 }
1375 }
1376
1377 @if (showPricesWithVat == "false" && !neverShowVat)
1378 {
1379 <span class="text-price fw-bold">@relProduct.Price.PriceWithoutVatFormatted</span>
1380 }
1381 else
1382 {
1383 <span class="text-price fw-bold">@relProduct.Price.PriceFormatted</span>
1384 }
1385 </p>
1386 @if (shippingFeeAmount != null)
1387 {
1388 <div class="fs-8 text-black-50 text-start">
1389 <p>@deliveryCost</p>
1390 </div>
1391 }
1392
1393
1394 @if (showPricesWithVat == "false" && !neverShowVat)
1395 {
1396 <small class="opacity-85 fst-normal">@relProduct.Price.PriceWithVatFormatted @Translate("Incl. VAT")</small>
1397 }
1398
1399 </div>
1400 }
1401 </div>
1402
1403 <div class="d-flex gap-4 p-3 pt-0">
1404 @{
1405 string stepQty = relProduct.PurchaseQuantityStep > 1 ? relProduct.PurchaseQuantityStep.ToString() : "1";
1406 string valueQty = relProduct.PurchaseMinimumQuantity > relProduct.PurchaseQuantityStep ? relProduct.PurchaseMinimumQuantity.ToString() : stepQty;
1407
1408 }
1409 <form method="post" action="@url">
1410 <input type="hidden" name="redirect" value="false">
1411 <input type="hidden" name="MainProductId" value="@mainProductId">
1412 <input type="hidden" name="ProductId" value="@relProduct.Id">
1413 <input type="hidden" name="ProductName" value="@relProduct.Name">
1414 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code">
1415 <input type="hidden" name="ProductReferer" value="product_list_listview">
1416 <input type="hidden" name="ProductPrice" value="@relProduct.Price.PriceFormatted">
1417 <input type="hidden" name="cartcmd" value="add">
1418 @* #38 EA: sbj *@
1419 <input type="hidden" name="ProductPriceFormatted" value="@relProduct.Price.PriceFormatted">
1420 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")">
1421 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")">
1422 <input type="hidden" name="ProductImage" value="@imagePathHidden">
1423 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost">
1424 <input type="hidden" name="ProductDeliveryName" value="@deliveryName">
1425 <input type="hidden" name="cartpage" value="@cartUrl">
1426
1427 <input id="Quantity_@(relProduct.Id)_@relProduct.VariantId" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart>
1428 <div class="d-flex gap-3 p-3 pt-0">
1429 <a href="@link" class="btn btn-secondary flex-fill">@Translate("View more")</a>
1430 <a onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) @flexFill flex-fill js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(relProduct.Id)_@Pageview.CurrentParagraph.ID">
1431 @if (!Model.Item.GetBoolean("HideButtonText"))
1432 {
1433 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2">
1434 @addToCartLabel
1435 </span>
1436 }
1437 else
1438 {
1439 @addToCartLabel
1440 }
1441 </a>
1442 </div>
1443 </form>
1444 </div>
1445
1446 </div>
1447 </div>
1448 @*@RenderModalRelatedProducts(relProduct)*@
1449 }
1450
1451 @helper RenderModal(ProductViewModel product)
1452 {
1453 <div id="cartNotificationModal_@(product.Id)" class="modal" tabindex="-1" aria-labelledby="cartNotificationModalTitel" aria-hidden="true">
1454 @{
1455 string modelId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ModelID")) ? Dynamicweb.Context.Current.Request.Form.Get("ModelID") : product.Id;
1456
1457 string scrollBarForceMobile = Dynamicweb.Context.Current.Request.Form.Get("NavigationShowScrollbar") != string.Empty ? "--swiffy-slider-track-height:0.5rem !important;" : string.Empty;
1458 bool hideSliderNavigation = false;
1459
1460 int itemsShown = 3;
1461
1462 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : "";
1463 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty;
1464 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
1465
1466 string imagePath = product?.DefaultImage.Value.ToString() ?? "";
1467 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70";
1468
1469 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : "";
1470 string ratioCssClass = ratio != "" ? "ratio" : "";
1471 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
1472
1473 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : "";
1474 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : "";
1475 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : "";
1476 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0;
1477 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : "";
1478
1479 var badgeParms = new Dictionary<string, object>();
1480 badgeParms.Add("saleBadgeType", saleBadgeType);
1481 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName);
1482 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName);
1483 badgeParms.Add("campaignBadgesValues", campaignBadgesValues);
1484 badgeParms.Add("newPublicationDays", newPublicationDays);
1485
1486 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false;
1487 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false;
1488 DateTime createdDate = product.Created.Value;
1489
1490 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false;
1491 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges;
1492 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges;
1493 var productCounter = 1;
1494
1495 var shopPageId = GetPageIdByNavigationTag("Shop");
1496
1497 bool relatedExist = false;
1498 string modalProductCon = "";
1499 string modalProductSize = "";
1500 foreach (var relatedGroup in product.RelatedGroups)
1501 {
1502 if (relatedGroup.Id == "Addon")
1503 {
1504 if (relatedGroup.Products.Count > 0)
1505 {
1506 relatedExist = true;
1507 modalProductCon = "col-xl-7";
1508 }
1509 }
1510 }
1511 if (relatedExist)
1512 {
1513 modalProductSize = "xl";
1514 }
1515 else
1516 {
1517 modalProductSize = "lg";
1518 }
1519
1520 }
1521 @*<script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script>
1522 <script type="module">
1523 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css');
1524 </script>*@
1525 <div class="modal-dialog theme plus-primary modal-@modalProductSize">
1526 <div class="modal-content rounded-0 container">
1527 <div class="row">
1528 <div class="col-12 @modalProductCon p-0 border-bottom border-lg-none">
1529 <div class="modal-header w-100 text-center">
1530 <h3 class="modal-title w-100" id="cartNotificationModalTitel">@Translate("Følgende er tilføjet til indkøbslisten", "Følgende er tilføjet til indkøbslisten")</h3>
1531 <button type="button" class="btn-close position-absolute m-0 end-0 me-3 d-block d-xl-none" data-bs-dismiss="modal" aria-label="Close"></button>
1532 </div>
1533 <div id="cartNotificationModalBody" class="modal-body">
1534 <div class="container-fluid">
1535 <div class="row">
1536 <div class="@(imageTheme) col-12" style="@imageOutlineStyle">
1537 <div class="@(ratioCssClass) col-4 col-xl-6 m-auto position-relative" style="@ratioVariable">
1538 @if (showBadges)
1539 {
1540 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2">
1541 @{
1542 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)
1543 }
1544 </div>
1545 }
1546 <img loading="lazy" decoding="async" src="@imagePath" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="@product.Name" id="cartNotificationModal_Image">
1547 </div>
1548 </div>
1549 <div class="d-flex flex-wrap py-3 col-12 gap-md-3">
1550 <div class="col-12 col-md-auto info">
1551 <div class="fc-dark efa-mod"><strong id="cartNotificationModal_Name"></strong></div>
1552 <div class="fs-5 fw-bold fc-dark efa-mod" id="cartNotificationModal_Price"></div>
1553 <div class="fs-8 fc-grey efa-mod" id="cartNotificationModal_DeliveryCost"></div>
1554 <div class="fs-8 fc-grey efa-mod" id="cartNotificationModal_DeliveryName"></div>
1555 <div id="cartNotificationModal_Quantity"></div>
1556 </div>
1557 <div class="col-12 col-lg action align-self-end">
1558 <div class="col-12 d-flex mt-3">
1559 <a id="cartNotificationModal_Button" class="btn btn-primary flex-fill" href="">@Translate("Gå til indkøbslisten", "Gå til indkøbslisten")</a>
1560 </div>
1561 </div>
1562 </div>
1563 </div>
1564 </div>
1565 </div>
1566 </div>
1567 @if (relatedExist)
1568 {
1569 <div class="col-12 col-xl-5 p-0">
1570 <div class="modal-header w-100 text-center">
1571 <h3 class="modal-title text-center w-100">@Translate("Add-on products", "Tilbehør")</h3>
1572 <button type="button" class="btn-close position-absolute m-0 end-0 me-3 d-none d-xl-block" data-bs-dismiss="modal" aria-label="Close"></button>
1573 </div>
1574 <div class="modal-body">
1575 <div id="addonsList_@product.Id" class="list row gy-3 overflow-auto">
1576 @{
1577 foreach (RelatedGroupViewModel relatedGroup in product.RelatedGroups.Where(x => x.Id == "Addon"))
1578 {
1579 foreach (ProductInfoViewModel relatedProduct in relatedGroup.Products)
1580 {
1581 <div class="elements col-6 col-xl-12 align-self-end">
1582 @RenderAddons(relatedProduct, productCounter, product.Id)
1583 </div>
1584 productCounter += 1;
1585 }
1586 }
1587
1588 }
1589 </div>
1590 </div>
1591 </div>
1592 }
1593 </div>
1594 </div>
1595 </div>
1596 </div>
1597 }
1598
1599 @helper RenderAddons(ProductInfoViewModel relatedProduct, int productCounter, string productID)
1600 {
1601 var relProduct = relatedProduct.GetProduct();
1602 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
1603 bool anonymousUser = Pageview.User == null;
1604 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable();
1605 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) : false;
1606
1607 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : "";
1608 string ratioCssClass = ratio != "" ? "ratio" : "";
1609 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
1610
1611 string theme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("Theme")) ? Dynamicweb.Context.Current.Request.Form.Get("Theme") : "";
1612 string themePadding = theme != string.Empty ? "p-3" : string.Empty;
1613 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : "";
1614 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty;
1615 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
1616 string ContentPadding = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ContentPadding")) ? Dynamicweb.Context.Current.Request.Form.Get("ContentPadding") : "";
1617
1618 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower();
1619 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat);
1620
1621 string variantIdForLink = !string.IsNullOrEmpty(relProduct.VariantId) ? $"&VariantID={relProduct.VariantId}" : "";
1622 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(relProduct.DefaultVariantId) ? $"&VariantID={relProduct.DefaultVariantId}" : variantIdForLink;
1623
1624 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop");
1625 link += $"&GroupID={relProduct.PrimaryOrDefaultGroup.Id}";
1626 link += $"&ProductID={relProduct.Id}";
1627 link += variantIdForLink;
1628 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link);
1629
1630 string imagePath = relProduct?.DefaultImage.Value.ToString() ?? "";
1631 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70";
1632
1633 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : "";
1634 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : "";
1635 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : "";
1636 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0;
1637 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : "";
1638
1639 var badgeParms = new Dictionary<string, object>();
1640 badgeParms.Add("saleBadgeType", saleBadgeType);
1641 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName);
1642 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName);
1643 badgeParms.Add("campaignBadgesValues", campaignBadgesValues);
1644 badgeParms.Add("newPublicationDays", newPublicationDays);
1645
1646 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false;
1647 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false;
1648 DateTime createdDate = relProduct.Created.Value;
1649 bool showBadges = saleBadgeEnabled && relProduct.Discount.Price != 0 ? true : false;
1650 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges;
1651 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges;
1652
1653 string disableAddToCart = (relProduct.StockLevel <= 0) ? "disabled" : "";
1654 bool isNeverOutOfStock = relProduct.NeverOutOfstock;
1655 disableAddToCart = isNeverOutOfStock ? "" : disableAddToCart;
1656
1657 string iconPath = "/Files/icons/";
1658 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg");
1659 string addToCartLabel = !addToCartIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(addToCartIcon) + "</span>" : "";
1660 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : "";
1661 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? Translate("Add to cart") : "";
1662 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : "";
1663 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular");
1664 string inputSize = string.Empty;
1665 string viewMoreIcon = Model.Item.GetRawValueString("ViewMoreIcon");
1666
1667 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
1668 if (!url.Contains("LayoutTemplate"))
1669 {
1670 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml";
1671 }
1672 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
1673 string imagePathHidden = relProduct?.DefaultImage.Value.ToString() ?? "";
1674 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70";
1675
1676 switch (buttonSize)
1677 {
1678 case "small":
1679 inputSize = " input-group-sm";
1680 buttonSize = " btn-sm";
1681 break;
1682 case "regular":
1683 buttonSize = string.Empty;
1684 break;
1685 case "large":
1686 inputSize = " input-group-lg";
1687 buttonSize = " btn-lg";
1688 break;
1689 }
1690
1691 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : "";
1692 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0;
1693 var productToShow = Services.Products.GetProductById(relProduct.Id, relProduct.VariantId, relProduct.LanguageId);
1694 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code;
1695 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : "";
1696 var shippingFeeAmount = string.Empty;
1697
1698 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null)
1699 {
1700 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency);
1701 }
1702
1703 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount;
1704 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, relProduct.LanguageId);
1705 string stepQty = relProduct.PurchaseQuantityStep > 1 ? relProduct.PurchaseQuantityStep.ToString() : "1";
1706 string valueQty = relProduct.PurchaseMinimumQuantity > relProduct.PurchaseQuantityStep ? relProduct.PurchaseMinimumQuantity.ToString() : stepQty;
1707
1708 relProduct.ProductFields.TryGetValue("ProductNextBackorderDate", out FieldValueViewModel backInStockDateValue);
1709 DateTime backInstockDate = backInStockDateValue.Value != null ? DateTime.Parse(backInStockDateValue.Value.ToString()) : DateTime.Today.AddDays(-1);
1710 bool hasBackInstockDate = backInStockDateValue != null && backInstockDate >= DateTime.Today;
1711 bool hasExpectedDelivery = relProduct.ExpectedDelivery != null && relProduct.ExpectedDelivery >= DateTime.Today;
1712 string expectedDeliveryDate = hasBackInstockDate ? backInstockDate.ToShortDateString() : relProduct.ExpectedDelivery?.ToShortDateString() ?? "";
1713
1714 <div class="element w-100 d-flex">
1715
1716 <div class="w-100 row m-0 align-items-end">
1717 <div class="product-info col ps-lg-0">
1718 <div class="col-6 col-xl-3 m-auto m-xl-0 image-container pe-2">
1719 <img class="h-100 w-100" src="@imagePath" alt="" style="object-fit: cover;" />
1720 </div>
1721 <div>
1722 <div><strong>@relProduct.Name</strong></div>
1723 @if (!hidePrice)
1724 {
1725 <div>
1726 <p class="h6 m-0">
1727 @if (showPricesWithVat == "false" && !neverShowVat)
1728 {
1729 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price)
1730 {
1731 <span class="text-decoration-line-through opacity-75 me-1">
1732 @relProduct.PriceBeforeDiscount.PriceWithoutVatFormatted
1733 </span>
1734 }
1735 }
1736 else
1737 {
1738 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price)
1739 {
1740 <span class="text-decoration-line-through opacity-75 me-1">
1741 @relProduct.PriceBeforeDiscount.PriceFormatted
1742 </span>
1743 }
1744 }
1745
1746 @if (showPricesWithVat == "false" && !neverShowVat)
1747 {
1748 <span class="text-price fw-bold">@relProduct.Price.PriceWithoutVatFormatted</span>
1749 }
1750 else
1751 {
1752 <span class="text-price fw-bold">@relProduct.Price.PriceFormatted</span>
1753 }
1754 </p>
1755
1756 <div class="fs-8 mt-1 mb-2 text-start">
1757 @if (!string.IsNullOrWhiteSpace(relProduct.StockStatus))
1758 {
1759 <p class="mb-1">@relProduct.StockStatus</p>
1760 if (!string.IsNullOrWhiteSpace(relProduct.StockDeliveryText))
1761 {
1762 <p class="mb-1">@relProduct.StockDeliveryText</p>
1763 }
1764 }
1765 @if (hasExpectedDelivery || hasBackInstockDate)
1766 {
1767 <div class="mb-1">
1768 <span>@Translate("Expected in stock"): </span>
1769 <span>@expectedDeliveryDate</span>
1770 </div>
1771 }
1772 </div>
1773
1774 @*<div class="fs-8 text-black-50 text-start">
1775 <p class="m-0">@Translate("Levering fra") @currency @shippingFeeAmount</p>
1776 </div>*@
1777 @if (showPricesWithVat == "false" && !neverShowVat)
1778 {
1779 <small class="opacity-85 fst-normal">@relProduct.Price.PriceWithVatFormatted @Translate("Incl. VAT")</small>
1780 }
1781
1782 </div>
1783 }
1784 </div>
1785 </div>
1786 <div class="action col-auto px-0 ps-lg-0 w-100 w-lg-auto">
1787 <form method="post" action="@url">
1788 <input type="hidden" name="redirect" value="false">
1789 <input type="hidden" name="ProductId" value="@relProduct.Id">
1790 <input type="hidden" name="ProductName" value="@relProduct.Name">
1791 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code">
1792 <input type="hidden" name="ProductReferer" value="product_list_listview">
1793 <input type="hidden" name="ProductPrice" value="@relProduct.Price.PriceFormatted">
1794 <input type="hidden" name="cartcmd" value="add">
1795 @* #38 EA: sbj *@
1796 <input type="hidden" name="ProductPriceFormatted" value="@relProduct.Price.PriceFormatted">
1797 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")">
1798 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")">
1799 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost">
1800 <input type="hidden" name="ProductDeliveryName" value="@deliveryName">
1801 <input type="hidden" name="ProductImage" value="@imagePathHidden">
1802 <input type="hidden" name="cartpage" value="@cartUrl">
1803 @if (relProduct.StockLevel > 0)
1804 {
1805 <div class="d-flex flex-nowrap w-100 mb-2 bg-light efa-mod" style="height:40px;">
1806
1807 <div class="d-flex align-items-center justify-content-center btn border-0" id="minus-btn" onclick="quantityBtn('Quantity_@(productID)_@(relProduct.Id)_@relProduct.VariantId', 'minus')" style="width:calc(100%/3);">
1808 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dash-lg" viewBox="0 0 16 16">
1809 <path fill-rule="evenodd" d="M2 8a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11A.5.5 0 0 1 2 8Z" />
1810 </svg>
1811 </div>
1812 <div class="d-flex align-items-center justify-content-center" style="width:calc(100%/3);">
1813 <input id="Quantity_@(productID)_@(relProduct.Id)_@relProduct.VariantId" name="Quantity" value="1" step="1" min="1" class="form-control p-0 bg-light border-0 fc-green quantity_selector efa-mod" style="max-width:40px;" type="number" onkeydown="swift.Cart.UpdateOnEnterKey(event)" @disableAddToCart>
1814 </div>
1815 <div class="d-flex align-items-center justify-content-center btn border-0" id="plus-btn" onclick="quantityBtn('Quantity_@(productID)_@(relProduct.Id)_@relProduct.VariantId', 'plus')" style="width:calc(100%/3);">
1816 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg" viewBox="0 0 16 16">
1817 <path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2Z" />
1818 </svg>
1819 </div>
1820 <script type="text/javascript">
1821 function quantityBtn(id, symbol) {
1822 console.log(document.getElementById(id));
1823 var idValue = document.getElementById(id).value;
1824 var value = parseInt(idValue, 10);
1825 var step = 1;
1826 value = isNaN(value) ? 0 : value;
1827 switch (symbol) {
1828 case "minus":
1829 value = value - step;
1830 break;
1831 case "plus":
1832 value = value + step;
1833 break;
1834 }
1835 console.log(value);
1836 if (value > 0) {
1837 document.getElementById(id).value = value;
1838 }
1839 //UpdateQuantity();
1840 }
1841 </script>
1842
1843 </div>
1844 <a onclick="swift.Cart.Update(event)" data-container="productRel" class="btn btn-primary @(buttonSize) @flexFill w-100 w-lg-auto flex-fill js-add-to-cart-button addToCart" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(relProduct.Id)_@Pageview.CurrentParagraph.ID">
1845 @if (!Model.Item.GetBoolean("HideButtonText"))
1846 {
1847 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2">
1848 @addToCartLabel
1849 </span>
1850 }
1851 else
1852 {
1853 @addToCartLabel
1854 }
1855 </a>
1856 }
1857 else
1858 {
1859 <div class="w-100 efa-mod" style="height:40px;">
1860 @*<button name="notify-cookie" data-href="@link" type="button" class="btn btn-primary flex-fill h-100 js-add-to-cart-button" title="@Translate("Produkt notifikations label")" id="AddCookie@(relProduct.Id)">
1861 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "envelope.svg")</span>
1862 </button>*@
1863 <a href="@link" class="btn btn-secondary flex-fill view-more-btn w-100"><div class="">@ReadFile(viewMoreIcon)<span>@Translate("View more")</span></div></a>
1864 </div>
1865 }
1866 </form>
1867 </div>
1868 </div>
1869 </div>
1870 }
1871
1872 @*@helper RenderModalRelatedProducts(ProductViewModel product)
1873 {
1874 <div id="cartNotificationModal_@(product.Id)" class="modal" tabindex="-1" aria-labelledby="cartNotificationModalTitel" aria-hidden="true">
1875 @{
1876 string modelId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ModelID")) ? Dynamicweb.Context.Current.Request.Form.Get("ModelID") : product.Id;
1877
1878 string scrollBarForceMobile = Dynamicweb.Context.Current.Request.Form.Get("NavigationShowScrollbar") != string.Empty ? "--swiffy-slider-track-height:0.5rem !important;" : string.Empty;
1879 bool hideSliderNavigation = false;
1880
1881 int itemsShown = 3;
1882
1883 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : "";
1884 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty;
1885 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
1886
1887 string imagePath = product?.DefaultImage.Value.ToString() ?? "";
1888 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70";
1889
1890 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : "";
1891 string ratioCssClass = ratio != "" ? "ratio" : "";
1892 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
1893
1894 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : "";
1895 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : "";
1896 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : "";
1897 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0;
1898 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : "";
1899
1900 var badgeParms = new Dictionary<string, object>();
1901 badgeParms.Add("saleBadgeType", saleBadgeType);
1902 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName);
1903 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName);
1904 badgeParms.Add("campaignBadgesValues", campaignBadgesValues);
1905 badgeParms.Add("newPublicationDays", newPublicationDays);
1906
1907 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false;
1908 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false;
1909 DateTime createdDate = product.Created.Value;
1910
1911 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false;
1912 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges;
1913 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges;
1914
1915 var shopPageId = GetPageIdByNavigationTag("Shop");
1916
1917 bool relatedExist = false;
1918 foreach (var relatedGroup in product.RelatedGroups)
1919 {
1920 if (relatedGroup.Id == "Addon")
1921 {
1922 if (relatedGroup.Products.Count > 0)
1923 {
1924 relatedExist = true;
1925 }
1926 }
1927 }
1928
1929 }
1930 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script>
1931 <script type="module">
1932 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css');
1933 </script>
1934 <div class="modal-dialog theme plus-primary modal-xl">
1935 <div class="modal-content rounded-0">
1936 <div class="modal-header w-100 text-center">
1937 <h3 class="modal-title w-100" id="cartNotificationModalTitel">@Translate("Følgende er tilføjet til indkøbslisten", "Følgende er tilføjet til indkøbslisten")</h3>
1938 <button type="button" class="btn-close position-absolute m-0 end-0 me-3" data-bs-dismiss="modal" aria-label="Close"></button>
1939 </div>
1940 <div id="cartNotificationModalBody" class="modal-body">
1941 <div class="container-fluid">
1942 <div class="row">
1943 <div class="col-12 col-md-6 col-lg-3 m-auto me-lg-0 @(imageTheme)" style="@imageOutlineStyle">
1944 <div class="@(ratioCssClass) position-relative" style="@ratioVariable">
1945 @if (showBadges)
1946 {
1947 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2">
1948 @{
1949 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)
1950 }
1951 </div>
1952 }
1953 <img loading="lazy" decoding="async" src="" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="" id="cartNotificationModal_Image">
1954 </div>
1955 </div>
1956 <div class="col-12 col-md-6 col-lg-4 m-auto ms-lg-0 d-flex align-content-between flex-wrap">
1957 <div class="col-12 ms-auto fc-dark efa-mod"><strong id="cartNotificationModal_Name"></strong></div>
1958 <div class="col-12 ms-auto">
1959 <div class="col-12 ms-auto fs-5 fw-bold fc-dark efa-mod" id="cartNotificationModal_Price"></div>
1960 <div class="fs-8 fc-grey efa-mod text-start">
1961 <p class="" id="cartNotificationModal_DeliveryCost"></p>
1962 </div>
1963
1964 <div class="col-12 d-flex flex-wrap mb-3 ms-auto" id="cartNotificationModal_Quantity"></div>
1965 <div class="col-12 d-flex mb-2">
1966 <a id="cartNotificationModal_Button" class="btn btn-primary flex-fill" href="">@Translate("Gå til indkøbslisten", "Gå til indkøbslisten")</a>
1967 </div>
1968 <div class="col-12 d-flex">
1969 <div class="btn btn-secondary col-12" data-bs-dismiss="modal">@Translate("Continue shopping")</div>
1970 </div>
1971 </div>
1972 </div>
1973 </div>
1974 </div>
1975 </div>
1976
1977 @if (relatedExist)
1978 {
1979 <div class="modal-footer d-none d-md-block d-lg-block d-xl-block" id="cartNotificationModal_Related">
1980 <div class="modal-title w-100 text-center">
1981 <h3>@Translate("Add-on products", "Tilbehør")</h3>
1982 </div>
1983 <div class="container-fluid">
1984 <div class="row">
1985 <div class="col-12 m-auto">
1986 <div id="slider_@(modelId)" class="swiffy-slider slider-item-show@(itemsShown) slider-nav-visible" style="--swiffy-slider-nav-light:var(--swift-foreground-color); --swiffy-slider-nav-dark:var(--swift-background-color); @(scrollBarForceMobile)">
1987 <ul class="slider-container">
1988 @{
1989 foreach (RelatedGroupViewModel relatedGroup in product.RelatedGroups.Where(x => x.Id == "Addon"))
1990 {
1991 foreach (ProductInfoViewModel relatedProduct in relatedGroup.Products)
1992 {
1993 <li>
1994 @RenderProduct(relatedProduct, product.Id)
1995 </li>
1996 }
1997 }
1998 }
1999
2000 </ul>
2001 <button type="button" title="@Translate("Previous slide")" class="slider-nav" style="z-index: 2;">
2002 <span class="visually-hidden">@Translate("Previous slide")</span>
2003 </button>
2004 <button type="button" title="@Translate("Next slide")" class="slider-nav slider-nav-next" style="z-index: 2;">
2005 <span class="visually-hidden">@Translate("Next slide")</span>
2006 </button>
2007
2008 </div>
2009 <script type="module">
2010 swiffyslider.initSlider(document.querySelector('#slider_@(modelId)'));
2011 </script>
2012 </div>
2013 </div>
2014 </div>
2015 </div>
2016 }
2017 </div>
2018 </div>
2019 </div>
2020 }*@
2021
2022 <script type="text/javascript">
2023
2024 var notifyCookieBtn = document.getElementsByName("notify-cookie");
2025 if (notifyCookieBtn) {
2026 for (let i = 0; i < notifyCookieBtn.length; i++) {
2027 notifyCookieBtn[i].addEventListener("click", function (event) {
2028 let link = notifyCookieBtn[i].getAttribute("data-href"),
2029 cName = "ProductNotifier",
2030 cValue = true,
2031 date = new Date(),
2032 expDays = 1;
2033 date.setTime(date.getTime() + (expDays * 24 * 60 * 60 * 1000));
2034 const expires = "expires=" + date.toUTCString();
2035 document.cookie = cName + "=" + cValue + "; " + expires + "; path=/";
2036
2037 window.location.href = link;
2038 });
2039 }
2040 }
2041
2042 document.addEventListener("update.swift.cart", function (event) {
2043 var data = Object.fromEntries(event.detail.formData.entries());
2044 var mainProductId = data.MainProductId;
2045
2046 if (mainProductId != "None") {
2047 var openqs = '#cartNotificationModal_' + mainProductId;
2048
2049 var prodId = data.ProductId;
2050
2051 var price = data.ProductPrice;
2052 if (price.includes(",")) {
2053 price = price.replace(/,/g, ".");
2054 price = parseFloat(price).toFixed(2);
2055 }
2056
2057 var quantity = data.Quantity;
2058 var detailsPrice = data.ProductPrice;
2059 var totalprice = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");;
2060 if (quantity > 1) {
2061 var total = price * quantity;
2062 var totalpriceCalc = parseFloat(total).toFixed(2);
2063 totalpriceCalc = totalpriceCalc.toString();
2064 if (totalpriceCalc.includes(".")) {
2065 totalprice = totalpriceCalc.replace(".", ",");
2066 }
2067 else {
2068 totalprice = totalpriceCalc.concat("", ",00");
2069 }
2070 }
2071
2072 var details = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");
2073
2074 document.querySelector(openqs).querySelector("#cartNotificationModal_Button").href = data.cartpage;
2075 document.querySelector(openqs).querySelector("#cartNotificationModal_Quantity").innerHTML = data.Quantity + " " + data.ProductUnit;
2076 document.querySelector(openqs).querySelector("#cartNotificationModal_Name").innerHTML = data.ProductName;
2077 document.querySelector(openqs).querySelector("#cartNotificationModal_Price").innerHTML = details;
2078 document.querySelector(openqs).querySelector("#cartNotificationModal_Image").src = data.ProductImage;
2079 document.querySelector(openqs).querySelector("#cartNotificationModal_Image").alt = data.ProductName;
2080 document.querySelector(openqs).querySelector("#cartNotificationModal_DeliveryCost").innerHTML = data.ProductDeliveryCost;
2081 document.querySelector(openqs).querySelector("#cartNotificationModal_DeliveryName").innerHTML = data.ProductDeliveryName;
2082 document.querySelector(openqs).querySelector("#cartNotificationModal_Related").classList.remove('d-md-block', 'd-lg-block', 'd-xl-block');
2083 }
2084 else {
2085 var prodId = data.ProductId;
2086 mainProductId = prodId;
2087
2088 var price = data.ProductPrice;
2089 if (price.includes(",")) {
2090 price = price.replace(/,/g, ".");
2091 price = parseFloat(price).toFixed(2);
2092 }
2093
2094 var quantity = data.Quantity;
2095 var detailsPrice = data.ProductPrice;
2096 var totalprice = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");;
2097 if (quantity > 1) {
2098 var total = price * quantity;
2099 var totalpriceCalc = parseFloat(total).toFixed(2);
2100 totalpriceCalc = totalpriceCalc.toString();
2101 if (totalpriceCalc.includes(".")) {
2102 totalprice = totalpriceCalc.replace(".", ",");
2103 }
2104 else {
2105 totalprice = totalpriceCalc.concat("", ",00");
2106 }
2107 }
2108
2109 var qs = "#cartNotificationModal_" + prodId;
2110 const myModalAlternative = new bootstrap.Modal(qs)
2111
2112 var details = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");
2113
2114 document.querySelector(qs).querySelector("#cartNotificationModal_Button").href = data.cartpage;
2115 document.querySelector(qs).querySelector("#cartNotificationModal_Quantity").innerHTML = data.Quantity + " " + data.ProductUnit;
2116 document.querySelector(qs).querySelector("#cartNotificationModal_Name").innerHTML = data.ProductName;
2117 document.querySelector(qs).querySelector("#cartNotificationModal_Price").innerHTML = details;
2118 document.querySelector(qs).querySelector("#cartNotificationModal_Image").src = data.ProductImage;
2119 document.querySelector(qs).querySelector("#cartNotificationModal_Image").alt = data.ProductName;
2120 document.querySelector(qs).querySelector("#cartNotificationModal_DeliveryCost").innerHTML = data.ProductDeliveryCost;
2121 document.querySelector(qs).querySelector("#cartNotificationModal_DeliveryName").innerHTML = data.ProductDeliveryName;
2122 if (document.querySelector(qs).querySelector("#cartNotificationModal_Related")) {
2123 if (!document.querySelector(qs).querySelector("#cartNotificationModal_Related").classList.contains('d-md-block', 'd-lg-block', 'd-xl-block')) {
2124 document.querySelector(qs).querySelector("#cartNotificationModal_Related").classList.add('d-md-block', 'd-lg-block', 'd-xl-block');
2125 }
2126 }
2127
2128 myModalAlternative.show();
2129 }
2130 let addonsListHeight = 0;
2131 console.log(mainProductId);
2132 let addonList = document.querySelector("#addonsList_" + mainProductId);
2133 console.log(addonList);
2134 let addonListElems = addonList.children;
2135 console.log(addonListElems);
2136 if (addonListElems.length > 3) {
2137 for (var i = 0; 3 > i; i++) {
2138 console.log(addonListElems[i].clientHeight);
2139 addonsListHeight = addonsListHeight + addonListElems[i].clientHeight + 16;
2140 }
2141 addonList.style.maxHeight = addonsListHeight + "px";
2142 }
2143
2144 });
2145
2146 </script>
2147 @* #38 EA: end *@
2148
2149
2150 <script>
2151 dataLayer = window.dataLayer || [];
2152 dataLayer.push({ ecommerce: null });
2153 dataLayer.push({
2154 event: 'view_item_list',
2155 ecommerce: {
2156 currency: '@Dynamicweb.Ecommerce.Common.Context.Currency.Code',
2157 ecomm_pagetype: 'category',
2158 items: [
2159 @foreach (ProductViewModel product in productList.Products)
2160 {
2161 <text>{
2162 item_name: '@product.Name',
2163 item_id: '@product.Number',
2164 price: @product.Price.Price.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture),
2165 quantity: 1,
2166 item_category: '@product.PrimaryOrDefaultGroup.Name',
2167 item_brand: 'PLUS',
2168 item_variant: ''
2169 },</text>
2170 }
2171 ]
2172 },
2173 user_data: {
2174 first_name: '@(string.IsNullOrEmpty(CartId) ? "" : order?.DeliveryName)',
2175 last_name: '',
2176 email_address: '@(string.IsNullOrEmpty(CartId) ? "" : order?.CustomerEmail)',
2177 phone_number: '@(string.IsNullOrEmpty(CartId) ? "" : order?.CustomerPhone)',
2178 }
2179 });
2180 </script>
2181