0
Din indkøbskurv

Opdaterer kurv

stk. a

-
+

Tilkøb:

stk. a

Din kurv i alt (ex. fragt)

Handel videre

Gå til kurven
Dankort VISA VISA Elektron American express Santander Mobilepay

Error executing template "Designs/Mobler2018/eCom/Product/Product.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_8e066a567e5249268ec8a559fc23f66e.Execute() in D:\dynamicweb.net\Solutions\mobler.LIVE\Files\Templates\Designs\Mobler2018\eCom\Product\Product.cshtml:line 614
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 2 @using System.Globalization 3 @using System.Net.Http 4 @using System.Net.Http.Headers 5 @using System.Web 6 @using Mobler.Website.CustomModules.MoblerHelpers 7 @using System.Text.RegularExpressions 8 @using CampaignModule.Models 9 @using Dynamicweb.Core 10 @using Dynamicweb.Ecommerce 11 @using Dynamicweb.Ecommerce.Products 12 @using Dynamicweb.Ecommerce.Variants 13 @using Dynamicweb.Frontend 14 @using Mobler.Website.CustomCode.Frontend 15 @using Mobler.Website.CustomCode.Models 16 @using Group = Dynamicweb.Ecommerce.Products.Group 17 @using StringExtensions = ServiceStack.StringExtensions 18 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 19 20 @{ 21 var request = HttpContext.Current.Request; 22 string shopname = MoblerHelpers.GetShopName(); 23 string SelectPlaceholder = Translate("ShopSelectPlaceholder", "Indtast by, postnummer eller adresse"); 24 var shopInfo = MoblerHelpers.ShopInfo(); 25 int ShopPageId = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetInt("ShopPageId"); 26 var AllShops = Firstweb.Custom.CustomCode.Frontend.Helpers.Shops.GetAllShops(ShopPageId); 27 int AjaxCartPageId = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetInt("AjaxCartPageId"); 28 int CustomersAlsoSawPageId = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetInt("CustomersAlsoSawPageId"); 29 string relewiseRecommandationsEndpoint = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("RelewiseRecommandationsPageId"); 30 bool userIsAnonymous = Mobler.Website.CustomCode.RelewiseCustom.RelewiseConnectorSetting.IsAnonymous; 31 bool showRecommendations = Mobler.Website.CustomCode.RelewiseCustom.RelewiseConnectorSetting.ShowRecommendations; 32 string CartPage = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("CartPage"); 33 string VariantsEndpoint = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("VariantsUrl"); 34 string VariantDetailsEndpoint = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("VariantDetailsUrl"); 35 // Theming 36 bool blackFridayTheme = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetBoolean("BlackFridayTheme"); 37 string blackFridayClass = blackFridayTheme ? "bf-bg-black bf-color-white" : ""; 38 string ProductID = GetString("Ecom:Product.ID"); 39 string GroupID = Firstweb.Custom.CustomCode.Frontend.Helpers.EcomGroups.GetProductDefaultShopGroupID(ProductID); //GetString("Ecom:Product.PrimaryOrFirstGroupID"); 40 string DefaultVariantID = GetString("Ecom:Product.DefaultVariantComboID"); 41 string ProductImage = MoblerHelpers.GetProductListImage(ProductID, DefaultVariantID); 42 string ProductName = GetString("Ecom:Product.Name"); 43 string ProductNumber = GetString("Ecom:Product.Number"); 44 string ProductCatalogLink = !String.IsNullOrEmpty(GetString("Ecom:Product:Field.ProductCatelog.Value.Clean")) ? GetString("Ecom:Product:Field.ProductCatelog.Value.Clean") : ""; 45 string VariantId = GetString("Ecom:Product.VariantID"); 46 string ShortDescription = GetString("Ecom:Product.ShortDescription"); 47 string LongDescription = GetString("Ecom:Product.LongDescription").Replace("||", "<br/>"); 48 string CurrentVariantName = GetString("Ecom:Product.SelectedVariantComboName"); 49 List<string> freeProductIdsFromDiscount = Mobler.Website.CustomCode.Frontend.ProductHelper.GetFreeProductIdsFromDiscount(ProductID); 50 var FaqQuestions = MoblerHelpers.GetProductFAQs(ProductID, GroupID); 51 52 List<LoopItem> fieldDisplayGroups = GetLoop("FieldDisplayGroups"); 53 IEnumerable<LoopItem> filteredFields = fieldDisplayGroups.SelectMany(x => x.GetLoop("Fields")).Where(field => (!field.GetString("Ecom:FieldDisplayGroup.Field.Value").IsNullOrEmpty() && field.GetString("Ecom:FieldDisplayGroup.Field.Value") != "0" && field.GetString("Ecom:FieldDisplayGroup.Field.Value") != "," && !field.GetString("Ecom:FieldDisplayGroup.Field.Value").Contains(",,"))); 54 55 //CYLINDO SPECIFIC 56 string cylindoAccountId = Pageview.Area.Item["CylindoAccountId"]?.ToString(); 57 string cylindoProductId = GetString("Ecom:Product:Field.CylindoId.Value.Clean");// ProductNumber.Split('-')[0]; 58 string cylindoDimensionShot = GetString("Ecom:Product:Field.CylindoDimensionShot.Value.Clean");// ProductNumber.Split('-')[0]; 59 bool isUsingCylindoAssets = Mobler.Website.CustomCode.Frontend.CylindoHelper.IsUsingCylindoAssets(cylindoProductId); 60 var cylindoProductFieldValuesDict = Mobler.Website.CustomCode.Frontend.CylindoHelper.GetCylindoProductFieldValues(GetString("Ecom:Product.ID"), VariantId, GetString("Ecom:Product.LanguageID")); 61 string cylindoProductFieldValues = string.Join("|", cylindoProductFieldValuesDict.Select(p => $"{p.Key} {p.Value}")); 62 string cylindoSelectedVariantValues = Mobler.Website.CustomCode.Frontend.CylindoHelper.GetCylindoSelectedVariantValues(VariantId, GetString("Ecom:Product.LanguageID")); 63 //CYLINDO SPECIFIC 64 65 // Group Specific - Exclude Bed Accessories 66 bool excludeBedAccessories = GetBoolean("Ecom:Product.CategoryField.Senge.ExcludeBedAccessories.Value"); 67 68 Mobler.Website.CustomCode.Frontend.PayeverHelper payeverHelper = new Mobler.Website.CustomCode.Frontend.PayeverHelper(); 69 Mobler.Website.CustomCode.Models.PayeverWidget PayeverWidget = payeverHelper.GetPayeverWidgetInformation(); 70 71 string MetaDescription = GetString("Ecom:Product.MetaDescription"); 72 73 if (string.IsNullOrEmpty(MetaDescription)) 74 { 75 MetaDescription = Mobler.Website.CustomCode.Frontend.Helpers.Text.GetFirstLine(LongDescription.Replace("<br/>", " ")); 76 } 77 78 string MetaTitle = GetString("Ecom:Product.MetaTitle"); 79 80 if (string.IsNullOrEmpty(MetaTitle)) 81 { 82 MetaTitle = ProductName + " | Mobler.dk"; //Meta data description bliver sat af DW, hvis dette felt er udfyldt, sÃ¥ vi skal ikke opsætte noget. 83 } 84 85 var RelatedBlogs = Firstweb.Custom.CustomCode.Frontend.Helpers.Blogs.GetBlogsRelatedToProduct(ProductID); 86 bool NewItem = GetBoolean("Ecom:Product:Field.NewItem"); 87 DateTime NewItemExpiryDate = GetDate("Ecom:Product:Field.NewItemExpiryDate"); 88 bool LowPrice = GetBoolean("Ecom:Product:Field.Splash3"); 89 90 Mobler.Website.CustomCode.Frontend.ProductsDisplayVariant productsDisplayVariant = Mobler.Website.CustomCode.Frontend.ProductsDisplayVariant.Instance(); 91 92 bool ForSale = !GetBoolean("Ecom:Product:Field.NotForSaleOnline"); 93 string FirstImage = ""; 94 List<string> Images = MoblerHelpers.GetProductImages(GetString("Ecom:Product.ID"), VariantId); 95 List<string> YouTubeVideoIds = new List<string>() 96 { 97 ("" + GetString("Ecom:Product:Field.VideoKlip.Value")), 98 ("" + GetString("Ecom:Product:Field.VideoKlip2.Value")), 99 ("" + GetString("Ecom:Product:Field.VideoKlip3.Value")), 100 ("" + GetString("Ecom:Product:Field.VideoKlip4.Value")), 101 ("" + GetString("Ecom:Product:Field.VideoKlip5.Value")), 102 }; 103 YouTubeVideoIds.RemoveAll(s => string.IsNullOrEmpty(s)); 104 bool First = true; 105 bool FirstIndicator = true; 106 int IndicatorIncrementer = 0; 107 string ProductLink = GetString("Ecom:Product.Link.Clean"); 108 109 double ActualPrice = GetDouble("Ecom:Product.Price.Price"); 110 double spar = 0; 111 double price = 0; 112 double normalPrice = 0; 113 114 string priceFormatted = ""; 115 string sparFormatted = ""; 116 string normalPriceFormatted = ""; 117 118 string campaignColor = ""; 119 string campaignText = ""; 120 string campaignTextClean = ""; 121 string campaignDate = ""; 122 123 string campaignDateStart = ""; 124 string campaignDateEnd = ""; 125 126 bool CampaignActiveOnProduct = GetBoolean("CampaignModule:Product.CampaignActiveOnProduct"); 127 128 bool IsLocalWebshopProduct = (Mobler.Website.CustomCode.Frontend.Helper.IsLocalWebshop() && !GetBoolean("Ecom:Product:Field.SystemIsMasterShopProduct")); 129 130 131 if (!CampaignActiveOnProduct && !IsLocalWebshopProduct) 132 { 133 price = ActualPrice; 134 priceFormatted = MoblerHelpers.formatPrice(price); 135 } 136 else if (IsLocalWebshopProduct) 137 { 138 price = ActualPrice; 139 priceFormatted = MoblerHelpers.formatPrice(price); 140 141 normalPrice = GetDouble("Ecom:Product:Field.ForPris"); 142 normalPriceFormatted = MoblerHelpers.formatPrice(normalPrice); 143 144 spar = GetDouble("Ecom:Product:Field.Spar"); 145 sparFormatted = MoblerHelpers.formatPrice(spar); 146 147 bool SplashLwCombiPrice = GetBoolean("Ecom:Product:Field.SplashLwCombiPrice"); 148 DateTime SplashLwCombiPriceExpiryDate = GetDate("Ecom:Product:Field.SplashLwCombiPriceExpiryDate"); 149 150 if (SplashLwCombiPrice && (SplashLwCombiPriceExpiryDate == DateTime.MinValue || SplashLwCombiPriceExpiryDate > DateTime.Now)) 151 { 152 campaignTextClean = "KOMBI"; 153 } 154 155 156 } 157 else 158 { 159 normalPrice = GetDouble("CampaignModule:Product.CampaignProduct.NormalPrice"); 160 normalPriceFormatted = GetString("CampaignModule:Product.CampaignProduct.NormalPrice.FormattedRetail"); 161 162 price = GetDouble("CampaignModule:Product.CampaignProduct.CampaignPrice"); 163 priceFormatted = GetString("CampaignModule:Product.CampaignProduct.CampaignPrice.FormattedRetail"); 164 165 spar = GetDouble("CampaignModule:Product.CampaignProduct.DiscountAmount"); 166 sparFormatted = GetString("CampaignModule:Product.CampaignProduct.DiscountAmount.FormattedRetail"); 167 168 campaignColor = GetString("CampaignModule:Product.Campaign.Color"); 169 campaignTextClean = GetString("CampaignModule:Product.Campaign.Text"); 170 171 campaignDateStart = GetString("CampaignModule:Product.CampaignProduct.Campaign.Start"); 172 campaignDateEnd = GetString("CampaignModule:Product.CampaignProduct.Campaign.End"); 173 if(!string.IsNullOrEmpty(campaignDateStart) && !string.IsNullOrEmpty(campaignDateEnd)) { 174 campaignText = $"{Translate("Campaign.BeforeDate.Text", "Gældende fra:")} {campaignDateStart} {Translate("Campaign.BeforeDate.Text2", "t.o.m.")} {campaignDateEnd}"; 175 } 176 } 177 178 double financingPrice = price / 10; 179 double financingPriceRounded = Math.Round(financingPrice, 0, MidpointRounding.AwayFromZero); 180 string financingPriceRoundedFormatted = financingPriceRounded.ToString("N0", CultureInfo.CurrentCulture); 181 182 string DataLayerPrice = ActualPrice.ToString().Replace(".", "").Replace(",", "."); 183 System.Web.HttpContext.Current.Items["OverrideOgTags"] = true; 184 string OgImage = "https://mobler.dk" + Images.FirstOrDefault(); 185 string ProductFriendlyUrl = "https://mobler.dk" + Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(ProductLink); 186 187 string TrimmedTeaser = Firstweb.Custom.CustomCode.Frontend.Helpers.JSFormatting.TrimLinebreaks(ShortDescription); 188 string TrimmedName = Firstweb.Custom.CustomCode.Frontend.Helpers.JSFormatting.TrimLinebreaks(ProductName); 189 190 var ParentGroups = Firstweb.Custom.CustomCode.Frontend.Helpers.EcomGroups.getBreadCrumbGroupList(GroupID, true); 191 string DataLayerParentGroup = ""; 192 var DataLayerParentGroups = ""; 193 194 if (ParentGroups != null && ParentGroups.Count > 0) 195 { 196 DataLayerParentGroup = ParentGroups.Last().Name; 197 List<string> DataLayerParentGroupStrings = ParentGroups.Select(group => group.Name).ToList(); 198 DataLayerParentGroups = string.Join(" > ", DataLayerParentGroupStrings); 199 } 200 201 // Handle USPs on the product page 202 int uspPageId = 0; 203 string uspProductdetailLinkProductLevel = GetString("Ecom:Product:Field.uspproductdetaillink.Value.Clean"); 204 string uspProductdetailLinkGroupLevel = ParentGroups.Any() ? ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("uspproductdetaillink").Value.ToString() : string.Empty; 205 bool hasUspProductdetailLinkProductLevel = !string.IsNullOrEmpty(uspProductdetailLinkProductLevel); 206 bool hasUspProductdetailLinkGroupLevel = !string.IsNullOrEmpty(uspProductdetailLinkGroupLevel); 207 208 // Check if there's a product detail link on the product level 209 if (hasUspProductdetailLinkProductLevel) 210 { 211 Int32.TryParse(uspProductdetailLinkProductLevel.Substring(uspProductdetailLinkProductLevel.LastIndexOf('=') + 1), out uspPageId); 212 } 213 // Check if there's a product detail link on the group level 214 else if (hasUspProductdetailLinkGroupLevel) 215 { 216 Int32.TryParse(uspProductdetailLinkGroupLevel.Substring(uspProductdetailLinkGroupLevel.LastIndexOf('=') + 1), out uspPageId); 217 } 218 219 // Handle display of product catalogue image, if it exists on the group level 220 string productCatalogueImage = ParentGroups.Any() ? ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("ProductCatalogueImage").Value.ToString() : string.Empty; 221 string parentGroupName = ""; 222 bool useInclExclLogic = false; 223 if (ParentGroups.Any()) 224 { 225 parentGroupName = "" + ParentGroups.Last().Name; 226 useInclExclLogic = ("" + ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("UseInclExclLogic").Value) == "True"; 227 } 228 229 // Handle USP Icons and Text 230 string usp1Icon = ParentGroups.Any() ? ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("usp_1_icon").Value.ToString() : string.Empty; 231 if (usp1Icon == "") 232 { 233 usp1Icon = shopInfo.ShippingFreeDeliveryAndHandling ? "truck" : "piggy-bank"; 234 } 235 236 string usp2Icon = ParentGroups.Any() ? ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("usp_2_icon").Value.ToString() : string.Empty; 237 if (usp2Icon == "") 238 { 239 usp2Icon = "truck"; 240 } 241 string usp3Icon = ParentGroups.Any() ? ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("usp_3_icon").Value.ToString() : string.Empty; 242 if (usp3Icon == "") 243 { 244 usp3Icon = "calendar"; 245 } 246 247 string usp1IconText = shopInfo.ShippingFreeDeliveryAndHandling ? Translate("USP.FreeDeliveryAndhandling.Header", "Gratis levering og indbæring") : Translate("Product.USPIcon.One.Title", "Prismatch"); 248 string usp2IconText = Translate("Product.USPIcon.Two.Title", "Fri kantstenslevering*"); 249 string usp3IconText = Translate("Product.USPIcon.Three.Title", "Rentefri finansiering"); 250 string usp1IconTextFromField = ParentGroups.Any() ? ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("uspicontext1").Value.ToString() : string.Empty; 251 string usp2IconTextFromField = ParentGroups.Any() ? ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("uspicontext2").Value.ToString() : string.Empty; 252 string usp3IconTextFromField = ParentGroups.Any() ? ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("uspicontext3").Value.ToString() : string.Empty; 253 254 if (!shopInfo.ShippingFreeDeliveryAndHandling && !string.IsNullOrEmpty(usp1IconTextFromField)) 255 { 256 usp1IconText = usp1IconTextFromField; 257 } 258 if (!string.IsNullOrEmpty(usp2IconTextFromField)) 259 { 260 usp2IconText = usp2IconTextFromField; 261 } 262 if (!string.IsNullOrEmpty(usp3IconTextFromField)) 263 { 264 usp3IconText = usp3IconTextFromField; 265 } 266 267 string usp1IconClass = "fa-solid fa-" + usp1Icon + " fs3"; 268 string usp2IconClass = "fa-solid fa-" + usp2Icon + " fs3"; 269 string usp3IconClass = "fa-solid fa-" + usp3Icon + " fs3"; 270 271 string seeProductInStore = ""; 272 if (!String.IsNullOrEmpty(GetString("Ecom:Product:Field.seeproductinstore.Value.Clean"))) 273 { 274 seeProductInStore = GetString("Ecom:Product:Field.seeproductinstore.Value.Clean"); 275 } 276 277 string ShowOnPageUrl = ""; 278 string GetCartEndpoint = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("GetCartJson"); 279 280 string QuickDeliveryColor = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("QuickDeliveryColor"); 281 string QuickDeliveryDescription = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("QuickDeliveryDescription"); 282 string NormalDeliveryDescription = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("NormalDeliveryDescription"); 283 string ExperienceInShopDescription = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("ExperienceInShopDescription"); 284 string BrandName = GetString("Ecom:Product:Field.Maerkevarer.Value.Clean"); 285 string DeliveryTime = Translate("DeliveryTime." + BrandName, "Gns. leveringstid 3-12 dage"); 286 string ExperienceInShopText = Translate("ExperienceInShop", "Oplev i butikken"); 287 bool HideDelivery = DeliveryTime == "Skjul"; 288 string DeliveryColor = ""; 289 bool QuickDelivery = GetBoolean("Ecom:Product:Field.QuickDelivery.Value.Clean"); 290 bool ExperienceInShop = GetBoolean("Ecom:Product:Field.ExperienceInShop.Value.Clean"); 291 string CustomDelivery = GetString("Ecom:Product:Field.CustomDeliveryTime.Value.Clean"); 292 if (QuickDelivery) 293 { 294 DeliveryColor = QuickDeliveryColor; 295 DeliveryTime = Translate("DeliveryTime.QuickDelivery", "Ekstra hurtig levering"); 296 } 297 else if (!String.IsNullOrEmpty(CustomDelivery)) 298 { 299 DeliveryTime = CustomDelivery; 300 } 301 string DeliveryFontWeightModifier = QuickDelivery ? "font-weight-bold" : ""; 302 bool DisplayAverageDeliveryTime = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetBoolean("DisplayAverageDeliveryTime"); 303 304 double CostPrice = GetDouble("Ecom:Product:Field.FirstwebCostPrice.Value.Raw"); 305 string DailyPriceBackgroundColor = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("AktuelDagsprisBackgroundColor"); 306 string DailyPriceTextColor = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("AktuelDagsprisTextColor"); 307 string EAN = GetString("Ecom:Product:Field.FirstEan.Value"); 308 string productionDescription = GetString("Ecom:Product:Field.FirstwebProductionDescription"); 309 310 Mobler.Website.CustomCode.RelewiseCustom.RelewiseCaller relewiseCaller = new Mobler.Website.CustomCode.RelewiseCustom.RelewiseCaller(); 311 relewiseCaller.TrackProductView(ProductID, VariantId); 312 bool hasVariants = false; 313 314 string purchasedWithStyle = Mobler.Website.CustomCode.Frontend.Helper.IsLocalWebshop() ? "display:none" : ""; 315 string splashType = ""; 316 string splashText = ""; 317 string normalPriceText = ""; 318 bool hasMedia = (Images.Any() || YouTubeVideoIds.Any() || isUsingCylindoAssets); 319 var splashPriceObj = GetSplashPriceObject(null); 320 dynamic productObj = !hasMedia ? null : new 321 { 322 productName = ProductName, 323 images = Images.Select(image => string.Format("/admin/public/getimage.ashx?Image={0}&Format=webP&Quality=90&width=1600&height=1000&crop=0", image.Replace("+", "%20"))).ToList(), 324 cylindoImages = new List<string>(), 325 cylindoAccountId = cylindoAccountId, 326 cylindoProductId = cylindoProductId, 327 cylindoDimensionShot = cylindoDimensionShot, 328 videos = YouTubeVideoIds, 329 isUsingCylindoAssets = isUsingCylindoAssets, 330 onTopSplatterText = splashPriceObj.onTopSplashText, 331 onTopSplatterType = splashPriceObj.splashType 332 }; 333 } 334 335 @SnippetStart("PricesContainer") 336 @if (ActualPrice != 0) 337 // Don't show any price info if the product is not for sale, e.g. 0 price 338 { 339 <div class="row"> 340 <div class="col-12 col-sm-12 offset-sm-0 d-flex justify-content-center justify-content-lg-start flex-column"> 341 342 <div class="d-flex flex-wrap flex-column justify-content-end border-grey border-bottom-0 p-4"> 343 @if (ForSale) 344 { 345 <div class="d-none" data-bind="setInitValue: { observable: BeforePriceDouble, value: @normalPrice }"></div> 346 <div class="d-none" data-bind="setInitValue: { observable: BeforePrice, value: '@normalPriceFormatted' }"></div> 347 <div class="d-none" data-bind="setInitValue: { observable: SavingDouble, value: @spar }"></div> 348 <div class="d-none" data-bind="setInitValue: { observable: Saving, value: '@sparFormatted' }"></div> 349 350 <div class="addtocart-price"> 351 <div class="d-flex flex-wrap flex-column justify-content-between unimportant-hidden"> 352 <span id="priceWithoutAddons" class="unimportant-hidden" data-bind="text: PriceDouble"></span> 353 @{ 354 if (splashPriceObj.splashTypeInt > 0) 355 { 356 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: @splashPriceObj.splashTypeInt }"></div> 357 } 358 } 359 <div class="js-product-price-html-target">@RenderProductPricing(null, splashPriceObj, true, false, "fs6", true)</div> 360 361 <div class="price-info fs-s"> 362 <p class="price-info-text color-subtle mt-4 mb-1 d-none js-update-price-info-text"></p> 363 @if (hasVariants) 364 { 365 <div class="variant-price-disclaimer color-subtle fs-s mb-1">@Translate("VariantPriceDisclaimer", "Prisen kan variere efter materialevalg")</div> 366 } 367 </div> 368 <div class="d-none" data-bind="setInitValue: { observable: CampaignTextIsEmpty, value: @(string.IsNullOrEmpty(campaignText).ToString().ToLower()) }"></div> 369 <div class="d-none" data-bind="setInitValue: { observable: CampaignText, value: '@campaignText' }"></div> 370 <div class="price-info fs-xs mt-3" data-bind="css: CampaignTextIsEmpty() ? 'd-none' : ''"> 371 <p class="mb-1"><span class="dot dot-yellow"></span> <span data-bind="text: CampaignText"></span></p> 372 </div> 373 @* <p data-bind="text: CampaignText"></p> *@ 374 </div> 375 376 <div class="optionalproducts d-none"> 377 <hr /> 378 <h5>@Translate("Tilvalg", "Tilvalg")</h5> 379 <div class="optionalproduct"></div> 380 <p><strong>Sum af tilvalg: </strong><span id="optionalPrice"></span></p> 381 </div> 382 383 </div> 384 } 385 else 386 { 387 <p class="tw-mb-0">@Translate("Ecom:Product.NotForSaleMessage", "Produktet kan ikke købes online.")</p> 388 } 389 </div> 390 391 @if (GetLoop("Co3VariantCombinations").Count > 0) 392 { 393 if (isUsingCylindoAssets == true) 394 { 395 <div class="d-none" data-bind="setInitValue: { observable: CylindoProductId, value: '@cylindoProductId' }"></div> 396 397 } 398 <div class="variant-selectors"> 399 @{ 400 hasVariants = true; 401 var VariantGroupsWithMultipleOptions = GetLoop("VariantGroups").Where(x => x.GetLoop("VariantAvailableOptions").Count > 1); 402 int VariantGroupCount = VariantGroupsWithMultipleOptions.Count(); 403 int VariantCombinationsCount = GetLoop("Co3VariantCombinations").Count; 404 int PossibleVariantCombinations = 1; 405 } 406 @foreach (var VG in GetLoop("VariantGroups")) 407 { 408 PossibleVariantCombinations *= VG.GetLoop("VariantAvailableOptions").Count; 409 410 411 } 412 413 414 @if (VariantGroupCount == 1) 415 { 416 417 var RelevantVariantGroup = VariantGroupsWithMultipleOptions.FirstOrDefault(); 418 var SelectedVariant = RelevantVariantGroup.GetLoop("VariantAvailableOptions").Where(x => x.GetBoolean("Ecom:VariantOption.Selected")).FirstOrDefault(); 419 int variantCount = RelevantVariantGroup.GetLoop("VariantAvailableOptions").Count; 420 var variantSelectorClass = "variant-options bg-white"; 421 if (RelevantVariantGroup.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") && variantCount <= 5) 422 { 423 variantSelectorClass = "variant-selector__color-picker d-flex flex-wrap"; 424 } 425 string variantInfoText = Translate("VariantInfo-" + RelevantVariantGroup.GetString("Ecom:VariantGroup.Name") + "-" + BrandName, ""); 426 if (string.IsNullOrEmpty(variantInfoText)) 427 { 428 variantInfoText = Translate("VariantInfo-" + RelevantVariantGroup.GetString("Ecom:VariantGroup.Name"), ""); 429 } 430 string variantInfoTextEscapedLineBreaks = Regex.Replace(variantInfoText, @"\r\n?|\n", "<br />"); 431 432 if (isUsingCylindoAssets == true) 433 { 434 string cylindoValue = string.Join(" ", cylindoSelectedVariantValues.Split('|')); 435 if (!string.IsNullOrEmpty(cylindoProductFieldValues)) 436 { 437 <div class="d-none" data-bind="setInitValue: { observable: ConstantCylindoFeatures, value: '@cylindoProductFieldValues' }"></div> 438 } 439 <div class="d-none" data-bind="setInitValue: { observable: InitialCylindoFeatures, value: '@cylindoValue' }"></div> 440 441 442 } 443 <div class="position-relative border-grey p-4"> 444 <div class="variant-selector__header"> 445 <div class="variant-selector__header-inner"> 446 <span class="font-weight-bold fs0">@RelevantVariantGroup.GetString("Ecom:VariantGroup.Label")</span> 447 <div class="variant-selector__header-choices"> 448 <span class="variantoptionname fs0" data-bind="text: SelectedVariantOption0">@SelectedVariant.GetString("Ecom:VariantOption.Name")</span> 449 </div> 450 </div> 451 <span class="variant-selector__toggle" data-bind="click: ToggleVariantInfo.bind($data, '@variantInfoTextEscapedLineBreaks')"> 452 <span>@Translate("InfoIconText", "info")</span> 453 <span class="fas fa-info-circle"></span> 454 </span> 455 </div> 456 457 @if (!RelevantVariantGroup.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve")) 458 { 459 460 <div class="variant-dropdown border-grey p-2 mt-3 d-flex justify-content-between align-items-center pointer js-variant-dropdown"> 461 <p class="m-0"><span data-bind="text: SelectedVariantOption0">@SelectedVariant.GetString("Ecom:VariantOption.Name")</span></p> 462 <i class="fas fa-chevron-down"></i> 463 </div> 464 465 466 } 467 else if (RelevantVariantGroup.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") && variantCount > 5) 468 { 469 470 <div class="variant-dropdown border-grey p-2 mt-3 d-flex justify-content-between align-items-center pointer" data-toggle="modal" data-target="#variantModal"> 471 <p class="m-0"> 472 <span class="font-weight-bold"> 473 <i class="fas fa-chevron-right fa-2xs"></i> Skift design 474 </span> 475 </p> 476 </div> 477 } 478 479 <div class="@variantSelectorClass"> 480 @foreach (LoopItem Variant in RelevantVariantGroup.GetLoop("VariantAvailableOptions")) 481 { 482 LoopItem VariantCombination = GetLoop("Co3VariantCombinations").FirstOrDefault(x => x.GetString("Ecom:VariantCombination.VariantID").ToString().Contains(Variant.GetString("Ecom:VariantOption.ID"))); 483 string VariantName = Variant.GetString("Ecom:VariantOption.Name"); 484 string VariantIdentifier = Variant.GetString("Ecom:VariantOption.ID"); 485 string VariantLink = VariantCombination.GetString("Ecom:VariantCombination.Link.Clean"); 486 string VariantPreview = String.Empty; 487 var ObservableToSet = "SelectedVariantOption0"; 488 var ObservableToSetId = "SelectedVariantOptionId0"; 489 bool ShowPreview = RelevantVariantGroup.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") || RelevantVariantGroup.GetString("Ecom:VariantGroup.Name").ToLower().Contains("materiale"); 490 491 List<string> variantCombinationVariantIds = GetLoop("Co3VariantCombinations").Select(p => p.GetString("Ecom:VariantCombination.VariantID")).ToList(); 492 string variantOptionColor = Variant.GetString("Ecom:VariantOption.ColorHex"); 493 string variantIdWithColorOptionId = variantCombinationVariantIds.FirstOrDefault(vid => vid.Contains(VariantIdentifier)); 494 495 if (!String.IsNullOrEmpty(Variant.GetString("Ecom:VariantOption.ImgSmall.Clean")) && ShowPreview) 496 { 497 if (Variant.GetString("Ecom:VariantOption.ImgSmall.Clean").StartsWith("#")) 498 { 499 VariantPreview = "style= \"background-color: " + Variant.GetString("Ecom:VariantOption.ImgSmall.Clean") + ";\""; 500 } 501 else 502 { 503 VariantPreview = "style=\"background-image: url('" + Variant.GetString("Ecom:VariantOption.ImgSmall.Clean") + "');\""; 504 } 505 } 506 507 if (!RelevantVariantGroup.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") || variantCount > 5) 508 { 509 <div class="option pointer color-primary d-flex w-100 p-2 bg-grey" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VariantIdentifier', @ObservableToSet, '@VariantName', @ObservableToSetId)"> 510 @if (!String.IsNullOrEmpty(VariantPreview)) 511 { 512 <span class="option-preview mr-2" @VariantPreview></span> 513 } 514 <span>@VariantName</span> 515 </div> 516 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSet, value: '@SelectedVariant.GetString("Ecom:VariantOption.Name")' }"></div> 517 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSetId, value: '@SelectedVariant.GetString("Ecom:VariantOption.ID")' }"></div> 518 } 519 else 520 { 521 if (variantIdWithColorOptionId.IsNullOrEmpty() == false) 522 { 523 bool selectedVariant = Variant.GetBoolean("Ecom:VariantOption.Selected"); 524 string selectedVariantClass = ""; 525 if (selectedVariant) 526 { 527 selectedVariantClass = "selected"; 528 } 529 530 if (isUsingCylindoAssets) 531 { 532 string cylindoInitialValue = string.Join(" ", cylindoSelectedVariantValues.Split('|')); 533 string cylindoValue = Mobler.Website.CustomCode.Frontend.CylindoHelper.GetCylindoSelectedVariantValues(Variant.GetString("Ecom:VariantOption.ID"), GetString("Ecom:Product.LanguageID")); 534 535 if (!string.IsNullOrEmpty(cylindoProductFieldValues)) 536 { 537 <div class="d-none" data-bind="setInitValue: { observable: ConstantCylindoFeatures, value: '@cylindoProductFieldValues' }"></div> 538 } 539 <div class="d-none" data-bind="setInitValue: { observable: InitialCylindoFeatures, value: '@cylindoInitialValue' }"></div> 540 541 <div id="@VariantIdentifier" class="product-item__variant large js-is-cylindo-option is-color-option @selectedVariantClass" style="background-color: @variantOptionColor;" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VariantIdentifier', @ObservableToSet, '@VariantName', @ObservableToSetId, $element, true)" data-cylindo-features="@cylindoValue" data-value="@Variant.GetString("Ecom:VariantOption.ID")" data-variant-label="@Variant.GetString("Ecom:VariantOption.Name")"></div> 542 } 543 else 544 { 545 <div id="@VariantIdentifier" class="product-item__variant large @selectedVariantClass" style="background-color: @variantOptionColor;" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VariantIdentifier', @ObservableToSet, '@VariantName', @ObservableToSetId)"></div> 546 } 547 548 549 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSet, value: '@SelectedVariant.GetString("Ecom:VariantOption.Name")' }"></div> 550 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSetId, value: '@SelectedVariant.GetString("Ecom:VariantOption.ID")' }"></div> 551 } 552 } 553 } 554 </div> 555 </div> 556 } 557 else if (PossibleVariantCombinations == VariantCombinationsCount) 558 { 559 Dictionary<string, string> CurrentlySelectedVariantOptions = new Dictionary<string, string>(); 560 foreach (var VG in GetLoop("VariantGroups")) 561 { 562 foreach (var VO in VG.GetLoop("VariantAvailableOptions").Where(x => x.GetBoolean("Ecom:VariantOption.Selected"))) 563 { 564 CurrentlySelectedVariantOptions.Add(VG.GetString("Ecom:VariantGroup.Name"), VO.GetString("Ecom:VariantOption.ID")); 565 } 566 } 567 int VGLoopCounter = 0; 568 foreach (var VG in GetLoop("VariantGroups")) 569 { 570 List<LoopItem> RelevantVariantCombinations = new List<LoopItem>(); 571 var VariantCombinationLink = GetLoop("Co3VariantCombinations"); 572 var SelectedVariant = VG.GetLoop("VariantAvailableOptions").FirstOrDefault(x => x.GetBoolean("Ecom:VariantOption.Selected")); 573 var ObservableToSet = "SelectedVariantOption" + VGLoopCounter.ToString(); 574 var ObservableToSetId = "SelectedVariantOptionId" + VGLoopCounter.ToString(); 575 int variantCount = VG.GetLoop("VariantAvailableOptions").Count; 576 var variantModalColorKey = ""; 577 578 VGLoopCounter++; 579 580 var variantSelectorClass = "variant-options bg-white"; 581 if (!VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("fasthed")) 582 { 583 if (VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") && variantCount <= 5) 584 { 585 variantSelectorClass = "variant-selector__color-picker d-flex flex-wrap"; 586 } 587 else if (VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") && variantCount > 5) 588 { 589 variantModalColorKey = VGLoopCounter.ToString(); 590 variantSelectorClass = "variant-options bg-white"; 591 } 592 else 593 { 594 variantSelectorClass = "radio-button d-flex flex-column"; 595 } 596 } 597 598 foreach (var VC in VariantCombinationLink) 599 { 600 var Add = true; 601 foreach (var KVP in CurrentlySelectedVariantOptions.Where(s => s.Key != VG.GetString("Ecom:VariantGroup.Name"))) 602 { 603 if (!VC.GetString("Ecom:VariantCombination.VariantID").ToString().Contains(KVP.Value)) 604 { 605 Add = false; 606 } 607 } 608 if (Add) 609 { 610 RelevantVariantCombinations.Add(VC); 611 } 612 } 613 614 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSet, value: '@SelectedVariant.GetString("Ecom:VariantOption.Name")' }"></div> 615 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSetId, value: '@SelectedVariant.GetString("Ecom:VariantOption.ID")' }"></div> 616 617 string variantInfoText = Translate("VariantInfo-" + VG.GetString("Ecom:VariantGroup.Name") + "-" + BrandName, ""); 618 if (string.IsNullOrEmpty(variantInfoText)) 619 { 620 variantInfoText = Translate("VariantInfo-" + VG.GetString("Ecom:VariantGroup.Name"), ""); 621 } 622 string variantInfoTextEscapedLineBreaks = Regex.Replace(variantInfoText, @"\r\n?|\n", "<br />"); 623 624 string cylindoValue = string.Join(" ", cylindoSelectedVariantValues.Split('|')); 625 if (!string.IsNullOrEmpty(cylindoProductFieldValues)) 626 { 627 <div class="d-none" data-bind="setInitValue: { observable: ConstantCylindoFeatures, value: '@cylindoProductFieldValues' }"></div> 628 } 629 <div class="d-none" data-bind="setInitValue: { observable: InitialCylindoFeatures, value: '@cylindoValue' }"></div> 630 631 <div class="variant-selector position-relative border-grey border-bottom-0 p-4" data-variantgroupid="@VG.GetString("Ecom:VariantGroup.ID")"> 632 <div class="variant-selector__header"> 633 <div class="variant-selector__header-inner"> 634 <span class="font-weight-bold fs0">@VG.GetString("Ecom:VariantGroup.Label")</span> 635 <div class="variant-selector__header-choices"> 636 <span class="variantoptionname fs0" data-bind="text: @ObservableToSet">@SelectedVariant.GetString("Ecom:VariantOption.Name")</span> 637 </div> 638 </div> 639 <span class="variant-selector__toggle" data-bind="click: ToggleVariantInfo.bind($data, '@variantInfoTextEscapedLineBreaks')"> 640 <span>@Translate("InfoIconText", "info")</span> 641 <span class="fas fa-info-circle"></span> 642 </span> 643 </div> 644 @if (VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("fasthed")) 645 { 646 <div class="variant-dropdown border-grey p-2 mt-3 d-flex justify-content-between align-items-center pointer js-variant-dropdown"> 647 <p class="m-0"><span data-bind="text: @ObservableToSet">@SelectedVariant.GetString("Ecom:VariantOption.Name")</span></p> 648 <i class="fas fa-chevron-down"></i> 649 </div> 650 } 651 else if (VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") && variantCount > 5) 652 { 653 654 <div class="d-none js-variant-key" data-value="@((int)VG.Values["VariantGroups.LoopCounter"] - 1)"></div> 655 //<div class="d-none" data-bind="setInitValue: { observable: 'SelectedVariantOptionIdForModal', value: '@((int)VG.Values["VariantGroups.LoopCounter"] - 1)' }"></div> 656 <div class="variant-dropdown border-grey p-2 mt-3 d-flex justify-content-between align-items-center pointer" data-toggle="modal" data-target="#variantModal"> 657 <p class="m-0"> 658 <span class="font-weight-bold"><i class="fas fa-chevron-right fa-2xs"></i> @Translate("Product - Change design dropdown - Title", "Skift design")</span> @*(@variantCount muligheder)*@ 659 </p> 660 </div> 661 } 662 663 664 <div class="@variantSelectorClass"> 665 @foreach (var VO in VG.GetLoop("VariantAvailableOptions")) 666 { 667 var VariantCombination = RelevantVariantCombinations.Where(vc => vc.GetString("Ecom:VariantCombination.VariantID").ToString().Contains(VO.GetString("Ecom:VariantOption.ID"))).FirstOrDefault(); 668 string VariantLink = VariantCombination.GetString("Ecom:VariantCombination.Link.Clean"); 669 string VariandOptionId = VariantCombination.GetString("Ecom:VariantCombination.VariantID"); 670 string VariantPreview = String.Empty; 671 string VOName = VO.GetString("Ecom:VariantOption.Name"); 672 string VOId = VO.GetString("Ecom:VariantOption.ID"); 673 bool ShowPreview = VG.GetString("Ecom:VariantGroup.Name").ToString().ToLower().Contains("farve") || VG.GetString("Ecom:VariantGroup.Name").ToString().ToLower().Contains("materiale") ? true : false; 674 675 List<string> variantCombinationVariantIds = GetLoop("Co3VariantCombinations").Select(p => p.GetString("Ecom:VariantCombination.VariantID")).ToList(); 676 string variantOptionColor = VO.GetString("Ecom:VariantOption.ColorHex"); 677 string variantIdWithColorOptionId = variantCombinationVariantIds.FirstOrDefault(vid => vid.Contains(VariandOptionId)); 678 679 if (!String.IsNullOrEmpty(VO.GetString("Ecom:VariantOption.ImgSmall.Clean")) && ShowPreview) 680 { 681 if (VO.GetString("Ecom:VariantOption.ImgSmall.Clean").StartsWith("#")) 682 { 683 VariantPreview = "style= \"background-color: " + VO.GetString("Ecom:VariantOption.ImgSmall.Clean") + ";\""; 684 } 685 else 686 { 687 VariantPreview = "style=\"background-image: url('" + VO.GetString("Ecom:VariantOption.ImgSmall.Clean") + "');\""; 688 } 689 } 690 691 if (VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("fasthed") || VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") && variantCount > 5) 692 { 693 <div class="option d-flex w-100 p-2 bg-grey color-primary pointer" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VOId', @ObservableToSet, '@VOName', @ObservableToSetId)"> 694 @if (!String.IsNullOrEmpty(VariantPreview)) 695 { 696 <span class="option-preview mr-2" @VariantPreview></span> 697 } 698 @VOName 699 </div> 700 } 701 else if (VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") && variantCount <= 5) 702 { 703 bool selectedVariant = VO.GetBoolean("Ecom:VariantOption.Selected"); 704 string selectedVariantClass = ""; 705 if (selectedVariant) 706 { 707 selectedVariantClass = "selected"; 708 } 709 710 if (variantIdWithColorOptionId.IsNullOrEmpty() == false) 711 { 712 <div id="@VOId" class="variantToggle product-item__variant large @selectedVariantClass" style="background-color: @variantOptionColor;" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VOId', @ObservableToSet, '@VOName', @ObservableToSetId, $element)"><i class="fa-solid fa-check"></i></div> 713 } 714 } 715 else if (!VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("fasthed") || VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve")) 716 { 717 bool selectedVariant = VO.GetBoolean("Ecom:VariantOption.Selected"); 718 string selectedVariantClass = ""; 719 if (selectedVariant) 720 { 721 selectedVariantClass = "selected"; 722 } 723 724 string cylindoVariantKeyValue = Mobler.Website.CustomCode.Frontend.CylindoHelper.GetCylindoSelectedVariantValues(VOId, GetString("Ecom:Product.LanguageID")); 725 726 if (isUsingCylindoAssets && !string.IsNullOrEmpty(cylindoVariantKeyValue)) 727 { 728 729 730 <div id="@VOId" class="variantToggle product-item__variant--radiobutton js-is-cylindo-option @selectedVariantClass mb-2" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VOId', @ObservableToSet, '@VOName', @ObservableToSetId, $element, true)" data-cylindo-features="@cylindoVariantKeyValue" data-variant-label="@VOName" data-value="@VOId"> 731 <span class="radiobutton"></span>@VOName 732 </div> 733 } 734 else 735 { 736 <div id="@VOId" class="variantToggle product-item__variant--radiobutton @selectedVariantClass mb-2" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VOId', @ObservableToSet, '@VOName', @ObservableToSetId, $element)"> 737 <span class="radiobutton"></span>@VOName 738 </div> 739 } 740 741 742 } 743 } 744 </div> 745 </div> 746 } 747 } 748 else 749 { 750 var SelectedVariantId = GetString("Ecom:Product.VariantID"); 751 var SelectedVariant = GetLoop("Co3VariantCombinations").Where(x => x.GetString("Ecom:VariantCombination.VariantID") == SelectedVariantId).FirstOrDefault(); 752 var SelectedVariantName = ""; 753 if (SelectedVariant != null) 754 { 755 SelectedVariantName = SelectedVariant.GetString("Ecom:VariantCombination.VariantText"); 756 } 757 <div class="d-none" data-bind="setInitValue: { observable: SelectedVariantOption0, value: '@SelectedVariantName' }"></div> 758 <div class="d-none" data-bind="setInitValue: { observable: SelectedVariantOptionId0, value: '@SelectedVariantId' }"></div> 759 760 <div class="position-relative"> 761 <div class="variant-dropdown p-2 box-shadow mt-3 d-flex justify-content-between align-items-center pointer js-variant-dropdown"> 762 <p class="m-0"><span class="font-weight-bold">@Translate("FallbackVariantsText", "VARIANTER")</span> - <span data-bind="text: SelectedVariantOption0">@SelectedVariantName</span></p> 763 <i class="fas fa-chevron-down"></i> 764 </div> 765 <div class="variant-options bg-white"> 766 @foreach (var Variant in GetLoop("Co3VariantCombinations")) 767 { 768 string VariantName = Variant.GetString("Ecom:VariantCombination.VariantText"); 769 string VCId = Variant.GetString("Ecom:VariantCombination.VariantID"); 770 <div class="option color-primary pointer d-block p-2 bg-grey" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VCId', SelectedVariantOption0, '@VariantName', SelectedVariantOptionId0)">@VariantName</div> 771 } 772 </div> 773 </div> 774 } 775 </div> 776 } 777 778 @{ 779 var tilvalgRelatedGroups = GetLoop("ProductRelatedGroups").Where(g => !g.GetString("Ecom:Product:RelatedGroup.Name").StartsWith("VAR_") && !g.GetString("Ecom:Product:RelatedGroup.Name").StartsWith("TIL_") && g.GetLoop("Products").Count > 0); 780 } 781 782 783 @if( isUsingCylindoAssets) 784 { 785 List<CylindoProductRelationGroup> cylindoProductRelationGroups = Mobler.Website.CustomCode.Frontend.CylindoHelper.GetCylindoProductRelations(GetString("Ecom:Product.ID"), GetString("Ecom:Product.LanguageID")); 786 787 string cylindoDefaultOverrideKey = GetString("Ecom:Product:Field.CylindoDefaultOverrideKey.Value.Clean"); 788 789 foreach (CylindoProductRelationGroup cylindoProductRelationGroup in cylindoProductRelationGroups) 790 { 791 if (cylindoProductRelationGroup != null && cylindoProductRelationGroup.Products.Any()) 792 { 793 string groupName = cylindoProductRelationGroup.Name; 794 string defaultFeatureKey = cylindoProductRelationGroup.CylindoKey; 795 string key = cylindoProductRelationGroup.Key; 796 797 string groupNameWithoutPrefix = groupName.Replace(key, string.Empty); 798 string groupNameTranslationKey = $"Cylindo - Related Group Label - {groupNameWithoutPrefix}"; 799 string groupNameInLowerCase = groupName.ToLower().Replace(" ", "-"); 800 801 <div class="optionalselectors position-relative border-grey border-bottom-0 p-4"> 802 <div class="optional-selectors__header"> 803 <span class="font-weight-bold fs0">@Translate("Tilvalg", "Tilvalg")</span> 804 <span class="variant-selector__toggle" data-bind="click: ToggleOptionalInfo"> 805 <span>@Translate("InfoIconText", "info")</span> 806 <span class="fas fa-info-circle"></span> 807 </span> 808 </div> 809 <div class="optional-selectors__body"> 810 <div class="optionalselectors--selector d-flex align-items-center mb-4 position-relative js-optional-selector" data-pretty-group-name="@Translate(groupNameTranslationKey, groupNameWithoutPrefix)" data-group-name="@groupNameInLowerCase"> 811 <div class="optionalselectors--selector__label u-inline-block d-flex"> 812 @Translate(groupNameTranslationKey, groupNameWithoutPrefix) 813 <span class="optionalselector-remove js-optional-selector-remove optionalselector-remove__@groupNameInLowerCase d-none" data-bind="click: RemoveOptionalProduct"><i class="fa-solid fa-circle-xmark"></i></span> 814 </div> 815 <div class="variant-dropdown border-grey p-2 d-flex justify-content-between align-items-center pointer js-variant-dropdown js-optional-dropdown"> 816 <p class="m-0"><span>@Translate("V&aelig;lg", "V&aelig;lg")</span></p> 817 <svg class="svg-inline--fa fa-chevron-down fa-w-14" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-down" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"></path></svg><!-- <i class="fas fa-chevron-down"></i> Font Awesome fontawesome.com --> 818 </div> 819 <div class="variant-options bg-white js-cylindo-relations-select" data-cylindo-key="@defaultFeatureKey"> 820 @foreach (CylindoProductRelation relationProduct in cylindoProductRelationGroup.Products) 821 { 822 string optionalProductId = relationProduct.Id; 823 string optionalProductName = relationProduct.Name; 824 bool relationProductHasBundleDiscount = freeProductIdsFromDiscount.Contains(optionalProductId); 825 string bundleDiscountedPrice = "0"; 826 string optionalProductPrice = relationProductHasBundleDiscount ? MoblerHelpers.formatPrice(bundleDiscountedPrice) : MoblerHelpers.formatPrice(relationProduct.Price); 827 string optionalProductPriceUnformatted = relationProductHasBundleDiscount ? bundleDiscountedPrice : relationProduct.Price.ToString(CultureInfo.InvariantCulture); 828 string optionalProductPriceBefore = relationProduct.CampaignNormalPrice; 829 string optionalProductPriceDiscount = relationProduct.CampaignDiscountAmount; 830 string optionalProductDiscountText = ""; 831 if (!String.IsNullOrEmpty(optionalProductPriceDiscount)) 832 { 833 optionalProductDiscountText = "(SPAR: " + optionalProductPriceDiscount + ")"; 834 } 835 string optionalProductVariantId = relationProduct.VariantId; 836 string optionalProductVariantText = relationProduct.VariantText; 837 838 <div class="option d-flex w-100 p-2 bg-grey color-primary pointer js-cylindo-relations-select-option" id="@optionalProductId@optionalProductVariantId" data-cylindo-feature="@relationProduct.Features" data-bind="click: UpdateOptionalProduct.bind($data, '@optionalProductId', '@optionalProductVariantId', '@optionalProductName', '@optionalProductVariantText', '@optionalProductPrice', @optionalProductPriceUnformatted, '@optionalProductPriceBefore', '@optionalProductPriceDiscount', '@groupNameInLowerCase')"> 839 @optionalProductName @optionalProductVariantText @(relationProduct.Price > 0 || key == "TIL_" ? $"- {optionalProductPrice} {optionalProductDiscountText}" : string.Empty ) 840 </div> 841 } 842 </div> 843 </div> 844 845 </div> 846 </div> 847 } 848 } 849 if (string.IsNullOrEmpty(cylindoDefaultOverrideKey) == false) 850 { 851 var splitConstantFeatures = cylindoProductFieldValues.Split('|'); 852 string selectedDefaultOverrideFeatureCode = string.Empty; 853 string selectedDefaultOverrideFeatureText = "Vælg"; 854 string cylindoDefaultOverrideTranslatioKey = $"Cylindo - Overide Feature - Label - {cylindoDefaultOverrideKey}"; 855 if (splitConstantFeatures.Any()) 856 { 857 foreach (var splitConstantFeature in splitConstantFeatures) 858 { 859 string[] splitValues = splitConstantFeature.Split(new char[] {' '}, 2); 860 if (splitValues[0].ToLowerInvariant() == cylindoDefaultOverrideKey.ToLowerInvariant()) 861 { 862 selectedDefaultOverrideFeatureCode = splitValues[1]; 863 } 864 } 865 866 } 867 868 <div class="d-none" data-bind="setInitValue: { observable: CylindoDefaultOverrideKey, value: '@cylindoDefaultOverrideKey' }"></div> 869 <div class="d-none" data-bind="setInitValue: { observable: CylindoSelectedDefaultOverrideText, value: '@selectedDefaultOverrideFeatureText' }"></div> 870 <div class="d-none" data-bind="setInitValue: { observable: CylindoSelectedDefaultOverrideCode, value: '@selectedDefaultOverrideFeatureCode' }"></div> 871 872 <div class="optionalselectors position-relative border-grey border-bottom-0 p-4"> 873 <div class="optional-selectors__header"> 874 <span class="font-weight-bold fs0"> 875 @Translate("Cylindo - Override Feature - Header", "Cylindo - Override Feature - Header") 876 </span> 877 </div> 878 <div class="optional-selectors__body"> 879 <div class="optionalselectors--selector d-flex align-items-center mb-4 position-relative js-cylindo-override-selector"> 880 <div class="optionalselectors--selector__label u-inline-block d-flex"> 881 @Translate(cylindoDefaultOverrideTranslatioKey, cylindoDefaultOverrideKey) 882 </div> 883 <div class="variant-dropdown border-grey p-2 d-flex justify-content-between align-items-center pointer js-variant-dropdown js-cylindo-override-dropdown"> 884 <p class="m-0" data-bind="text: CylindoSelectedDefaultOverrideText"> 885 886 </p> 887 <svg class="svg-inline--fa fa-chevron-down fa-w-14" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-down" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"></path></svg><!-- <i class="fas fa-chevron-down"></i> Font Awesome fontawesome.com --> 888 </div> 889 <div class="variant-options bg-white js-cylindo-override-select" data-cylindo-key="@cylindoDefaultOverrideKey" data-bind="foreach: CylindoDefaultOverrideOptions"> 890 <div data-bind="attr: {'id': `cylindo-override-option-${code}`, 'data-cylindo-feature': code, 'data-cylindo-key': '@cylindoDefaultOverrideKey', 'data-cylindo-default-value': '@selectedDefaultOverrideFeatureCode'}, click: $parent.UpdateSelectedCylindoOverrideOption" class="option d-flex w-100 p-2 bg-grey color-primary pointer js-cylindo-override-select-option"> 891 <span data-bind="text:name"></span> 892 </div> 893 </div> 894 </div> 895 896 </div> 897 </div> 898 } 899 } 900 901 @if (tilvalgRelatedGroups.Count() > 0) 902 { 903 <div class="optionalselectors position-relative border-grey border-bottom-0 p-4"> 904 <div class="optional-selectors__header"> 905 <span class="font-weight-bold fs0">@Translate("Tilvalg", "Tilvalg")</span> 906 <span class="variant-selector__toggle" data-bind="click: ToggleOptionalInfo"> 907 <span>@Translate("InfoIconText", "info")</span> 908 <span class="fas fa-info-circle"></span> 909 </span> 910 </div> 911 <div class="optional-selectors__body"> 912 @foreach (LoopItem relatedGroup in tilvalgRelatedGroups) 913 { 914 string groupName = relatedGroup.GetString("Ecom:Product:RelatedGroup.Name"); 915 string groupNameInLowerCase = groupName.ToLower().Replace(" ", "-"); 916 917 <div class="optionalselectors--selector d-flex align-items-center mb-4 position-relative js-optional-selector @(useInclExclLogic ? "js-optional-selector-use-incl-excl-logic" : "")" data-parentgroupname="@parentGroupName" data-pretty-group-name="@groupName" data-group-name="@groupNameInLowerCase"> 918 <div class="optionalselectors--selector__label u-inline-block d-flex"> 919 @groupName 920 <span class="optionalselector-remove js-optional-selector-remove optionalselector-remove__@groupNameInLowerCase d-none" data-bind="click: RemoveOptionalProduct"><i class="fa-solid fa-circle-xmark"></i></span> 921 </div> 922 <div class="variant-dropdown border-grey p-2 d-flex justify-content-between align-items-center pointer js-variant-dropdown js-optional-dropdown"> 923 <p class="m-0"><span>@Translate("V&aelig;lg", "V&aelig;lg")</span></p> 924 <svg class="svg-inline--fa fa-chevron-down fa-w-14" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-down" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"></path></svg><!-- <i class="fas fa-chevron-down"></i> Font Awesome fontawesome.com --> 925 </div> 926 <div class="variant-options bg-white"> 927 @foreach (LoopItem product in relatedGroup.GetLoop("Products")) 928 { 929 string optionalProductId = product.GetString("Ecom:Product.ID"); 930 string optionalProductName = product.GetString("Ecom:Product.Name"); 931 bool productHasBundleDiscount = freeProductIdsFromDiscount.Contains(optionalProductId); 932 string bundleDiscountedPrice = "0"; 933 string optionalProductPrice = productHasBundleDiscount ? MoblerHelpers.formatPrice(bundleDiscountedPrice) : MoblerHelpers.formatPrice(product.GetString("Ecom:Product.Price.PriceWithVAT.Value")); 934 string optionalProductPriceUnformatted = productHasBundleDiscount ? bundleDiscountedPrice : product.GetString("Ecom:Product.Price.Price.Value"); 935 string optionalProductPriceBefore = product.GetString("CampaignModule:Product.CampaignProduct.NormalPrice"); 936 string optionalProductPriceDiscount = product.GetString("CampaignModule:Product.CampaignProduct.DiscountAmount.FormattedRetail"); 937 string optionalProductDiscountText = ""; 938 if (!String.IsNullOrEmpty(optionalProductPriceDiscount)) 939 { 940 optionalProductDiscountText = "(SPAR: " + optionalProductPriceDiscount + ")"; 941 } 942 string optionalProductVariantId = product.GetString("Ecom:Product.VariantID"); 943 string optionalProductVariantText = product.GetString("Ecom:Product.VariantText"); 944 945 <div class="option d-flex w-100 p-2 bg-grey color-primary pointer" id="@optionalProductId@optionalProductVariantId" data-bind="click: UpdateOptionalProduct.bind($data, '@optionalProductId', '@optionalProductVariantId', '@optionalProductName', '@optionalProductVariantText', '@optionalProductPrice', @optionalProductPriceUnformatted, '@optionalProductPriceBefore', '@optionalProductPriceDiscount', '@groupNameInLowerCase')"> 946 @optionalProductName @optionalProductVariantText - @optionalProductPrice @optionalProductDiscountText 947 </div> 948 } 949 </div> 950 </div> 951 } 952 </div> 953 </div> 954 } 955 956 @if (seeProductInStore != "") 957 { 958 <div class="d-flex flex-wrap flex-column justify-content-end border-grey border-bottom-0 p-4 mt-2"> 959 <div class="align-self-center w-100"> 960 <a href="@seeProductInStore" class="btn brand-primary-outline see-product-btn font-weight-bold add-to-cart m-0 d-flex align-items-center justify-content-center w-100 px-4">@Translate("Se produktet i en af vores butikker", "Se produktet i en af vores butikker")</a> 961 </div> 962 </div> 963 } 964 965 <div class="position-relative border-grey p-4"> 966 967 @if (ForSale) // Indicates that the item is actually NOT for sale.. 968 { 969 <div class="w-100 d-flex justify-content-center flex-wrap"> 970 <div class="col-lg-12 col-md-6 col-sm-12 p-0 mb-3"> 971 @{ 972 string addToCartButtonClass = blackFridayTheme ? "btn-black-friday" : "btn-primary"; 973 } 974 <div class="btn @addToCartButtonClass btn-primary-border btn-big add-to-cart w-100 justify-content-center d-flex align-items-center font-weight-bold" data-bind="click: AddToCart.bind($data, '@DataLayerPrice')"> 975 <div class="mr-2"><i class="fa-solid fa-cart-shopping fs3"></i></div> 976 <div>@Translate("ProductAddToCart", "L&aelig;g i kurv")</div> 977 </div> 978 </div> 979 </div> 980 } 981 982 @{ 983 string findDealerLink = ""; 984 if (!string.IsNullOrEmpty(shopname) && shopname.ToLower() != CStrings.moblerName.ToLower()) 985 { 986 findDealerLink = $"/{shopname}/om-os"; 987 } 988 else 989 { 990 findDealerLink = "" + PageView.Current().Area.Item["FindDealerPage"]; 991 } 992 } 993 <div class="w-100 d-flex justify-content-center flex-wrap"> 994 <div class="col-lg-12 col-md-6 col-sm-12 p-0"> 995 <a data-shopname="@(shopname) @(CStrings.moblerName)" href="@(findDealerLink)" class="btn brand-dark-grey-outline btn-big add-to-cart w-100 justify-content-center d-flex align-items-center font-weight-bold"> 996 <div class="mr-2"><i class="fa-solid fa-shop fs3"></i></div> 997 <div>@Translate("HeaderNav.FindShop", "Find forhandler")</div> 998 </a> 999 </div> 1000 </div> 1001 1002 @if (!string.IsNullOrEmpty(PayeverWidget.WidgetId) && !string.IsNullOrEmpty(PayeverWidget.BusinessId) && !string.IsNullOrEmpty(PayeverWidget.CheckoutId) && ForSale) 1003 { 1004 <div class="financing-calculation mt-3 d-flex justify-content-center flex-wrap"> 1005 <svg viewBox="0 0 22 20" width="22px" xmlns="http://www.w3.org/2000/svg"><path fill="#FF0000" d="M11.4298851,0 C11.3287356,0.609137056 11.4298851,1.31979695 11.5310345,1.82741117 C11.7333333,2.53807107 12.137931,3.04568528 13.3517241,4.67005076 C14.6666667,6.29441624 15.0712644,6.9035533 15.3747126,7.81725888 C15.5770115,8.52791878 15.6781609,8.93401015 15.4758621,9.54314721 C16.5885057,9.74619289 18.4091954,10.3553299 19.6229885,11.0659898 C20.7356322,11.7766497 21.5448276,12.6903553 21.8482759,13.6040609 C22.0505747,14.1116751 22.0505747,14.9238579 21.8482759,15.4314721 C21.645977,15.9390863 21.4436782,16.3451777 21.0390805,16.751269 C19.5218391,18.3756345 16.6896552,19.4923858 13.1494253,19.8984772 C12.3402299,20 10.4183908,20 9.81149425,20 C5.15862069,19.6954315 1.31494253,18.071066 0.303448276,15.8375635 C-0.101149425,14.9238579 -0.101149425,14.1116751 0.303448276,13.1979695 C0.708045977,12.4873096 1.71954023,11.4720812 3.03448276,10.8629442 C4.24827586,10.2538071 5.36091954,9.84771574 6.57471264,9.54314721 C6.67586207,10.3553299 8.19310345,12.284264 8.69885057,12.893401 C9.81149425,14.3147208 10.216092,15.0253807 10.5195402,15.6345178 C10.7218391,15.9390863 10.8229885,16.142132 10.7218391,16.8527919 C10.7218391,16.8527919 11.1264368,16.4467005 11.3287356,16.142132 C11.5310345,15.7360406 11.6321839,15.3299492 11.5310345,14.7208122 C11.5310345,14.0101523 11.3287356,13.6040609 11.0252874,12.9949239 C10.7218391,12.3857868 10.3172414,11.6751269 9.40689655,10.6598985 C8.3954023,9.03553299 7.9908046,8.22335025 7.78850575,7.20812183 C7.5862069,6.09137056 7.88965517,4.7715736 8.59770115,4.16243655 C8.69885057,4.06091371 9.00229885,3.95939086 9.00229885,3.95939086 L9.00229885,4.56852792 C9.00229885,5.27918782 9.10344828,5.68527919 9.40689655,6.1928934 C9.71034483,6.9035533 10.216092,7.51269036 11.6321839,9.23857868 C12.9471264,10.7614213 13.3517241,11.5736041 13.3517241,12.4873096 L13.3517241,12.9949239 C13.3517241,12.9949239 13.6551724,12.6903553 13.8574713,12.4873096 C14.3632184,11.7766497 14.4643678,10.5583756 14.0597701,9.54314721 C13.7563218,8.83248731 13.3517241,8.22335025 12.137931,6.59898477 C10.8229885,4.97461929 10.3172414,3.75634518 10.3172414,2.63959391 C10.3172414,1.52284264 10.5195402,0.609137056 11.4298851,0 L11.4298851,0 Z"></path></svg> 1006 <span class="d-none" data-bind="setInitValue: { observable: FinancingPrice, value: '@financingPriceRoundedFormatted' }"></span> 1007 <span class="financing-calculation--price" data-bind="text: FinancingPrice"></span><span> @Translate("kr./måned i 10 måneder", "kr./måned i 10 måneder")</span> 1008 </div> 1009 } 1010 1011 </div> 1012 1013 <div class="usps-table w-100 p-4"> 1014 @if ((QuickDelivery || DisplayAverageDeliveryTime) && !HideDelivery) { 1015 <div class="d-none" data-bind="setInitValue: { observable: DeliveryTime, value: '@DeliveryTime' }"></div> 1016 <div class="d-none" data-bind="setInitValue: { observable: DisplayDeliveryFontWeightModifier, value: @(QuickDelivery ? "true" : "false" ) }"></div> 1017 1018 <div class="usp-row" role="button" data-bind="click: ToggleDeliveryInfo" style="color: @DeliveryColor"> 1019 <div class="usp-cell text-center"><i class="fa-solid fa-truck-fast fs3"></i></div> 1020 <div class="usp-cell"><div class="delivery-text text-underline" data-bind="css: { 'font-weight-bold': DisplayDeliveryFontWeightModifier }"><span data-bind="text: DeliveryTime">@DeliveryTime</span></div></div> 1021 </div> 1022 } 1023 @if (ExperienceInShop) 1024 { 1025 <div class="usp-row" role="button" data-bind="click: ToggleExperienceInShopInfo"> 1026 <div class="usp-cell text-center"><i class="fa-solid fa-shop fs3"></i></div> 1027 <div class="usp-cell text-underline"><span>@ExperienceInShopText</span></div> 1028 </div> 1029 } 1030 <div class="usp-row"> 1031 <div class="usp-cell text-center"><i class="@usp1IconClass"></i></div> 1032 <div class="usp-cell">@usp1IconText</div> 1033 </div> 1034 <div class="usp-row"> 1035 <div class="usp-cell text-center"><i class="@usp2IconClass"></i></div> 1036 <div class="usp-cell">@usp2IconText</div> 1037 </div> 1038 <div class="usp-row"> 1039 <div class="usp-cell text-center"><i class="@usp3IconClass"></i></div> 1040 <div class="usp-cell">@usp3IconText</div> 1041 </div> 1042 </div> 1043 1044 </div> 1045 </div> 1046 1047 if (CostPrice > 0) 1048 { 1049 string PriceDescription = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("DagsprisDescription"); 1050 <div class="delivery-information-popup" data-bind="css: { 'd-flex': PriceInfoOpen }, click: TogglePriceInfo"> 1051 <div class="description-content bg-white px-3 py-4 px-lg-4 position-relative" data-bind="click: OpenPriceInfo, clickBubble: false"> 1052 <div class="modal-closer-custom" data-bind="click: TogglePriceInfo, clickBubble: false"> 1053 <img src="/Files/Templates/Designs/Mobler2018/dist/icons/close.svg" /> 1054 </div> 1055 <div> 1056 @PriceDescription 1057 </div> 1058 </div> 1059 </div> 1060 } 1061 1062 <div class="delivery-information-popup" data-bind="css: { 'd-flex': DeliveryInfoOpen }, click: ToggleDeliveryInfo"> 1063 <div class="description-content bg-white px-3 py-4 px-lg-4 position-relative" data-bind="click: OpenDeliveryInfo, clickBubble: false"> 1064 <div class="modal-closer-custom" data-bind="click: ToggleDeliveryInfo, clickBubble: false"> 1065 <img src="/Files/Templates/Designs/Mobler2018/dist/icons/close.svg" /> 1066 </div> 1067 @if (QuickDelivery) 1068 { 1069 <div> 1070 @QuickDeliveryDescription 1071 </div> 1072 } 1073 else if (DisplayAverageDeliveryTime) 1074 { 1075 <div> 1076 @NormalDeliveryDescription 1077 </div> 1078 } 1079 </div> 1080 </div> 1081 1082 <div class="experienceinshop-information-popup" data-bind="css: { 'd-flex': ExperienceInShopInfoOpen }, click: ToggleExperienceInShopInfo"> 1083 <div class="description-content bg-white px-3 py-4 px-lg-4 position-relative" data-bind="click: OpenExperienceInShopInfo, clickBubble: false"> 1084 <div class="modal-closer-custom" data-bind="click: ToggleExperienceInShopInfo, clickBubble: false"> 1085 <img src="/Files/Templates/Designs/Mobler2018/dist/icons/close.svg" /> 1086 </div> 1087 <div> 1088 @ExperienceInShopDescription 1089 </div> 1090 </div> 1091 </div> 1092 1093 <div class="variant-information-popup" data-bind="css: { 'd-flex': VariantInfoOpen }, click: ToggleVariantInfo"> 1094 <div class="description-content bg-white px-3 py-4 px-lg-4 position-relative" data-bind="click: OpenVariantInfo, clickBubble: false"> 1095 <div class="modal-closer-custom" data-bind="click: ToggleVariantInfo, clickBubble: false"> 1096 <img src="/Files/Templates/Designs/Mobler2018/dist/icons/close.svg" /> 1097 </div> 1098 <p></p> 1099 </div> 1100 </div> 1101 1102 <div class="delivery-information-popup" data-bind="css: { 'd-flex': OptionalInfoOpen }, click: ToggleOptionalInfo"> 1103 <div class="description-content bg-white px-3 py-4 px-lg-4 position-relative" data-bind="click: OpenOptionalInfo, clickBubble: false"> 1104 <div class="modal-closer-custom" data-bind="click: ToggleOptionalInfo, clickBubble: false"> 1105 <img src="/Files/Templates/Designs/Mobler2018/dist/icons/close.svg" /> 1106 </div> 1107 <div> 1108 @Translate("TilvalgInfoboks", "Tilvalg tekst") 1109 </div> 1110 </div> 1111 </div> 1112 1113 if (CostPrice > 0) 1114 { 1115 <div class="w-100 mt-3"> 1116 1117 <div class="delivery-information"> 1118 <div class="live-price-animation"></div> 1119 <p class="delivery-text">@Translate("Product.PriceInfo.RecommendedPriceInfo", "Aktuel dagspris - ")<a href="#" class="text-underline" data-bind="click: TogglePriceInfo">@Translate("Product.PriceInfo.RecommendedPriceLink", "L&aelig;s mere")</a></p> 1120 </div> 1121 1122 </div> 1123 } 1124 1125 <div class="w-100"> 1126 @if (GetBoolean("Ecom:Product:Field.Farvevarianter1.Value")) 1127 { 1128 <img class="img-fluid mb-3 mt-5" src="/Files/Images/Farver/farver1.png" /> 1129 <p class="mb-0">@Translate("ProductColor1Text", "F&aring;s i flere forskellige varianter og moduler, bes&oslash;g dit lokale M&oslash;blér bolighus og f&aring; vejledning til design af din sofa.")</p> 1130 } 1131 else if (GetBoolean("Ecom:Product:Field.Farvevarianter2.Value")) 1132 { 1133 <img class="img-fluid mb-3 mt-5" src="/Files/Images/Farver/farver2.png" /> 1134 <p class="mb-0">@Translate("ProductColor2Text", "Farve 2 hj&aelig;lpetekst")</p> 1135 } 1136 else if (GetBoolean("Ecom:Product:Field.Farvevarianter3.Value")) 1137 { 1138 <img class="img-fluid mb-3 mt-5" src="/Files/Images/Farver/farver3.png" /> 1139 <p class="mb-0">@Translate("ProductColor3Text", "Farve 3 hj&aelig;lpetekst")</p> 1140 } 1141 else if (GetBoolean("Ecom:Product:Field.Farvevarianter4.Value")) 1142 { 1143 <img class="img-fluid mb-3 mt-5" src="/Files/Images/Farver/farver4.png" /> 1144 <p class="mb-0">@Translate("ProductColor4Text", "Farve 4 hj&aelig;lpetekst")</p> 1145 } 1146 else if (GetBoolean("Ecom:Product:Field.Farvevarianter5.Value")) 1147 { 1148 <img class="img-fluid mb-3 mt-5" src="/Files/Images/Farver/farver5.png" /> 1149 <p class="mb-0">@Translate("ProductColor5Text", "Farve 5 hj&aelig;lpetekst")</p> 1150 } 1151 </div> 1152 } 1153 @SnippetEnd("PricesContainer") 1154 1155 @SnippetStart("ProductDetailsMeta") 1156 @if (!string.IsNullOrEmpty(MetaDescription)) 1157 { 1158 <meta name="description" content="@MetaDescription" /> 1159 } 1160 1161 @if (!string.IsNullOrEmpty(MetaTitle)) 1162 { 1163 <title>@MetaTitle</title> 1164 } 1165 else 1166 { 1167 <title>@GetValue("Title")</title> 1168 } 1169 1170 @SnippetEnd("ProductDetailsMeta") 1171 1172 @SnippetStart("OgTags") 1173 <meta property="og:type" content="product" /> 1174 <meta property="og:description" content="@Regex.Replace(ShortDescription, "<.*?>", String.Empty)" /> 1175 <meta property="og:image" content="@OgImage" /> 1176 @SnippetEnd("OgTags") 1177 1178 @using System.Collections 1179 @using Dynamicweb.Core 1180 @using Dynamicweb.Ecommerce 1181 @using Mobler.Website.CustomModules.MoblerHelpers 1182 @using Dynamicweb.Ecommerce.Products 1183 @using Dynamicweb.Ecommerce.Variants 1184 @using Humanizer 1185 @using Mobler.Website.CustomCode 1186 @using Mobler.Website.CustomCode.Models 1187 @using Newtonsoft.Json 1188 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 1189 1190 @using Mobler.Website.CustomModules.MoblerHelpers 1191 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 1192 @functions { 1193 1194 public class SplashPriceObject 1195 { 1196 public string splashType { get; set; } 1197 public string splashText { get; set; } 1198 public string splashTextPrefix { get; set; } 1199 public string onTopSplashText { get; set; } 1200 public string priceFormatted { get; set; } 1201 public string OLprice { get; set; } 1202 public string normalPriceFormatted { get; set; } 1203 public string normalPricePrefix { get; set; } 1204 public int splashTypeInt { get; set; } 1205 } 1206 1207 public double GetDoubleValue(LoopItem loopItem, string key) 1208 { 1209 return loopItem == null ? GetDouble(key) : loopItem.GetDouble(key); 1210 } 1211 public DateTime GetDateValue(LoopItem loopItem, string key) 1212 { 1213 return loopItem == null ? GetDate(key) : loopItem.GetDate(key); 1214 } 1215 public bool GetBooleanValue(LoopItem loopItem, string key) 1216 { 1217 return loopItem == null ? GetBoolean(key) : loopItem.GetBoolean(key); 1218 } 1219 public string GetStringValue(LoopItem loopItem, string key) 1220 { 1221 return loopItem == null ? GetString(key) : loopItem.GetString(key); 1222 } 1223 public SplashPriceObject GetSplashPriceObject(LoopItem product) 1224 { 1225 var result = new SplashPriceObject() 1226 { 1227 normalPriceFormatted = "", 1228 normalPricePrefix = "", 1229 splashText = "", 1230 splashTextPrefix = "", 1231 onTopSplashText = "", 1232 priceFormatted = "", 1233 splashType = "", 1234 splashTypeInt = 0, 1235 OLprice = MoblerHelpers.formatPrice(GetDoubleValue(product, "Ecom:Order:olPrice.Price")) 1236 }; 1237 // Pricing 1238 DateTime newItemExpiryDate = GetDateValue(product, "Ecom:Product:Field.NewItemExpiryDate"); 1239 bool hasNewItemExpiryDate = newItemExpiryDate.Date > DateTime.Now.Date; 1240 bool newItem = GetBooleanValue(product, "Ecom:Product:Field.NewItem") && hasNewItemExpiryDate; 1241 bool LowPrice = GetBooleanValue(product, "Ecom:Product:Field.Splash3"); 1242 bool JubileePrice = GetBooleanValue(product, "Ecom:Product:Field.JubileePrice"); 1243 double CostPrice = GetDoubleValue(product, "Ecom:Product:Field.FirstwebCostPrice.Value.Raw"); 1244 string PriceSaving = GetStringValue(product, "Ecom:Product:Field.Spar.Value"); 1245 double ActualPrice = GetDoubleValue(product, "Ecom:Product.Price.Price"); 1246 1247 // Initialize variables 1248 double spar = 0; 1249 double price = 0; 1250 double normalPrice = 0; 1251 string sparFormatted = ""; 1252 string campaignColor = ""; 1253 string campaignText = ""; 1254 string campaignDate = ""; 1255 bool CampaignActiveOnProduct = GetBooleanValue(product, "CampaignModule:Product.CampaignActiveOnProduct"); 1256 bool IsMasterShopProduct = GetBooleanValue(product, "Ecom:Product:Field.SystemIsMasterShopProduct"); 1257 bool IsLocalWebshopProduct = Mobler.Website.CustomCode.Frontend.Helper.IsLocalWebshop() && !IsMasterShopProduct; 1258 1259 if (!CampaignActiveOnProduct && !IsLocalWebshopProduct) 1260 { 1261 price = ActualPrice; 1262 result.priceFormatted = MoblerHelpers.formatPrice(price); 1263 } 1264 else if (IsLocalWebshopProduct) 1265 { 1266 price = ActualPrice; 1267 result.priceFormatted = MoblerHelpers.formatPrice(price); 1268 1269 normalPrice = GetDoubleValue(product, "Ecom:Product:Field.ForPris"); 1270 if(normalPrice > 0) { 1271 result.normalPriceFormatted = MoblerHelpers.formatPrice(normalPrice); 1272 } 1273 1274 spar = GetDoubleValue(product, "Ecom:Product:Field.Spar"); 1275 sparFormatted = MoblerHelpers.formatPrice(spar); 1276 1277 bool SplashLwCombiPrice = GetBooleanValue(product, "Ecom:Product:Field.SplashLwCombiPrice"); 1278 DateTime SplashLwCombiPriceExpiryDate = GetDateValue(product, "Ecom:Product:Field.SplashLwCombiPriceExpiryDate"); 1279 1280 if (SplashLwCombiPrice && (SplashLwCombiPriceExpiryDate == DateTime.MinValue || SplashLwCombiPriceExpiryDate > DateTime.Now)) 1281 { 1282 campaignText = "KOMBI"; 1283 } 1284 } 1285 else 1286 { 1287 normalPrice = GetDoubleValue(product, "CampaignModule:Product.CampaignProduct.NormalPrice"); 1288 result.normalPriceFormatted = GetStringValue(product, "CampaignModule:Product.CampaignProduct.NormalPrice.FormattedRetail"); 1289 1290 price = GetDoubleValue(product, "CampaignModule:Product.CampaignProduct.CampaignPrice"); 1291 result.priceFormatted = GetStringValue(product, "CampaignModule:Product.CampaignProduct.CampaignPrice.FormattedRetail"); 1292 1293 spar = GetDoubleValue(product, "CampaignModule:Product.CampaignProduct.DiscountAmount"); 1294 sparFormatted = GetStringValue(product, "CampaignModule:Product.CampaignProduct.DiscountAmount.FormattedRetail"); 1295 1296 campaignColor = GetStringValue(product, "CampaignModule:Product.Campaign.Color"); 1297 campaignText = GetStringValue(product, "CampaignModule:Product.Campaign.Text"); 1298 } 1299 1300 // Set Splash Types 1301 if (JubileePrice) 1302 { 1303 result.splashType = "jubileeprice"; 1304 result.splashTypeInt = 6; 1305 result.splashText = sparFormatted; 1306 result.splashTextPrefix = Translate("PriceSaving", "Spar"); 1307 result.onTopSplashText = Translate("ProductJubileePrice", "Jubilæums pris"); 1308 if (campaignText == "KOMBI") { 1309 result.normalPricePrefix = Translate("PriceBeforeCombo", "Samlet normpris."); 1310 } 1311 else if (spar > 0) 1312 { 1313 result.normalPricePrefix = Translate("PriceBefore", "Normpris."); 1314 } 1315 } 1316 else if (campaignText == "KOMBI") 1317 { 1318 result.splashType = "combo"; 1319 result.splashTypeInt = 5; 1320 result.splashText = sparFormatted; 1321 result.splashTextPrefix = Translate("PriceSaving", "Spar"); 1322 result.onTopSplashText = Translate("ProductCombinationOffer", "Sætpris"); 1323 result.normalPricePrefix = Translate("PriceBeforeCombo", "Samlet normpris."); 1324 } 1325 else if (CostPrice > 0) 1326 { 1327 result.splashType = "priceshape"; 1328 result.splashTypeInt = 4; 1329 result.splashText = ""; 1330 result.splashTextPrefix = ""; 1331 result.onTopSplashText = Translate("ProductDailyPriceSplash", "Dagspris"); 1332 } 1333 else if (spar > 0) 1334 { 1335 result.splashType = "offer"; 1336 result.splashTypeInt = 1; 1337 result.splashText = sparFormatted; 1338 result.splashTextPrefix = Translate("PriceSaving", "Spar"); 1339 result.onTopSplashText = Translate("ProductOffer", "Tilbud"); 1340 result.normalPricePrefix = Translate("PriceBefore", "Normpris."); 1341 } 1342 else if (newItem) 1343 { 1344 result.splashType = "new"; 1345 result.splashTypeInt = 2; 1346 result.splashText = ""; 1347 result.splashTextPrefix = ""; 1348 result.onTopSplashText = Translate("ProductNew", "Nyhed"); 1349 } 1350 else if (LowPrice) 1351 { 1352 result.splashType = "low"; 1353 result.splashTypeInt = 3; 1354 result.splashText = ""; 1355 result.splashTextPrefix = ""; 1356 result.onTopSplashText = Translate("ProductLowPrice", "Fast lavpris"); 1357 } 1358 else 1359 { 1360 result.splashType = "invisible"; 1361 result.splashText = ""; 1362 } 1363 return result; 1364 } 1365 1366 } 1367 @helper RenderProductPricing(LoopItem product, SplashPriceObject splashPriceObject = null, bool renderAsCard = false, bool showPriceFromText = false, string priceFSClass = "fs3", bool priceCanBeUpdated = false) 1368 { 1369 if (splashPriceObject == null) 1370 { 1371 splashPriceObject = GetSplashPriceObject(product); 1372 } 1373 // Theming 1374 bool blackFridayTheme = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetBoolean("BlackFridayTheme"); 1375 string blackFridayClass = blackFridayTheme ? "bf-bg-black bf-color-white" : ""; 1376 string lineHeight = renderAsCard ? "tight-line-height" : ""; 1377 bool hideStrikeThroughText = string.IsNullOrEmpty(splashPriceObject.normalPriceFormatted); 1378 if (hideStrikeThroughText) 1379 { 1380 splashPriceObject.normalPriceFormatted = ""; 1381 } 1382 1383 string priceTargetClass = priceCanBeUpdated ? "js-orderline-price-target" : ""; 1384 1385 1386 if (!string.IsNullOrEmpty(splashPriceObject.splashText) || !string.IsNullOrEmpty(splashPriceObject.splashTextPrefix)) 1387 { 1388 <p class="@lineHeight orderline-splash font-weight-bold fs0 text-uppercase m-0 px-1 @splashPriceObject.splashType @blackFridayClass">@(!string.IsNullOrEmpty(splashPriceObject.splashTextPrefix) ? (splashPriceObject.splashTextPrefix + " ") : "")<span class="js-saving-price-target">@splashPriceObject.splashText</span></p> 1389 } 1390 1391 <div class="@lineHeight"> 1392 @if (showPriceFromText) 1393 { 1394 <span class="font-weight-semibold fs0"><small>@Translate("ProductPrice.From", "Fra:")</small></span> 1395 } 1396 <span class="@priceFSClass m-0 orderline-price @priceTargetClass font-weight-bold">@(renderAsCard ? splashPriceObject.priceFormatted : splashPriceObject.OLprice)</span> 1397 </div> 1398 1399 if(!string.IsNullOrEmpty(splashPriceObject.normalPriceFormatted)) 1400 { 1401 <p class='@lineHeight m-0 font-weight-bold strike-through @(hideStrikeThroughText ? "invisible" : "")'><small>@(!string.IsNullOrEmpty(splashPriceObject.normalPricePrefix) ? (splashPriceObject.normalPricePrefix + " ") : "") <span class="js-normal-price-target">@splashPriceObject.normalPriceFormatted</span></small></p> 1402 } 1403 } 1404 1405 @helper RenderOnTopSplash(LoopItem product, SplashPriceObject splashPriceObject = null, bool isProductCardSplash = false) 1406 { 1407 if (splashPriceObject == null) 1408 { 1409 splashPriceObject = GetSplashPriceObject(product); 1410 } 1411 string DailyPriceBackgroundColor = ""; 1412 string DailyPriceTextColor = ""; 1413 string splashClass = isProductCardSplash ? "product-card-splash" : ""; 1414 if (splashPriceObject.splashType == "priceshape") 1415 { 1416 DailyPriceBackgroundColor = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("AktuelDagsprisBackgroundColor"); 1417 if (!string.IsNullOrEmpty(DailyPriceBackgroundColor)) 1418 { 1419 DailyPriceBackgroundColor = string.Format("background-color:{0};", DailyPriceBackgroundColor); 1420 } 1421 DailyPriceTextColor = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("AktuelDagsprisTextColor"); 1422 if (!string.IsNullOrEmpty(DailyPriceTextColor)) 1423 { 1424 DailyPriceTextColor = string.Format("color:{0};", DailyPriceTextColor); 1425 } 1426 } 1427 1428 // Theming 1429 bool blackFridayTheme = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetBoolean("BlackFridayTheme"); 1430 string blackFridayClass = blackFridayTheme ? "bf-bg-black bf-color-white" : ""; 1431 1432 <div class="splash splash--on-top p-1 @blackFridayClass @splashClass @splashPriceObject.splashType" style="@DailyPriceBackgroundColor"> 1433 <p class="m-0" style="@DailyPriceTextColor">@splashPriceObject.onTopSplashText</p> 1434 </div> 1435 } 1436 1437 1438 @helper RenderProduct(LoopItem product, Int32 productListIndex, String productListGroupId, String productListGroupName, Boolean powerStep = false) 1439 { 1440 // Group Specific - Exclude Bed Accessories 1441 bool excludeBedAccessories = product.GetBoolean("Ecom:Product.CategoryField.Senge.ExcludeBedAccessories.Value"); 1442 string multipleExcludeBedAccessoriesOptionLabel = product.GetString("Ecom:Product.CategoryField.Senge.DisplayMultipleExcludeBedAccessories.OptionLabel"); 1443 1444 // Product General Info 1445 string productId = product.GetString("Ecom:Product.ID"); 1446 string cylindoProductId = product.GetString("Ecom:Product:Field.CylindoId.Value.Clean"); 1447 string productName = product.GetString("Ecom:Product.Name"); 1448 string productLink = product.GetString("Ecom:Product.Link.Clean"); 1449 string shortDescription = product.GetString("Ecom:Product.ShortDescription"); 1450 string languageId = product.GetString("Ecom:Product.LanguageID"); 1451 1452 // Variants 1453 string defaultVariantId = product.GetString("Ecom:Product.DefaultVariantComboID"); 1454 string variantId = product.GetString("Ecom:Product.VariantID"); 1455 if (String.IsNullOrEmpty(variantId)) { 1456 variantId = defaultVariantId; 1457 } 1458 if (!String.IsNullOrEmpty(variantId)) { 1459 productLink = productLink + "&variantid=" + variantId; 1460 } 1461 1462 // Product Images 1463 List<string> images = MoblerHelpers.GetProductImages(productId, variantId); 1464 string productImage = string.Format("/Admin/Public/GetImage.ashx?Image={0}&Width=280&height=220&Format=webP&Quality=90&Crop=5&resolution=50", images.FirstOrDefault()); //MoblerHelpers.GetProductListImageWithMainImage(ProductID, ProductNumber); 1465 1466 bool isUsingCylindoAssets = Mobler.Website.CustomCode.Frontend.CylindoHelper.IsUsingCylindoAssets(cylindoProductId); 1467 1468 if (isUsingCylindoAssets) 1469 { 1470 productImage = Mobler.Website.CustomCode.Frontend.CylindoHelper.GetCylindoProductImage(productId, variantId, languageId); 1471 } 1472 1473 // Product Dimensions 1474 string productDepth = product.GetString("Ecom:Product:Field.dybdeint.Value.Clean"); 1475 string productHeight = product.GetString("Ecom:Product:Field.hoejdeint.Value.Clean"); 1476 string productWidth = product.GetString("Ecom:Product:Field.breddeint.Value.Clean"); 1477 bool hasProductDepth = !string.IsNullOrWhiteSpace(productDepth) && productDepth != "0"; 1478 bool hasProductHeight = !string.IsNullOrWhiteSpace(productHeight) && productHeight != "0"; 1479 bool hasProductWidth = !string.IsNullOrWhiteSpace(productWidth) && productWidth != "0"; 1480 1481 // Campaign 1482 string campaignDateStart = ""; 1483 string campaignDateEnd = ""; 1484 1485 bool campaignActiveOnProduct = product.GetBoolean("CampaignModule:Product.CampaignActiveOnProduct"); 1486 if (campaignActiveOnProduct) 1487 { 1488 campaignDateStart = product.GetString("CampaignModule:Product.CampaignProduct.Campaign.Start"); 1489 campaignDateEnd = product.GetString("CampaignModule:Product.CampaignProduct.Campaign.End"); 1490 } 1491 1492 bool hasCampaignDateStart = !string.IsNullOrWhiteSpace(campaignDateStart); 1493 1494 bool hasCampaignDateEnd = !string.IsNullOrWhiteSpace(campaignDateEnd); 1495 string campaignText = ""; 1496 if (hasCampaignDateStart && hasCampaignDateStart) 1497 { 1498 campaignText = $"{Translate("Campaign.BeforeDate.Text", "Gældende fra:")} {campaignDateStart} {Translate("Campaign.BeforeDate.Text2", "t.o.m.")} {campaignDateEnd}"; 1499 } 1500 var splashPriceObject = GetSplashPriceObject(product); 1501 1502 // Google Analytics Ecommerce Data Item 1503 var googleAnalyticsViewItem = new GA4EcommerceItem() 1504 { 1505 item_id = productId, 1506 item_name = productName, 1507 currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code, 1508 index = productListIndex - 1, 1509 item_list_id = productListGroupId, 1510 item_list_name = productListGroupName, 1511 price = product.GetDouble("Ecom:Product.Price.Price"), 1512 quantity = 1 1513 }; 1514 string googleAnalyticsViewItemAsJson = string.Empty; 1515 try 1516 { 1517 googleAnalyticsViewItemAsJson = JsonConvert.SerializeObject(googleAnalyticsViewItem, Formatting.None, new JsonSerializerSettings 1518 { 1519 NullValueHandling = NullValueHandling.Ignore 1520 }); 1521 } catch {} 1522 // {"item_id":"@productId", "item_name":"@productName", "currency":"@Dynamicweb.Ecommerce.Common.Context.Currency.Code", "index":@productListIndex, "item_list_id":"@productListGroupId", "item_list_name":"@productListGroupName", "price":@product.GetDouble("Ecom:Product.Price.Price"), "quantity":1 } 1523 1524 <div class="col-12 col-sm-6 col-lg-3 product-item js-product-list-item" data-gaitem='@googleAnalyticsViewItemAsJson' data-gaitemsent="false"> 1525 <div class="product-item-inner rounded p-3"> 1526 <div class="product-item__header"> 1527 <a href="@productLink" class="product-item__image p-0 mb-3 h-auto position-relative w-100"> 1528 <img width="290" height="170" class="js-product-image-@productId product-item-image" src="@productImage" alt="@productName" loading="lazy"/> 1529 @RenderOnTopSplash(product, splashPriceObject, true) 1530 </a> 1531 </div> 1532 1533 <div class="product-item__body"> 1534 @{ 1535 int colorOptionsToShow = 4; 1536 List<string> variantCombinationVariantIds = product.GetLoop("Co3VariantCombinations").Select(p => p.GetString("Ecom:VariantCombination.VariantID")).ToList(); 1537 bool hasVariants = false; 1538 } 1539 @if (variantCombinationVariantIds.Any() && !powerStep) 1540 { 1541 hasVariants = true; 1542 1543 <div class="product-item__variants d-flex align-items-center mb-2"> 1544 @foreach (LoopItem vg in product.GetLoop("VariantGroups")) 1545 { 1546 int variantOptionWithColorCount = 0; 1547 1548 @* Only show variants with hex colors *@ 1549 foreach (LoopItem variantOption in vg.GetLoop("VariantAvailableOptions")) 1550 { 1551 <p class="unimportant-hidden">@variantOptionWithColorCount</p> 1552 string variantOptionColor = variantOption.GetString("Ecom:VariantOption.ColorHex"); 1553 1554 // Get count of variants with color 1555 if (variantOptionColor.Any()) 1556 { 1557 variantOptionWithColorCount++; 1558 } 1559 // Only show this specific amount of variants 1560 if (variantOptionColor.Any() && variantOptionWithColorCount < colorOptionsToShow) 1561 { 1562 // Get variant option id, then compare with variant combinations, to get the link to the specific variant 1563 string variantOptionId = variantOption.GetString("Ecom:VariantOption.ID"); 1564 string variantIdWithColorOptionId = variantCombinationVariantIds.FirstOrDefault(vid => vid.Contains(variantOptionId)); 1565 1566 if (variantIdWithColorOptionId.IsNullOrEmpty() == false) 1567 { 1568 string variantLink = product.GetString("Ecom:Product.Link.Clean") + "&variantid=" + variantIdWithColorOptionId; 1569 <a href="@variantLink" class="product-item__variant" style="background-color: @variantOptionColor;"></a> 1570 } 1571 } 1572 else if (variantOptionColor.Any() && variantOptionWithColorCount == colorOptionsToShow) 1573 { 1574 <a href="@productLink" class="fs2 product-item__more-variants">+</a> 1575 } 1576 else 1577 { 1578 break; 1579 } 1580 } 1581 } 1582 @if (hasVariants) 1583 { 1584 <a href="@productLink" class="ml-4 fs-s"> 1585 <i class="fas fa-pen mr-1 fs-2"></i> 1586 <u>@Translate("Ecom:Product.Pricing.Designer", "Design selv")</u> 1587 </a> 1588 } 1589 </div> 1590 } 1591 <a href="@productLink" class="product-item__title"> 1592 <h3 class="fs0">@productName</h3> 1593 </a> 1594 <div class="product-item__description fs0 color-subtle">@shortDescription</div> 1595 @if (hasProductDepth && !powerStep || hasProductHeight && !powerStep || hasProductWidth && !powerStep) 1596 { 1597 <p class="product-item__dimensions fs0 color-subtle"> 1598 @if (hasProductDepth) 1599 { 1600 <span><strong>D/L:</strong> @productDepth</span> 1601 } 1602 @if (hasProductHeight) 1603 { 1604 <span><strong>H:</strong> @productHeight</span> 1605 } 1606 @if (hasProductWidth) 1607 { 1608 <span><strong>B:</strong> @productWidth</span> 1609 } 1610 </p> 1611 } 1612 </div> 1613 1614 <div class="product-item__footer w-100"> 1615 @if (!powerStep) 1616 { 1617 if (hasCampaignDateStart && hasCampaignDateEnd) 1618 { 1619 <div class="product-item__campaign"> 1620 <p class="color-subtle fs-s m-0">@campaignText</p> 1621 </div> 1622 } 1623 } 1624 <div class="py-1 my-2 border-top border-bottom"> 1625 <div class="row d-flex align-items-center justify-content-between"> 1626 <div class="col-8"> 1627 @RenderProductPricing(product, splashPriceObject, true, hasVariants) 1628 </div> 1629 <div class="col-4 pl-0"> 1630 <a href="@productLink" class="btn btn-show-product float-right my-1 px-4"><strong>@Translate("Ecom:Product.Show", "Vis")</strong></a> 1631 </div> 1632 </div> 1633 </div> 1634 @if (!powerStep) 1635 { 1636 bool createEmptyLine = true; 1637 if (!string.IsNullOrEmpty(multipleExcludeBedAccessoriesOptionLabel)) 1638 { 1639 createEmptyLine = false; 1640 <p class="color-subtle fs-s m-0">@Translate("Ecom:Product.Pricing.Excludes.Beds - " + multipleExcludeBedAccessoriesOptionLabel, "")</p> 1641 } 1642 else if (excludeBedAccessories) 1643 { 1644 createEmptyLine = false; 1645 <p class="color-subtle fs-s m-0">@Translate("Ecom:Product.Pricing.Excludes.Beds", "Prisen er ekskl. ben og gavl.")</p> 1646 } 1647 if (hasVariants) 1648 { 1649 <div class="variant-price-disclaimer color-subtle fs-s">@Translate("VariantPriceDisclaimer", "Prisen kan variere efter materialevalg")</div> 1650 } 1651 else 1652 { 1653 <div class="variant-price-disclaimer color-subtle fs-s invisible">&nbsp;</div> 1654 } 1655 1656 } 1657 </div> 1658 @* <script> *@ 1659 @* var ga4ProductListItems = ga4ProductListItems || [] *@ 1660 @* *@ 1661 @* var item = {}; *@ 1662 @* item.item_id = '@productId'; *@ 1663 @* item.item_name = '@productName'; *@ 1664 @* item.currency = '@Dynamicweb.Ecommerce.Common.Context.Currency.Code'; *@ 1665 @* item.index = @productListIndex; *@ 1666 @* item.item_list_id = '@productListGroupId'; *@ 1667 @* item.item_list_name = '@productListGroupName'; *@ 1668 @* item.price = @product.GetDouble("Ecom:Product.Price.Price"); *@ 1669 @* item.quantity = 1; *@ 1670 @* ga4ProductListItems.push(item); *@ 1671 @* </script> *@ 1672 </div> 1673 </div> 1674 } 1675 1676 @using Mobler.Website.CustomModules.MoblerHelpers 1677 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 1678 1679 @helper RenderBlogTeaser(Firstweb.Custom.CustomCode.Frontend.Helpers.Blog Blog) 1680 { 1681 string PageId = GetGlobalValue( "Global:Page.ID" ); 1682 string CleanLink = Blog.Link.Replace("default.aspx?id", "Default.aspx?Id"); 1683 string BlogImage = "/Admin/Public/GetImage.ashx?Image=" + Blog.Image + "&Width=400&height=200&Format=webP&Quality=90"; 1684 <div class="col-12 col-md-4 px-0 px-md-3 mb-4"> 1685 <a href="@CleanLink" class="blog d-block"> 1686 <div class="image" style="background-image:url('@BlogImage')"> 1687 <img class="d-none" width="400" height="200" src="@BlogImage" alt="Alternate Text"/> 1688 </div> 1689 <div class="text"> 1690 <div class="positioning"> 1691 <h4 class="col-10 col-md-9 px-2 pt-3 bg-white font-weight-bold text-center mx-auto">@Blog.Header</h4> 1692 <p class="text-center font-weight-bold m-0 color-primary">@Translate("BlogReadMore", "L&aelig;s mere her")</p> 1693 </div> 1694 </div> 1695 </a> 1696 </div> 1697 } 1698 1699 @helper RenderLargeBlogTeaser(Firstweb.Custom.CustomCode.Frontend.Helpers.Blog Blog, string ImagePosition) 1700 { 1701 string ImageClass = ImagePosition == "Left" ? "" : "offset-md-4"; 1702 string TextClass = ImagePosition == "Left" ? "" : "left"; 1703 string Image = "/Admin/Public/GetImage.ashx?Image=" + Blog.Image + "&Format=webP&Quality=90&Height=400"; 1704 string CleanLink = Blog.Link.Replace("default.aspx?id", "Default.aspx?Id"); 1705 <div class="col-12 blog"> 1706 <div class="row"> 1707 <div class="col-12 col-md-8 px-0 px-md-3 @ImageClass"> 1708 <div class="image-container" style="background-image:url('@Image')"> 1709 <img class="img-fluid" src="@Image" alt="@Blog.Header" width="1020" height="400" /> 1710 </div> 1711 </div> 1712 <div class="col-12 col-md-6 text-container mb-5 mb-md-0 @TextClass"> 1713 <div class="text bg-white p-3"> 1714 <div> 1715 <h4>@Blog.Header</h4> 1716 @Blog.Teaser 1717 </div> 1718 <a class="font-weight-bold" href="@CleanLink">@Translate("BlogReadMore", "L&aelig;s mere her")</a> 1719 </div> 1720 </div> 1721 </div> 1722 </div> 1723 } 1724 1725 @helper RenderLatestBlogsTeasers() 1726 { 1727 var LatestBlogs = Firstweb.Custom.CustomCode.Frontend.Helpers.Blogs.GetLatestBlogs(); 1728 foreach (var Blog in LatestBlogs) 1729 { 1730 @RenderBlogTeaser(Blog) 1731 } 1732 } 1733 1734 @using Group = Dynamicweb.Ecommerce.Products.Group 1735 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 1736 @helper RenderBreadCrumbs(List<Group> ParentGroups, string ProductName, bool IsProductPage = false) 1737 { 1738 string ShowOnPageUrl = ""; 1739 string lastItemClasses = IsProductPage ? "d-md-flex d-none" : "d-flex"; 1740 1741 <div class="bg-grey w-100"> 1742 <div class="container"> 1743 <div class="bread-crumb-flex py-3 d-flex align-items-center flex-nowrap text-nowrap"> 1744 @if (IsProductPage) 1745 { 1746 <div onclick="window.history.back()" class="btn btn-history-back mr-0 mr-md-2"><span><i class="fa-solid fa-arrow-left mr-1"></i> Tilbage</span></div> 1747 } 1748 else 1749 { 1750 <p class="color-subtle m-0 mr-2">@Translate("Breadcrumb.CurrentPage", "Her er du:")</p> 1751 } 1752 <ul class="bread-item-list list-unstyled m-0 d-flex align-items-center flex-nowrap text-nowrap"> 1753 <li class="bread-item d-none d-md-flex mb-0 color-subtle"> 1754 <a href="/" class="d-inline-block"> 1755 @Translate("Breadcrumb.Frontpage", "Forside") 1756 </a> 1757 </li> 1758 @foreach (var Group in ParentGroups) 1759 { 1760 <li class="bread-item d-none d-md-flex mb-0 color-subtle"> 1761 @if (Group == ParentGroups.First()) 1762 { 1763 @Group.Name 1764 ShowOnPageUrl = Group.ProductGroupFieldValues.GetProductGroupFieldValue("FirstwebGroupPrimaryPage").Value.ToString(); 1765 } 1766 else 1767 { 1768 <a class="d-inline-block" href="/@ShowOnPageUrl&GroupId=@Group.Id"> 1769 @Group.Name 1770 </a> 1771 } 1772 </li> 1773 } 1774 <li class="bread-item @(lastItemClasses) active color-subtle">@ProductName</li> 1775 </ul> 1776 1777 </div> 1778 </div> 1779 </div> 1780 } 1781 1782 @using Mobler.Website.CustomModules.MoblerHelpers 1783 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 1784 1785 @helper RenderOrderlines(bool asMiniCart = false) 1786 { 1787 <div class="border @(asMiniCart ? "" : "rounded") cart-command-loader unimportant-hidden justify-content-center align-items-center" data-bind="css: { 'd-flex': Loading }"> 1788 <div class="d-flex align-items-center"> 1789 <p class="m-0 mr-2">@Translate("Cart.UpdatingCart", "Opdaterer kurv")</p> 1790 <i class="fas fa-circle-notch fa-spin"></i> 1791 </div> 1792 </div> 1793 1794 <div class="lines" data-bind="foreach: Orderlines"> 1795 <div class=" cart-orderline js-cartline d-flex justify-content-between align-items-center p-2 p-md-4 flex-wrap @(asMiniCart ? "border-top" : "border rounded mb-3")" data-bind="attr:{'data-guid': GUID, 'data-orderline-id': OrderlineId}"> 1796 1797 <div class="d-none cartline-gaitem-data" data-bind="gaEcommerceItem: GAItem"></div> 1798 1799 <div class="col-2 d-flex justify-content-between align-items-center p-0"> 1800 <div class="w-100 text-center"> 1801 <a class="product-image mb-2" data-bind="css: { 'd-none': IsDiscount || IsRelated }, attr: { href: ProductLink } "> 1802 <img width="260" height="200" class="img-fit-contain-x" data-bind="attr: { src: ImagePath }"/> 1803 </a> 1804 <a href="#" class="text-decoration-underline fs0 color-black" data-bind="click: $parent.DeleteOrderline.bind($data, OrderlineId, GAItem, GUID), css: { 'd-none': IsDiscount || IsRelated }"> 1805 @Translate("Cart.DeleteItemCart", "Slet") 1806 </a> 1807 </div> 1808 </div> 1809 1810 <div class="col-10 d-flex justify-content-between align-items-center flex-wrap"> 1811 <div class="col-12 col-lg-6 d-flex justify-content-between align-items-center"> 1812 1813 <div class="flex-fill pr-3 w-75" data-bind="css: { 'd-none': IsDiscount || IsRelated }"> 1814 <a class="font-weight-bold color-black m-0" data-bind="text: ProductName, attr: { href: ProductLink }"></a> 1815 <p class="mb-0 fs-s" data-bind="text: ProductNumber"></p> 1816 1817 <ul class="variant-dimensions list-unstyled my-2 p-0" data-bind="foreach: VariantDimensions"> 1818 <li class="m-0 fs-xs"> 1819 <span class="font-weight-bold" data-bind="text: Label"></span> 1820 <span data-bind="text: Value"></span> 1821 </li> 1822 </ul> 1823 <div class="delivery-time-checkout"> 1824 <p class="orderline-delivery-time fs-s" data-bind="text: DeliveryTime"></p> 1825 </div> 1826 </div> 1827 1828 <div class="flex-fill" data-bind="css: { 'd-none': !IsDiscount}"> 1829 <a class="font-weight-bold color-black m-0" data-bind="text: ProductName, attr: { href: ProductLink }"></a> 1830 </div> 1831 1832 <div class="flex-fill" data-bind="css: { 'd-none': !IsRelated }"> 1833 <p class="m-0"><span class="font-weight-bold">Tilkøb:</span> <span data-bind="text: ProductName"></span></p> 1834 <p class="m-0 color-subtle small-quantity-indicator"><span data-bind="text: Quantity"></span> stk. a <span data-bind="text: QuantityPrice"></span></p> 1835 </div> 1836 1837 </div> 1838 1839 <div class="col-12 col-lg-6 d-flex justify-content-between align-items-center p-0"> 1840 1841 <div class="col-6 p-0"> 1842 <div class="qty-counter-checkout"> 1843 <div class="quantity-controls d-flex align-items-center justify-content-center"> 1844 <div class="control minus" data-bind="click: $parent.QuantityControl.bind($data, -1, OrderlineId, GAItem, GUID)"> 1845 - 1846 </div> 1847 <input disabled type="text" name="Quantity" value="1" data-bind="value: Quantity, attr: { 'data-id': 'js-input-' + OrderlineId }"/> 1848 <div class="control plus" data-bind="click: $parent.QuantityControl.bind($data, 1, OrderlineId, GAItem, GUID)"> 1849 + 1850 </div> 1851 </div> 1852 <p class="mt-1 mb-0 fs0 small-quantity-indicator text-center" data-bind="css: { 'd-none': Quantity < 2 }"><span>pr. stk.</span> <span data-bind="text: QuantityPrice"></span></p> 1853 </div> 1854 </div> 1855 1856 <div class="col-6 p-0 text-right" data-bind="html: OrderlinePriceHtml"></div> 1857 1858 </div> 1859 </div> 1860 1861 </div> 1862 </div> 1863 } 1864 1865 1866 @RenderBreadCrumbs(ParentGroups, ProductName, true) 1867 1868 <div class="container mt-4 product-details position-relative" data-bind="viewModel: 'ProductViewModel'"> 1869 <div class="d-none" data-bind="setInitValue: { observable: CartPageId, value: '@AjaxCartPageId' }"></div> 1870 <div class="d-none" data-bind="setInitValue: { observable: MiniCart, value: '.js-mini-cart-lines' }"></div> 1871 <div class="d-none" data-bind="setInitValue: { observable: CartCount, value: '.js-cart-count' }"></div> 1872 <div class="d-none" data-bind="setInitValue: { observable: CustomersAlsoSawLines, value: '.js-customers-also-saw-lines' }"></div> 1873 <div class="d-none" data-bind="setInitValue: { observable: CustomersAlsoSawPage, value: '@CustomersAlsoSawPageId' }"></div> 1874 <div class="d-none" data-bind="setInitValue: { observable: RelewiseRecommandationsEndpoint, value: '@relewiseRecommandationsEndpoint' }"></div> 1875 <div class="d-none" data-bind="setInitValue: { observable: MainProductId, value: '@ProductID' }"></div> 1876 <div class="d-none" data-bind="setInitValue: { observable: VariantsEndpoint, value: '@VariantsEndpoint' }"></div> 1877 <div class="d-none" data-bind="setInitValue: { observable: VariantDetailsEndpoint, value: '@VariantDetailsEndpoint' }"></div> 1878 <div class="d-none" data-bind="setInitValue: { observable: TeaserText, value: '@TrimmedTeaser' }"></div> 1879 <div class="d-none" data-bind="setInitValue: { observable: ProductName, value: '@TrimmedName' }"></div> 1880 <div class="d-none" data-bind="setInitValue: { observable: ProductImage, value: '@Images[0]' }"></div> 1881 <div class="d-none" data-bind="setInitValue: { observable: ProductNumber, value: '@ProductNumber' }"></div> 1882 <div class="d-none" data-bind="setInitValue: { observable: VariantId, value: '@VariantId' }"></div> 1883 <div class="d-none" data-bind="setInitValue: { observable: Price, value: '@priceFormatted' }"></div> 1884 <div class="d-none" data-bind="setInitValue: { observable: ModalPrice, value: '@priceFormatted' }"></div> 1885 <div class="d-none" data-bind="setInitValue: { observable: PriceDouble, value: '@price' }"></div> 1886 <div class="d-none" data-bind="setInitValue: { observable: BrandName, value: '@BrandName' }"></div> 1887 <div class="d-none" data-bind="setInitValue: { observable: Category, value: '@DataLayerParentGroup' }"></div> 1888 <div class="d-none" data-bind="setInitValue: {observable: VideoKlip, value: '@GetString("Ecom:Product:Field.VideoKlip.Value")'}"></div> 1889 <div class="d-none" data-bind="setInitValue: { observable: HasCylindo, value: '@isUsingCylindoAssets' }"></div> 1890 <div class="d-none" data-bind="setInitValue: {observable: InitialDetailsImages, value: '@(string.Join(",", Images))'}"></div> 1891 1892 1893 <div class="d-none js-update-cart" data-bind="setInitValue: { observable: CartEndpoint, value: '@GetCartEndpoint' }, click: GetCart"></div> 1894 1895 1896 1897 <div class="d-flex flex-wrap product-top"> 1898 <div class="image-container position-relative overflow-hidden"> 1899 1900 <h1 class="header fs4 mb-4" data-bind="text: ProductName">@ProductName</h1> 1901 1902 @if(productObj != null) { 1903 <ProductCarousel 1904 class="@(isUsingCylindoAssets ? "js-productcarousel-is-using-cyldino-assets" : "")" 1905 data-product="@(HttpUtility.HtmlAttributeEncode((Newtonsoft.Json.JsonConvert.SerializeObject(productObj))))" 1906 ></ProductCarousel> 1907 } 1908 1909 <div class="shortcuts d-none d-lg-flex"> 1910 <div class="shortcuts__header fs0"><b>@Translate("ShortCuts", "Genveje:")</b></div> 1911 <div class="shortcuts__body"> 1912 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProviderLink.Value.Clean"))) 1913 { 1914 <div class="shortcut"><i class="fa-solid fa-pen"></i> <a href="@GetString("Ecom:Product:Field.ProviderLink.Value.Clean")" target="_blank">@Translate("Product.DesignYourself.Title", "Design selv")</a></div> 1915 } 1916 <div class="shortcut" data-bind="click: ScrollToElement.bind($element, productdesktopspecifications)"><i class="fa fa-clipboard"></i> <a href="#">@Translate("Product.Specifications.Title", "Specifikationer")</a></div> 1917 @if (!String.IsNullOrEmpty(ProductCatalogLink)) 1918 { 1919 <div class="shortcut"><i class="fa fa-book-open"></i> <a href="@ProductCatalogLink" target="_blank">@Translate("Product.InspirationBrochure.Title", "Inspirationsbrochure")</a></div> 1920 } 1921 </div> 1922 </div> 1923 <div class="mobile-shortcuts d-flex justify-content-center flex-wrap d-lg-none w-100"> 1924 <div class="col-12 col-sm-6"> 1925 <div class="btn brand-primary-outline w-100" role="button" data-bind="click: ScrollToElement.bind($element, productmobilefulldescription)">@Translate("Product.ProductDescription.AboutLinkText", "Om produktet")</div> 1926 </div> 1927 <div class="col-12 col-sm-6"> 1928 <div class="btn brand-primary-outline w-100 mt-3 mt-sm-0" role="button" data-bind="click: ScrollToElement.bind($element, productmobilespecifications)">@Translate("Product.Specifications.Title", "Specifikationer")</div> 1929 </div> 1930 </div> 1931 1932 <div class="mt-3 d-none d-lg-block desktop-description"> 1933 <div class="product-bottom-details"> 1934 @SnippetStart("ShortDescription") 1935 <div class="short-description mb-2 fs2"> 1936 @ShortDescription 1937 <p class="m-0"> 1938 @if (!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.dybdeint.Value.Clean"))) 1939 { 1940 if (GetString("Ecom:Product:Field.dybdeint.Value.Clean") != "0") 1941 { 1942 <span>D/L: @GetValue("Ecom:Product:Field.dybdeint.Value.Clean")</span> 1943 } 1944 } 1945 @if (!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.hoejdeint.Value.Clean"))) 1946 { 1947 if (GetString("Ecom:Product:Field.hoejdeint.Value.Clean") != "0") 1948 { 1949 <span>H: @GetValue("Ecom:Product:Field.hoejdeint.Value.Clean")</span> 1950 } 1951 } 1952 @if (!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.breddeint.Value.Clean"))) 1953 { 1954 if (GetString("Ecom:Product:Field.breddeint.Value.Clean") != "0") 1955 { 1956 <span>B: @GetValue("Ecom:Product:Field.breddeint.Value.Clean")</span> 1957 } 1958 } 1959 </p> 1960 </div> 1961 @SnippetEnd("ShortDescription") 1962 @RenderSnippet("ShortDescription") 1963 <div id="productdesktopfulldescription" class="d-flex flex-wrap mt-5"> 1964 @SnippetStart("ProductDescription") 1965 <h2 class="fs3 mb-2 font-weight-bold">@Translate("Product.ProductDescription.Title", "Produktbeskrivelse")</h2> 1966 1967 <div class="js-read-more-outer-container product-description"> 1968 <div class="product-description--inner read-more-container"> 1969 <div data-bind="css: { 'd-none': FullDescription().length > 0 }"> 1970 @LongDescription 1971 </div> 1972 <div class="unimportant-hidden" data-bind="html: FullDescription, css: { 'd-block': FullDescription().length > 0 }"></div> 1973 @if (!string.IsNullOrEmpty(productionDescription)) 1974 { 1975 <h4 class="mt-4">@Translate("Product.ProductionDescription.Title", "Produktionsbeskrivelse")</h4> 1976 @:@productionDescription 1977 } 1978 </div> 1979 <div class="read-more-fade"> 1980 <div class="col-12 col-sm-6"> 1981 <a href="#" class="btn brand-black-outline w-100 js-evaluate-read-more" data-bind="click: ProductDescriptionReadMore.bind($data, $element, false)">@Translate("Product.ProductDescription.ReadMore.Title", "Læs hele beskrivelsen")<i class="ml-2 fas fa-chevron-down"></i></a> 1982 </div> 1983 </div> 1984 </div> 1985 @SnippetEnd("ProductDescription") 1986 @RenderSnippet("ProductDescription") 1987 </div> 1988 <div class="d-flex flex-wrap mt-5"> 1989 <div class="d-none" data-bind="setInitValue: {observable: HasSpecifications, value: @(filteredFields.Any() ? "true" : "false")}"></div> 1990 <div id="productdesktopspecifications" class="specifications-container w-100" data-bind="css: { 'd-none': !HasSpecifications() }"> 1991 1992 @SnippetStart("ProductSpecifications") 1993 1994 @if (GetLoop("FieldDisplayGroups").Where(f => f.GetLoop("Fields").Any()).Any()) 1995 { 1996 <h2 class="fs3 mb-4 font-weight-bold">@Translate("Product.Specifications.Title", "Specifikationer")</h2> 1997 <div class="js-read-more-outer-container product-specifications"> 1998 @* Render product specifications from variants *@ 1999 <dl class="d-flex flex-wrap w-100 read-more-container unimportant-hidden" data-bind="foreach: Specifications, css: { 'd-flex': Specifications().length > 0, 'd-none': Specifications().length < 1 }"> 2000 <dt class="w-50 border-top" data-bind="text: Name"></dt> 2001 <dd class="w-50 border-top" data-bind="text: Value"></dd> 2002 </dl> 2003 2004 <dl class="d-flex flex-wrap w-100 read-more-container" data-bind="css: { 'd-none': Specifications().length > 0, 'd-flex': Specifications().length < 1 }"> 2005 @foreach (LoopItem field in filteredFields) 2006 { 2007 <dt class="w-50 border-top">@field.GetString("Ecom:FieldDisplayGroup.Field.Name")</dt> 2008 <dd class="w-50 border-top"> 2009 @if (field.GetBoolean("Ecom:FieldDisplayGroup.Field.IsList")) 2010 { 2011 @field.GetString("Ecom:FieldDisplayGroup.Field.OptionLabel") 2012 } 2013 else 2014 { 2015 @field.GetString("Ecom:FieldDisplayGroup.Field.Value") 2016 } 2017 </dd> 2018 } 2019 </dl> 2020 <div class="read-more-fade"> 2021 <div class="col-12 col-sm-6"> 2022 <a href="#" class="btn brand-black-outline w-100 js-evaluate-read-more" data-bind="click: ProductDescriptionReadMore.bind($data, $element, false)">@Translate("Product.Specifications.ReadMore.Title", "Vis alle specifikationer")<i class="ml-2 fas fa-chevron-down"></i></a></div> 2023 </div> 2024 </div> 2025 } 2026 2027 @SnippetEnd("ProductSpecifications") 2028 @RenderSnippet("ProductSpecifications") 2029 </div> 2030 </div> 2031 2032 </div> 2033 2034 </div> 2035 </div> 2036 2037 <div class="pl-lg-4 prices-container flex-wrap flex-column mt-3 mt-lg-0 position-relative"> 2038 <p class="color-subtle fs0 mt-0 mb-2 text-right">@Translate("ProductProductNumber", "Produktnummer:") <span data-bind="text: ProductNumber">@ProductNumber</span></p> 2039 @RenderSnippet("PricesContainer") 2040 <div class="mt-3 mt-3 d-block d-lg-none mobile-description"> 2041 @RenderSnippet("ShortDescription") 2042 <div id="productmobilefulldescription" class="d-flex flex-wrap mt-5 mb-3"> 2043 @RenderSnippet("ProductDescription") 2044 </div> 2045 2046 <div class="mobile-shortcuts d-flex justify-content-center flex-wrap d-lg-none w-100"> 2047 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProviderLink.Value.Clean"))) 2048 { 2049 <div class="col-12 col-sm-6 mb-3"> 2050 <a href="@GetString("Ecom:Product:Field.ProviderLink.Value.Clean")" target="_blank" class="btn brand-primary-outline w-100" role="button">@Translate("Product.DesignYourself.Title", "Design selv")</a> 2051 </div> 2052 } 2053 @if (!String.IsNullOrEmpty(ProductCatalogLink)) 2054 { 2055 <div class="col-12 col-sm-6 mb-3"> 2056 <a href="@ProductCatalogLink" target="_blank" class="btn brand-primary-outline w-100" role="button">@Translate("Product.InspirationBrochure.Title", "Inspirationsbrochure")</a> 2057 </div> 2058 } 2059 </div> 2060 2061 <div id="productmobilespecifications" class="specifications-container w-100" data-bind="css: { 'd-none': !HasSpecifications() }"> 2062 @RenderSnippet("ProductSpecifications") 2063 </div> 2064 </div> 2065 </div> 2066 </div> 2067 2068 @if (!string.IsNullOrEmpty(productCatalogueImage)) 2069 { 2070 <img width="320" height="200" id="GroupCatalogueImage" class="unimportant-hidden" src="/Admin/Public/GetImage.ashx?Image=/Files/Images/@productCatalogueImage&Width=320&height=200&Format=webP&Quality=90&Crop=0&resolution=50" /> 2071 } 2072 @if (!string.IsNullOrEmpty(ProductCatalogLink)) 2073 { 2074 <a id="ProductCatalogueLink" class="unimportant-hidden" href="@ProductCatalogLink" target="_blank"></a> 2075 } 2076 2077 @if (showRecommendations) 2078 { 2079 <div id="customersAlsoViewed" class="related-products mt-8 unimportant-hidden"> 2080 <div class="related-products__header mb-4"> 2081 <h4>@Translate("Recommendations:CustomersAlsoViewed.Heading", "Andre har ogs&aring; set")</h4> 2082 </div> 2083 <div id="customersAlsoViewedContainer" class="related-products__body"> 2084 </div> 2085 </div> 2086 } 2087 2088 @if (uspPageId > 0) 2089 { 2090 <div class="productdetail-usps mt-8 d-flex"> 2091 <div class="row"> 2092 @RenderPageContent(uspPageId) 2093 </div> 2094 </div> 2095 } 2096 2097 @if (showRecommendations && !userIsAnonymous) 2098 { 2099 <div id="recentlyViewedProducts" class="related-products unimportant-hidden"> 2100 <div class="related-products__header mb-4"> 2101 <h4>@Translate("Recommendations:LatestVisitedProducts.Heading", "Dine seneste bes&oslash;gte produkter")</h4> 2102 </div> 2103 <div id="recentlyViewedProductsContainer" class="related-products__body"> 2104 2105 </div> 2106 </div> 2107 } 2108 2109 <div class="after-add-to-cart bg-white box-shadow" data-bind="css: { 'd-block': ProductAddedToCart }"> 2110 <div class="closer fs3 pointer" data-bind="click: ProductAddedToCart(false)"> 2111 <i class="fas fa-times-circle color-primary"></i> 2112 </div> 2113 <div class="after-add-to-cart__inner"> 2114 <div class="p-3"> 2115 <p class="font-weight-bold fs text-uppercase m-0"> 2116 <i class="fas fa-check-circle color-primary"></i> 2117 @Translate("ProductProductAddedToCart", "Varen er lagt i indk&oslash;bskurven") 2118 </p> 2119 </div> 2120 <div class="mini-cart large-cart"> 2121 2122 <div class="position-relative"> 2123 2124 2125 @RenderOrderlines(true) 2126 2127 @if (blackFridayTheme) 2128 { 2129 <div class="bf-bg-black p-3 d-flex justify-content-between align-items-center price-summary"> 2130 <p class="m-0 color-white fs-12px font-weight-semibold">@Translate("MiniCartTotal", "Din kurv i alt (ex. fragt)")</p> 2131 <p class="m-0 color-white fs1 font-weight-bold" data-bind="text: CartTotalNoFees"></p> 2132 </div> 2133 } 2134 else 2135 { 2136 <div class="bg-brand p-3 d-flex justify-content-between align-items-center price-summary"> 2137 <p class="m-0 color-white fs-12px font-weight-semibold">@Translate("MiniCartTotal", "Din kurv i alt (ex. fragt)")</p> 2138 <p class="m-0 color-white fs1 font-extra-bold" data-bind="text: CartTotalNoFees"></p> 2139 </div> 2140 } 2141 </div> 2142 2143 </div> 2144 2145 <div class="p-3 d-flex justify-content-between after-add-to-cart__buttons"> 2146 <button class="btn btn-tertiary" type="button" data-bind="click: ProductAddedToCart(false)">@Translate("MiniCartContinueShopping", "Handel videre")</button> 2147 2148 @if (blackFridayTheme) 2149 { 2150 <a href="@CartPage" class="btn btn-black-friday">@Translate("MiniCartGoToCheckout", "G&aring; til kassen")</a> 2151 } 2152 else 2153 { 2154 <a href="@CartPage" class="btn btn-primary">@Translate("MiniCartGoToCheckout", "G&aring; til kassen")</a> 2155 } 2156 2157 </div> 2158 2159 <div id="purchasedWith" class="power-step unimportant-hidden" style="@purchasedWithStyle"> 2160 <div class="power-step__header"> 2161 <h6>@Translate("Recommendations:PowerStep.Heading", "Andre har ogs&aring; k&oslash;bt") </h6> 2162 </div> 2163 <div id="purchasedWithContainer"></div> 2164 </div> 2165 2166 <div class="js-customers-also-saw-lines"></div> 2167 </div> 2168 </div> 2169 2170 <div class="add-to-cart-overlay" data-bind="css: { 'd-block': ProcessingAjax }"> 2171 <p class="fs4 m-0 color-white">@Translate("ProductAjaxAddingToCart", "Tilf&oslash;jer til kurv") <i class="fa fa-circle-notch fa-spin ml-3 fs5"></i></p> 2172 </div> 2173 2174 2175 <div class="add-to-cart-overlay" data-bind="css: { 'd-block': ProcessingVariantDetailsAjax }"> 2176 <p class="fs4 m-0 color-white">@Translate("ProductAjaxGettingVariant", "Henter variant") <i class="fa fa-circle-notch fa-spin ml-3 fs5"></i></p> 2177 </div> 2178 2179 @if (isUsingCylindoAssets == true) 2180 { 2181 dynamic cylindoTranslations = new 2182 { 2183 tooltipDragText = Translate("Cylindo - Translation - tooltipDragText", "Træk for at rotere. Klik for at zoom"), 2184 tooltipZoomText = Translate("Cylindo - Translation - tooltipZoomText", "Bevæg musen for at panorere") 2185 }; 2186 2187 <div class="d-none js-cylindo-area-settings" data-accountId="@cylindoAccountId" data-translations="@HttpUtility.HtmlAttributeEncode(Newtonsoft.Json.JsonConvert.SerializeObject(cylindoTranslations))"></div> 2188 2189 } 2190 2191 @using System.Net.Http 2192 @using System.Net.Http.Headers 2193 @using System.Text.RegularExpressions 2194 @using System.Web.Razor.Parser.SyntaxTree 2195 @using Dynamicweb.Frontend 2196 @using Dynamicweb.SystemTools 2197 <div class="modal fade variant-modal" id="variantModal" tabindex="-1" role="dialog"> 2198 @{ 2199 var VariantGroupsWithMultipleOptionsModal = GetLoop("VariantGroups").Where(x => x.GetLoop("VariantAvailableOptions").Count > 1); 2200 var RelevantVariantGroupModal = VariantGroupsWithMultipleOptionsModal.FirstOrDefault(); 2201 Dictionary<string, List<LoopItem>> GroupedVariantGroups = new Dictionary<string, List<LoopItem>>(); 2202 2203 string[] areaSettingCylindoColorGroups = PageView.Current().Area.Item["CylindoVariantColorGroups"] != null ? PageView.Current().Area.Item["CylindoVariantColorGroups"].ToString().Split(',') : new string[]{}; 2204 var colorVariantGroup = GetLoop("VariantGroups").Where(vg => areaSettingCylindoColorGroups.Contains(vg.GetString("Ecom:VariantGroup.ID"))); 2205 2206 int modalOptionsCount = 0; 2207 bool shouldGroup = false; 2208 bool hasFilters = false; 2209 2210 2211 } 2212 <div class="modal-dialog modal-dialog-centered" role="document"> 2213 <div class="w-100"> 2214 <div class="row"> 2215 <div class="modal-content col-12 col-lg-6 "> 2216 2217 <div class="modal-body d-flex flex-column modal-body mobler-modal-image-container pt-5 pt-sm-3"> 2218 <p class="lead d-none d-md-block"> 2219 @Translate("Cylindo - Modal - Intro - Text", "Vælg dit design af materiale og farver") 2220 </p> 2221 <p class="h4 font-weight-bold mb-4">@GetString("Ecom:Product.Name")</p> 2222 <p class="font-weight-bold pt-4 border-top"> 2223 @Translate("Cylindo - Modal - Price - Text", "Foreløbig pris ")<span data-bind="text: ModalPrice()"></span> 2224 </p> 2225 @if(isUsingCylindoAssets){ 2226 <cylindo-viewer customer-id="@cylindoAccountId" code="@cylindoProductId" id="cylindo-modal-container" controls="zoom"></cylindo-viewer> 2227 } 2228 else 2229 { 2230 <img width="800" height="500" class="product-image main-image modal-main-image" data-bind="attr: { src: '/Admin/Public/GetImage.ashx?Image=' + GetModalImage() + '&Format=webP&Quality=90&width=800&height=500&crop=5' }" alt="@GetString("Ecom:Product.Name")" /> 2231 } 2232 2233 </div> 2234 </div> 2235 <div class="modal-content col-12 col-lg-6 pl-lg-0 pr-lg-0 bg-light"> 2236 <div class="modal-header bg-light border-0 d-block"> 2237 <button type="button" class="close d-none d-lg-block" data-dismiss="modal" aria-label="Close"> 2238 <a class="small pointer" aria-hidden="true"> 2239 <i class="fas fa-xmark fa-2xl"></i> 2240 </a> 2241 </button> 2242 </div> 2243 <div class="modal-header mx-2 py-2 bg-light border-0 d-block"> 2244 2245 <div class="d-flex flex-wrap mb-4"> 2246 2247 @foreach (LoopItem variantGroup in colorVariantGroup) 2248 { 2249 List<string> excludedVariantOptionValues = new List<string>(); 2250 List<dynamic> MoblerVariantGroupValueOptions = new List<dynamic>(); 2251 2252 2253 modalOptionsCount = variantGroup.GetLoop("VariantAvailableOptions").Count(); 2254 2255 foreach (LoopItem opt in variantGroup.GetLoop("VariantAvailableOptions")) 2256 { 2257 if (GroupedVariantGroups.ContainsKey(opt.GetString("Ecom:VariantOption.MaterialType")) == false) 2258 { 2259 GroupedVariantGroups[opt.GetString("Ecom:VariantOption.MaterialType")] = new List<LoopItem>(); 2260 } 2261 GroupedVariantGroups[opt.GetString("Ecom:VariantOption.MaterialType")].Add(opt); 2262 2263 MoblerVariantGroupValueOptions.Add(new 2264 { 2265 ID = opt.GetString("Ecom:VariantOption.ID"), 2266 Name = opt.GetString("Ecom:VariantOption.Name"), 2267 MaterialType = opt.GetString("Ecom:VariantOption.MaterialType"), 2268 HexColor = opt.GetString("Ecom:VariantOption.ColorHex"), 2269 Color = opt.GetString("Ecom:VariantOption.Color") 2270 }); 2271 } 2272 2273 2274 List<dynamic> allMaterials = MoblerVariantGroupValueOptions.GroupBy(p => !string.IsNullOrEmpty(p.MaterialType) && p.MaterialType.Split('_').Length > 1 ? p.MaterialType.Split('_')[1] : string.Empty) 2275 .Select(g => g.First()) 2276 .ToList(); 2277 List<dynamic> allColors = MoblerVariantGroupValueOptions.GroupBy(p => !string.IsNullOrEmpty(p.Color) ? p.Color : string.Empty) 2278 .Select(g => g.First()) 2279 .ToList(); 2280 2281 if (allColors.Any(c => c.Color != "")) 2282 { 2283 hasFilters = true; 2284 2285 2286 <div class="mobler-modal-filter-dropdown-container"> 2287 <p class="font-weight-bold"> 2288 @Translate("Cylindo - Modal - Colors - Header", "Farver") 2289 </p> 2290 <div class="dropdown js-mobler-modal-filter-dropdown mobler-modal-filter-dropdown border-0 mr-1"> 2291 <button class="bg-transparent border btn-block dropdown-toggle p-1 d-flex justify-content-between align-items-center" type="button" id="dropdownColor" data-toggle="dropdown"> 2292 <span class="mobler-modal-filter-dropdown-text" data-bind="text: (SelectedFilterOptionColor().length > 0 ? GetSelectedFilterOptionMaterialNames($element) : '@Translate("Cylindo - Modal - All - Text", "Alle")')"></span> 2293 </button> 2294 <div class="dropdown-menu border"> 2295 <div class="px-2"> 2296 @{ 2297 List<string> control = new List<string>(); 2298 } 2299 @foreach (var color in allColors) 2300 { 2301 var foundColor = !string.IsNullOrEmpty(color.Color) ? color.Color : string.Empty; 2302 if (control.Contains(foundColor)) 2303 { 2304 continue; 2305 } 2306 control.Add(foundColor); 2307 2308 string colorValue = foundColor; 2309 2310 if (string.IsNullOrEmpty(colorValue) == false) 2311 { 2312 string label = colorValue; 2313 2314 <div class="custom-control custom-checkbox"> 2315 <input type="checkbox" class="custom-control-input js-modal-facet-option" id="facet_Color_@color.ID" name="Color" value="@colorValue" data-name="@label" data-bind="event:{change: ChangeModalFilter}, checked: SelectedFilterOptionColor().indexOf('@colorValue') > -1" data-type="Color"> 2316 <label class="custom-control-label" for="facet_Color_@color.ID"> 2317 <span class="custom-control-preview mr-2" style="background-color: @color.HexColor"></span> @label 2318 </label> 2319 </div> 2320 } 2321 2322 2323 } 2324 </div> 2325 </div> 2326 </div> 2327 </div> 2328 } 2329 if (allMaterials.Any(c => c.MaterialType != "")) 2330 { 2331 hasFilters = true; 2332 2333 <div class="mobler-modal-filter-dropdown-container"> 2334 <p class="font-weight-bold"> 2335 @Translate("Cylindo - Modal - Material - Header", "Materialer") 2336 </p> 2337 <div class="dropdown js-mobler-modal-filter-dropdown mobler-modal-filter-dropdown border-0 mr-1"> 2338 <button class="bg-transparent border btn-block dropdown-toggle p-1 d-flex justify-content-between align-items-center" type="button" id="dropdownMaterial" data-toggle="dropdown"> 2339 <span class="mobler-modal-filter-dropdown-text" data-bind="text: (SelectedFilterOptionMaterial().length > 0 ? GetSelectedFilterOptionMaterialNames($element) : '@Translate("Cylindo - Modal - All - Text", "Alle")')"></span> 2340 </button> 2341 <div class="dropdown-menu border"> 2342 <div class="px-2"> 2343 @{ 2344 List<string> control = new List<string>(); 2345 } 2346 @foreach (var material in allMaterials) 2347 { 2348 if (control.Contains(material.MaterialType) || string.IsNullOrEmpty(material.MaterialType)) 2349 { 2350 continue; 2351 } 2352 control.Add(material.MaterialType); 2353 2354 string materialValue = material.MaterialType.Split('_').Length > 1 ? material.MaterialType.Split('_')[1] : string.Empty; 2355 2356 if (string.IsNullOrEmpty(materialValue) == false) 2357 { 2358 string labelTranslationString = $"Cylindo - Modal - Material - {materialValue} - Label"; 2359 <div class="custom-control custom-checkbox"> 2360 <input type="checkbox" class="custom-control-input js-modal-facet-option" id="facet_Material_@material.ID" value="@materialValue" data-name="@Translate(labelTranslationString, materialValue)" data-bind="event:{change: ChangeModalFilter}, checked: SelectedFilterOptionMaterial().indexOf('@materialValue') > -1" data-type="Material"> 2361 <label class="custom-control-label" for="facet_Material_@material.ID"> 2362 @Translate(labelTranslationString, materialValue) 2363 </label> 2364 </div> 2365 } 2366 } 2367 </div> 2368 </div> 2369 </div> 2370 </div> 2371 } 2372 } 2373 @if (hasFilters) 2374 { 2375 <div class="mobler-modal-filter-reset"> 2376 <p>&nbsp;</p> 2377 <a class="text-underline py-1 px-3 d-block pointer" data-bind="click: ResetModalFilters(), clickBubble: false"> 2378 @Translate("Cylindo - Modal - Reset - Text", "Nulstil") 2379 </a> 2380 </div> 2381 } 2382 </div> 2383 </div> 2384 <div class="modal-body mobler-modal-body-list bg-white pb-6 mx-2 mb-3 border-grey"> 2385 2386 @if (GroupedVariantGroups.Any()) 2387 { 2388 modalOptionsCount = GroupedVariantGroups.Sum(vg => vg.Value.Count); 2389 2390 <span data-bind="setInitValue: {observable: ModalOptionsCount, value: '@modalOptionsCount'}"></span> 2391 2392 <div class="form-group js-e-product-form-variant-form-group mb-0"> 2393 2394 <div class="js-mobler-modal-variant-group"> 2395 <div class="mobler-custom-select-container mobler-custom-select-variant"> 2396 @foreach (var variantGroup in GroupedVariantGroups) 2397 { 2398 2399 @GetVariantOptions(variantGroup.Value, isUsingCylindoAssets, cylindoProductId) 2400 } 2401 </div> 2402 </div> 2403 </div> 2404 } 2405 </div> 2406 <div class="modal-footer product-configurator-modal-footer py-1"> 2407 <div class="w-100"> 2408 <div class="d-flex justify-content-between align-items-center px-2 py-2 "> 2409 <a class="text-underline py-1 px-3 pointer" data-dismiss="modal"> 2410 @Translate("Cylindo - Modal - Cancel - Text", "Skjul vælger") 2411 </a> 2412 <button class="btn btn-primary" data-dismiss="modal" data-bind="click: UpdateMain"> 2413 @Translate("Cylindo - Modal - Save - Text", "Gem valg") 2414 </button> 2415 </div> 2416 </div> 2417 </div> 2418 </div> 2419 </div> 2420 </div> 2421 </div> 2422 </div> 2423 2424 2425 @helper GetVariantOptions(List<LoopItem> variantGroup, bool isUsingCylindoAssets, string cylindoProductId) 2426 { 2427 2428 int loadCount = 21; 2429 2430 foreach (LoopItem variantOption in variantGroup) 2431 { 2432 string cylindoAccountId = Pageview.Area.Item["CylindoAccountId"]?.ToString(); 2433 string variantOptionId = variantOption.GetString("Ecom:VariantOption.ID"); 2434 string variantOptionLabel = variantOption.GetString("Ecom:VariantOption.Name"); 2435 string variantOptionLabelTranslationString = $"Cylindo - Modal - Option - {variantOptionLabel} - Label"; 2436 string variantOptionLabelTranslated = Translate(variantOptionLabelTranslationString, variantOptionLabel); 2437 string variantOptionMaterialType = variantOption.GetString("Ecom:VariantOption.MaterialType"); 2438 string variantOptionColor = variantOption.GetString("Ecom:VariantOption.Color"); 2439 string variantOptionHex = variantOption.GetString("Ecom:VariantOption.ColorHex"); 2440 string variantOptionImage = variantOption.GetString("Ecom:VariantOption.ImgSmall.Clean"); 2441 string cylindoFeatures = string.Empty; 2442 string backgroundFabric = string.Empty; 2443 string backgroundZoom = string.Empty; 2444 string materialUrl = string.Empty; 2445 int counter = variantOption.GetInteger("VariantAvailableOptions.LoopCounter"); 2446 2447 if(isUsingCylindoAssets) 2448 { 2449 materialUrl = $"https://content.cylindo.com/api/v2/{cylindoAccountId}/products/{cylindoProductId}/material/{variantOptionMaterialType}.png?feature=MATERIAL:{variantOptionMaterialType}"; 2450 2451 if (!string.IsNullOrEmpty(variantOptionMaterialType)) 2452 { 2453 cylindoFeatures = $"Material {variantOptionMaterialType}"; 2454 2455 } 2456 } 2457 2458 backgroundFabric = $"background-color: #fff"; 2459 2460 if (string.IsNullOrEmpty(variantOptionHex) == false) 2461 { 2462 backgroundFabric = $"background-color: {variantOptionHex}"; 2463 } 2464 if (string.IsNullOrEmpty(variantOptionImage) == false) 2465 { 2466 backgroundFabric = $"background-image: url({variantOptionImage})"; 2467 } 2468 2469 backgroundZoom = backgroundFabric; 2470 2471 if (string.IsNullOrEmpty(materialUrl) == false) 2472 { 2473 string materialUrlSmall = $"{materialUrl}&size=140&crop=(0,0,140,140)"; 2474 string materialUrlLarge = $"{materialUrl}&size=166&crop=(0,0,166,166)"; 2475 2476 backgroundFabric = $"background-image: url('{materialUrlSmall}')"; 2477 backgroundZoom = $"background-image: url('{materialUrlLarge}')"; 2478 } 2479 bool hasVisualContent = !(string.IsNullOrEmpty(variantOptionMaterialType) && string.IsNullOrEmpty(variantOptionColor)); 2480 2481 <label class="js-mobler-modal-variant-option @((hasVisualContent && counter > loadCount) ? "js-mobler-modal-variant-option-skeleton" : string.Empty)" data-material="@variantOptionMaterialType" data-color="@variantOptionColor"> 2482 <input class="variant-color" name="ModelVariantOption" title="@variantOptionLabelTranslated" type="radio" value="@variantOptionId" data-cylindo-features="@cylindoFeatures" data-variant-label="@variantOptionLabel" data-bind="event: {change: UpdateModalByVariant}, checked: SelectedModalVariantId().split('.')[SelectedVariantOptionIdForModal()]" data-combination="@($"{variantOptionMaterialType}_{variantOptionColor}")"> 2483 @if (hasVisualContent) 2484 { 2485 <span class="border select-option js-mobler-modal-variant-option-background" style="@(counter <= loadCount ? backgroundFabric : string.Empty)" data-counter="@counter" data-background="@backgroundFabric"> 2486 <span class="image-magnifier-large js-mobler-modal-variant-option-zoom" style="@(counter <= loadCount ? backgroundZoom : string.Empty)" data-background="@backgroundZoom"></span> 2487 </span> 2488 } 2489 2490 <span class="custom-control-description text-center d-block small text-muted @(!hasVisualContent ? "text-only-modal-variant-option" : string.Empty)">@variantOptionLabelTranslated</span> 2491 </label> 2492 2493 } 2494 } 2495 2496 </div> 2497 2498 <div class="modal fade video-modal" id="videoModal" tabindex="-1" role="dialog" aria-labelledby="video-modal" aria-hidden="true"> 2499 <div class="modal-dialog modal-dialog-centered" role="document"> 2500 <div class="modal-content"> 2501 <div class="modal-body"> 2502 <div id="player"></div> 2503 </div> 2504 </div> 2505 </div> 2506 </div> 2507 @SnippetStart("DataLayerOverwrites") 2508 <script> 2509 ecomm_pagetype = "Product"; 2510 ecomm_totalvalue = @GetString("Ecom:Product.Price.Price").Replace(".","").Replace(",","."); 2511 ecomm_prodid = "@GetString("Ecom:Product.ID")"; 2512 </script> 2513 @SnippetEnd("DataLayerOverwrites") 2514 2515 <script type="application/ld+json"> 2516 { 2517 "@@context": "https://schema.org/", 2518 "@@type": "Product", 2519 "name": "@ProductName", 2520 "image": "@(OgImage.Replace("+", "%20"))", 2521 "description": "@(Regex.Replace(TrimmedTeaser, "<.*?>", String.Empty))", 2522 "sku": "@ProductNumber", 2523 "category": "@DataLayerParentGroup", 2524 "brand": { 2525 "@@type": "Brand", 2526 "name": "@BrandName" 2527 }, 2528 "offers": { 2529 "@@type": "Offer", 2530 "url": "@ProductFriendlyUrl", 2531 "priceCurrency": "DKK", 2532 "price": "@DataLayerPrice", 2533 "itemCondition": "https://schema.org/NewCondition", 2534 "availability": "https://schema.org/InStock", 2535 "seller": { 2536 "@@type": "Organization", 2537 "name": "@CStrings.moblerName" 2538 } 2539 } 2540 } 2541 </script> 2542