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

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