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