Komponent editor til: EFA Combi group and product list - 2024

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