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_b377c3e001e0492bbaee6f09a5c2e7cb.Execute() i C:\inetpub\wwwroot\plus-prod\Files\Templates\Designs\Swift\Paragraph\Swift_ProductListGridView_EA.cshtml:linje 2054
   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 ? "px-0 py-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="@themePadding 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 ? "px-0 py-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 px-0 py-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-0 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 var extraBadgeTitle = extraBadge.Value; 489 490 switch (product.LanguageId) 491 { 492 case "LANG3": 493 { 494 if (badgeTitle.StartsWith(extraBadgeTitle) && badgeTitle.EndsWith("_DE")) 495 { 496 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString()) 497 } 498 break; 499 } 500 case "LANG4": 501 { 502 if (badgeTitle.StartsWith(extraBadgeTitle) && badgeTitle.EndsWith("_SE")) 503 { 504 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString()) 505 } 506 break; 507 } 508 case "LANG5": 509 { 510 if (badgeTitle.StartsWith(extraBadgeTitle) && badgeTitle.EndsWith("_NO")) 511 { 512 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString()) 513 } 514 break; 515 } 516 default: 517 { 518 if (badgeTitle.StartsWith(extraBadgeTitle) && !badgeTitle.EndsWith("_NO") && !badgeTitle.EndsWith("_SE") && !badgeTitle.EndsWith("_DE")) 519 { 520 @RenderExtraBadge(System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image")), extraBadge.Value.ToString()) 521 } 522 break; 523 } 524 } 525 } 526 } 527 } 528 } 529 530 } 531 @if (string.IsNullOrEmpty(alternativeImage)) 532 { 533 <img id="@imageId" itemprop="image" 534 srcset=" 535 @imagePathXs 480w, 536 @imagePathS 640w" 537 sizes="(min-width: 992px) 33vw, 50vw" 538 src="@imagePathFallBack" 539 loading="lazy" 540 decoding="async" 541 class="w-75 mw-100 mh-100 @imageThemePadding" 542 alt="@product.Name"> 543 } 544 else 545 { 546 <img id="@imageId" itemprop="image" 547 src="@imagePathFallBack" 548 loading="lazy" 549 decoding="async" 550 class="w-75 mw-100 mh-100 @imageThemePadding" 551 alt="@product.Name" 552 onmouseover="this.src='@alternativeImage'" 553 onmouseout="this.src='@imagePathFallBack'"> 554 555 } 556 </div> 557 558 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "images") 559 { 560 int variantGroupCount = 0; 561 int showMaxVariantGroups = 2; 562 int showMaxVariants = 3; 563 var productVariantTheme = productTheme != "" ? productTheme : "bg-white"; 564 565 <div class="position-relative"> 566 <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;"> 567 568 @foreach (var variantGroup in product.VariantGroups()) 569 { 570 int variantsCount = 0; 571 572 <div class="d-flex gap-2 mb-2"> 573 @foreach (var variant in variantGroup.Options) 574 { 575 if (variantGroupCount < showMaxVariantGroups) 576 { 577 var optionsCount = variantGroup.Options.Count(); 578 579 if (variantsCount < showMaxVariants) 580 { 581 string optionWidth = !string.IsNullOrEmpty(variant.Color) ? "w-25" : ""; 582 583 <article class="static-variants-option @optionWidth @(productVariantTheme)" title="@product.Name @variant.Name" style="pointer-events: initial;"> 584 @if (!string.IsNullOrEmpty(variant.Color)) 585 { 586 string defaultProductImage = Dynamicweb.Context.Current.Server.UrlEncode(product.DefaultImage.Value); 587 string variantImage = Dynamicweb.Context.Current.Server.UrlEncode(variant.Image.Value); 588 string defaultPrice = !hidePrice ? product.Price.PriceFormatted : "0"; 589 string variantPrice = !hidePrice ? product.Price.PriceFormatted : "0"; 590 591 if (isLazyLoadingForProductInfoEnabled) 592 { 593 <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')"> 594 <div class="d-flex align-items-center justify-content-center"> 595 <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"> 596 </div> 597 </figure> 598 } 599 else 600 { 601 <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')"> 602 <div class="d-flex align-items-center justify-content-center"> 603 <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"> 604 </div> 605 </figure> 606 } 607 } 608 else 609 { 610 <div class="d-flex align-items-center justify-content-center"> 611 @variant.Name 612 </div> 613 } 614 <div class="visually-hidden"> 615 <h4>@product.Name, @variant.Name</h4> 616 @if (!hidePrice) 617 { 618 if (isLazyLoadingForProductInfoEnabled) 619 { 620 <span class="text-price js-text-price"></span> 621 } 622 else 623 { 624 <span class="text-price">@product.Price.PriceFormatted</span> 625 626 } 627 } 628 </div> 629 </article> 630 } 631 632 variantsCount++; 633 634 if (variantsCount == showMaxVariants && optionsCount != showMaxVariants) 635 { 636 int left = optionsCount - showMaxVariants; 637 <div class="variant-option ms-1 d-flex justify-content-center align-items-center"> 638 <span>+@left</span> 639 </div> 640 } 641 } 642 } 643 </div> 644 variantGroupCount++; 645 } 646 </div> 647 </div> 648 } 649 </div> 650 </a> 651 @if (!hidePrice) 652 { 653 string priceMin = ""; 654 string priceMax = ""; 655 <div itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 656 <div class="px-0"> 657 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 658 659 @if (showPricesWithVat == "false" && !neverShowVat) 660 { 661 if (isLazyLoadingForProductInfoEnabled) 662 { 663 <span itemprop="price" content="" class="d-none"></span> 664 <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> } 665 else 666 { 667 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted; 668 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 669 if (product.Price.Price != product.PriceBeforeDiscount.Price) 670 { 671 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span> } 672 } 673 674 } 675 else 676 { 677 678 if (isLazyLoadingForProductInfoEnabled) 679 { 680 <span itemprop="price" content="" class="d-none"></span> 681 <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> 682 } 683 else 684 { 685 string beforePrice = product.PriceBeforeDiscount.PriceFormatted; 686 string googlepriceraw = product.Price.PriceWithVat.ToString(); 687 string googlepriceformatted = googlepriceraw.Replace(',', '.'); 688 689 <span itemprop="price" content="@googlepriceformatted" class="d-none"></span> 690 if (product.Price.Price != product.PriceBeforeDiscount.Price) 691 { 692 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span> 693 694 } 695 } 696 } 697 698 @if (showPricesWithVat == "false" && !neverShowVat) 699 { 700 if (isLazyLoadingForProductInfoEnabled) 701 { 702 <span class="text-price js-text-price"><span class="spinner-border" role="status"></span></span> } 703 else 704 { 705 string price = product.Price.PriceWithoutVatFormatted; 706 if (product?.VariantInfo?.VariantInfo != null) 707 { 708 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 709 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 710 } 711 if (priceMin != priceMax) 712 { 713 price = priceMin + " - " + priceMax; 714 } 715 <span class="text-price">@price</span> 716 } 717 } 718 else 719 { 720 if (isLazyLoadingForProductInfoEnabled) 721 { 722 <span class="text-price js-text-price"><span class="spinner-border" role="status"></span></span> 723 } 724 else 725 { 726 string price = product.Price.PriceFormatted; 727 var priceStandardFieldValueConverted = CampaignPrice.GetConvertedPriceWithVat(product); 728 bool campaignPrice = CampaignPrice.CheckCampaign(product, priceStandardFieldValueConverted); 729 var priceBefore = CampaignPrice.PriceBeforeWithVat(product, priceStandardFieldValueConverted); 730 731 if (product?.VariantInfo?.VariantInfo != null) 732 { 733 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 734 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 735 } 736 if (priceMin != priceMax) 737 { 738 price = priceMin + " - " + priceMax; 739 } 740 741 <div class="d-flex flex-wrap"> 742 @if (campaignPrice) 743 { 744 <span class="text-decoration-line-through opacity-75 me-3 text-price w-100">@priceBefore</span> 745 } 746 <span class="text-price fw-bold w-100">@price</span> 747 </div> 748 749 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : ""; 750 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0; 751 var productToShow = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId); 752 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code; 753 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : ""; 754 product.ProductFields.TryGetValue("ProductNextBackorderDate", out FieldValueViewModel backInStockDateValue); 755 DateTime backInstockDate = backInStockDateValue.Value != null ? DateTime.Parse(backInStockDateValue.Value.ToString()) : DateTime.Today.AddDays(-1); 756 bool hasBackInstockDate = backInStockDateValue != null && backInstockDate >= DateTime.Today; 757 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery >= DateTime.Today; 758 string expectedDeliveryDate = hasBackInstockDate ? backInstockDate.ToShortDateString() : product.ExpectedDelivery?.ToShortDateString() ?? ""; 759 760 <div class="fs-8 mt-1 mb-2 text-start"> 761 @if (!string.IsNullOrWhiteSpace(product.StockStatus)) 762 { 763 <p class="mb-1">@product.StockStatus</p> 764 if (!string.IsNullOrWhiteSpace(product.StockDeliveryText)) 765 { 766 <p class="mb-1">@product.StockDeliveryText</p> 767 } 768 } 769 @if (hasExpectedDelivery || hasBackInstockDate) 770 { 771 <div class="mb-1"> 772 <span>@Translate("Expected in stock"): </span> 773 <span>@expectedDeliveryDate</span> 774 </div> 775 } 776 </div> 777 778 @*if (!string.IsNullOrWhiteSpace(product.StockStatus)) { 779 <div class="">@product.StockStatus</div> 780 <div class="">@product.StockDeliveryText</div> 781 { 782 <div> 783 <span>@Translate("Expected in stock"): </span> 784 <span>@expectedDeliveryDate</span> 785 </div> 786 } 787 } else { 788 789 if (hasExpectedDelivery || hasBackInstockDate) 790 { 791 <div> 792 <span>@Translate("Expected in stock"): </span> 793 <span>@expectedDeliveryDate</span> 794 </div> 795 } 796 }*@ 797 798 @*if (!string.IsNullOrEmpty(ecomCountryCode) && !hidePrice && product.Id != null && shipId != null) 799 { 800 var shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency); 801 802 803 804 if (shippingFeeAmount != null) 805 { 806 <div class="fs-8 text-black-50 text-start"> 807 <p>@Translate("Levering fra") @currency @shippingFeeAmount</p> 808 </div> 809 } 810 }*@ 811 } 812 } 813 814 @if (showPricesWithVat == "false" && !neverShowVat) 815 { 816 if (isLazyLoadingForProductInfoEnabled) 817 { 818 <div class="fs-7 opacity-85 text-price js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></div> 819 } 820 else 821 { 822 string price = product.Price.PriceWithVatFormatted; 823 if (product?.VariantInfo?.VariantInfo != null) 824 { 825 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 826 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 827 } 828 if (priceMin != priceMax) 829 { 830 price = priceMin + " - " + priceMax; 831 } 832 <div class="fs-7 opacity-85 text-price">@price @Translate("Incl. VAT")</div> 833 834 } 835 } 836 </div> 837 </div> 838 } 839 @RenderAddToCart(product, link, clickProductLink) 840 </div> 841 </article> 842 } 843 844 } 845 </div> 846 847 <div class="my-3" id="LoadMoreButton"> 848 <div class="text-center d-flex flex-column gap-3"> 849 <div class="opacity-85">@loadedProducts @Translate("out of") @productList.TotalProductsCount @Translate("products")</div> 850 @if (productList.PageCount != 1) 851 { 852 string sortBySelection = Dynamicweb.Context.Current.Request?.Form["SortBy"] ?? ""; 853 sortBySelection = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SortBy")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SortBy") : sortBySelection; 854 855 string searchQuery = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("q")) ? Dynamicweb.Context.Current.Request.QueryString.Get("q") : ""; 856 string searchLayout = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout") : ""; 857 858 <form method="get" action="@url" data-response-target-element="content" class="w-100"> 859 @foreach (FacetGroupViewModel facetGroup in productList.FacetGroups) 860 { 861 foreach (FacetViewModel facetItem in facetGroup.Facets) 862 { 863 foreach (FacetOptionViewModel facetOption in facetItem.Options) 864 { 865 if (facetOption.Selected) 866 { 867 <input type="hidden" name="@facetItem.QueryParameter" value="[@facetOption.Value]"> 868 } 869 } 870 } 871 } 872 873 <input type="hidden" name="PageSize" value="@pageSize"> 874 <input type="hidden" name="SortBy" value="@sortBySelection"> 875 <input type="hidden" name="RequestType" value="UpdateList"> 876 877 @if (!string.IsNullOrEmpty(searchQuery)) 878 { 879 <input type="hidden" name="q" value="@searchQuery"> 880 <input type="hidden" name="SearchLayout" value="@searchLayout"> 881 } 882 883 @{ 884 string nextPageLink = "/Default.aspx?ID=" + Pageview.Page.ID + "&PageSize=" + pageSize + "&SortBy=" + sortBySelection; 885 886 foreach (FacetGroupViewModel facetGroup in productList.FacetGroups) 887 { 888 foreach (FacetViewModel facetItem in facetGroup.Facets) 889 { 890 foreach (FacetOptionViewModel facetOption in facetItem.Options) 891 { 892 if (facetOption.Selected) 893 { 894 nextPageLink += "&" + facetItem.QueryParameter + "=[" + facetOption.Value + "]"; 895 } 896 } 897 } 898 } 899 900 nextPageLink += !string.IsNullOrEmpty(searchQuery) ? "&q=" + searchQuery : ""; 901 } 902 903 <a href="@nextPageLink" class="btn btn-primary" onclick="swift.ProductList.Update(event)" id="LoadMoreButton_@Model.ID">@Translate("Load more products")</a> 904 </form> 905 906 } 907 </div> 908 </div> 909 } 910 } 911 else 912 { 913 <div class="alert alert-dark m-0"> 914 @Translate("We did not find anything matching your search result") 915 </div> 916 } 917 } 918 919 @helper RenderBadge(string badge, string plusBadge) 920 { 921 <div class="plus-badge d-flex justify-content-center w-100 efa-mod"> 922 <div class="d-flex justify-content-end w-75 h-75"> 923 <img class="plus-badge_img efa-mod" srcset="@badge" src="" alt="@plusBadge" /> 924 </div> 925 </div> 926 } 927 928 @helper RenderExtraBadge(string badge, string extraBadge) 929 { 930 <div class="plus-badge d-flex justify-content-center w-100 efa-mod"> 931 <div class="d-flex justify-content-start w-75 h-75"> 932 <img class="extra-plus-badge_img efa-mod" srcset="@System.Web.HttpUtility.UrlPathEncode(badge)" src="" alt="@extraBadge" /> 933 </div> 934 </div> 935 } 936 937 938 @helper RenderViewMore(string link, string clickProductLink) 939 { 940 <a href="@link" class="btn btn-secondary flex-fill" @clickProductLink>@Translate("View more")</a> 941 } 942 943 @helper RenderAddToCart(ProductViewModel product, string link, string clickProductLink) 944 { 945 string iconPath = "/Files/icons/"; 946 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 947 if (!url.Contains("LayoutTemplate")) 948 { 949 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 950 } 951 952 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 953 bool anonymousUser = Pageview.User == null; 954 955 bool hideAddToCart = !string.IsNullOrEmpty(Model.Item.GetString("HideAddToCart")) ? Model.Item.GetBoolean("HideAddToCart") : false; 956 hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable() ? true : hideAddToCart; 957 bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("QuantitySelector")) ? Model.Item.GetBoolean("QuantitySelector") : false; 958 959 bool isDiscontinued = product.Discontinued; 960 bool IsNeverOutOfStock = product.NeverOutOfstock; 961 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : ""; 962 disableAddToCart = isDiscontinued ? "disabled" : disableAddToCart; 963 disableAddToCart = IsNeverOutOfStock ? "" : disableAddToCart; 964 disableAddToCart = isLazyLoadingForProductInfoEnabled ? "disabled" : disableAddToCart; 965 966 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : ""; 967 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0; 968 var productToShow = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId); 969 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code; 970 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : ""; 971 var shippingFeeAmount = string.Empty; 972 973 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null) 974 { 975 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency); 976 } 977 978 var addToCartLabel = Model.Item.GetRawValueString("GridLayoutMobile", "grid-1") == "grid-2" ? "" : Translate("Add to cart"); 979 string hideOnMobile = Model.Item.GetRawValueString("GridLayoutMobile") == "grid-2" ? "hide-on-mobile" : ""; 980 string viewMoreIcon = Model.Item.GetRawValueString("ViewMoreIcon"); 981 982 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount; 983 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, product.LanguageId); 984 985 if (!hideAddToCart) 986 { 987 if (product.VariantInfo.VariantInfo == null) 988 { 989 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\""; 990 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1"; 991 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty; 992 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : ""; 993 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 994 string imagePathHidden = product?.DefaultImage.Value.ToString() ?? ""; 995 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70"; 996 <div class="px-0 pb-3"> 997 <form method="post" action="@url"> 998 <input type="hidden" name="redirect" value="false"> 999 <input type="hidden" name="MainProductId" value="None"> 1000 <input type="hidden" name="ProductId" value="@product.Id"> 1001 <input type="hidden" name="ProductName" value="@product.Name"> 1002 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 1003 <input type="hidden" name="ProductReferer" value="product_list_listview"> 1004 <input type="hidden" name="ProductPrice" value="@product.Price.PriceFormatted"> 1005 <input type="hidden" name="cartcmd" value="add"> 1006 @* #38 EA: sbj *@ 1007 <input type="hidden" name="ProductPriceFormatted" value="@product.Price.PriceFormatted"> 1008 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")"> 1009 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")"> 1010 <input type="hidden" name="ProductImage" value="@imagePathHidden"> 1011 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost"> 1012 <input type="hidden" name="ProductDeliveryName" value="@deliveryName"> 1013 <input type="hidden" name="cartpage" value="@cartUrl"> 1014 1015 @if (!string.IsNullOrEmpty(product.VariantId)) 1016 { 1017 <input type="hidden" name="VariantId" value="@product.VariantId">} 1018 1019 @if (quantitySelector) 1020 { 1021 <div class="input-group input-primary-button-group"> 1022 <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> 1023 <div class="d-flex gap-2"> 1024 @RenderViewMore(link, clickProductLink) 1025 @*<a href="@link" class="btn btn-secondary flex-fill" @clickProductLink>@Translate("View more")</a>*@ 1026 @if (disableAddToCart == "disabled") 1027 { 1028 <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)"> 1029 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "envelope.svg") @Translate("Produkt notifikations label")</span> 1030 </button> 1031 } 1032 else 1033 { 1034 <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)"> 1035 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "shopping-cart.svg") @Translate("Add to cart")</span> 1036 </button> 1037 } 1038 </div> 1039 </div> 1040 if (stepQty != "1") 1041 { 1042 <div class="invalid-feedback d-none"> 1043 @Translate("Please select a quantity that is dividable by") @stepQty 1044 </div> 1045 1046 } 1047 <label for="Quantity_@(product.Id)_@product.VariantId" class="visually-hidden">@Translate("Quantity")</label> 1048 } 1049 else 1050 { 1051 <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart> 1052 <div class="d-flex gap-2"> 1053 <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> 1054 @if (disableAddToCart == "disabled") 1055 { 1056 <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)"> 1057 <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> 1058 </button> 1059 } 1060 else 1061 { 1062 <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)"> 1063 <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> 1064 @*<span class="icon-2 d-flex gap-2 align-items-center justify-content-center d-md-none">@ReadFile(iconPath + "shopping-cart.svg")</span>*@ 1065 </button> 1066 } 1067 </div> 1068 1069 } 1070 </form> 1071 </div> 1072 } 1073 else 1074 { 1075 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>"; 1076 1077 string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : ""; 1078 variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString(); 1079 1080 string disableVariantSelector = isLazyLoadingForProductInfoEnabled ? "disabled" : ""; 1081 <div class="px-0 pb-3"> 1082 <form action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" class="d-inline-block"> 1083 <input type="hidden" name="ProductID" value="@product.Id"> 1084 <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()"> 1085 <input type="hidden" name="HideInventory" value="@Model.Item.GetBoolean("HideInventory").ToString()"> 1086 <input type="hidden" name="HideStockState" value="@Model.Item.GetBoolean("HideStockState").ToString()"> 1087 <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId"> 1088 <input type="hidden" name="ViewType" value="ModalContent"> 1089 <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> 1090 </form> 1091 </div> 1092 1093 } 1094 } 1095 @RenderModal(product) 1096 1097 } 1098 1099 @helper RenderProduct(ProductInfoViewModel relatedProduct, string productId) 1100 { 1101 var relProduct = relatedProduct.GetProduct(); 1102 var mainProductId = productId.IsNotNullOrEmpty() ? productId : "None"; 1103 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 1104 bool anonymousUser = Pageview.User == null; 1105 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable(); 1106 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) : false; 1107 1108 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : ""; 1109 string ratioCssClass = ratio != "" ? "ratio" : ""; 1110 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 1111 1112 string theme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("Theme")) ? Dynamicweb.Context.Current.Request.Form.Get("Theme") : ""; 1113 string themePadding = theme != string.Empty ? "p-3" : string.Empty; 1114 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : ""; 1115 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty; 1116 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty; 1117 string ContentPadding = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ContentPadding")) ? Dynamicweb.Context.Current.Request.Form.Get("ContentPadding") : ""; 1118 1119 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 1120 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 1121 1122 string variantIdForLink = !string.IsNullOrEmpty(relProduct.VariantId) ? $"&VariantID={relProduct.VariantId}" : ""; 1123 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(relProduct.DefaultVariantId) ? $"&VariantID={relProduct.DefaultVariantId}" : variantIdForLink; 1124 1125 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 1126 link += $"&GroupID={relProduct.PrimaryOrDefaultGroup.Id}"; 1127 link += $"&ProductID={relProduct.Id}"; 1128 link += variantIdForLink; 1129 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link); 1130 1131 string imagePath = relProduct?.DefaultImage.Value.ToString() ?? ""; 1132 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70"; 1133 1134 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : ""; 1135 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : ""; 1136 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : ""; 1137 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0; 1138 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : ""; 1139 1140 var badgeParms = new Dictionary<string, object>(); 1141 badgeParms.Add("saleBadgeType", saleBadgeType); 1142 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName); 1143 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName); 1144 badgeParms.Add("campaignBadgesValues", campaignBadgesValues); 1145 badgeParms.Add("newPublicationDays", newPublicationDays); 1146 1147 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false; 1148 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false; 1149 DateTime createdDate = relProduct.Created.Value; 1150 bool showBadges = saleBadgeEnabled && relProduct.Discount.Price != 0 ? true : false; 1151 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges; 1152 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges; 1153 1154 string disableAddToCart = (relProduct.StockLevel <= 0) ? "disabled" : ""; 1155 bool isNeverOutOfStock = relProduct.NeverOutOfstock; 1156 disableAddToCart = isNeverOutOfStock ? "" : disableAddToCart; 1157 1158 string iconPath = "/Files/icons/"; 1159 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg"); 1160 string addToCartLabel = !addToCartIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(addToCartIcon) + "</span>" : ""; 1161 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 1162 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? Translate("Add to cart") : ""; 1163 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 1164 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 1165 string inputSize = string.Empty; 1166 1167 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 1168 if (!url.Contains("LayoutTemplate")) 1169 { 1170 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 1171 } 1172 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 1173 string imagePathHidden = relProduct?.DefaultImage.Value.ToString() ?? ""; 1174 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70"; 1175 1176 switch (buttonSize) 1177 { 1178 case "small": 1179 inputSize = " input-group-sm"; 1180 buttonSize = " btn-sm"; 1181 break; 1182 case "regular": 1183 buttonSize = string.Empty; 1184 break; 1185 case "large": 1186 inputSize = " input-group-lg"; 1187 buttonSize = " btn-lg"; 1188 break; 1189 } 1190 1191 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : ""; 1192 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0; 1193 var productToShow = Services.Products.GetProductById(relProduct.Id, relProduct.VariantId, relProduct.LanguageId); 1194 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code; 1195 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : ""; 1196 var shippingFeeAmount = string.Empty; 1197 1198 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null) 1199 { 1200 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency); 1201 } 1202 1203 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount; 1204 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, relProduct.LanguageId); 1205 1206 <div class="text-decoration-none d-block h-100"> 1207 <div class="h-100 d-flex flex-column justify-content-between@(theme)"> 1208 @{ 1209 FieldValueViewModel plusDesignerModalIndikator; 1210 relProduct.ProductFields.TryGetValue("PlusDesignerLinkEA", out plusDesignerModalIndikator); 1211 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "target=\"_blank\"" : string.Empty; 1212 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "rel=\"noopener\"" : string.Empty; 1213 1214 if (!string.IsNullOrEmpty(plusDesignerModalIndikator.Value.ToString())) 1215 { 1216 <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; "> 1217 <object> 1218 <a href="@plusDesignerModalIndikator.Value" @target @rel class="text-decoration-none"> 1219 <div class="plus-designer__inner p-1 ps-3"> 1220 <p class="m-0 opacity-75 fs-7">Try me in</p> 1221 <div class="plus-designer__image-container d-flex w-100"> 1222 <img class="" src="/Files/Templates/Designs/Swift/Assets/Images/plusdesignerlogo.svg" alt="Alternate Text" style="width:90%" /> 1223 <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> 1224 </div> 1225 </div> 1226 </a> 1227 </object> 1228 </div> 1229 1230 } 1231 1232 } 1233 <div class="@(imageTheme)" style="@imageOutlineStyle"> 1234 <div class="@(ratioCssClass) position-relative m-auto mw200 efa-mod" style="@ratioVariable"> 1235 @if (showBadges) 1236 { 1237 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2"> 1238 @{ 1239 @RenderPartial("Components/EcommerceBadge.cshtml", relProduct, badgeParms) 1240 } 1241 </div> 1242 } 1243 <img loading="lazy" decoding="async" src="@imagePath" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="@relProduct.Name"> 1244 </div> 1245 </div> 1246 <div class="flex-fill p-3 pb-0 d-flex flex-column justify-content-between @themePadding"> 1247 <h3 class="h6 opacity-85">@relProduct.Name @relProduct.VariantName</h3> 1248 1249 @if (!hidePrice) 1250 { 1251 <div> 1252 <p class="h6 m-0"> 1253 @if (showPricesWithVat == "false" && !neverShowVat) 1254 { 1255 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price) 1256 { 1257 <span class="text-decoration-line-through opacity-75 me-1"> 1258 @relProduct.PriceBeforeDiscount.PriceWithoutVatFormatted 1259 </span> 1260 } 1261 } 1262 else 1263 { 1264 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price) 1265 { 1266 <span class="text-decoration-line-through opacity-75 me-1"> 1267 @relProduct.PriceBeforeDiscount.PriceFormatted 1268 </span> 1269 } 1270 } 1271 1272 @if (showPricesWithVat == "false" && !neverShowVat) 1273 { 1274 <span class="text-price fw-bold">@relProduct.Price.PriceWithoutVatFormatted</span> 1275 } 1276 else 1277 { 1278 <span class="text-price fw-bold">@relProduct.Price.PriceFormatted</span> 1279 } 1280 </p> 1281 @if (shippingFeeAmount != null) 1282 { 1283 <div class="fs-8 text-black-50 text-start"> 1284 <p>@deliveryCost</p> 1285 </div> 1286 } 1287 1288 1289 @if (showPricesWithVat == "false" && !neverShowVat) 1290 { 1291 <small class="opacity-85 fst-normal">@relProduct.Price.PriceWithVatFormatted @Translate("Incl. VAT")</small> 1292 } 1293 1294 </div> 1295 } 1296 </div> 1297 1298 <div class="d-flex gap-4 p-3 pt-0"> 1299 @{ 1300 string stepQty = relProduct.PurchaseQuantityStep > 1 ? relProduct.PurchaseQuantityStep.ToString() : "1"; 1301 string valueQty = relProduct.PurchaseMinimumQuantity > relProduct.PurchaseQuantityStep ? relProduct.PurchaseMinimumQuantity.ToString() : stepQty; 1302 1303 } 1304 <form method="post" action="@url"> 1305 <input type="hidden" name="redirect" value="false"> 1306 <input type="hidden" name="MainProductId" value="@mainProductId"> 1307 <input type="hidden" name="ProductId" value="@relProduct.Id"> 1308 <input type="hidden" name="ProductName" value="@relProduct.Name"> 1309 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 1310 <input type="hidden" name="ProductReferer" value="product_list_listview"> 1311 <input type="hidden" name="ProductPrice" value="@relProduct.Price.PriceFormatted"> 1312 <input type="hidden" name="cartcmd" value="add"> 1313 @* #38 EA: sbj *@ 1314 <input type="hidden" name="ProductPriceFormatted" value="@relProduct.Price.PriceFormatted"> 1315 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")"> 1316 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")"> 1317 <input type="hidden" name="ProductImage" value="@imagePathHidden"> 1318 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost"> 1319 <input type="hidden" name="ProductDeliveryName" value="@deliveryName"> 1320 <input type="hidden" name="cartpage" value="@cartUrl"> 1321 1322 <input id="Quantity_@(relProduct.Id)_@relProduct.VariantId" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart> 1323 <div class="d-flex gap-3 p-3 pt-0"> 1324 <a href="@link" class="btn btn-secondary flex-fill">@Translate("View more")</a> 1325 <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"> 1326 @if (!Model.Item.GetBoolean("HideButtonText")) 1327 { 1328 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 1329 @addToCartLabel 1330 </span> 1331 } 1332 else 1333 { 1334 @addToCartLabel 1335 } 1336 </a> 1337 </div> 1338 </form> 1339 </div> 1340 1341 </div> 1342 </div> 1343 @*@RenderModalRelatedProducts(relProduct)*@ 1344 } 1345 1346 @helper RenderModal(ProductViewModel product) 1347 { 1348 <div id="cartNotificationModal_@(product.Id)" class="modal" tabindex="-1" aria-labelledby="cartNotificationModalTitel" aria-hidden="true"> 1349 @{ 1350 string modelId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ModelID")) ? Dynamicweb.Context.Current.Request.Form.Get("ModelID") : product.Id; 1351 1352 string scrollBarForceMobile = Dynamicweb.Context.Current.Request.Form.Get("NavigationShowScrollbar") != string.Empty ? "--swiffy-slider-track-height:0.5rem !important;" : string.Empty; 1353 bool hideSliderNavigation = false; 1354 1355 int itemsShown = 3; 1356 1357 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : ""; 1358 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty; 1359 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty; 1360 1361 string imagePath = product?.DefaultImage.Value.ToString() ?? ""; 1362 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70"; 1363 1364 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : ""; 1365 string ratioCssClass = ratio != "" ? "ratio" : ""; 1366 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 1367 1368 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : ""; 1369 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : ""; 1370 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : ""; 1371 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0; 1372 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : ""; 1373 1374 var badgeParms = new Dictionary<string, object>(); 1375 badgeParms.Add("saleBadgeType", saleBadgeType); 1376 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName); 1377 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName); 1378 badgeParms.Add("campaignBadgesValues", campaignBadgesValues); 1379 badgeParms.Add("newPublicationDays", newPublicationDays); 1380 1381 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false; 1382 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false; 1383 DateTime createdDate = product.Created.Value; 1384 1385 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 1386 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges; 1387 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges; 1388 var productCounter = 1; 1389 1390 var shopPageId = GetPageIdByNavigationTag("Shop"); 1391 1392 bool relatedExist = false; 1393 string modalProductCon = ""; 1394 string modalProductSize = ""; 1395 foreach (var relatedGroup in product.RelatedGroups) 1396 { 1397 if (relatedGroup.Id == "Addon") 1398 { 1399 if (relatedGroup.Products.Count > 0) 1400 { 1401 relatedExist = true; 1402 modalProductCon = "col-xl-7"; 1403 } 1404 } 1405 } 1406 if (relatedExist) 1407 { 1408 modalProductSize = "xl"; 1409 } 1410 else 1411 { 1412 modalProductSize = "lg"; 1413 } 1414 1415 } 1416 @*<script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 1417 <script type="module"> 1418 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 1419 </script>*@ 1420 <div class="modal-dialog theme plus-primary modal-@modalProductSize"> 1421 <div class="modal-content rounded-0 container"> 1422 <div class="row"> 1423 <div class="col-12 @modalProductCon p-0 border-bottom border-lg-none"> 1424 <div class="modal-header w-100 text-center"> 1425 <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> 1426 <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> 1427 </div> 1428 <div id="cartNotificationModalBody" class="modal-body"> 1429 <div class="container-fluid"> 1430 <div class="row"> 1431 <div class="@(imageTheme) col-12" style="@imageOutlineStyle"> 1432 <div class="@(ratioCssClass) col-4 col-xl-6 m-auto position-relative" style="@ratioVariable"> 1433 @if (showBadges) 1434 { 1435 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2"> 1436 @{ 1437 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 1438 } 1439 </div> 1440 } 1441 <img loading="lazy" decoding="async" src="@imagePath" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="@product.Name" id="cartNotificationModal_Image"> 1442 </div> 1443 </div> 1444 <div class="d-flex flex-wrap py-3 col-12 gap-md-3"> 1445 <div class="col-12 col-md-auto info"> 1446 <div class="fc-dark efa-mod"><strong id="cartNotificationModal_Name"></strong></div> 1447 <div class="fs-5 fw-bold fc-dark efa-mod" id="cartNotificationModal_Price"></div> 1448 <div class="fs-8 fc-grey efa-mod" id="cartNotificationModal_DeliveryCost"></div> 1449 <div class="fs-8 fc-grey efa-mod" id="cartNotificationModal_DeliveryName"></div> 1450 <div id="cartNotificationModal_Quantity"></div> 1451 </div> 1452 <div class="col-12 col-lg action align-self-end"> 1453 <div class="col-12 d-flex mt-3"> 1454 <a id="cartNotificationModal_Button" class="btn btn-primary flex-fill" href="">@Translate("Gå til indkøbslisten", "Gå til indkøbslisten")</a> 1455 </div> 1456 </div> 1457 </div> 1458 </div> 1459 </div> 1460 </div> 1461 </div> 1462 @if (relatedExist) 1463 { 1464 <div class="col-12 col-xl-5 p-0"> 1465 <div class="modal-header w-100 text-center"> 1466 <h3 class="modal-title text-center w-100">@Translate("Add-on products", "Tilbehør")</h3> 1467 <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> 1468 </div> 1469 <div class="modal-body"> 1470 <div id="addonsList_@product.Id" class="list row gy-3 overflow-auto"> 1471 @{ 1472 foreach (RelatedGroupViewModel relatedGroup in product.RelatedGroups.Where(x => x.Id == "Addon")) 1473 { 1474 foreach (ProductInfoViewModel relatedProduct in relatedGroup.Products) 1475 { 1476 <div class="elements col-6 col-xl-12 align-self-end"> 1477 @RenderAddons(relatedProduct, productCounter, product.Id) 1478 </div> 1479 productCounter += 1; 1480 } 1481 } 1482 1483 } 1484 </div> 1485 </div> 1486 </div> 1487 } 1488 </div> 1489 </div> 1490 </div> 1491 </div> 1492 } 1493 1494 @helper RenderAddons(ProductInfoViewModel relatedProduct, int productCounter, string productID) 1495 { 1496 var relProduct = relatedProduct.GetProduct(); 1497 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 1498 bool anonymousUser = Pageview.User == null; 1499 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable(); 1500 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) : false; 1501 1502 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : ""; 1503 string ratioCssClass = ratio != "" ? "ratio" : ""; 1504 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 1505 1506 string theme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("Theme")) ? Dynamicweb.Context.Current.Request.Form.Get("Theme") : ""; 1507 string themePadding = theme != string.Empty ? "p-3" : string.Empty; 1508 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : ""; 1509 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty; 1510 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty; 1511 string ContentPadding = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ContentPadding")) ? Dynamicweb.Context.Current.Request.Form.Get("ContentPadding") : ""; 1512 1513 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 1514 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 1515 1516 string variantIdForLink = !string.IsNullOrEmpty(relProduct.VariantId) ? $"&VariantID={relProduct.VariantId}" : ""; 1517 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(relProduct.DefaultVariantId) ? $"&VariantID={relProduct.DefaultVariantId}" : variantIdForLink; 1518 1519 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 1520 link += $"&GroupID={relProduct.PrimaryOrDefaultGroup.Id}"; 1521 link += $"&ProductID={relProduct.Id}"; 1522 link += variantIdForLink; 1523 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link); 1524 1525 string imagePath = relProduct?.DefaultImage.Value.ToString() ?? ""; 1526 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70"; 1527 1528 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : ""; 1529 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : ""; 1530 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : ""; 1531 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0; 1532 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : ""; 1533 1534 var badgeParms = new Dictionary<string, object>(); 1535 badgeParms.Add("saleBadgeType", saleBadgeType); 1536 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName); 1537 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName); 1538 badgeParms.Add("campaignBadgesValues", campaignBadgesValues); 1539 badgeParms.Add("newPublicationDays", newPublicationDays); 1540 1541 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false; 1542 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false; 1543 DateTime createdDate = relProduct.Created.Value; 1544 bool showBadges = saleBadgeEnabled && relProduct.Discount.Price != 0 ? true : false; 1545 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges; 1546 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges; 1547 1548 string disableAddToCart = (relProduct.StockLevel <= 0) ? "disabled" : ""; 1549 bool isNeverOutOfStock = relProduct.NeverOutOfstock; 1550 disableAddToCart = isNeverOutOfStock ? "" : disableAddToCart; 1551 1552 string iconPath = "/Files/icons/"; 1553 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg"); 1554 string addToCartLabel = !addToCartIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(addToCartIcon) + "</span>" : ""; 1555 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : ""; 1556 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? Translate("Add to cart") : ""; 1557 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : ""; 1558 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular"); 1559 string inputSize = string.Empty; 1560 string viewMoreIcon = Model.Item.GetRawValueString("ViewMoreIcon"); 1561 1562 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 1563 if (!url.Contains("LayoutTemplate")) 1564 { 1565 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 1566 } 1567 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart")); 1568 string imagePathHidden = relProduct?.DefaultImage.Value.ToString() ?? ""; 1569 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70"; 1570 1571 switch (buttonSize) 1572 { 1573 case "small": 1574 inputSize = " input-group-sm"; 1575 buttonSize = " btn-sm"; 1576 break; 1577 case "regular": 1578 buttonSize = string.Empty; 1579 break; 1580 case "large": 1581 inputSize = " input-group-lg"; 1582 buttonSize = " btn-lg"; 1583 break; 1584 } 1585 1586 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : ""; 1587 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0; 1588 var productToShow = Services.Products.GetProductById(relProduct.Id, relProduct.VariantId, relProduct.LanguageId); 1589 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code; 1590 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : ""; 1591 var shippingFeeAmount = string.Empty; 1592 1593 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null) 1594 { 1595 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency); 1596 } 1597 1598 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount; 1599 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, relProduct.LanguageId); 1600 string stepQty = relProduct.PurchaseQuantityStep > 1 ? relProduct.PurchaseQuantityStep.ToString() : "1"; 1601 string valueQty = relProduct.PurchaseMinimumQuantity > relProduct.PurchaseQuantityStep ? relProduct.PurchaseMinimumQuantity.ToString() : stepQty; 1602 1603 relProduct.ProductFields.TryGetValue("ProductNextBackorderDate", out FieldValueViewModel backInStockDateValue); 1604 DateTime backInstockDate = backInStockDateValue.Value != null ? DateTime.Parse(backInStockDateValue.Value.ToString()) : DateTime.Today.AddDays(-1); 1605 bool hasBackInstockDate = backInStockDateValue != null && backInstockDate >= DateTime.Today; 1606 bool hasExpectedDelivery = relProduct.ExpectedDelivery != null && relProduct.ExpectedDelivery >= DateTime.Today; 1607 string expectedDeliveryDate = hasBackInstockDate ? backInstockDate.ToShortDateString() : relProduct.ExpectedDelivery?.ToShortDateString() ?? ""; 1608 1609 <div class="element w-100 d-flex"> 1610 1611 <div class="w-100 row m-0 align-items-end"> 1612 <div class="product-info col ps-lg-0"> 1613 <div class="col-6 col-xl-3 m-auto m-xl-0 image-container pe-2"> 1614 <img class="h-100 w-100" src="@imagePath" alt="" style="object-fit: cover;" /> 1615 </div> 1616 <div> 1617 <div><strong>@relProduct.Name</strong></div> 1618 @if (!hidePrice) 1619 { 1620 <div> 1621 <p class="h6 m-0"> 1622 @if (showPricesWithVat == "false" && !neverShowVat) 1623 { 1624 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price) 1625 { 1626 <span class="text-decoration-line-through opacity-75 me-1"> 1627 @relProduct.PriceBeforeDiscount.PriceWithoutVatFormatted 1628 </span> 1629 } 1630 } 1631 else 1632 { 1633 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price) 1634 { 1635 <span class="text-decoration-line-through opacity-75 me-1"> 1636 @relProduct.PriceBeforeDiscount.PriceFormatted 1637 </span> 1638 } 1639 } 1640 1641 @if (showPricesWithVat == "false" && !neverShowVat) 1642 { 1643 <span class="text-price fw-bold">@relProduct.Price.PriceWithoutVatFormatted</span> 1644 } 1645 else 1646 { 1647 <span class="text-price fw-bold">@relProduct.Price.PriceFormatted</span> 1648 } 1649 </p> 1650 1651 <div class="fs-8 mt-1 mb-2 text-start"> 1652 @if (!string.IsNullOrWhiteSpace(relProduct.StockStatus)) 1653 { 1654 <p class="mb-1">@relProduct.StockStatus</p> 1655 if (!string.IsNullOrWhiteSpace(relProduct.StockDeliveryText)) 1656 { 1657 <p class="mb-1">@relProduct.StockDeliveryText</p> 1658 } 1659 } 1660 @if (hasExpectedDelivery || hasBackInstockDate) 1661 { 1662 <div class="mb-1"> 1663 <span>@Translate("Expected in stock"): </span> 1664 <span>@expectedDeliveryDate</span> 1665 </div> 1666 } 1667 </div> 1668 1669 @*<div class="fs-8 text-black-50 text-start"> 1670 <p class="m-0">@Translate("Levering fra") @currency @shippingFeeAmount</p> 1671 </div>*@ 1672 @if (showPricesWithVat == "false" && !neverShowVat) 1673 { 1674 <small class="opacity-85 fst-normal">@relProduct.Price.PriceWithVatFormatted @Translate("Incl. VAT")</small> 1675 } 1676 1677 </div> 1678 } 1679 </div> 1680 </div> 1681 <div class="action col-auto px-0 ps-lg-0 w-100 w-lg-auto"> 1682 <form method="post" action="@url"> 1683 <input type="hidden" name="redirect" value="false"> 1684 <input type="hidden" name="ProductId" value="@relProduct.Id"> 1685 <input type="hidden" name="ProductName" value="@relProduct.Name"> 1686 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 1687 <input type="hidden" name="ProductReferer" value="product_list_listview"> 1688 <input type="hidden" name="ProductPrice" value="@relProduct.Price.PriceFormatted"> 1689 <input type="hidden" name="cartcmd" value="add"> 1690 @* #38 EA: sbj *@ 1691 <input type="hidden" name="ProductPriceFormatted" value="@relProduct.Price.PriceFormatted"> 1692 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")"> 1693 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")"> 1694 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost"> 1695 <input type="hidden" name="ProductDeliveryName" value="@deliveryName"> 1696 <input type="hidden" name="ProductImage" value="@imagePathHidden"> 1697 <input type="hidden" name="cartpage" value="@cartUrl"> 1698 @if (relProduct.StockLevel > 0) 1699 { 1700 <div class="d-flex flex-nowrap w-100 mb-2 bg-light efa-mod" style="height:40px;"> 1701 1702 <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);"> 1703 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dash-lg" viewBox="0 0 16 16"> 1704 <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" /> 1705 </svg> 1706 </div> 1707 <div class="d-flex align-items-center justify-content-center" style="width:calc(100%/3);"> 1708 <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> 1709 </div> 1710 <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);"> 1711 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg" viewBox="0 0 16 16"> 1712 <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" /> 1713 </svg> 1714 </div> 1715 <script type="text/javascript"> 1716 function quantityBtn(id, symbol) { 1717 console.log(document.getElementById(id)); 1718 var idValue = document.getElementById(id).value; 1719 var value = parseInt(idValue, 10); 1720 var step = 1; 1721 value = isNaN(value) ? 0 : value; 1722 switch (symbol) { 1723 case "minus": 1724 value = value - step; 1725 break; 1726 case "plus": 1727 value = value + step; 1728 break; 1729 } 1730 console.log(value); 1731 if (value > 0) { 1732 document.getElementById(id).value = value; 1733 } 1734 //UpdateQuantity(); 1735 } 1736 </script> 1737 1738 </div> 1739 <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"> 1740 @if (!Model.Item.GetBoolean("HideButtonText")) 1741 { 1742 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2"> 1743 @addToCartLabel 1744 </span> 1745 } 1746 else 1747 { 1748 @addToCartLabel 1749 } 1750 </a> 1751 } 1752 else 1753 { 1754 <div class="w-100 efa-mod" style="height:40px;"> 1755 @*<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)"> 1756 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "envelope.svg")</span> 1757 </button>*@ 1758 <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> 1759 </div> 1760 } 1761 </form> 1762 </div> 1763 </div> 1764 </div> 1765 } 1766 1767 @*@helper RenderModalRelatedProducts(ProductViewModel product) 1768 { 1769 <div id="cartNotificationModal_@(product.Id)" class="modal" tabindex="-1" aria-labelledby="cartNotificationModalTitel" aria-hidden="true"> 1770 @{ 1771 string modelId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ModelID")) ? Dynamicweb.Context.Current.Request.Form.Get("ModelID") : product.Id; 1772 1773 string scrollBarForceMobile = Dynamicweb.Context.Current.Request.Form.Get("NavigationShowScrollbar") != string.Empty ? "--swiffy-slider-track-height:0.5rem !important;" : string.Empty; 1774 bool hideSliderNavigation = false; 1775 1776 int itemsShown = 3; 1777 1778 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : ""; 1779 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty; 1780 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty; 1781 1782 string imagePath = product?.DefaultImage.Value.ToString() ?? ""; 1783 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70"; 1784 1785 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : ""; 1786 string ratioCssClass = ratio != "" ? "ratio" : ""; 1787 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 1788 1789 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : ""; 1790 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : ""; 1791 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : ""; 1792 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0; 1793 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : ""; 1794 1795 var badgeParms = new Dictionary<string, object>(); 1796 badgeParms.Add("saleBadgeType", saleBadgeType); 1797 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName); 1798 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName); 1799 badgeParms.Add("campaignBadgesValues", campaignBadgesValues); 1800 badgeParms.Add("newPublicationDays", newPublicationDays); 1801 1802 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false; 1803 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false; 1804 DateTime createdDate = product.Created.Value; 1805 1806 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 1807 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges; 1808 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges; 1809 1810 var shopPageId = GetPageIdByNavigationTag("Shop"); 1811 1812 bool relatedExist = false; 1813 foreach (var relatedGroup in product.RelatedGroups) 1814 { 1815 if (relatedGroup.Id == "Addon") 1816 { 1817 if (relatedGroup.Products.Count > 0) 1818 { 1819 relatedExist = true; 1820 } 1821 } 1822 } 1823 1824 } 1825 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 1826 <script type="module"> 1827 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 1828 </script> 1829 <div class="modal-dialog theme plus-primary modal-xl"> 1830 <div class="modal-content rounded-0"> 1831 <div class="modal-header w-100 text-center"> 1832 <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> 1833 <button type="button" class="btn-close position-absolute m-0 end-0 me-3" data-bs-dismiss="modal" aria-label="Close"></button> 1834 </div> 1835 <div id="cartNotificationModalBody" class="modal-body"> 1836 <div class="container-fluid"> 1837 <div class="row"> 1838 <div class="col-12 col-md-6 col-lg-3 m-auto me-lg-0 @(imageTheme)" style="@imageOutlineStyle"> 1839 <div class="@(ratioCssClass) position-relative" style="@ratioVariable"> 1840 @if (showBadges) 1841 { 1842 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2"> 1843 @{ 1844 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 1845 } 1846 </div> 1847 } 1848 <img loading="lazy" decoding="async" src="" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="" id="cartNotificationModal_Image"> 1849 </div> 1850 </div> 1851 <div class="col-12 col-md-6 col-lg-4 m-auto ms-lg-0 d-flex align-content-between flex-wrap"> 1852 <div class="col-12 ms-auto fc-dark efa-mod"><strong id="cartNotificationModal_Name"></strong></div> 1853 <div class="col-12 ms-auto"> 1854 <div class="col-12 ms-auto fs-5 fw-bold fc-dark efa-mod" id="cartNotificationModal_Price"></div> 1855 <div class="fs-8 fc-grey efa-mod text-start"> 1856 <p class="" id="cartNotificationModal_DeliveryCost"></p> 1857 </div> 1858 1859 <div class="col-12 d-flex flex-wrap mb-3 ms-auto" id="cartNotificationModal_Quantity"></div> 1860 <div class="col-12 d-flex mb-2"> 1861 <a id="cartNotificationModal_Button" class="btn btn-primary flex-fill" href="">@Translate("Gå til indkøbslisten", "Gå til indkøbslisten")</a> 1862 </div> 1863 <div class="col-12 d-flex"> 1864 <div class="btn btn-secondary col-12" data-bs-dismiss="modal">@Translate("Continue shopping")</div> 1865 </div> 1866 </div> 1867 </div> 1868 </div> 1869 </div> 1870 </div> 1871 1872 @if (relatedExist) 1873 { 1874 <div class="modal-footer d-none d-md-block d-lg-block d-xl-block" id="cartNotificationModal_Related"> 1875 <div class="modal-title w-100 text-center"> 1876 <h3>@Translate("Add-on products", "Tilbehør")</h3> 1877 </div> 1878 <div class="container-fluid"> 1879 <div class="row"> 1880 <div class="col-12 m-auto"> 1881 <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)"> 1882 <ul class="slider-container"> 1883 @{ 1884 foreach (RelatedGroupViewModel relatedGroup in product.RelatedGroups.Where(x => x.Id == "Addon")) 1885 { 1886 foreach (ProductInfoViewModel relatedProduct in relatedGroup.Products) 1887 { 1888 <li> 1889 @RenderProduct(relatedProduct, product.Id) 1890 </li> 1891 } 1892 } 1893 } 1894 1895 </ul> 1896 <button type="button" title="@Translate("Previous slide")" class="slider-nav" style="z-index: 2;"> 1897 <span class="visually-hidden">@Translate("Previous slide")</span> 1898 </button> 1899 <button type="button" title="@Translate("Next slide")" class="slider-nav slider-nav-next" style="z-index: 2;"> 1900 <span class="visually-hidden">@Translate("Next slide")</span> 1901 </button> 1902 1903 </div> 1904 <script type="module"> 1905 swiffyslider.initSlider(document.querySelector('#slider_@(modelId)')); 1906 </script> 1907 </div> 1908 </div> 1909 </div> 1910 </div> 1911 } 1912 </div> 1913 </div> 1914 </div> 1915 }*@ 1916 1917 <script type="text/javascript"> 1918 1919 var notifyCookieBtn = document.getElementsByName("notify-cookie"); 1920 if (notifyCookieBtn) { 1921 for (let i = 0; i < notifyCookieBtn.length; i++) { 1922 notifyCookieBtn[i].addEventListener("click", function (event) { 1923 let link = notifyCookieBtn[i].getAttribute("data-href"), 1924 cName = "ProductNotifier", 1925 cValue = true, 1926 date = new Date(), 1927 expDays = 1; 1928 date.setTime(date.getTime() + (expDays * 24 * 60 * 60 * 1000)); 1929 const expires = "expires=" + date.toUTCString(); 1930 document.cookie = cName + "=" + cValue + "; " + expires + "; path=/"; 1931 1932 window.location.href = link; 1933 }); 1934 } 1935 } 1936 1937 document.addEventListener("update.swift.cart", function (event) { 1938 var data = Object.fromEntries(event.detail.formData.entries()); 1939 var mainProductId = data.MainProductId; 1940 1941 if (mainProductId != "None") { 1942 var openqs = '#cartNotificationModal_' + mainProductId; 1943 1944 var prodId = data.ProductId; 1945 1946 var price = data.ProductPrice; 1947 if (price.includes(",")) { 1948 price = price.replace(/,/g, "."); 1949 price = parseFloat(price).toFixed(2); 1950 } 1951 1952 var quantity = data.Quantity; 1953 var detailsPrice = data.ProductPrice; 1954 var totalprice = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");; 1955 if (quantity > 1) { 1956 var total = price * quantity; 1957 var totalpriceCalc = parseFloat(total).toFixed(2); 1958 totalpriceCalc = totalpriceCalc.toString(); 1959 if (totalpriceCalc.includes(".")) { 1960 totalprice = totalpriceCalc.replace(".", ","); 1961 } 1962 else { 1963 totalprice = totalpriceCalc.concat("", ",00"); 1964 } 1965 } 1966 1967 var details = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00"); 1968 1969 document.querySelector(openqs).querySelector("#cartNotificationModal_Button").href = data.cartpage; 1970 document.querySelector(openqs).querySelector("#cartNotificationModal_Quantity").innerHTML = data.Quantity + " " + data.ProductUnit; 1971 document.querySelector(openqs).querySelector("#cartNotificationModal_Name").innerHTML = data.ProductName; 1972 document.querySelector(openqs).querySelector("#cartNotificationModal_Price").innerHTML = details; 1973 document.querySelector(openqs).querySelector("#cartNotificationModal_Image").src = data.ProductImage; 1974 document.querySelector(openqs).querySelector("#cartNotificationModal_Image").alt = data.ProductName; 1975 document.querySelector(openqs).querySelector("#cartNotificationModal_DeliveryCost").innerHTML = data.ProductDeliveryCost; 1976 document.querySelector(openqs).querySelector("#cartNotificationModal_DeliveryName").innerHTML = data.ProductDeliveryName; 1977 document.querySelector(openqs).querySelector("#cartNotificationModal_Related").classList.remove('d-md-block', 'd-lg-block', 'd-xl-block'); 1978 } 1979 else { 1980 var prodId = data.ProductId; 1981 mainProductId = prodId; 1982 1983 var price = data.ProductPrice; 1984 if (price.includes(",")) { 1985 price = price.replace(/,/g, "."); 1986 price = parseFloat(price).toFixed(2); 1987 } 1988 1989 var quantity = data.Quantity; 1990 var detailsPrice = data.ProductPrice; 1991 var totalprice = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");; 1992 if (quantity > 1) { 1993 var total = price * quantity; 1994 var totalpriceCalc = parseFloat(total).toFixed(2); 1995 totalpriceCalc = totalpriceCalc.toString(); 1996 if (totalpriceCalc.includes(".")) { 1997 totalprice = totalpriceCalc.replace(".", ","); 1998 } 1999 else { 2000 totalprice = totalpriceCalc.concat("", ",00"); 2001 } 2002 } 2003 2004 var qs = "#cartNotificationModal_" + prodId; 2005 const myModalAlternative = new bootstrap.Modal(qs) 2006 2007 var details = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00"); 2008 2009 document.querySelector(qs).querySelector("#cartNotificationModal_Button").href = data.cartpage; 2010 document.querySelector(qs).querySelector("#cartNotificationModal_Quantity").innerHTML = data.Quantity + " " + data.ProductUnit; 2011 document.querySelector(qs).querySelector("#cartNotificationModal_Name").innerHTML = data.ProductName; 2012 document.querySelector(qs).querySelector("#cartNotificationModal_Price").innerHTML = details; 2013 document.querySelector(qs).querySelector("#cartNotificationModal_Image").src = data.ProductImage; 2014 document.querySelector(qs).querySelector("#cartNotificationModal_Image").alt = data.ProductName; 2015 document.querySelector(qs).querySelector("#cartNotificationModal_DeliveryCost").innerHTML = data.ProductDeliveryCost; 2016 document.querySelector(qs).querySelector("#cartNotificationModal_DeliveryName").innerHTML = data.ProductDeliveryName; 2017 if (document.querySelector(qs).querySelector("#cartNotificationModal_Related")) { 2018 if (!document.querySelector(qs).querySelector("#cartNotificationModal_Related").classList.contains('d-md-block', 'd-lg-block', 'd-xl-block')) { 2019 document.querySelector(qs).querySelector("#cartNotificationModal_Related").classList.add('d-md-block', 'd-lg-block', 'd-xl-block'); 2020 } 2021 } 2022 2023 myModalAlternative.show(); 2024 } 2025 let addonsListHeight = 0; 2026 console.log(mainProductId); 2027 let addonList = document.querySelector("#addonsList_" + mainProductId); 2028 console.log(addonList); 2029 let addonListElems = addonList.children; 2030 console.log(addonListElems); 2031 if (addonListElems.length > 3) { 2032 for (var i = 0; 3 > i; i++) { 2033 console.log(addonListElems[i].clientHeight); 2034 addonsListHeight = addonsListHeight + addonListElems[i].clientHeight + 16; 2035 } 2036 addonList.style.maxHeight = addonsListHeight + "px"; 2037 } 2038 2039 }); 2040 2041 </script> 2042 @* #38 EA: end *@ 2043 2044 2045 <script> 2046 dataLayer = window.dataLayer || []; 2047 dataLayer.push({ ecommerce: null }); 2048 dataLayer.push({ 2049 event: 'view_item_list', 2050 ecommerce: { 2051 currency: '@Dynamicweb.Ecommerce.Common.Context.Currency.Code', 2052 ecomm_pagetype: 'category', 2053 items: [ 2054 @foreach (ProductViewModel product in productList.Products) 2055 { 2056 <text>{ 2057 item_name: '@product.Name', 2058 item_id: '@product.Number', 2059 price: @product.Price.Price, 2060 quantity: 1, 2061 item_category: '@product.PrimaryOrDefaultGroup.Name', 2062 item_brand: 'PLUS', 2063 item_variant: '' 2064 },</text> 2065 } 2066 ] 2067 }, 2068 user_data: { 2069 first_name: '@(string.IsNullOrEmpty(CartId) ? "" : order?.DeliveryName)', 2070 last_name: '', 2071 email_address: '@(string.IsNullOrEmpty(CartId) ? "" : order?.CustomerEmail)', 2072 phone_number: '@(string.IsNullOrEmpty(CartId) ? "" : order?.CustomerPhone)', 2073 } 2074 }); 2075 </script> 2076

Tangent hegn og låger i træ

Leder du efter et hegn med rene linjer og smukke detaljer til din haveindretning?

Tangent er et super elegant og gennemtænkt træhegn, som giver rene linjer i dit hegnsforløb fra start til slut. Kombinationen af brede og smalle lister skaber en smuk effekt, hvor stolperne kommer til at indgå som et naturligt element i hegnsforløbet. Kombinerer du hegnet med de ligeafskårede stolper med en flad stolpehat på toppen, får hegnet et helt roligt udtryk, der vil klæde de fleste huse og haver uanset byggestil og haveindretning.

I Tangent serien finder du både hegn, enkeltlåger og dobbeltlåger i flere forskellige mål og dimensioner, så du kan bygge dit hegn, præcist som du ønsker. Hegnet er ideelt til brug som afskærmning og rumdeler i dit uderum og i et haveskel til en nabo eller sti, da hegnet med sin listeopbygning virker kamuflerende og skærmer for indkig.

Hegnet er fremstillet i certificeret grundmalet træ af højeste kvalitet, så du er sikker på, at din hegn holder i årevis – du skal blot huske at vedligeholde det.