Skip to main content
Error executing template "Designs/Keflico/eCom/Product/Product.cshtml"
System.InvalidOperationException: Sequence contains no elements
   at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
   at CompiledRazorTemplates.Dynamic.RazorEngine_539275074db04380b34d2c5d8f325f57.Execute() in D:\dynamicweb.net\Solutions\keflico.live\Files\Templates\Designs\Keflico\eCom\Product\Product.cshtml:line 1148
   at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
   at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
   at Dynamicweb.Rendering.Template.RenderRazorTemplate()

1 @using System.Web; 2 @using System.Web.Helpers; 3 @using System.IO; 4 5 @using Dynamicweb.Rendering; 6 @using Dynamicweb.Ecommerce.ProductCatalog; 7 @using Dynamicweb.Ecommerce.Variants; 8 @using Dynamicweb.Core; 9 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 10 @using Keflico.Website.Custom 11 12 @{ 13 System.Web.HttpCookie lastProductIdCookie = new System.Web.HttpCookie("LastProductIdCookie"); 14 lastProductIdCookie.Value = GetString("Ecom:Product.ID"); 15 lastProductIdCookie.Expires = DateTime.MinValue; 16 HttpContext.Current.Response.Cookies.Add(lastProductIdCookie); 17 18 var viewmodelSettings = new ProductViewModelSettings(Dynamicweb.Ecommerce.Common.Context.LanguageID, Dynamicweb.Ecommerce.Common.Context.Currency.Code, Dynamicweb.Ecommerce.Common.Context.Country.Code2, Pageview.Area.EcomShopId, Dynamicweb.Security.UserManagement.User.GetCurrentExtranetUserId()); 19 var productViewmodel = Dynamicweb.Ecommerce.ProductCatalog.ViewModelFactory.CreateView(viewmodelSettings, GetString("Ecom:Product.ID"), GetString("Ecom:Product.VariantId"), ""); 20 var combination = new VariantCombination(GetString("Ecom:Product.ID")); 21 IList<VariantCombination> productVariants = new List<VariantCombination>(); 22 var singleVariant = new VariantCombination(); 23 24 if(combination != null && combination.Product != null) { 25 productVariants = combination.Product.VariantCombinations; 26 } 27 28 if(productVariants.Count == 1) { 29 singleVariant = productVariants.FirstOrDefault(); 30 } 31 32 33 string CartPage = Pageview.Area.Item["CartPage"].ToString(); 34 string cartCount = GetGlobalValue("Global:eCommerce.Order.OrderLines.TotalProductQuantity"); 35 36 if(string.IsNullOrWhiteSpace(cartCount)) { 37 cartCount = "0"; 38 } 39 40 string EcomPage = Pageview.Area.Item["EcomPage"].ToString(); 41 string VariantsLookup = "/" + Pageview.Area.Item["Variantslookup"].ToString(); 42 string BundleLookup = "/" + Pageview.Area.Item["Bundlelookup"].ToString(); 43 string PriceLookup = "/" + Pageview.Area.Item["Pricelookup"].ToString(); 44 string productNumber = GetString("Ecom:Product.Number"); 45 string dbNumber = GetString("Ecom:Product:Field.ProductDbNumber.Value"); 46 string productId = GetString("Ecom:Product.ID"); 47 string productName = GetString("Ecom:Product.Name"); 48 string primaryGroup = GetString("Ecom:Product.PrimaryGroupID").ToLower(); 49 string formAction = "/" + CartPage; 50 string priceCurrencySymbol = GetString("Ecom:Product.Currency.Symbol"); 51 string loopCounter = GetString("Ecom:Product.LoopCounter"); 52 string inputName = "Quantity" + loopCounter; 53 var categories = GetLoop("AssociatedGroups"); 54 var mainCategory = (LoopItem)null; 55 var productLayout = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductLayout")) ? GetString("Ecom:Product:Field.ProductLayout") : primaryGroup; 56 57 string labelCode = GetString("Ecom:Product:Field.ProductPurchaseCode"); 58 59 var breadcrumbs = new List<LoopItem>(); 60 61 bool useDifferentLengths = false; 62 63 foreach (var cat in categories) { 64 if(cat.GetString("Ecom:Group.ID").ToLower() == primaryGroup) { 65 mainCategory = cat; 66 } 67 } 68 69 70 if(mainCategory != null) { 71 breadcrumbs.Add(mainCategory); 72 73 foreach (var child in mainCategory.GetLoop("Childgroups")) { 74 foreach(var category in categories) { 75 if (category.GetString("Ecom:Group.ID").ToLower() == child.GetString("Ecom:Group.ID").ToLower()) { 76 breadcrumbs.Add(category); 77 useDifferentLengths = category.GetBoolean("Ecom:Group:Field.DiffLengths"); 78 } 79 } 80 } 81 } 82 83 bool enquireProduct = false; 84 bool isSunDryProduct = System.Convert.ToBoolean(GetString("Ecom:Product:Field.ProductSundryItem")); 85 bool isBundleOnly = System.Convert.ToBoolean(GetString("Ecom:Product:Field.BundleOnly")); 86 87 var totalStock = GetDouble("Ecom:Product.Stock"); 88 totalStock += GetDouble("Ecom:Product:Field.StockSea.Value"); 89 90 int totalStockWareHouse = GetInteger("Ecom:Product.Stock"); 91 int totalStockSea = GetInteger("Ecom:Product:Field.StockSea.Value"); 92 93 if(primaryGroup != "group32") { 94 totalStock += GetDouble("Ecom:Product:Field.ProductPieceOnPurchase.Value"); 95 totalStockSea += int.Parse(GetString("Ecom:Product:Field.ProductPieceOnPurchase.Value").Replace(",", string.Empty)); 96 } 97 98 bool isSingleVariant = GetInteger("Ecom:Product.VariantCount") == 1 ? true : false; 99 100 double singleVariantStock = 0; 101 int singleVariantStockSea = 0; 102 103 if(isSingleVariant) { 104 singleVariantStock = singleVariant.Product.UnitStock; 105 singleVariantStockSea = !String.IsNullOrWhiteSpace(singleVariant.Product.ProductFieldValues.GetProductFieldValue("ProductPieceOnPurchase").Value.ToString()) ? Convert.ToInt32(singleVariant.Product.ProductFieldValues.GetProductFieldValue("ProductPieceOnPurchase").Value.ToString().Replace(",", "")) : 0; 106 107 totalStock += singleVariantStock; 108 totalStock += singleVariantStockSea; 109 } 110 111 string halfParcelAmount = GetString("Ecom:Product:Field.ProductNumberPerHalfPackage.Value.Raw"); 112 string completeParcelAmount = GetString("Ecom:Product:Field.ProductNumberPerPackage"); 113 114 string standardPrice = GetString("Ecom:Product.Price.PriceWithoutVAT"); 115 string standardPriceJS = !string.IsNullOrWhiteSpace(GetString("Ecom:Product.Discount.Price.PriceWithoutVAT.Value")) ? GetString("Ecom:Product.Discount.Price.PriceWithoutVAT.Value").Replace(",", ".") : "0"; 116 string halfPrice = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceHalfParcel")) ? GetString("Ecom:Product:Field.ProductPriceHalfParcel").Replace(".","-").Replace(",", ".").Replace("-", ",") : "0"; 117 string fullPrice = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceCompleteParcel")) ? GetString("Ecom:Product:Field.ProductPriceCompleteParcel").Replace(".","-").Replace(",", ".").Replace("-", ",") : "0"; 118 string bundlePrice = GetString("Ecom:Product:Field.ProductPriceBundle.Value").Replace(",", "-").Replace(".", ",").Replace("-", "."); 119 string bundlePriceJS = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceBundle.Value")) ? GetString("Ecom:Product:Field.ProductPriceBundle.Value").Replace(",", "") : "0"; 120 string above100Price = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceAbove100SQM")) ? GetString("Ecom:Product:Field.ProductPriceAbove100SQM") : "0"; 121 string above100PriceJS = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceAbove100SQM.Value.Raw")) ? GetString("Ecom:Product:Field.ProductPriceAbove100SQM.Value.Raw").Replace(",", ".") : "0"; 122 string above3000Price = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceAbove3000M.Value")) ? GetString("Ecom:Product:Field.ProductPriceAbove3000M.Value").Replace(".", ",") : "0"; 123 124 string salesUnit = GetString("Ecom:Product.DefaultUnitName").ToLower(); 125 string salesUnitCode = GetString("Ecom:Product:Field.ProductLengthUnitCode"); 126 127 string configuratorUrl = GetString("Ecom:Product:Field.ProductConfiguratorUrl"); 128 129 string fscLink = Pageview.Area.Item["FSC"].ToString(); 130 string pefcLink = Pageview.Area.Item["PEFC"].ToString(); 131 string ecoNordicLink = Pageview.Area.Item["EcoNordic"].ToString(); 132 string cradleToCradleLink = Pageview.Area.Item["CradleToCradle"].ToString(); 133 134 bool hasNewDownloads = false; 135 bool hasDownloads = false; 136 137 foreach(var fileCat in GetLoop("ImageCategories")) { 138 139 if(fileCat.GetString("Category.SystemName").StartsWith("docView_") && fileCat.GetLoop("Category.Images").Count() > 0) { 140 hasNewDownloads = true; 141 hasDownloads = false; 142 break; 143 } 144 else if(fileCat.GetString("Category.SystemName") != "Images" && fileCat.GetLoop("Category.Images").Count() > 0) { 145 hasDownloads = true; 146 } 147 } 148 149 bool isloggedin = false; 150 151 if (!string.IsNullOrWhiteSpace(GetGlobalValue("Global:Extranet.UserName").ToString())) { 152 isloggedin = true; 153 } 154 155 var isFavorite = false; 156 if (isloggedin) 157 { 158 var user = Dynamicweb.Security.UserManagement.User.GetCurrentFrontendUser(); 159 var favoriteList = user.GetFavoriteLists().FirstOrDefault(); 160 if (favoriteList != null) 161 { 162 var service = new FavoriteProductService(); 163 isFavorite = service.GetFavoriteProducts(favoriteList.ListId).Any(x => x.ProductId == productId); 164 } 165 166 } 167 168 169 170 171 172 if(GetString("Ecom:Product:Field.EcomProduct").Contains("forespoerg")) { 173 enquireProduct = true; 174 } 175 176 if(isSunDryProduct) { 177 enquireProduct = true; 178 } 179 180 if(labelCode.ToLower() == "skaffe" || labelCode.ToLower() == "relatordre") { 181 enquireProduct = true; 182 } 183 184 var shouldRefreshFavorite = (isloggedin && !Dynamicweb.Security.UserManagement.User.GetCurrentFrontendUser().UserHasFavoriteList()).ToString().ToLower(); 185 } 186 187 @SnippetStart("PageTemplate") 188 product-page 189 @SnippetEnd("PageTemplate") 190 191 192 <article class="module module-sand product-details"> 193 <div class="breadcrumbs"> 194 <ul class="breadcrumbs__list"> 195 <li class="breadcrumbs__item"> 196 <a href="@EcomPage">@Translate("Translate_Breadcrumb_Products")</a> 197 </li> 198 199 @if(breadcrumbs.Count > 0) { 200 foreach(var group in breadcrumbs) { 201 <li class="breadcrumbs__item"> 202 <a href="@group.GetString("Ecom:Group.Link.Clean")">@group.GetString("Ecom:Group.Name")</a> 203 </li> 204 } 205 } 206 <li class="breadcrumbs__item active">@productName</li> 207 </ul> 208 </div> 209 <div class="product-details__container"> 210 @switch (labelCode.ToLower()) 211 { 212 case "prøver": 213 <div class="product-details__label-code label-code label-code--samples">@Translate("LabelCode_" + labelCode.ToLower().Replace("ø", "oe"))</div> 214 break; 215 case "skaffe": 216 case "relatordre": 217 <div class="product-details__label-code label-code">@Translate("LabelCode_" + labelCode.ToLower())</div> 218 break; 219 default: 220 break; 221 } 222 <div class="product-details__media" data-igo-gallery> 223 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product.ImageDefault.Clean"))) 224 { 225 <picture class="product-details__image-wrap" data-igo-image="@GetString("Ecom:Product.ImageDefault.Clean")"> 226 <img src="@GetString("Ecom:Product.ImageDefault.Clean")" alt="@productName" loading="lazy"> 227 </picture> 228 } 229 @foreach (var category in GetLoop("ImageCategories")) 230 { 231 if (category.GetString("Category.SystemName") == "Images") 232 { 233 foreach (var image in category.GetLoop("Category.Images")) 234 { 235 <picture class="product-details__image-wrap" data-igo-image="@image.GetString("Ecom:Product:Detail.Image.Clean")"> 236 <img src="@image.GetString("Ecom:Product:Detail.Image.Clean")" alt="@productName" loading="lazy"> 237 </picture> 238 } 239 } 240 } 241 <div class="product-details__media-steps"> 242 <span class="disclaimer">* @Translate("Translate_Product_Page_ImageDisclaimer")</span> 243 <span class="current">1/1</span> 244 </div> 245 </div> 246 <div class="product-details__info"> 247 <div class="product-details__info-labels"> 248 <div class="product-details__info-labels-icons"> 249 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductCertification")) && GetString("Ecom:Product:Field.ProductCertification") != "-1") 250 { 251 string certification = GetString("Ecom:Product:Field.ProductCertification").ToLower(); 252 if (certification.Contains("pefc")) 253 { 254 <a href="@pefcLink" target="_blank"> 255 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/pefc-logo.svg"))) 256 { 257 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/pefc-logo.svg")) 258 } 259 </a> 260 } 261 else if (certification.Contains("fsc")) 262 { 263 <a href="@fscLink" target="_blank"> 264 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/fsc-logo.svg"))) 265 { 266 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/fsc-logo.svg")) 267 } 268 </a> 269 } 270 } 271 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductNordicEcoLabel")) && GetString("Ecom:Product:Field.ProductNordicEcoLabel") != "-1") 272 { 273 <a href="@ecoNordicLink" target="_blank"> 274 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/svanemaerket-logo.svg"))) 275 { 276 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/svanemaerket-logo.svg")) 277 } 278 </a> 279 } 280 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductCradleToCradle.Value")) && GetString("Ecom:Product:Field.ProductCradleToCradle.Value") != "-1" && GetString("Ecom:Product:Field.ProductCradleToCradle.Value") != "N/A") 281 { 282 <a href="@cradleToCradleLink" target="_blank"> 283 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/cradle-logo.svg"))) 284 { 285 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/cradle-logo.svg")) 286 } 287 </a> 288 } 289 </div> 290 @if (isloggedin) 291 { 292 <button id="favoriteAdd" style="@(isFavorite ? "display:none" : "")" class="product-details__info-favorite" onclick="favoriteAdd()"> 293 <svg id="Lag_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100"> 294 <!-- Generator: Adobe Illustrator 29.5.0, SVG Export Plug-In . SVG Version: 2.1.0 Build 137) --> 295 <path fill="#696969" d="M50,92.9c-.2,0-.5,0-.6-.3L10,53.2c-4.9-4.9-7.5-11.3-7.5-18.2s2.7-13.4,7.5-18.2c4.9-4.9,11.3-7.5,18.2-7.5s13.4,2.7,18.2,7.5l3.5,3.5,3.5-3.5c4.9-4.9,11.3-7.5,18.2-7.5s13.4,2.7,18.2,7.5c4.9,4.9,7.5,11.3,7.5,18.2s-2.7,13.4-7.5,18.2l-39.4,39.4c-.2.2-.4.3-.6.3ZM50.1,90.8l38.8-38.8c4.5-4.5,7-10.6,7-17s-2.5-12.5-7-17c-4.5-4.5-10.6-7-17-7s-12.5,2.5-17,7l-4.2,4.2c-.2.2-.4.3-.6.3s-.5,0-.6-.3l-4.2-4.2c-4.5-4.5-10.6-7-17-7s-12.5,2.5-17,7c-4.5,4.5-7,10.6-7,17s2.5,12.5,7,17l.4.4,38.4,38.4Z"/> 296 </svg> 297 </button> 298 <button id="favoriteRemove" style="@(isFavorite ? "" : "display:none")" class="product-details__info-favorite" onclick="favoriteRemove()"> 299 <svg id="Lag_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100"> 300 <!-- Generator: Adobe Illustrator 29.5.0, SVG Export Plug-In . SVG Version: 2.1.0 Build 137) --> 301 <path fill="#964B00" d="M50,92.9c-.2,0-.5,0-.6-.3L10,53.2c-4.9-4.9-7.5-11.3-7.5-18.2s2.7-13.4,7.5-18.2c4.9-4.9,11.3-7.5,18.2-7.5s13.4,2.7,18.2,7.5l3.5,3.5,3.5-3.5c4.9-4.9,11.3-7.5,18.2-7.5s13.4,2.7,18.2,7.5c4.9,4.9,7.5,11.3,7.5,18.2s-2.7,13.4-7.5,18.2l-39.4,39.4c-.2.2-.4.3-.6.3Z"/> 302 </svg> 303 </button> 304 } 305 306 </div> 307 <div class="product-details__info-meta"> 308 <ul class="product-details__info-meta-list"> 309 <li id="productNumber">@Translate("Translate_Product_Page_ProductNumber"): @productNumber</li> 310 @if (!string.IsNullOrWhiteSpace(dbNumber)) 311 { 312 <li>@GetString("Ecom:Product:Field.ProductDbNumber.Name"): @dbNumber</li> 313 } 314 </ul> 315 </div> 316 <h1 id="productName" class="product-details__info-title">@productName</h1> 317 <div class="product-details__info-size">@GetString("Ecom:Product:Field.Name2")</div> 318 319 @switch (productLayout) 320 { 321 case "group1": // Hårdtræ 322 if (isloggedin) 323 { 324 if ((standardPriceJS != "0" && !isBundleOnly) || bundlePriceJS != "0") 325 { 326 <div class="product-details__info-price-container"> 327 <div class="product-details__info-price-heading"> 328 @Translate("Translate_ProductPage_PriceDefinition_HardWood") 329 <span class="tooltip"> 330 <span class="tooltip__icon"> 331 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 332 { 333 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 334 } 335 </span> 336 <span class="tooltip__text tooltip__text--top"> 337 @Translate("Translate_ProductPage_PriceDefinition_HardWood_Tooltip") 338 </span> 339 </span> 340 </div> 341 @if (standardPriceJS != "0" && !isBundleOnly) 342 { 343 <div class="product-details__info-price-option"> 344 <div class="product-details__info-price-option-title">@Translate("Translate_ProductPage_Breakup")</div> 345 <div class="product-details__info-price-option-price">@standardPrice @priceCurrencySymbol pr. @salesUnit</div> 346 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 347 </div> 348 } 349 @if (bundlePriceJS != "0") 350 { 351 if (bundlePrice.Contains(",")) 352 { 353 string[] sp = bundlePrice.Split(','); 354 355 if (sp[1].Length == 1) 356 { 357 bundlePrice += "0"; 358 } 359 } 360 else 361 { 362 bundlePrice += ",00"; 363 } 364 365 <div class="product-details__info-price-option"> 366 <div class="product-details__info-price-option-title">@Translate("Translate_ProductPage_Bundle")</div> 367 <div class="product-details__info-price-option-price">@bundlePrice @priceCurrencySymbol pr. @salesUnit</div> 368 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 369 </div> 370 } 371 </div> 372 } 373 } 374 375 if (standardPriceJS == "0" && bundlePriceJS == "0") 376 { 377 enquireProduct = true; 378 } 379 380 <div id="product-order-button" style="min-height: 64px;"> 381 @if (enquireProduct) 382 { 383 <a data-action="open-content" data-target="#enquireForm" class="product-details__info-button btn btn--block btn-secondary product-order-form-button">@Translate("Translate_Product_Page_EnquireButton")</a> 384 } 385 else 386 { 387 <div style="display: none;" class="loader"></div> 388 <a style="display: none;" href="#" class="product-details__info-button btn btn--block btn-secondary product-order-button">@Translate("Translate_Product_Page_OrderButton")</a> 389 <a style="display: none;" data-action="open-content" data-target="#enquireForm" class="product-details__info-button btn btn--block btn-secondary product-order-form-button">@Translate("Translate_Product_Page_EnquireButton")</a> 390 } 391 </div> 392 393 break; 394 395 case "group32": // Terrasse 396 397 if (isloggedin) 398 { 399 if (standardPriceJS != "0" || above100PriceJS != "0") 400 { 401 <div class="product-details__info-price-container"> 402 <div class="product-details__info-price-heading"> 403 @Translate("Translate_ProductPage_PriceDefinition_Terrasse") 404 405 @if (primaryGroup == "group32") 406 { 407 <span class="tooltip"> 408 <span class="tooltip__icon"> 409 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 410 { 411 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 412 } 413 </span> 414 <span class="tooltip__text tooltip__text--top"> 415 @Translate("Translate_ProductPage_PriceDefinition_Terrasse_Tooltip") 416 </span> 417 </span> 418 } 419 else 420 { 421 <span class="tooltip"> 422 <span class="tooltip__icon"> 423 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 424 { 425 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 426 } 427 </span> 428 <span class="tooltip__text tooltip__text--top"> 429 @Translate("Translate_ProductPage_PriceDefinition_Facade_Tooltip") 430 </span> 431 </span> 432 } 433 </div> 434 435 @if (standardPriceJS != "0") 436 { 437 <div class="product-details__info-price-option"> 438 <div class="product-details__info-price-option-title">@Translate("Translate_ProductPage_Under100")</div> 439 <div class="product-details__info-price-option-price">@standardPrice @priceCurrencySymbol pr. @salesUnit</div> 440 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 441 </div> 442 } 443 444 @if (above100PriceJS != "0") 445 { 446 if (above100Price.Contains(",")) 447 { 448 string[] sp = above100Price.Split(','); 449 450 if (sp[1].Length == 1) 451 { 452 above100Price += "0"; 453 } 454 } 455 else 456 { 457 above100Price += ",00"; 458 } 459 460 <div class="product-details__info-price-option"> 461 <div class="product-details__info-price-option-title">@Translate("Translate_ProductPage_Above100")</div> 462 <div class="product-details__info-price-option-price">@above100Price @priceCurrencySymbol pr. @salesUnit</div> 463 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 464 </div> 465 } 466 </div> 467 } 468 } 469 470 if (standardPriceJS == "0" && above100PriceJS == "0") 471 { 472 enquireProduct = true; 473 } 474 475 <div id="product-order-button" style="min-height: 64px;"> 476 @if (enquireProduct) 477 { 478 <a data-action="open-content" data-target="#enquireForm" class="product-details__info-button btn btn--block btn-secondary product-order-form-button">@Translate("Translate_Product_Page_EnquireButton")</a> 479 } 480 else 481 { 482 <div style="display: none;" class="loader"></div> 483 <a style="display: none;" href="#" class="product-details__info-button btn btn--block btn-secondary product-order-button">@Translate("Translate_Product_Page_OrderButton")</a> 484 <a style="display: none;" data-action="open-content" data-target="#enquireForm" class="product-details__info-button btn btn--block btn-secondary product-order-form-button">@Translate("Translate_Product_Page_EnquireButton")</a> 485 } 486 </div> 487 488 break; 489 490 case "group73": // Facadebeklædning 491 if (isloggedin) 492 { 493 if (standardPriceJS != "0" || above100PriceJS != "0" || above3000Price != "0") 494 { 495 <div class="product-details__info-price-container"> 496 <div class="product-details__info-price-heading"> 497 @Translate("Translate_ProductPage_PriceDefinition_Terrasse") 498 499 @if (primaryGroup == "group32") 500 { 501 <span class="tooltip"> 502 <span class="tooltip__icon"> 503 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 504 { 505 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 506 } 507 </span> 508 <span class="tooltip__text tooltip__text--top"> 509 @Translate("Translate_ProductPage_PriceDefinition_Terrasse_Tooltip") 510 </span> 511 </span> 512 } 513 else 514 { 515 <span class="tooltip"> 516 <span class="tooltip__icon"> 517 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 518 { 519 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 520 } 521 </span> 522 <span class="tooltip__text tooltip__text--top"> 523 @Translate("Translate_ProductPage_PriceDefinition_Facade_Tooltip") 524 </span> 525 </span> 526 } 527 </div> 528 529 @if (standardPriceJS != "0") 530 { 531 <div class="product-details__info-price-option"> 532 <div class="product-details__info-price-option-title">@Translate("Translate_ProductPage_Under1000")</div> 533 <div class="product-details__info-price-option-price">@standardPrice @priceCurrencySymbol pr. @salesUnit</div> 534 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 535 </div> 536 } 537 538 @if (above100PriceJS != "0") 539 { 540 if (above100Price.Contains(",")) 541 { 542 string[] sp = above100Price.Split(','); 543 544 if (sp[1].Length == 1) 545 { 546 above100Price += "0"; 547 } 548 } 549 else 550 { 551 above100Price += ",00"; 552 } 553 554 <div class="product-details__info-price-option"> 555 <div class="product-details__info-price-option-title">@Translate("Translate_ProductPage_Between1000and3000")</div> 556 <div class="product-details__info-price-option-price">@above100Price @priceCurrencySymbol pr. @salesUnit</div> 557 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 558 </div> 559 } 560 561 @if (above3000Price != "0") 562 { 563 if (above3000Price.Contains(",")) 564 { 565 string[] tp = above3000Price.Split(','); 566 567 if (tp[1].Length == 1) 568 { 569 above3000Price += "0"; 570 } 571 } 572 else 573 { 574 above3000Price += ",00"; 575 } 576 577 <div class="product-details__info-price-option"> 578 <div class="product-details__info-price-option-title">@Translate("Translate_ProductPage_Above3000")</div> 579 <div class="product-details__info-price-option-price">@above3000Price @priceCurrencySymbol pr. @salesUnit</div> 580 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 581 </div> 582 } 583 </div> 584 } 585 } 586 587 if (standardPriceJS == "0" && above100PriceJS == "0" && above3000Price == "0") 588 { 589 enquireProduct = true; 590 } 591 592 <div id="product-order-button" style="min-height: 64px;"> 593 @if (enquireProduct) 594 { 595 <a data-action="open-content" data-target="#enquireForm" class="product-details__info-button btn btn--block btn-secondary product-order-form-button">@Translate("Translate_Product_Page_EnquireButton")</a> 596 } 597 else 598 { 599 <div style="display: none;" class="loader"></div> 600 <a style="display: none;" href="#" class="product-details__info-button btn btn--block btn-secondary product-order-button">@Translate("Translate_Product_Page_OrderButton")</a> 601 <a style="display: none;" data-action="open-content" data-target="#enquireForm" class="product-details__info-button btn btn--block btn-secondary product-order-form-button">@Translate("Translate_Product_Page_EnquireButton")</a> 602 } 603 </div> 604 605 break; 606 607 case "group52": // Plader 608 case "group66": // Dekorative Overflader 609 610 if (isloggedin) 611 { 612 if (standardPriceJS != "0" || halfPrice != "0" || fullPrice != "0") 613 { 614 <div class="product-details__info-price-container"> 615 <div class="product-details__info-price-heading"> 616 @Translate("Translate_ProductPage_PriceDefinition_Plader") 617 @if (primaryGroup == "group52") 618 { 619 <span class="tooltip"> 620 <span class="tooltip__icon"> 621 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 622 { 623 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 624 } 625 </span> 626 <span class="tooltip__text tooltip__text--top"> 627 @Translate("Translate_ProductPage_PriceDefinition_Plader_Tooltip") 628 </span> 629 </span> 630 } 631 else 632 { 633 <span class="tooltip"> 634 <span class="tooltip__icon"> 635 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 636 { 637 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 638 } 639 </span> 640 <span class="tooltip__text tooltip__text--top"> 641 @Translate("Translate_ProductPage_PriceDefinition_Overflader_Tooltip") 642 </span> 643 </span> 644 } 645 </div> 646 647 @if (standardPriceJS != "0") 648 { 649 <div class="product-details__info-price-option"> 650 <div class="product-details__info-price-option-title">@Translate("Translate_ProductPage_PriceUnit_Anbrud")</div> 651 <div class="product-details__info-price-option-price">@standardPrice @priceCurrencySymbol pr. @salesUnit</div> 652 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 653 </div> 654 } 655 656 @if (halfPrice != "0") 657 { 658 if (halfPrice.Contains(",")) 659 { 660 string[] sp = halfPrice.Split(','); 661 662 if (sp[1].Length == 1) 663 { 664 halfPrice += "0"; 665 } 666 } 667 else 668 { 669 halfPrice += ",00"; 670 } 671 672 <div class="product-details__info-price-option"> 673 <div class="product-details__info-price-option-title">@Translate("Translate_ProductPage_PriceUnit_HalfParcel") (@halfParcelAmount @Translate("Translate_Ecom_Piece"))</div> 674 <div class="product-details__info-price-option-price">@halfPrice @priceCurrencySymbol pr. @salesUnit</div> 675 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 676 </div> 677 } 678 679 @if (fullPrice != "0") 680 { 681 if (fullPrice.Contains(",")) 682 { 683 string[] sp = fullPrice.Split(','); 684 685 if (sp[1].Length == 1) 686 { 687 fullPrice += "0"; 688 } 689 } 690 else 691 { 692 fullPrice += ",00"; 693 } 694 695 <div class="product-details__info-price-option"> 696 <div class="product-details__info-price-option-title">@Translate("Translate_ProductPage_PriceUnit_CompleteParcel") (@completeParcelAmount @Translate("Translate_Ecom_Piece"))</div> 697 <div class="product-details__info-price-option-price">@fullPrice @priceCurrencySymbol pr. @salesUnit</div> 698 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 699 </div> 700 } 701 </div> 702 } 703 } 704 705 <div class="product-details__info-quantity"> 706 @{ 707 if (standardPriceJS == "0" && halfPrice == "0" && fullPrice == "0") 708 { 709 enquireProduct = true; 710 } 711 } 712 @if (totalStock > 0 && isloggedin && !enquireProduct) 713 { 714 <form class="form" action="@(formAction)&Redirect=@(CartPage)" method="post" name="@productId" id="@productId"> 715 <input type="hidden" name="CartCmd" value="addMulti" /> 716 717 @if (isSingleVariant) 718 { 719 <input type="hidden" name="ProductLoopCounter@(loopCounter)" id="ProductLoopCounter@(loopCounter)" value="@loopCounter"> 720 <input type="hidden" name="ProductID@(loopCounter)" id="ProductID@(loopCounter)" value="@productId"> 721 <input type="hidden" name="VariantID@(loopCounter)" id="VariantID@(loopCounter)" value="@singleVariant.VariantId"> 722 } 723 else 724 { 725 @GetString("Ecom:Product.Form.Multi.HiddenFields") 726 } 727 728 <div class="counter"> 729 <a class="substract">-</a> 730 <input name="@inputName" id="Quantity" class="amount" type="tel" value="1"> 731 <label for="Quanity" class="unit">@Translate("Translate_Piece_Short")</label> 732 <a class="add">+</a> 733 </div> 734 <button type="submit" id="product-order-button" class="product-details__info-button btn btn--block btn-secondary">Bestil</button> 735 </form> 736 } 737 else 738 { 739 <a href="#product-inquire-form" id="product-order-button" data-action="open-content" data-target="#enquireForm" class="product-details__info-button btn btn--block btn-secondary">@Translate("Translate_Product_Page_EnquireButton")</a> 740 } 741 </div> 742 743 break; 744 745 case "group126": // Tilbehør 746 747 if (isloggedin) 748 { 749 if (standardPriceJS != "0") 750 { 751 <div class="product-details__info-price-container"> 752 <div class="product-details__info-price-heading"> 753 @Translate("Translate_ProductPage_PriceDefinition_Andet") 754 <span class="tooltip"> 755 <span class="tooltip__icon"> 756 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 757 { 758 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 759 } 760 </span> 761 <span class="tooltip__text tooltip__text--top"> 762 @Translate("Translate_ProductPage_PriceDefinition_Andet_Tooltip") 763 </span> 764 </span> 765 </div> 766 <div class="product-details__info-price-option"> 767 <div class="product-details__info-price-option-price">@standardPrice @priceCurrencySymbol pr. @salesUnit</div> 768 <div class="product-details__info-price-option-vat">@Translate("Translate_Ecom_ExVat")</div> 769 </div> 770 </div> 771 } 772 } 773 774 <div class="product-details__info-quantity"> 775 @{ 776 if (standardPriceJS == "0") 777 { 778 enquireProduct = true; 779 } 780 } 781 @if (totalStock > 0 && isloggedin && !enquireProduct) 782 { 783 <form class="form" action="@(formAction)&Redirect=@(CartPage)" method="post" name="@productId" id="@productId"> 784 <input type="hidden" name="CartCmd" value="addMulti" /> 785 @GetString("Ecom:Product.Form.Multi.HiddenFields") 786 <div class="counter"> 787 <a class="substract">-</a> 788 <input name="@inputName" id="Quantity" class="amount" type="tel" value="1"> 789 <label for="Quanity" class="unit">@Translate("Translate_Piece_Short")</label> 790 <a class="add">+</a> 791 </div> 792 <button type="submit" id="product-order-button" class="product-details__info-button btn btn--block btn-secondary">Bestil</button> 793 </form> 794 } 795 else 796 { 797 <a href="#product-inquire-form" id="product-order-button" data-action="open-content" data-target="#enquireForm" class="product-details__info-button btn btn--block btn-secondary">@Translate("Translate_Product_Page_EnquireButton")</a> 798 } 799 </div> 800 break; 801 802 default: 803 804 <h1>Produkt mangler at få valgt primær gruppe</h1> 805 break; 806 } 807 808 <div class="product-details__info-more"> 809 @switch (productLayout) 810 { 811 case "group52": // Plader 812 case "group66": // Dekorative Overflader 813 case "group126": // Tilbehør 814 var warehouseStock = GetDouble("Ecom:Product.Stock"); 815 var seaStock = int.Parse(GetString("Ecom:Product:Field.ProductPieceOnPurchase.Value").Replace(",", string.Empty)); 816 817 if (isSingleVariant) 818 { 819 warehouseStock = singleVariantStock; 820 seaStock = totalStockSea; 821 } 822 823 string displayWarehouseStock = warehouseStock.ToString("N0"); 824 string displaySeaStock = seaStock.ToString("N0"); 825 826 if (isloggedin) 827 { 828 if (warehouseStock > 500) 829 { 830 displayWarehouseStock = "+500"; 831 } 832 833 if (seaStock > 500) 834 { 835 displaySeaStock = "+500"; 836 } 837 } 838 else 839 { 840 if (warehouseStock > 100) 841 { 842 displayWarehouseStock = "+100"; 843 } 844 845 if (seaStock > 100) 846 { 847 displaySeaStock = "+100"; 848 } 849 } 850 851 if(labelCode.ToLower() != "skaffe" && labelCode.ToLower() != "relatordre") { 852 <div class="product-details__info-stock product-details__info-stock--inline"> 853 <div class="option option--in-house"> 854 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 23.818"> 855 <g class="warehouse" transform="translate(-551.005 -159.082)"> 856 <path class="box box--top" d="M563.005,177.15h-5.25a.75.75,0,0,0-.75.75v4.5a.75.75,0,0,0,.75.75h4.5a.75.75,0,0,0,.75-.75Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 857 <path class="box box--bottom-right" d="M568.255,177.15h-5.25v5.25a.75.75,0,0,0,.75.75h4.5a.75.75,0,0,0,.75-.75v-4.5A.75.75,0,0,0,568.255,177.15Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 858 <path class="box box--bottom-left" d="M565.255,171.15h-4.5a.75.75,0,0,0-.75.75v5.25h6V171.9A.75.75,0,0,0,565.255,171.15Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 859 <path class="building" d="M574.255,183.15v-15.6a1.5,1.5,0,0,0-.794-1.324l-9.75-5.2a1.5,1.5,0,0,0-1.411,0l-9.75,5.2a1.5,1.5,0,0,0-.795,1.324v15.6" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 860 </g> 861 </svg> 862 <span>@Translate("Translate_ProductPage_StockOnWarehouse")</span> @displayWarehouseStock @Translate("Translate_Ecom_Piece") 863 </div> 864 <div class="option option--in-route"> 865 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 16.5"> 866 <g class="truck" transform="translate(-215 -899.75)"> 867 <path class="back" d="M238.25,909.5V902a1.5,1.5,0,0,0-1.5-1.5h-12a1.5,1.5,0,0,0-1.5,1.5v6" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 868 <path class="front" d="M223.25,908v-6h-3a4.5,4.5,0,0,0-4.5,4.5v6a1.5,1.5,0,0,0,1.5,1.5H218" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 869 <path class="window" d="M215.75,908h3a1.5,1.5,0,0,0,1.5-1.5V902" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 870 <path class="wheel wheel--front" d="M222.5,915.5a2.25,2.25,0,1,0-2.25-2.25A2.25,2.25,0,0,0,222.5,915.5Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 871 <path class="wheel wheel--back" d="M234.5,915.5a2.25,2.25,0,1,0-2.25-2.25A2.25,2.25,0,0,0,234.5,915.5Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 872 <path class="axis" d="M227,914h3" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 873 </g> 874 </svg> 875 <span>@Translate("Translate_ProductPage_StockOnSea")</span> @displaySeaStock @Translate("Translate_Ecom_Piece") 876 </div> 877 </div> 878 } 879 880 break; 881 882 default: 883 if(labelCode.ToLower() != "skaffe" && labelCode.ToLower() != "relatordre") { 884 <a href="#" class="product-details__info-stock product-order-button"> 885 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 23.818"> 886 <g class="warehouse" transform="translate(-551.005 -159.082)"> 887 <path class="box box--top" d="M563.005,177.15h-5.25a.75.75,0,0,0-.75.75v4.5a.75.75,0,0,0,.75.75h4.5a.75.75,0,0,0,.75-.75Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 888 <path class="box box--bottom-right" d="M568.255,177.15h-5.25v5.25a.75.75,0,0,0,.75.75h4.5a.75.75,0,0,0,.75-.75v-4.5A.75.75,0,0,0,568.255,177.15Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 889 <path class="box box--bottom-left" d="M565.255,171.15h-4.5a.75.75,0,0,0-.75.75v5.25h6V171.9A.75.75,0,0,0,565.255,171.15Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 890 <path class="building" d="M574.255,183.15v-15.6a1.5,1.5,0,0,0-.794-1.324l-9.75-5.2a1.5,1.5,0,0,0-1.411,0l-9.75,5.2a1.5,1.5,0,0,0-.795,1.324v15.6" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 891 </g> 892 </svg> 893 894 @Translate("Translate_Product_Page_StockStatus") 895 896 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.006 11.031"> 897 <g class="arrow" transform="translate(441 901.014) rotate(180)"> 898 <path class="angle" d="M-17182.074-20447.988l4.809-4.809,4.809,4.809" transform="translate(20875.289 -16281.768) rotate(-90)" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" /> 899 <line class="line" x2="17" transform="translate(423.5 895.5)" stroke-linecap="round" stroke-width="1" /> 900 </g> 901 </svg> 902 </a> 903 } 904 break; 905 } 906 907 <div class="product-details__info-delivery"> 908 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 16.5"> 909 <g class="truck" transform="translate(-215 -899.75)"> 910 <path class="back" d="M238.25,909.5V902a1.5,1.5,0,0,0-1.5-1.5h-12a1.5,1.5,0,0,0-1.5,1.5v6" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 911 <path class="front" d="M223.25,908v-6h-3a4.5,4.5,0,0,0-4.5,4.5v6a1.5,1.5,0,0,0,1.5,1.5H218" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 912 <path class="window" d="M215.75,908h3a1.5,1.5,0,0,0,1.5-1.5V902" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 913 <path class="wheel wheel--front" d="M222.5,915.5a2.25,2.25,0,1,0-2.25-2.25A2.25,2.25,0,0,0,222.5,915.5Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 914 <path class="wheel wheel--back" d="M234.5,915.5a2.25,2.25,0,1,0-2.25-2.25A2.25,2.25,0,0,0,234.5,915.5Z" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 915 <path class="axis" d="M227,914h3" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" /> 916 </g> 917 </svg> 918 919 @if(labelCode.ToLower() == "skaffe" || labelCode.ToLower() == "relatordre") { 920 @Translate("Translate_Product_Page_DeliveryInformation")<text> </text> @GetString("Ecom:Product:Field.Leveringstid") 921 } else { 922 @Translate("Translate_Product_Page_DeliveryInformation") 923 } 924 925 <span class="tooltip"> 926 <span class="tooltip__icon"> 927 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 928 { 929 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 930 } 931 </span> 932 <span class="tooltip__text tooltip__text--left"> 933 @if(labelCode.ToLower() == "skaffe" || labelCode.ToLower() == "relatordre") { 934 <p>@Translate("Translate_ProductPage_DeliverInfo_Skaffe")</p> 935 } else { 936 <p>@Translate("Translate_ProductPage_DeliveryInfo")</p> 937 } 938 </span> 939 </span> 940 </div> 941 942 943 <a class="product-details__info-reseller" href="https://keflico.com/find-forhandler"> 944 <svg width="800px" height="800px" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" stroke-width="3" stroke="#000000" fill="none"> 945 <path d="M52,27.18V52.76a2.92,2.92,0,0,1-3,2.84H15a2.92,2.92,0,0,1-3-2.84V27.17"></path> 946 <polyline points="26.26 55.52 26.26 38.45 37.84 38.45 37.84 55.52"></polyline> 947 <path d="M8.44,19.18s-1.1,7.76,6.45,8.94a7.17,7.17,0,0,0,6.1-2A7.43,7.43,0,0,0,32,26a7.4,7.4,0,0,0,5,2.49,11.82,11.82,0,0,0,5.9-2.15,6.66,6.66,0,0,0,4.67,2.15,8,8,0,0,0,7.93-9.3L50.78,9.05a1,1,0,0,0-.94-.65H14a1,1,0,0,0-.94.66Z"></path> 948 <line x1="8.44" y1="19.18" x2="55.54" y2="19.18"></line> 949 <line x1="21.04" y1="19.18" x2="21.04" y2="8.4"></line> 950 <line x1="32.05" y1="19.18" x2="32.05" y2="8.4"></line> 951 <line x1="43.01" y1="19.18" x2="43.01" y2="8.4"></line> 952 </svg> 953 954 @Translate("Translate_Product_Page_Reseller") 955 956 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.006 11.031"> 957 <g class="arrow" transform="translate(441 901.014) rotate(180)"> 958 <path class="angle" d="M-17182.074-20447.988l4.809-4.809,4.809,4.809" transform="translate(20875.289 -16281.768) rotate(-90)" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" /> 959 <line class="line" x2="17" transform="translate(423.5 895.5)" stroke-linecap="round" stroke-width="1" /> 960 </g> 961 </svg> 962 </a> 963 964 @if (!string.IsNullOrEmpty(configuratorUrl)) 965 { 966 <a class="product-details__info-configurator" href="@configuratorUrl"> 967 <svg width="800px" height="800px" viewBox="0 0 32 32" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="svg820" version="1.1"> 968 <defs id="defs814"> 969 <linearGradient gradientTransform="matrix(1.25 0 0 1.2 -484.714 468.561)" gradientUnits="userSpaceOnUse" y2="520.79797" x2="401.57144" y1="535.79797" x1="401.57144" id="linearGradient4358" xlink:href="#linearGradient4424"></linearGradient> 970 <linearGradient id="linearGradient4424"> 971 <stop style="stop-color:#60a5e7;stop-opacity:0" offset="0" id="stop4426"></stop> 972 <stop style="stop-color:#a6f3fb;stop-opacity:.25773194" offset="1" id="stop4428"></stop> 973 </linearGradient> 974 </defs> 975 <g transform="translate(0 -1090.52)" id="layer1"> 976 <path id="rect2370" transform="translate(0 1090.52)" d="M2 4v24h28V4zm2 2h24v20H4z" style="opacity:1;vector-effect:none;fill:#373737;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:3.20000005;stroke-opacity:1"></path> 977 <path id="path2381" d="M22.424 1100.096a1.996 1.996 0 0 0-2.829 0l-.707.707-7.778 7.778 2.828 2.829 7.779-7.778.707-.708a1.996 1.996 0 0 0 0-2.828zm-12.021 9.192-1.414 4.243 4.242-1.414z" style="fill:#373737;fill-opacity:1;stroke:none;stroke-width:1.26491106px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"></path> 978 </g> 979 </svg> 980 981 @Translate("Translate_Product_Page_Configurator") 982 983 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.006 11.031"> 984 <g class="arrow" transform="translate(441 901.014) rotate(180)"> 985 <path class="angle" d="M-17182.074-20447.988l4.809-4.809,4.809,4.809" transform="translate(20875.289 -16281.768) rotate(-90)" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" /> 986 <line class="line" x2="17" transform="translate(423.5 895.5)" stroke-linecap="round" stroke-width="1" /> 987 </g> 988 </svg> 989 </a> 990 } 991 <button id="downloadPdfButton" class="product-details__info-download-pdf"> 992 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"> 993 <g class="download-icon" transform="translate(2 2)"> 994 <path class="arrow" d="M10 0 L10 16 M5 11 L10 16 L15 11" 995 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> 996 <path class="line" d="M0 20 L20 20" 997 fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/> 998 </g> 999 </svg> 1000 Download PDF 1001 </button> 1002 </div> 1003 </div> 1004 1005 <script> 1006 document.addEventListener('DOMContentLoaded', function() { 1007 var downloadButton = document.getElementById('downloadPdfButton'); 1008 if (downloadButton) { 1009 downloadButton.addEventListener('click', function(e) { 1010 e.preventDefault(); // Prevent the default action 1011 1012 // Get the product number and name 1013 var productNumber = '@productId'; 1014 var productName = '@productName'; 1015 1016 // Function to replace Danish characters 1017 function replaceDanishChars(str) { 1018 return str 1019 .replace(/æ/g, 'ae') 1020 .replace(/ø/g, 'oe') 1021 .replace(/å/g, 'aa') 1022 .replace(/Æ/g, 'Ae') 1023 .replace(/Ø/g, 'Oe') 1024 .replace(/Å/g, 'Aa'); 1025 } 1026 1027 // Clean up the product name for use in a filename 1028 var cleanProductName = replaceDanishChars(productName) 1029 .replace(/[^\w\s-]/g, '') // Remove special characters 1030 .trim() // Remove leading and trailing spaces 1031 .replace(/\s+/g, '-') // Replace spaces with hyphens 1032 .toLowerCase(); // Convert to lowercase 1033 1034 // Capitalize the first letter of cleanProductName 1035 cleanProductName = cleanProductName.charAt(0).toUpperCase() + cleanProductName.slice(1); 1036 1037 // Construct the filename with _Keflico at the end 1038 var filename = `${productNumber}_${cleanProductName}_Keflico.pdf`; 1039 1040 // Encode the filename for use in a URL 1041 var encodedFilename = encodeURIComponent(filename); 1042 1043 // Navigate to the new URL to trigger the PDF download 1044 window.location.href = `/Default.aspx?ID=1243&ProductID=${productNumber}&PDF=True&filename=${encodedFilename}&topBottomMargin=10`; 1045 }); 1046 } 1047 }); 1048 </script> 1049 </article> 1050 <article class="module module-sand product-description"> 1051 <div class="tabs"> 1052 <div class="tabs__nav"> 1053 <ul role="tablist" class="tabs__list" data-action="tabs"> 1054 <li role="none" class="tabs__item"> 1055 <a href="#description" class="tab tab--active" role="tab" id="description-tab" aria-controls="description" aria-selected="true"> 1056 <span class="tabs__item__text--web">@Translate("Translate_Product_Page_ProductDescription")</span> 1057 <span class="tabs__item__text--mobile">@Translate("Translate_Product_Page_ProductDescription_Short")</span> 1058 </a> 1059 </li> 1060 <li role="none" class="tabs__item"> 1061 <a href="#technical" class="tab" role="tab" id="technical-tab" aria-controls="technical" aria-selected="false"> 1062 <span class="tabs__item__text--web">@Translate("Translate_Product_Page_TechnicalSpecifications")</span> 1063 <span class="tabs__item__text--mobile">@Translate("Translate_Product_Page_TechnicalSpecifications_Short")</span> 1064 </a> 1065 </li> 1066 @if (hasNewDownloads) 1067 { 1068 <li role="none" class="tabs__item"> 1069 <a href="#documents" class="tab" role="tab" id="documents-tab" aria-controls="documents" aria-selected="false"> 1070 <span class="tabs__item__text--web">@Translate("Translate_Product_Page_Documents")</span> 1071 <span class="tabs__item__text--mobile">@Translate("Translate_Product_Page_Documents_Short")</span> 1072 </a> 1073 </li> 1074 } 1075 <li role="none" class="tabs__item"> 1076 <a href="#more-info" class="tab" role="tab" id="more-info-tab" aria-controls="more-info" aria-selected="false"> 1077 <span class="tabs__item__text--web">@Translate("Translate_Product_Page_MoreInformation")</span> 1078 <span class="tabs__item__text--mobile">@Translate("Translate_Product_Page_MoreInformation_Short")</span> 1079 </a> 1080 </li> 1081 </ul> 1082 1083 </div> 1084 <div id="description" class="tab__content tab__content--active" role="tabpanel" aria-hidden="false" aria-labelledby="description-tab"> 1085 <h2>@Translate("Translate_Product_Page_ProductDescription")</h2> 1086 <div class="tab__content-wrapper"> 1087 <div class="rich-text"> 1088 <div class="large-text">@GetString("Ecom:Product.LongDescription")</div> 1089 @GetString("Ecom:Product:Field.ProductDescriptionShortLongText.Value") 1090 </div> 1091 1092 @if (hasDownloads) 1093 { 1094 <div class="tab__content-sidebar"> 1095 <strong>@Translate("Translate__Product_Page_Downloads")</strong> 1096 <ul class="tab__content-list"> 1097 @foreach (var fileCat in GetLoop("ImageCategories")) 1098 { 1099 if (fileCat.GetString("Category.SystemName") != "Images") 1100 { 1101 foreach (var file in fileCat.GetLoop("Category.Images")) 1102 { 1103 string fileLink = file.GetString("Ecom:Product:Detail.Image.Clean"); 1104 string fileName = file.GetString("Ecom:Product:Detail.Name"); 1105 1106 if (String.IsNullOrWhiteSpace(fileName)) 1107 { 1108 fileName = Path.GetFileNameWithoutExtension(fileLink); 1109 } 1110 1111 <li> 1112 <a class="swap-link" href="@fileLink" data-link="@fileLink" target="_blank"> 1113 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/file.svg"))) 1114 { 1115 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/file.svg")) 1116 } 1117 @fileName.Replace("-", " ").Replace("_", " ") 1118 </a> 1119 </li> 1120 } 1121 } 1122 } 1123 1124 </ul> 1125 </div> 1126 } 1127 </div> 1128 </div> 1129 <div id="technical" class="tab__content technical-specs" role="tabpanel" aria-hidden="false" aria-labelledby="technical-tab"> 1130 <div class="rich-text"> 1131 <h2>@Translate("Translate_Product_Page_TechnicalSpecifications")</h2> 1132 1133 @{ 1134 CategoryFieldViewModel specs = productViewmodel.FieldDisplayGroups.Values.Where(x => x.Id.ToLower() == "technicalspecifications").FirstOrDefault(); 1135 CategoryFieldViewModel specsWithInfo = productViewmodel.FieldDisplayGroups.Values.Where(x => x.Id.ToLower() == "technicalspecificationswithinfo").FirstOrDefault(); 1136 } 1137 1138 @if (specs != null) 1139 { 1140 <ul class="product-description__technical-list"> 1141 @foreach (var field in specs.Fields.Values) 1142 { 1143 if (field.Type.ToLower() == "list") 1144 { 1145 List<FieldOptionValueViewModel> fieldValue = (List<FieldOptionValueViewModel>)field.Value; 1146 int i = 0; 1147 1148 if (fieldValue.First().Value.ToString() != "-1" && fieldValue.Where(x => x.Name.ToLower() == "ingen").Count() == 0) 1149 { 1150 <li class="product-description__technical-list-item"> 1151 <div class="key">@field.Name</div> 1152 <div class="value"> 1153 <span class="line"> 1154 @foreach (FieldOptionValueViewModel info in fieldValue) 1155 { 1156 if (i > 0) 1157 { 1158 @(", " + info.Name) 1159 } 1160 else 1161 { 1162 @info.Name 1163 } 1164 i++; 1165 } 1166 </span> 1167 </div> 1168 </li> 1169 } 1170 } 1171 else 1172 { 1173 if (field.Value.ToString() != "0" && field.Value.ToString().ToLower() != "ingen") 1174 { 1175 <li class="product-description__technical-list-item"> 1176 @if (field.Name.ToLower() == "number") 1177 { 1178 <div class="key">@Translate("Translate_General_ProductNumber")</div> 1179 } 1180 else if (field.Name.ToLower() == "weight") 1181 { 1182 <div class="key">@Translate("Translate_General_Weight")</div> 1183 } 1184 else 1185 { 1186 <div class="key">@field.Name</div> 1187 } 1188 <div class="value"> 1189 <span class="line">@field.Value</span> 1190 </div> 1191 </li> 1192 } 1193 } 1194 } 1195 </ul> 1196 } 1197 @if (specsWithInfo != null) 1198 { 1199 <ul class="product-description__technical-list"> 1200 @foreach (var field in specsWithInfo.Fields.Values) 1201 { 1202 if (field.Type.ToLower() == "list") 1203 { 1204 List<FieldOptionValueViewModel> fieldValue = (List<FieldOptionValueViewModel>)field.Value; 1205 int i = 0; 1206 1207 if (fieldValue.First().Value.ToString() != "-1" && fieldValue.Where(x => x.Name.ToLower() == "ingen").Count() == 0) 1208 { 1209 <li class="product-description__technical-list-item"> 1210 <div class="key">@field.Name</div> 1211 <div class="value"> 1212 <span class="line with-toolip"> 1213 @foreach (FieldOptionValueViewModel info in fieldValue) 1214 { 1215 if (i > 0) 1216 { 1217 @(", " + info.Name) 1218 } 1219 else 1220 { 1221 @info.Name 1222 } 1223 i++; 1224 } 1225 <span class="tooltip"> 1226 <span class="tooltip__icon"> 1227 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 1228 { 1229 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 1230 } 1231 </span> 1232 <span class="tooltip__text tooltip__text--left"> 1233 <p>@Translate("Translate_Product_Spec_Info_" + field.SystemName)</p> 1234 </span> 1235 </span> 1236 </span> 1237 </div> 1238 </li> 1239 } 1240 } 1241 else 1242 { 1243 if (field.Value.ToString() != "0" && field.Value.ToString().ToLower() != "ingen") 1244 { 1245 <li class="product-description__technical-list-item"> 1246 <div class="key">@field.Name</div> 1247 <div class="value"> 1248 <span class="line with-tooltip"> 1249 @if (field.SystemName.ToLower() == "productdensity") 1250 { 1251 @field.Value.ToString().Replace(",", ".") 1252 } 1253 else 1254 { 1255 @field.Value 1256 } 1257 <span class="tooltip"> 1258 <span class="tooltip__icon"> 1259 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 1260 { 1261 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 1262 } 1263 </span> 1264 <span class="tooltip__text tooltip__text--left"> 1265 <p>@Translate("Translate_Product_Spec_Info_" + field.SystemName)</p> 1266 </span> 1267 </span> 1268 </span> 1269 </div> 1270 </li> 1271 } 1272 } 1273 } 1274 </ul> 1275 } 1276 </div> 1277 </div> 1278 @if (hasNewDownloads) 1279 { 1280 <div id="documents" class="tab__content documents" role="tabpanel" aria-hidden="false" aria-labelledby="documents-tab"> 1281 <div class="rich-text"> 1282 <h2>@Translate("Translate_Product_Page_Documents")</h2> 1283 1284 <ul class="tab__content-list product-description__documents-list"> 1285 @foreach (var fileCat in GetLoop("ImageCategories")) 1286 { 1287 if (fileCat.GetString("Category.SystemName").StartsWith("docView_") && fileCat.GetLoop("Category.Images").Count() > 0) 1288 { 1289 <li class="product-description__documents-list-item"> 1290 <p class="product-description__documents-list-item-title">@fileCat.GetString("Category.Name")</p> 1291 @foreach (var file in fileCat.GetLoop("Category.Images")) 1292 { 1293 string fileLink = file.GetString("Ecom:Product:Detail.Image.Clean"); 1294 string fileName = file.GetString("Ecom:Product:Detail.Name"); 1295 1296 if (String.IsNullOrWhiteSpace(fileName)) 1297 { 1298 fileName = Path.GetFileNameWithoutExtension(fileLink); 1299 } 1300 1301 if (!String.IsNullOrEmpty(fileLink)) 1302 { 1303 <a class="swap-link" href="@fileLink" data-link="@fileLink" target="_blank"> 1304 @GetFileIcon(fileLink) 1305 @fileName.Replace("-", " ").Replace("_", " ") 1306 </a> 1307 } 1308 } 1309 </li> 1310 } 1311 } 1312 </ul> 1313 </div> 1314 </div> 1315 } 1316 1317 <div id="more-info" class="tab__content more-info-content" role="tabpanel" aria-hidden="false" aria-labelledby="more-info-tab"> 1318 <div class="rich-text"> 1319 <h2>@Translate("Translate_Product_Page_MoreInformation")</h2> 1320 @{ 1321 CategoryFieldViewModel moreInfo = productViewmodel.FieldDisplayGroups.Values.Where(x => x.Id.ToLower() == "yderlig_information").FirstOrDefault(); 1322 1323 if (moreInfo != null && moreInfo.Fields != null) 1324 { 1325 var groups = moreInfo.Fields.Values; 1326 1327 foreach (var group in groups) 1328 { 1329 <strong>@Translate("Translate_ProductPage_" + group.SystemName)</strong> 1330 @group.Value 1331 } 1332 } 1333 } 1334 </div> 1335 </div> 1336 1337 </article> 1338 1339 <div id="related-products"> 1340 <article v-if="relatedProducts && relatedProducts.length > 0 && relatedList.length > 0" class="module module-sand-light related-products"> 1341 <div class="rich-text"> 1342 <h2 v-html="relatedGroups.find(x => x.Id == 'RELGRP2').Name"></h2> 1343 </div> 1344 <div class="card-swiper swiper" data-action="async-swiper"> 1345 <div class="swiper-wrapper"> 1346 <div v-for="product in relatedList" :key="product.Id" class="card-swiper__card swiper-slide"> 1347 <picture v-if="product.DefaultImage.Value != ''" class="card-swiper__card-media"> 1348 <source :srcset="productImage(product.DefaultImage.Value, 'lg')" media="(min-width: 1536px)"> 1349 <source :srcset="productImage(product.DefaultImage.Value, 'md')" media="(min-width: 992px)"> 1350 <source :srcset="productImage(product.DefaultImage.Value, 'sm')" media="(min-width: 768px)"> 1351 <img :src="productImage(product.DefaultImage.Value, 'default')" :alt="product.Name"> 1352 </picture> 1353 <div v-else class="card__picture card__picture--dummie"></div> 1354 <div class="card-swiper__card-info"> 1355 <div class="card-swiper__card-preline">@Translate("Translate_Product_Page_ProductNumber"): {{ product.Number }}</div> 1356 <h4 class="card-swiper__card-headline">{{ product.Name }}</h4> 1357 <div class="card-swiper__card-teaser">{{ product.ProductFields.Name2.Value }}</div> 1358 <a class="card-swiper__card-link" :href="'/@(EcomPage)&ProductID=' + product.Id">@Translate("Translate_Product_Page_ProductLink")</a> 1359 </div> 1360 </div> 1361 </div> 1362 <div class="swiper-pagination"></div> 1363 </div> 1364 </article> 1365 <article v-if="relatedProducts && relatedProducts.length > 0 && accessoriesList.length > 0" class="module accessories-products" :class="{ 'module-sand': relatedList.length > 0, 'module-sand-light': relatedList.length == 0}"> 1366 <div class="rich-text"> 1367 <h2 v-html="relatedGroups.find(x => x.Id == 'RELGRP1').Name"></h2> 1368 </div> 1369 <div class="card-swiper swiper" data-action="async-swiper"> 1370 <div class="swiper-wrapper"> 1371 <div v-for="product in accessoriesList" :key="product.Id" class="card-swiper__card swiper-slide"> 1372 <picture v-if="product.DefaultImage.Value != ''" class="card__picture"> 1373 <source :srcset="productImage(product.DefaultImage.Value, 'lg')" media="(min-width: 1536px)"> 1374 <source :srcset="productImage(product.DefaultImage.Value, 'md')" media="(min-width: 992px)"> 1375 <source :srcset="productImage(product.DefaultImage.Value, 'sm')" media="(min-width: 768px)"> 1376 <img :src="productImage(product.DefaultImage.Value, 'default')" :alt="product.Name"> 1377 </picture> 1378 <div v-else class="card__picture card__picture--dummie"></div> 1379 <div class="card-swiper__card-info"> 1380 <div class="card-swiper__card-preline">@Translate("Translate_Product_Page_ProductNumber"): {{ product.Number }}</div> 1381 <h4 class="card-swiper__card-headline">{{ product.Name }}</h4> 1382 <div class="card-swiper__card-teaser">{{ product.ProductFields.Name2.Value }}</div> 1383 <a class="card-swiper__card-link" :href="'/@(EcomPage)&ProductID=' + product.Id">@Translate("Translate_Product_Page_ProductLink")</a> 1384 </div> 1385 </div> 1386 </div> 1387 <div class="swiper-pagination"></div> 1388 </div> 1389 </article> 1390 @RenderItemList(new { 1391 ItemType = "Case", 1392 ListSourceType = "SelfArea", 1393 ItemFieldsList = "Headline,MainImage,Types,WoodTypes", 1394 ListTemplate = "itempublisher/list/productInspiration.cshtml", 1395 ListOrderBy = "Sort", 1396 ListPageSize = 100, 1397 Filter = BuildFilterString(primaryGroup) 1398 }) 1399 </div> 1400 1401 @RenderSnippet("EnquireProductForm") 1402 1403 <div class="product-floating-bar"> 1404 <div class="product-floating-bar__text">@productName @GetString("Ecom:Product:Field.Name2")</div> 1405 @if(totalStock > 0 && isloggedin && !enquireProduct) { 1406 if(primaryGroup == "group1" || primaryGroup == "group32" || primaryGroup == "group73") { 1407 <a class="btn btn-secondary product-floating-bar__button product-order-button">@Translate("Translate_Product_Page_OrderButton")</a> 1408 } else { 1409 <a href="#main" class="btn btn-secondary product-floating-bar__button">@Translate("Translate_Product_Page_OrderButton")</a> 1410 } 1411 } else { 1412 <a data-action="open-content" data-target="#enquireForm" class="btn btn-secondary product-floating-bar__button">@Translate("Translate_Product_Page_EnquireButton")</a> 1413 } 1414 </div> 1415 1416 @SnippetStart("JavaScripts") 1417 <script type="module" async> 1418 import Swiper from 'https://cdn.jsdelivr.net/npm/swiper@8/swiper-bundle.esm.browser.min.js'; 1419 1420 new Vue({ 1421 el: "#related-products", 1422 name: "Related Products", 1423 data() { 1424 return { 1425 relatedGroups: null, 1426 relatedProducts: null, 1427 } 1428 }, 1429 computed: { 1430 relatedList() { 1431 let list = []; 1432 1433 if(this.relatedProducts && this.relatedProducts.length > 0) { 1434 this.relatedProducts.forEach(product => { 1435 if(product.Active && Object.keys(product.PrimaryOrDefaultGroup).length > 0) { 1436 let group = this.relatedGroups.find(x => x.Id == "RELGRP2"); 1437 1438 if(group && group.Products.find(x => x.ProductId == product.Id)) { 1439 list.push(product); 1440 } 1441 } 1442 }) 1443 } 1444 1445 return list; 1446 }, 1447 accessoriesList() { 1448 let list = []; 1449 1450 if(this.relatedProducts && this.relatedProducts.length > 0) { 1451 this.relatedProducts.forEach(product => { 1452 if(product.Active && Object.keys(product.PrimaryOrDefaultGroup).length > 0) { 1453 let group = this.relatedGroups.find(x => x.Id == "RELGRP1"); 1454 1455 if(group && group.Products.find(x => x.ProductId == product.Id)) { 1456 list.push(product); 1457 } 1458 } 1459 }) 1460 } 1461 1462 return list; 1463 } 1464 }, 1465 async mounted() { 1466 await fetch(`/dwapi/ecommerce/products/@(productNumber)`) 1467 .then(response => response.json()) 1468 .then(response => { 1469 this.relatedGroups = response.RelatedGroups; 1470 }) 1471 1472 await fetch(`/dwapi/ecommerce/products/@(productNumber)/related?ShopId=@(Pageview.Area.EcomShopId)`) 1473 .then(response => response.json()) 1474 .then(response => { 1475 if(response.TotalProductsCount > 0) { 1476 this.relatedProducts = response.Products; 1477 } 1478 }) 1479 .catch(error => { 1480 console.log(error) 1481 }) 1482 1483 const productsSliders = document.querySelectorAll('[data-action="async-swiper"]'); 1484 1485 productsSliders.forEach(swiperContainer => { 1486 const productSlider = new Swiper(swiperContainer, { 1487 pagination: { 1488 el: '.swiper-pagination', 1489 type: 'progressbar' 1490 }, 1491 slidesPerView: 'auto', // Slide sizes are controlled with css - not here 1492 speed: 500, 1493 parallax: true, 1494 centeredSlides: false, 1495 }); 1496 }); 1497 }, 1498 methods: { 1499 productImage(path, size) { 1500 let imagePath = "/admin/public/getimage.ashx?Image=" + encodeURIComponent(path); 1501 1502 switch(size) { 1503 case 'lg': 1504 imagePath += '&Width=416&Height=416&Crop=0&Compression=100'; 1505 break; 1506 case 'md': 1507 imagePath += '&Width=310&Height=310&Crop=0&Compression=100'; 1508 break; 1509 case 'sm': 1510 imagePath += '&Width=&230Height=230&Crop=0&Compression=100'; 1511 break; 1512 default: 1513 imagePath += '&Width=560&Height=560&Crop=0&Compression=100'; 1514 break; 1515 } 1516 1517 return imagePath; 1518 } 1519 } 1520 }) 1521 </script> 1522 1523 @SnippetEnd("Javascripts") 1524 1525 1526 1527 @if(productLayout != "group32" && productLayout != "group73" && productLayout != "group1") { 1528 @SnippetStart("JavaScripts") 1529 <script> 1530 const orderButton = document.getElementById("product-order-button"); 1531 1532 if(orderButton) { 1533 const form = orderButton.closest('form'); 1534 1535 orderButton.addEventListener('click', (event) => { 1536 event.preventDefault(); 1537 1538 const formData = new FormData(form); 1539 const qty = parseInt(formData.get('Quantity1')) 1540 1541 const price = @GetString("Ecom:Product.Discount.Price.PriceWithoutVAT.Value").Replace(",", "."); 1542 const halfPrice = @GetString("Ecom:Product:Field.ProductPriceHalfParcel").Replace(",", ""); 1543 const fullPrice = @GetString("Ecom:Product:Field.ProductPriceCompleteParcel").Replace(",", ""); 1544 1545 const halfParcel = @halfParcelAmount; 1546 const fullParcel = @completeParcelAmount; 1547 1548 const salesUnit = "@salesUnit"; 1549 const productLength = @GetString("Ecom:Product:Field.ProductLengthSale.Value.Raw"); 1550 const lengthUnit = "@GetString("Ecom:Product:Field.ProductLengthUnitCode.Value")"; 1551 1552 let value = 0; 1553 1554 addToValue(qty) 1555 1556 function addToValue(remainingQty) { 1557 if(salesUnit == "m") { 1558 value += remainingQty * (productLength/1000) * price; 1559 } else { 1560 value += remainingQty * price; 1561 } 1562 } 1563 1564 dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object. 1565 dataLayer.push({ 1566 event: "add_to_cart", 1567 ecommerce: { 1568 currency: "@GetString("Ecom:Product.Currency.Code")", 1569 value: value, 1570 items: [ 1571 { 1572 item_id: "@productNumber", 1573 item_name: "@productName", 1574 quantity: qty 1575 1576 } 1577 ] 1578 } 1579 }); 1580 form.submit() 1581 }); 1582 } 1583 </script> 1584 @SnippetEnd("Javascripts") 1585 } 1586 1587 @if(productLayout == "group32" || productLayout == "group73" || productLayout == "group1") { 1588 <div id="orderFlowApp" class="product-modal" :class="{ 'not-logged-in': !isLoggedIn, 'upsell js-slide-in' : showAccessories || showStep1 }"> 1589 <div v-if="showAccessories" class="product-modal__content product-modal__upsell-section"> 1590 <div class="product-modal__content-top"> 1591 <a href="/" class="navigation__logo"> 1592 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/keflico_logo.svg"))) 1593 { 1594 <text>@System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/keflico_logo.svg"))</text> 1595 } 1596 </a> 1597 <a href="@CartPage" class="navigation__cart"> 1598 <svg xmlns="http://www.w3.org/2000/svg" width="21.773" height="19.513" viewBox="0 0 21.773 19.513"> 1599 <g transform="translate(0.75 0.75)"> 1600 <path d="M122.747,269.657h9.076a1.359,1.359,0,0,0,1.342-1.148l1.28-5.631a.994.994,0,0,0-.982-1.148h-13.3" transform="translate(-114.186 -258.966)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1601 <path d="M88.321,247h2.551a.748.748,0,0,1,.724.563l1.973,8.555" transform="translate(-88.321 -247)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1602 <path d="M115.7,293.281l1.193,4.686h.043" transform="translate(-110.564 -284.597)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1603 <path d="M125.924,329.157a1.433,1.433,0,1,1-1.433-1.433A1.433,1.433,0,0,1,125.924,329.157Z" transform="translate(-116.54 -312.578)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1604 <path d="M169.05,329.157a1.433,1.433,0,1,1-1.433-1.433A1.433,1.433,0,0,1,169.05,329.157Z" transform="translate(-151.574 -312.578)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1605 <line x2="11.102" transform="translate(6.374 13.37)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1606 </g> 1607 </svg> 1608 <span class="cart-qty" data-count="@cartCount">@cartCount</span> 1609 </a> 1610 <a href="#" class="navigation__return product-modal__back-link" @@click="showAccessories = false"> 1611 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20.006 12.445"> 1612 <g transform="translate(-421.494 -889.275)"> 1613 <path d="M-17182.074-20447.988l4.809-4.809,4.809,4.809" transform="translate(20875.289 -16281.768) rotate(-90)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> 1614 <line x2="17" transform="translate(423.5 895.5)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="2" /> 1615 </g> 1616 </svg> 1617 </a> 1618 <div v-if="accessoriesList.length > 0" class="breadcrumbs breadcrumbs--no-seperator"> 1619 <ul class="breadcrumbs__list"> 1620 <li class="breadcrumbs__item">1. Vælg produkter</li> 1621 <li class="breadcrumbs__item active">2. Vælg tilbehør</li> 1622 </ul> 1623 </div> 1624 </div> 1625 <div class="upsell-section__top"> 1626 <h1 class="upsell-section__title" v-html="'@Translate("Translate_OrderFlow_ProductsAdded")'.replace('{0}', '<span>' + productsAdded + '</span>')">Du har lagt <span>{{ productsAdded }}</span> varer i kurven</h1> 1627 <h2 class="upsell-section__subtitle">@Translate("Translate_OrderFlow_UpsellReminder")</h2> 1628 </div> 1629 <div class="upsell-section__card-list"> 1630 1631 <div v-for="(product, index) in accessoriesList" class="upsell-card stock-card" :class="{ active: currentMobileProduct == product.Number, dirty: getQty(product.Number) > 0 }" @@click="toggleBar(product.Number)"> 1632 <div class="counter-amount" v-if="getQty(product.Number) > 0">{{ getQty(product.Number) }}</div> 1633 <div class="upsell__content-info"> 1634 <div class="upsell__content-info-media"> 1635 <a :href="'/@(EcomPage)&ProductID=' + product.Number" target="_blank"> 1636 <img :src="'/admin/public/getimage.ashx?Image=' + product.DefaultImage.Value + '&width=175&height=175&Crop=0&Compression=100'" :alt="product.Name" loading="lazy"> 1637 </a> 1638 </div> 1639 <div class="upsell__content-info-container"> 1640 <div class="product-details__info-meta"> 1641 <ul class="product-details__info-meta-list"> 1642 <li>@Translate("Translate_Product_Page_ProductNumber") {{ product.Number }}</li> 1643 @if(!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductDbNumber.Value"))) { 1644 <li>@GetString("Ecom:Product:Field.ProductDbNumber.Name") {{ product.EAN }}</li> 1645 } 1646 </ul> 1647 </div> 1648 <div class="product-details__info-title">{{ product.Name }}</div> 1649 <div class="product-details__info-size">{{ product.ProductFields.Name2.Value }}</div> 1650 <div class="product-details__info-quantity"> 1651 <div class="info-quantity" v-if="product.ProductFields.ProductNumberPerPackage.Value && product.ProductFields.ProductNumberPerPackage.Value > 0">{{ product.ProductFields.ProductNumberPerPackage.Value }} @Translate("Translate_General_Pieces")</div> 1652 <div class="info-price" v-if="product.Price.Price">{{ prettyPrice(product.Price.Price) }} @priceCurrencySymbol</div> 1653 </div> 1654 <div class="product-details__info-actions" v-if="product.Price.Price && product.Price.Price > 0 && !product.VariantInfo.VariantInfo"> 1655 <vue-counter :ref="'accCounter' + product.Number" :min="0" @@valueupdate="updateAccButton($event, product.Number)"></vue-counter> 1656 <a href="#" class="confirm disabled" :ref="'confirmBtn' + product.Number" @@click="addAcc($event, product.Number)"> 1657 <svg v-if="pickedAcc.find(x => x.id == product.Number)" enable-background="new 0 0 24 24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> 1658 <path fill="currentColor" d="m19.6025 12.6348c-.5586-.085-1.0547.2979-1.1348.8438-.2012 1.3711-.834 2.6221-1.8301 3.6182-2.5352 2.5352-6.6572 2.5332-9.1914 0-2.5337-2.5342-2.5337-6.6577 0-9.1914.9531-.9526 2.1563-1.5737 3.5029-1.7998.5791-.1099 1.2017-.1289 1.8477-.0557.887.1021 1.7126.3964 2.466.8285l-1.3019.2223c-.5439.0933-.9102.6099-.8164 1.1543.083.4873.5059.8315.9844.8315.0557 0 .1123-.0044.1699-.0142l3.4902-.5967c.2607-.0449.4941-.1914.6475-.4082.1533-.2163.2139-.4849.1689-.7466l-.5977-3.4897c-.0918-.5439-.6016-.9082-1.1543-.8169-.5439.0933-.9102.6104-.8164 1.1548l.1573.9185c-.9679-.543-2.0356-.8943-3.17-1.0249-.8496-.0967-1.6738-.0698-2.4282.0747-1.7368.291-3.3149 1.105-4.564 2.354-3.3135 3.3135-3.3135 8.7051 0 12.0195 1.6567 1.6572 3.8335 2.4854 6.0098 2.4854 2.1768 0 4.3525-.8281 6.0098-2.4854 1.3018-1.3018 2.1299-2.9414 2.3945-4.7412.0802-.5469-.2978-1.0548-.8437-1.1348z"/> 1659 </svg> 1660 <svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21.773 19.513"> 1661 <g transform="translate(-87.571 -246.25)"> 1662 <g transform="translate(88.321 247)"> 1663 <path d="M122.747,269.657h9.076a1.359,1.359,0,0,0,1.342-1.148l1.28-5.631a.994.994,0,0,0-.982-1.148h-13.3" transform="translate(-114.186 -258.966)" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1664 <path d="M88.321,247h2.551a.748.748,0,0,1,.724.563l1.973,8.555" transform="translate(-88.321 -247)" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1665 <path d="M115.7,293.281l1.193,4.686h.043" transform="translate(-110.564 -284.597)" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1666 <path d="M125.924,329.157a1.433,1.433,0,1,1-1.433-1.433A1.433,1.433,0,0,1,125.924,329.157Z" transform="translate(-116.54 -312.578)" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1667 <path d="M169.05,329.157a1.433,1.433,0,1,1-1.433-1.433A1.433,1.433,0,0,1,169.05,329.157Z" transform="translate(-151.574 -312.578)" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1668 <line x2="11.102" transform="translate(6.374 13.37)" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1669 </g> 1670 </g> 1671 </svg> 1672 </a> 1673 </div> 1674 </div> 1675 </div> 1676 </div> 1677 </div> 1678 <div class="notification-bar"> 1679 <div class="notification-bar__text"> 1680 @Translate("Translate_OrderFlow_HelpText") 1681 <a href="#product-inquire-form" @@click.prevent="openForm" id="product-orderflow-inquire-button" data-action="open-content" data-target="#enquireForm">@Translate("Translate_Product_Page_EnquireButton")</a> 1682 </div> 1683 </div> 1684 <div class="product-modal__content-bottom"></div> 1685 </div> 1686 <div v-if="!showAccessories" class="product-modal__content"> 1687 <div class="product-modal__content-top"> 1688 <a href="/" class="navigation__logo"> 1689 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/keflico_logo.svg"))) 1690 { 1691 <text>@System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/keflico_logo.svg"))</text> 1692 } 1693 </a> 1694 <a href="@CartPage" class="navigation__cart"> 1695 <svg xmlns="http://www.w3.org/2000/svg" width="21.773" height="19.513" viewBox="0 0 21.773 19.513"> 1696 <g transform="translate(0.75 0.75)"> 1697 <path d="M122.747,269.657h9.076a1.359,1.359,0,0,0,1.342-1.148l1.28-5.631a.994.994,0,0,0-.982-1.148h-13.3" transform="translate(-114.186 -258.966)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1698 <path d="M88.321,247h2.551a.748.748,0,0,1,.724.563l1.973,8.555" transform="translate(-88.321 -247)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1699 <path d="M115.7,293.281l1.193,4.686h.043" transform="translate(-110.564 -284.597)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1700 <path d="M125.924,329.157a1.433,1.433,0,1,1-1.433-1.433A1.433,1.433,0,0,1,125.924,329.157Z" transform="translate(-116.54 -312.578)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1701 <path d="M169.05,329.157a1.433,1.433,0,1,1-1.433-1.433A1.433,1.433,0,0,1,169.05,329.157Z" transform="translate(-151.574 -312.578)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1702 <line x2="11.102" transform="translate(6.374 13.37)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 1703 </g> 1704 </svg> 1705 <span class="cart-qty" data-count="@cartCount">@cartCount</span> 1706 </a> 1707 <a href="#" class="navigation__return product-modal__back-link" @@click="showAccessories = false"> 1708 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20.006 12.445"> 1709 <g transform="translate(-421.494 -889.275)"> 1710 <path d="M-17182.074-20447.988l4.809-4.809,4.809,4.809" transform="translate(20875.289 -16281.768) rotate(-90)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> 1711 <line x2="17" transform="translate(423.5 895.5)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="2" /> 1712 </g> 1713 </svg> 1714 </a> 1715 <div v-if="accessoriesList.length > 0" class="breadcrumbs breadcrumbs--no-seperator"> 1716 <ul class="breadcrumbs__list"> 1717 <li class="breadcrumbs__item active">1. Vælg produkter</li> 1718 <li class="breadcrumbs__item">2. Vælg tilbehør</li> 1719 </ul> 1720 </div> 1721 </div> 1722 <div class="product-modal__content-info"> 1723 <div class="product-modal__content-info-media"> 1724 <picture v-if="mainProduct.image"> 1725 <img :src="mainProduct.image" :alt="mainProduct.name" loading="lazy"> 1726 </picture> 1727 </div> 1728 <div class="product-modal__content-info-container"> 1729 <div class="product-details__info-meta"> 1730 <ul class="product-details__info-meta-list"> 1731 <li>{{ mainProduct.number }}</li> 1732 @if(!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductDbNumber.Value"))) { 1733 <li>{{ mainProduct.dbNumber }}</li> 1734 } 1735 </ul> 1736 </div> 1737 <div class="product-details__info-title">{{ mainProduct.name }}</div> 1738 <div class="product-details__info-size"> 1739 @if(primaryGroup == "group1") { 1740 <text>{{ mainProduct.size }}</text> 1741 } else { 1742 <div class="product-details__info-size-thickness"> 1743 <strong>@Translate("Translate_OrderFlow_Thickness")</strong> 1744 {{ mainProduct.thickness }} {{ thicknessUnit }} 1745 </div> 1746 <div class="product-details__info-size-width"> 1747 <strong>@Translate("Translate_OrderFlow_Width")</strong> 1748 {{ mainProduct.width }} {{ widthUnit }} 1749 </div> 1750 } 1751 </div> 1752 </div> 1753 </div> 1754 <div class="product-modal__content-product-stock"> 1755 @if(primaryGroup == "group1") { 1756 <div class="tabs"> 1757 <div v-if="bundles.length > 0" class="tabs__nav"> 1758 <ul role="tablist" class="tabs__list" data-action="tabs"> 1759 <li role="none" class="tabs__item"> 1760 @Translate("Translate_OrderFlow_Choose_ProductType") 1761 </li> 1762 <li role="none" class="tabs__item"> 1763 <a href="#helbundt" @@click.prevent="showBundles = true;" class="tab" :class="{ 'tab--active': showBundles }" role="tab" id="helbundt-tab" aria-controls="helbundt" aria-selected="true"> 1764 <span>@Translate("Translate_OrderFlow_Bundle")</span> 1765 </a> 1766 </li> 1767 @if(!isBundleOnly) { 1768 <li role="none" class="tabs__item"> 1769 <a href="#anbrud" @@click.prevent="showBundles = false;" class="tab" :class="{ 'tab--active': !showBundles }" role="tab" id="anbrud-tab" aria-controls="anbrud" aria-selected="false"> 1770 <span>@Translate("Translate_OrderFlow_Pieces")</span> 1771 </a> 1772 </li> 1773 } 1774 </ul> 1775 </div> 1776 <div id="helbundt" v-if="bundles.length > 0" class="tab__content" :class="{ 'tab__content--active': showBundles }" role="tabpanel" aria-hidden="false" aria-labelledby="helbundt-tab"> 1777 <div class="product-modal__stock"> 1778 <div class="product-modal__stock-line product-modal__stock-line--header bundle-header"> 1779 <div class="product-modal__stock-cell mobile-cell filler"></div> 1780 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_BundleNo")</div> 1781 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Thickness")</div> 1782 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Width")</div> 1783 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Length")</div> 1784 <div v-if="!enquireProduct" class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_InStock")</div> 1785 <div v-if="!enquireProduct" class="product-modal__stock-cell"> 1786 @Translate("Translate_OrderFlow_OnWayHome"): 1787 <span v-html="onTheWayHome"></span> 1788 </div> 1789 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted">@Translate("Translate_OrderFlow_ChooseAmount")</div> 1790 </div> 1791 <div v-if="!loadingBundles" v-for="bundle in sortedBundles" ref="bundle-line" :key="bundle.bundleNo" :class="{ active: currentMobileProduct == bundle.bundleNo, dirty: getQty(bundle.bundleNo) > 0, expanded: isExpanded(bundle.bundleNo) }" class="product-modal__stock-line stock-card stock-bundle-card" @@click="toggleBar(bundle.bundleNo)"> 1792 <div class="stock-bundle__header"> 1793 <div class="product-modal__stock-cell mobile-cell"> 1794 <div class="cell-radio counter-amount bundle-amount" v-html="getQty(bundle.bundleNo) > 0 ? getQty(bundle.bundleNo) : ''" :data-id="bundle.bundleNo"></div> 1795 </div> 1796 <div class="product-modal__stock-cell"> 1797 <span class="bundle-toggle" @@click="toggleBundle(bundle.bundleNo)"> 1798 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12.446 7.223"> 1799 <path d="M0,0,4.809,4.809,9.617,0" transform="translate(11.031 5.809) rotate(180)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> 1800 </svg> 1801 </span> 1802 {{ bundle.bundleNo }} 1803 </div> 1804 <div class="product-modal__stock-cell mobile-cell"></div> 1805 <div class="product-modal__stock-cell mobile-cell"></div> 1806 </div> 1807 <div class="stock-bundle__body"> 1808 <div class="stock-bundle__body-columns"> 1809 <div class="stock-bundle__cell"> 1810 @Translate("Translate_OrderFlow_Dimensions") 1811 </div> 1812 <div class="stock-bundle__cell"> 1813 @Translate("Translate_OrderFlow_InStock") 1814 </div> 1815 </div> 1816 <div v-for="(product, index) in bundle.productsInBundle" class="stock-bundle__stick"> 1817 <div class="stock-bundle__cell mobile-cell"> 1818 <div class="cell-stack"> 1819 @Translate("Translate_OrderFlow_Thickness_Short") {{ product.thickness }} {{ thicknessUnit }} 1820 </div> 1821 <div class="cell-stack"> 1822 @Translate("Translate_OrderFlow_Width_Short") {{ product.width }} {{ widthUnit }} 1823 </div> 1824 <div class="cell-stack"> 1825 @Translate("Translate_OrderFlow_Length_Short") {{ product.length.replace(",", "") }} {{ lenghtUnit }} 1826 </div> 1827 </div> 1828 <div class="stock-bundle__cell desktop-cell">{{ product.thickness }} {{ thicknessUnit }}</div> 1829 <div class="stock-bundle__cell desktop-cell">{{ product.width }} {{ widthUnit }}</div> 1830 <div class="stock-bundle__cell desktop-cell">{{ product.length.replace(",", "") }} {{ lenghtUnit }}</div> 1831 <div v-if="!enquireProduct" class="stock-bundle__cell">{{ product.stock.units > 500 ? '+500' : product.stock.units }} @Translate("Translate_General_Pieces")</div> 1832 <div v-if="!enquireProduct" class="stock-bundle__cell desktop-cell"></div> 1833 <div v-if="isLoggedIn && !enquireProduct" class="stock-bundle__cell product-modal__stock-cell--highlighted desktop-cell"> 1834 <vue-counter v-if="index == 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" :max="1" @@valueupdate="addBundleToTempCart($event, bundle.bundleNo)" /> 1835 </div> 1836 </div> 1837 </div> 1838 </div> 1839 <div v-if="loadingBundles" class="bundle-loading"> 1840 <span class="loader"></span> 1841 </div> 1842 </div> 1843 </div> 1844 @if(!isBundleOnly) { 1845 <div id="anbrud" class="tab__content" :class="{ 'tab__content--active': !showBundles }" role="tabpanel" aria-hidden="false" aria-labelledby="anbrud-tab"> 1846 <div class="product-modal__stock"> 1847 <div class="product-modal__stock-line product-modal__stock-line--header"> 1848 <div class="product-modal__stock-cell filler mobile-cell" v-if="isLoggedIn && !enquireProduct"></div> 1849 <div class="product-modal__stock-cell mobile-cell">@Translate("Translate_OrderFlow_Dimension")</div> 1850 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Thickness")</div> 1851 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Width")</div> 1852 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Length")</div> 1853 <div v-if="!enquireProduct" class="product-modal__stock-cell">@Translate("Translate_OrderFlow_InStock")</div> 1854 <div v-if="!enquireProduct" class="product-modal__stock-cell"> 1855 @Translate("Translate_OrderFlow_OnWayHome"): 1856 <span v-html="onTheWayHome"></span> 1857 </div> 1858 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted">@Translate("Translate_OrderFlow_ChooseAmount")</div> 1859 </div> 1860 <div v-for="(variant, index) in sortedVariants" v-if="variant.stock.warehouse > 0" class="product-modal__stock-line stock-card" :class="{ active: currentMobileProduct == variant.id, dirty: getQty(variant.id) > 0 }" @@click="toggleBar(variant.id)" :key="index"> 1861 <div class="product-modal__stock-cell quantity" v-if="isLoggedIn && !enquireProduct"> 1862 <div class="cell-radio counter-amount" :data-id="variant.id" v-html="getQty(variant.id) > 0 ? getQty(variant.id) : ''"></div> 1863 </div> 1864 <div class="product-modal__stock-cell cell-stack"> 1865 <span class="stack">@Translate("Translate_OrderFlow_Thickness_Short") {{ variant.size.thickness }} {{ thicknessUnit }}</span> 1866 <span class="stack">@Translate("Translate_OrderFlow_Width_Short") {{ variant.size.width }} {{ widthUnit }}</span> 1867 <span class="stack">@Translate("Translate_OrderFlow_Length_Short") {{ variant.size.length }} {{ lenghtUnit }}</span> 1868 </div> 1869 <div class="product-modal__stock-cell desktop-cell">{{ variant.size.thickness }} {{ thicknessUnit }}</div> 1870 <div class="product-modal__stock-cell desktop-cell">{{ variant.size.width }} {{ widthUnit }}</div> 1871 <div class="product-modal__stock-cell desktop-cell">{{ variant.size.length }} {{ lenghtUnit }}</div> 1872 <div v-if="!enquireProduct" class="product-modal__stock-cell">{{ calculateStockLevel(variant.stock.warehouse) }} @Translate("Translate_General_Pieces") </div> 1873 <div v-if="!enquireProduct" class="product-modal__stock-cell"></div> 1874 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted" :class="{'out-of-stock': variant.stock.warehouse + variant.stock.pieces == 0 || enquireProduct}"> 1875 <vue-counter v-if="variant.stock.warehouse + variant.stock.purchase > 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" @@valueupdate="addToTempCart($event, variant.id)" /> 1876 </div> 1877 </div> 1878 </div> 1879 </div> 1880 } 1881 </div> 1882 } 1883 else 1884 { 1885 if (useDifferentLengths) // Is 'useDifferentLengths' checkbox enabled on group' 1886 { 1887 <div v-if="shortLengthVariants?.length || longLengthVariants?.length" class="product-modal__stock-wrapper product-modal__stock-wrapper--multiple-lengths"> 1888 <div v-if="shortLengthVariants?.length" class="product-modal__stock"> 1889 <div> 1890 <p class="product-modal__stock-wrapper__length-title">@Translate("Translate_ProductPage_ShortLengths")</p> 1891 </div> 1892 <div> 1893 <div class="product-modal__stock-line product-modal__stock-line--header product-modal__stock-line--terrace"> 1894 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell filler mobile-cell"></div> 1895 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_Length")</div> 1896 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_InStock")</div> 1897 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_OnWayHome")</div> 1898 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted">@Translate("Translate_OrderFlow_ChooseAmount")</div> 1899 </div> 1900 <div v-for="(variant, index) in shortLengthVariants" class="product-modal__stock-line stock-card" v-on="variant.stock.pieces > 0 || variant.stock.warehouse > 0 || variant.stock.sea > 0 ? { click: () => toggleBar(variant.id) } : {}" :class="{ 'out-of-stock': variant.stock.pieces == 0 && variant.stock.warehouse == 0 && variant.stock.sea == 0, dirty: getQty(variant.id) > 0, active: variant.id == currentMobileProduct }" :key="index"> 1901 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell quantity"> 1902 <div class="cell-radio counter-amount" :data-id="variant.id" v-html="getQty(variant.id) > 0 ? getQty(variant.id) : ''"></div> 1903 </div> 1904 <div class="product-modal__stock-cell">{{ variant.size.length }} {{ lenghtUnit }}</div> 1905 <div class="product-modal__stock-cell">{{ calculateStockLevel(variant.stock.warehouse) }}</div> 1906 @if (primaryGroup == "group73") 1907 { 1908 <div class="product-modal__stock-cell">{{ variant.stock.purchase > 0 ? "@Translate("Translate_General_Yes")" : "@Translate("Translate_General_No")" }}</div> 1909 } 1910 else 1911 { 1912 <div class="product-modal__stock-cell">{{ calculateStockLevel(variant.stock.sea) }}</div> 1913 } 1914 1915 @if (primaryGroup == "group32") 1916 { 1917 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted" :class="{'out-of-stock': variant.stock.warehouse + variant.stock.sea == 0 || enquireProduct}"> 1918 <vue-counter v-if="variant.stock.warehouse + variant.stock.sea > 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" @@valueupdate="addToTempCart($event, variant.id)" /> 1919 </div> 1920 } 1921 else 1922 { 1923 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted" :class="{'out-of-stock': variant.stock.warehouse + variant.stock.pieces == 0 || enquireProduct}"> 1924 <vue-counter v-if="variant.stock.warehouse + variant.stock.purchase > 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" @@valueupdate="addToTempCart($event, variant.id)" /> 1925 </div> 1926 } 1927 </div> 1928 </div> 1929 </div> 1930 1931 <div v-if="longLengthVariants?.length" class="product-modal__stock"> 1932 <div> 1933 <p class="product-modal__stock-wrapper__length-title">@Translate("Translate_ProductPage_LongLengths")</p> 1934 </div> 1935 <div> 1936 <div class="product-modal__stock-line product-modal__stock-line--header product-modal__stock-line--terrace"> 1937 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell filler mobile-cell"></div> 1938 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_Length")</div> 1939 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_InStock")</div> 1940 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_OnWayHome")</div> 1941 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted">@Translate("Translate_OrderFlow_ChooseAmount")</div> 1942 </div> 1943 <div v-for="(variant, index) in longLengthVariants" class="product-modal__stock-line stock-card" v-on="variant.stock.pieces > 0 || variant.stock.warehouse > 0 || variant.stock.sea > 0 ? { click: () => toggleBar(variant.id) } : {}" :class="{ 'out-of-stock': variant.stock.pieces == 0 && variant.stock.warehouse == 0 && variant.stock.sea == 0, dirty: getQty(variant.id) > 0, active: variant.id == currentMobileProduct }" :key="index"> 1944 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell quantity"> 1945 <div class="cell-radio counter-amount" :data-id="variant.id" v-html="getQty(variant.id) > 0 ? getQty(variant.id) : ''"></div> 1946 </div> 1947 <div class="product-modal__stock-cell">{{ variant.size.length }} {{ lenghtUnit }}</div> 1948 <div class="product-modal__stock-cell">{{ calculateStockLevel(variant.stock.warehouse) }}</div> 1949 @if (primaryGroup == "group73") 1950 { 1951 <div class="product-modal__stock-cell">{{ variant.stock.purchase > 0 ? "@Translate("Translate_General_Yes")" : "@Translate("Translate_General_No")" }}</div> 1952 } 1953 else 1954 { 1955 <div class="product-modal__stock-cell">{{ calculateStockLevel(variant.stock.sea) }}</div> 1956 } 1957 1958 @if (primaryGroup == "group32") 1959 { 1960 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted" :class="{'out-of-stock': variant.stock.warehouse + variant.stock.sea == 0 || enquireProduct}"> 1961 <vue-counter v-if="variant.stock.warehouse + variant.stock.sea > 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" @@valueupdate="addToTempCart($event, variant.id)" /> 1962 </div> 1963 } 1964 else 1965 { 1966 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted" :class="{'out-of-stock': variant.stock.warehouse + variant.stock.pieces == 0 || enquireProduct}"> 1967 <vue-counter v-if="variant.stock.warehouse + variant.stock.purchase > 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" @@valueupdate="addToTempCart($event, variant.id)" /> 1968 </div> 1969 } 1970 </div> 1971 </div> 1972 </div> 1973 </div> 1974 } 1975 else 1976 { 1977 <div v-if="sortedVariants?.length"> 1978 <div class="product-modal__stock-line product-modal__stock-line--header product-modal__stock-line--terrace"> 1979 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell filler mobile-cell"></div> 1980 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_Length")</div> 1981 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_InStock")</div> 1982 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_OnWayHome")</div> 1983 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted">@Translate("Translate_OrderFlow_ChooseAmount")</div> 1984 </div> 1985 <div v-for="(variant, index) in sortedVariants" class="product-modal__stock-line stock-card" v-on="variant.stock.pieces > 0 || variant.stock.warehouse > 0 || variant.stock.sea > 0 ? { click: () => toggleBar(variant.id) } : {}" :class="{ 'out-of-stock': variant.stock.pieces == 0 && variant.stock.warehouse == 0 && variant.stock.sea == 0, dirty: getQty(variant.id) > 0, active: variant.id == currentMobileProduct }" :key="index"> 1986 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell quantity"> 1987 <div class="cell-radio counter-amount" :data-id="variant.id" v-html="getQty(variant.id) > 0 ? getQty(variant.id) : ''"></div> 1988 </div> 1989 <div class="product-modal__stock-cell">{{ variant.size.length }} {{ lenghtUnit }}</div> 1990 <div class="product-modal__stock-cell">{{ calculateStockLevel(variant.stock.warehouse) }}</div> 1991 @if (primaryGroup == "group73") 1992 { 1993 <div class="product-modal__stock-cell">{{ variant.stock.purchase > 0 ? "@Translate("Translate_General_Yes")" : "@Translate("Translate_General_No")" }}</div> 1994 } 1995 else 1996 { 1997 <div class="product-modal__stock-cell">{{ calculateStockLevel(variant.stock.sea) }}</div> 1998 } 1999 2000 @if (primaryGroup == "group32") 2001 { 2002 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted" :class="{'out-of-stock': variant.stock.warehouse + variant.stock.sea == 0 || enquireProduct}"> 2003 <vue-counter v-if="variant.stock.warehouse + variant.stock.sea > 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" @@valueupdate="addToTempCart($event, variant.id)" /> 2004 </div> 2005 } 2006 else 2007 { 2008 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted" :class="{'out-of-stock': variant.stock.warehouse + variant.stock.pieces == 0 || enquireProduct}"> 2009 <vue-counter v-if="variant.stock.warehouse + variant.stock.purchase > 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" @@valueupdate="addToTempCart($event, variant.id)" /> 2010 </div> 2011 } 2012 </div> 2013 </div> 2014 } 2015 2016 } 2017 </div> 2018 2019 2020 2021 @if(primaryGroup == "group32") { 2022 <div v-if="isLoggedIn" class="product-modal__stock-wrapper--multiple-lengths-information"> 2023 <span class="tooltip__icon"> 2024 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 2025 { 2026 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 2027 } 2028 </span> 2029 <span>@Translate("Translate_ProductPage_LengthsInformation")</span> 2030 </div> 2031 <div class="notification-bar"> 2032 <div v-if="isLoggedIn" class="notification-bar__text"> 2033 @Translate("Translate_ProductPage_GeneralPricingRules") 2034 </div> 2035 <div v-else class="notification-bar__text"> 2036 @Translate("Translate_OrderFlow_HelpText") 2037 <a href="#product-inquire-form" id="product-orderflow-inquire-button" data-action="open-content" data-target="#enquireForm">@Translate("Translate_Product_Page_EnquireButton")</a> 2038 </div> 2039 </div> 2040 } else { 2041 <div class="notification-bar"> 2042 <div class="notification-bar__text"> 2043 @Translate("Translate_OrderFlow_HelpText") 2044 <a href="#product-inquire-form" id="product-orderflow-inquire-button" data-action="open-content" data-target="#enquireForm">@Translate("Translate_Product_Page_EnquireButton")</a> 2045 </div> 2046 </div> 2047 } 2048 <div class="product-modal__content-bottom"></div> 2049 </div> 2050 <div class="product-modal__side"> 2051 <div class="product-modal__side-top"> 2052 <a href="#" class="product-modal__back-link" @@click="showAccessories = false"> 2053 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20.006 12.445"> 2054 <g transform="translate(-421.494 -889.275)"> 2055 <path d="M-17182.074-20447.988l4.809-4.809,4.809,4.809" transform="translate(20875.289 -16281.768) rotate(-90)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/> 2056 <line x2="17" transform="translate(423.5 895.5)" fill="none" stroke="#fff" stroke-linecap="round" stroke-width="2" /> 2057 </g> 2058 </svg> 2059 @Translate("Translate_ProductFlow_Back") 2060 </a> 2061 <div class="product-modal__basket-icon"> 2062 <span class="icon"></span> 2063 <span class="badge"></span> 2064 </div> 2065 </div> 2066 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__side-basket" :class="{ 'popup--active': showMobileBasket }"> 2067 <div v-if="pickedVariants.length > 0" class="side-basket__product"> 2068 <div class="side-basket__product-head"> 2069 <div class="product-name">{{ mainProduct.name }}</div> 2070 <div class="product-price"> 2071 @if(primaryGroup == "group1") { 2072 <span v-if="!loadingPrice">{{ prettyPrice(priceObject.unitPrice) }} @priceCurrencySymbol / {{ mainProduct.prices.unit.label }}</span> 2073 <span v-if="loadingPrice" class="loader"></span> 2074 } else { 2075 <span v-if="!loadingPrice">{{ prettyPrice(priceObject.unitPrice) }} @priceCurrencySymbol / @Translate("Translate_General_RunningMeters")</span> 2076 <span v-if="loadingPrice" class="loader"></span> 2077 } 2078 </div> 2079 <div class="product-toggle" @@click="toggleState"> 2080 <span class="close">@Translate("Translate_General_Close")</span> 2081 <span class="open">@Translate("Translate_General_Open")</span> 2082 </div> 2083 </div> 2084 <div v-for="(line, index) in sortedPickedVariants" class="side-basket__product-variant"> 2085 @if(primaryGroup == "group1") { 2086 <template v-if="line.bundle"> 2087 <div class="size size__header"> 2088 <strong>@Translate("Translate_OrderFlow_BundleNo") {{ line.bundleNo }}</strong> 2089 </div> 2090 <template v-for="variant in line.productsInBundle"> 2091 <div class="size"> 2092 <template v-if="lenghtUnit == 'mm'"> 2093 {{ variant.thickness.replace(".", ",") }} x {{ variant.width }} x {{ variant.length.replace(",", "") }}mm 2094 </template> 2095 <template v-else> 2096 {{ variant.thickness.replace(".", ",") }}{{ thicknessUnit }} x {{ variant.width }}{{ widthUnit }} x {{ variant.length.replace(",", "") }} {{ lenghtUnit }} 2097 </template> 2098 </div> 2099 <div class="quantity">{{ prettyNumber(variant.stock.units) }} @Translate("Translate_General_Pieces")</div> 2100 </template> 2101 </template> 2102 <template v-else> 2103 <div class="size size__header"> 2104 <strong>@Translate("Translate_OrderFlow_Pieces")</strong> 2105 </div> 2106 <div class="size">{{ line.thickness }} x {{ line.width }} x {{ line.length }}mm</div> 2107 <div class="quantity">{{ prettyNumber(line.qty) }} @Translate("Translate_General_Pieces")</div> 2108 </template> 2109 } else { 2110 <div class="size">{{ mainProduct.thickness }} x {{ mainProduct.width }} x {{ line.length }}mm</div> 2111 <div class="quantity">{{ prettyNumber(line.qty) }} @Translate("Translate_General_Pieces")</div> 2112 } 2113 </div> 2114 <div class="side-basket__product-total"> 2115 <div class="product-meters"> 2116 @Translate("Translate_General_InTotal") 2117 <span v-if="mainProduct.prices.unit.label == 'm²' || mainProduct.prices.unit.label == 'm'">{{ prettyNumber(calculateMeters()) }} {{ mainProduct.prices.unit.label }}</span> 2118 <span v-else>{{ prettyNumber(calculatePieces()) }} @Translate("Translate_General_Pieces")</span> 2119 </div> 2120 <div class="product-square-meters"> 2121 @Translate("Translate_General_TranslatesTo") 2122 <span v-if="mainProduct.prices.unit.label == 'm²' || mainProduct.prices.unit.label == 'm'">{{ prettyNumber(calculateSquareMeters()) }} m<sup>2</sup></span> 2123 <span v-else-if="mainProduct.prices.unit.label == 'm³'">{{ prettyNumber(calculateCubicMeters(), 3) }} m<sup>3</sup></span> 2124 <span v-else-if="mainProduct.prices.unit.label == 'kbf'">{{ prettyNumber(calculateCubicFeet()) }} kbf</span> 2125 </div> 2126 </div> 2127 </div> 2128 <div v-for="product in pickedAcc" class="side-basket__product"> 2129 <div class="side-basket__product-head"> 2130 <div class="product-name">{{ product.name }}</div> 2131 <div class="product-price">{{ prettyPrice(product.price.unitPrice) }}</div> 2132 <div class="product-toggle" @@click="toggleState"> 2133 <span class="close">@Translate("Translate_General_Close")</span> 2134 <span class="open">@Translate("Translate_General_Open")</span> 2135 </div> 2136 </div> 2137 <div class="side-basket__product-variant"> 2138 <div class="size">{{ product.name2 }}</div> 2139 <div class="quantity">{{ product.qty }} @Translate("Translate_General_Pieces")</div> 2140 </div> 2141 </div> 2142 <div class="side-basket__summary"> 2143 <div class="price-raw"> 2144 @Translate("Translate_General_PriceWithoutVat") 2145 <span v-if="!loadingPrice">{{ prettyPrice(priceObject.totalEx) }} @priceCurrencySymbol</span> 2146 <span v-if="loadingPrice" class="loader"></span> 2147 </div> 2148 <div class="vat"> 2149 @Translate("Translate_General_Vat") 2150 <span v-if="!loadingPrice">{{ prettyPrice(priceObject.vat) }} @priceCurrencySymbol</span> 2151 <span v-if="loadingPrice" class="loader"></span> 2152 </div> 2153 <div class="price-vat"> 2154 @Translate("Translate_General_PriceWithVat") 2155 <span v-if="!loadingPrice">{{ prettyPrice(priceObject.totalInc) }} @priceCurrencySymbol</span> 2156 <span v-if="loadingPrice" class="loader"></span> 2157 </div> 2158 </div> 2159 2160 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__floating-cart"> 2161 <div class="floating-cart__bar" :class="{ 'active-bar': showMobileActionBar }"> 2162 <div v-if="showMobileActionControls" class="product-modal__stock-control floating-cart__stock-control counter"> 2163 <button id="amount-subtract" @@click="currentMobileQty > 0 ? currentMobileQty-- : currentMobileQty = 0" class="substract"><span>−</span></button> 2164 <input id="amount-input" @@keyup="editMobileQty" class="amount" pattern="[0-9.]+" type="tel" data-min="0" min="0" :value="currentMobileQty"> 2165 <button id="amount-add" @@click="currentMobileQty++" class="add"><span>+</span></button> 2166 </div> 2167 <div v-if="!showMobileActionControls" @@click="showMobileBasket = !showMobileBasket" class="floating-cart__toggle cart-toggle"> 2168 <span v-if="!showMobileBasket" class="cart-toggle--open">Se kurv</span> 2169 <span v-if="showMobileBasket" class="cart-toggle--close">Luk kurv</span> 2170 </div> 2171 <div class="floating-cart__btn-wrapper"> 2172 <button v-if="currentActiveMobileButton == 'add'" class="btn btn-secondary floating-cart__btn btn--add" :class="{ disabled: currentMobileQty == 0 }" @@click="updateMobileBasket">Tilføj til kurv</button> 2173 <button v-if="currentActiveMobileButton == 'update'" class="btn btn-secondary floating-cart__btn btn--update" :class="{ disabled: (currentMobileQty == 0 && currentActiveMobileButton == 'add') || currentMobileQty == getQty(currentMobileProduct) }" @@click="updateMobileBasket">Opdatér kurv</button> 2174 <button v-if="currentActiveMobileButton == 'next'" class="btn btn-secondary floating-cart__btn btn--next" @@click="addToBasket"> 2175 <template v-if="!loading">Gå videre</template> 2176 <span v-if="loading" class="loader"></span> 2177 </button> 2178 <button v-if="currentActiveMobileButton == 'order' && !showAccessories" class="btn btn-secondary floating-cart__btn btn--order" @@click="addToBasket"> 2179 <template v-if="!loading">Gå til bestilling</template> 2180 <span v-if="loading" class="loader"></span> 2181 </button> 2182 <button v-if="currentActiveMobileButton == 'order' && showAccessories" class="btn btn-secondary floating-cart__btn btn--order" @@click="addAccToBasket"> 2183 <template v-if="!loading">Gå til bestilling</template> 2184 <span v-if="loading" class="loader"></span> 2185 </button> 2186 </div> 2187 </div> 2188 </div> 2189 2190 <div class="side-basket__actions"> 2191 <template v-if="!showAccessories"> 2192 <button type="button" @@click="resetBasket" class="btn btn-link btn-link--underlined" :class="{ disabled: pickedVariants.length == 0 }">@Translate("Translate_General_EmptyCart")</button> 2193 <button type="button" :class="{ disabled: pickedVariants.length == 0 }" @@click="addToBasket" class="btn btn-secondary"> 2194 <template v-if="!loading">@Translate("Translate_General_AddToCart")</template> 2195 <span v-if="loading" class="loader"></span> 2196 </button> 2197 </template> 2198 <template v-else> 2199 <a href="@CartPage" class="btn btn-link btn-link--underlined">@Translate("Translate_General_GoToCheckout")</a> 2200 <button type="button" @@click="addAccToBasket" class="btn btn-secondary" :class="{ disabled: pickedAcc.length == 0}"> 2201 <template v-if="!loading">@Translate("Translate_OrderFlow_AddAccessories")</template> 2202 <span v-if="loading" class="loader"></span> 2203 </button> 2204 </template> 2205 </div> 2206 </div> 2207 </div> 2208 </div> 2209 2210 2211 @SnippetStart("JavaScripts") 2212 <script> 2213 dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object. 2214 dataLayer.push({ 2215 event: "view_item", 2216 ecommerce: { 2217 items: [ 2218 { 2219 item_id: "@productNumber", 2220 item_name: "@productName", 2221 } 2222 ] 2223 } 2224 }); 2225 2226 2227 2228 2229 </script> 2230 2231 2232 2233 <script async> 2234 const vueCounter = { 2235 name: "VueCounter", 2236 props: ['min', 'max'], 2237 data: () => { 2238 return { 2239 value: 0, 2240 hasError: false, 2241 } 2242 }, 2243 methods: { 2244 substract() { 2245 if(this.min || this.min === 0) { 2246 if(this.value > this.min) { 2247 this.value-- 2248 } else { 2249 this.handleError(); 2250 } 2251 } else { 2252 this.value-- 2253 } 2254 }, 2255 add() { 2256 if(this.max) { 2257 if(this.value < this.max) { 2258 this.value++ 2259 } else { 2260 this.handleError(); 2261 } 2262 } else { 2263 this.value++ 2264 } 2265 }, 2266 handleError() { 2267 this.hasError = true; 2268 setTimeout(() => { 2269 this.hasError = false; 2270 }, 1000); 2271 } 2272 }, 2273 watch: { 2274 value(newValue, oldValue) { 2275 this.$emit('valueupdate', newValue) 2276 } 2277 }, 2278 template: `<div class="vue-counter" :class="hasError ? 'counter--error' : ''" > 2279 <a class="counter__sub" @@click="substract">-</a> 2280 <input class="counter__value" type="tel" v-model="value" :disabled="value == max ? true : false"> 2281 <a class="counter__add" @@click="add">+</a> 2282 </div>` 2283 } 2284 2285 new Vue({ 2286 el: '#orderFlowApp', 2287 name: 'Bestillings flow', 2288 components: { 2289 'vue-counter': vueCounter 2290 }, 2291 computed: { 2292 lenghtUnit() { 2293 return this.sizeUnitValues[this.mainProduct.sizeUnits.length]; 2294 }, 2295 widthUnit() { 2296 return this.sizeUnitValues[this.mainProduct.sizeUnits.width]; 2297 }, 2298 thicknessUnit() { 2299 return this.sizeUnitValues[this.mainProduct.sizeUnits.thickness]; 2300 }, 2301 sortedVariants() { 2302 return this.variants.sort((a,b) => { 2303 if (a.size.length !== b.size.length) { 2304 return a.size.length - b.size.length; 2305 } else if (a.width !== b.width) { 2306 return a.width - b.width; 2307 } else { 2308 return a.thickness - b.thickness; 2309 } 2310 }).filter(x => x.size.length > 0); 2311 }, 2312 shortLengthVariants() { 2313 return this.sortedVariants?.filter(variant => variant.size.length < this.lengthThreshold); 2314 }, 2315 longLengthVariants() { 2316 return this.sortedVariants?.filter(variant => variant.size.length >= this.lengthThreshold); 2317 }, 2318 sortedBundles() { 2319 let newBundleList = []; 2320 2321 if(this.bundles.length > 0) { 2322 this.bundles.forEach(bundle => { 2323 const index = newBundleList.findIndex(x => x.bundleNo == bundle.bundleNo); 2324 2325 if(parseFloat(bundle.stock.warehouse) > 0) { 2326 if(index > -1) { 2327 newBundleList[index].productsInBundle.push( 2328 { 2329 length: bundle.length, 2330 width: bundle.width, 2331 thickness: bundle.thickness, 2332 stock: bundle.stock 2333 } 2334 ) 2335 } else { 2336 this.toggleBundles.push({ 2337 bundle: bundle.bundleNo, 2338 expanded: true, 2339 }); 2340 newBundleList.push( 2341 { 2342 bundleNo: bundle.bundleNo, 2343 productsInBundle: [ 2344 { 2345 length: bundle.length, 2346 width: bundle.width, 2347 thickness: bundle.thickness, 2348 stock: bundle.stock 2349 } 2350 ] 2351 } 2352 ) 2353 } 2354 } 2355 }) 2356 } 2357 2358 return newBundleList; 2359 }, 2360 sortedPickedVariants() { 2361 return this.pickedVariants.sort((x,y) => x.bundle - y.bundle); 2362 }, 2363 ga4Products() { 2364 let list = []; 2365 2366 this.sortedPickedVariants.forEach(product => { 2367 let productObject = {}; 2368 2369 if(product.bundle) { 2370 productObject.item_name = this.mainProduct.name; 2371 productObject.item_id = product.bundleNo; 2372 productObject.productsInBundle = product.productsInBundle; 2373 } else { 2374 productObject.item_id = product.id, 2375 productObject.item_name = this.mainProduct.name, 2376 productObject.item_variant = product.variantId, 2377 productObject.quantity = product.qty, 2378 productObject.price = this.priceObject.unitPrice 2379 } 2380 2381 list.push(productObject) 2382 }) 2383 2384 return list; 2385 }, 2386 onTheWayHome() { 2387 if(this.variants.filter(x => x.stock.pieces > 0).length > 0) { 2388 return "@Translate("Translate_General_Yes")"; 2389 } 2390 2391 return "@Translate("Translate_General_No")"; 2392 }, 2393 accessoriesList() { 2394 let list = []; 2395 2396 if(this.relatedProducts && this.relatedProducts.length > 0) { 2397 this.relatedProducts.forEach(product => { 2398 let group = this.relatedGroups.find(x => x.Id == "RELGRP1"); 2399 2400 if(group && group.Products.find(x => x.ProductId == product.Id)) { 2401 list.push(product); 2402 } 2403 }) 2404 } 2405 2406 return list; 2407 } 2408 }, 2409 mounted() { 2410 this.getVariants(@(productNumber)) 2411 @if(primaryGroup == "group1") { 2412 <text> 2413 this.lookUpBundle(@(productNumber)); 2414 </text> 2415 } 2416 2417 this.getAccessories(); 2418 2419 if(!this.enquireProduct && this.isLoggedIn) { 2420 this.orderButtonSpinner = setTimeout(() => { 2421 document.querySelector('#product-order-button .loader').style.display = "inline-block"; 2422 }, 3000); 2423 } else { 2424 document.querySelector('#product-order-button .product-order-form-button').style.display = "block"; 2425 } 2426 }, 2427 data() { 2428 return { 2429 isLoggedIn: @isloggedin.ToString().ToLower(), 2430 enquireProduct: @enquireProduct.ToString().ToLower(), 2431 lang: document.querySelector('html').getAttribute('data-lang'), 2432 loading: false, 2433 loadingPrice: false, 2434 loadingBundles: false, 2435 loadingVariants: false, 2436 orderLoader: true, 2437 orderButtonSpinner: null, 2438 timeOut: null, 2439 priceObject: { 2440 unitPrice: 0, 2441 totalEx: 0, 2442 vat: 0, 2443 totalInc: 0, 2444 corePrice: 0 2445 }, 2446 customerNumber: '@GetGlobalValue("Global:Extranet.CustomerNumber")', 2447 sizeUnitValues: { 2448 'ZOLL/00': '@Translate("Translate_Product_SizeUnit_Inch")', 2449 'ZOLL/01': '@Translate("Translate_Product_SizeUnit_Inch")', 2450 'FUß/00': '@Translate("Translate_Product_SizeUnit_Foot")', 2451 'FUß/01': '@Translate("Translate_Product_SizeUnit_Foot")', 2452 'MM/00': '@Translate("Translate_Product_SizeUnit_Millimeter")', 2453 'MM/01': '@Translate("Translate_Product_SizeUnit_Millimeter")' 2454 }, 2455 mainProduct: { 2456 name: '@productName', 2457 id: @productNumber, 2458 number: '@Translate("Translate_Product_Page_ProductNumber"): @productNumber', 2459 dbNumber: '@GetString("Ecom:Product:Field.ProductDbNumber.Name"): @GetString("Ecom:Product:Field.ProductDbNumber.Value")', 2460 width: '@GetString("Ecom:Product:Field.ProductWidthSale.Value.Raw")', 2461 thickness: '@GetString("Ecom:Product:Field.ProductThicknessSale.Value.Raw")', 2462 image: '@GetString("Ecom:Product.ImageDefault.Clean")', 2463 size: @Json.Encode(GetString("Ecom:Product:Field.Name2")), 2464 prices: { 2465 unit: { 2466 code: "@salesUnitCode", 2467 label: "@salesUnit" 2468 }, 2469 standard: @standardPriceJS, 2470 above100: @above100PriceJS, 2471 bundle: @bundlePriceJS 2472 }, 2473 sizeUnits: { 2474 length: '@GetString("Ecom:Product:Field.ProductLengthUnitCode.Value")', 2475 width: '@GetString("Ecom:Product:Field.ProductWidthUnitCode.Value")', 2476 thickness: '@GetString("Ecom:Product:Field.ProductThicknessUnitCode.Value")', 2477 } 2478 }, 2479 relatedGroups: null, 2480 relatedProducts: null, 2481 lengthThreshold: 3000, // <-- Field in DW? 2482 variants: [], 2483 showAccessories: false, 2484 showStep1: false, 2485 showBundles: false, 2486 bundles: [], 2487 pickedVariants: [], 2488 productsAdded: 0, 2489 pickedAcc: [], 2490 currentMobileProduct: '', 2491 showMobileBasket: false, 2492 showMobileActionBar: false, 2493 showMobileActionControls: false, 2494 currentActiveMobileButton: 'none', 2495 currentMobileQty: 0, 2496 toggleBundles: [], 2497 } 2498 }, 2499 methods: { 2500 doesCookieExist(cookieName) { 2501 const cookies = document.cookie.split('; '); 2502 const cookieExists = cookies.some(cookie => cookie.startsWith(cookieName + '=')); 2503 return cookieExists; 2504 }, 2505 setCookie(name, value, days) { 2506 let expires = ""; 2507 if (days) { 2508 const date = new Date(); 2509 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 2510 expires = "; expires=" + date.toUTCString(); 2511 } 2512 document.cookie = name + "=" + (value || "") + expires + "; path=/"; 2513 }, 2514 deleteCookie(name) { 2515 document.cookie = name + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; 2516 }, 2517 async getAccessories() { 2518 await fetch(`/dwapi/ecommerce/products/@(productNumber)`) 2519 .then(response => response.json()) 2520 .then(response => { 2521 this.relatedGroups = response.RelatedGroups; 2522 }) 2523 2524 await fetch(`/dwapi/ecommerce/products/@(productNumber)/related`) 2525 .then(response => response.json()) 2526 .then(response => { 2527 if(response.TotalProductsCount > 0) { 2528 this.relatedProducts = response.Products; 2529 } 2530 }) 2531 .catch(error => { 2532 console.log(error) 2533 }) 2534 }, 2535 getVariants: async function(productNo, url) { 2536 this.loadingVariants = true; 2537 let requestUrl = `@(VariantsLookup)&ICC_itemId=${productNo}`; 2538 2539 if(url && url != "") { 2540 requestUrl = url; 2541 } 2542 2543 if(!this.isLoggedIn) { 2544 if(requestUrl.indexOf('username') < 0) { 2545 requestUrl += '&username=marketing@keflico.com&password=Keflico100%'; 2546 } 2547 2548 if (!this.doesCookieExist('tempLogin')) { 2549 this.setCookie('tempLogin', true, 1); 2550 } 2551 } 2552 2553 let credentials = this.isLoggedIn ? "same-origin" : "omit" 2554 2555 fetch(requestUrl, { 2556 credentials: credentials 2557 }) 2558 .then(response => response.json()) 2559 .then(response => { 2560 if(response.variants && response.variants.length > 0) { 2561 this.variants = this.variants.concat(response.variants); 2562 2563 this.orderLoader = false; 2564 2565 if(response.nextPage != "") { 2566 this.getVariants(productNo, response.nextPage); 2567 } else { 2568 this.loadingVariants = false; 2569 2570 if(!this.isLoggedIn) { 2571 fetch('/Admin/Public/ExtranetLogoff.aspx'); 2572 } 2573 } 2574 } else { 2575 this.loadingVariants = false; 2576 2577 if(!this.loadingBundles) { 2578 clearTimeout(this.orderButtonSpinner); 2579 document.querySelector('#product-order-button .product-order-form-button').style.display = "block"; 2580 document.querySelector('#product-order-button .loader').style.display = "none"; 2581 } 2582 } 2583 }) 2584 }, 2585 lookUpBundle: async function(productNo, url) { 2586 this.loadingBundles = true; 2587 let requestUrl = `@(BundleLookup)&ICC_itemId=${productNo}`; 2588 2589 if(url && url != "") { 2590 requestUrl = url; 2591 } 2592 2593 if(!this.isLoggedIn) { 2594 if(requestUrl.indexOf('username') < 0) { 2595 requestUrl += '&username=marketing@keflico.com&password=Keflico100%'; 2596 } 2597 2598 if (!this.doesCookieExist('tempLogin')) { 2599 this.setCookie('tempLogin', true, 1); 2600 } 2601 } 2602 2603 let credentials = this.isLoggedIn ? "same-origin" : "omit" 2604 2605 fetch(requestUrl, { 2606 credentials: credentials 2607 }) 2608 .then(response => response.json()) 2609 .then(response => { 2610 if(response.bundles && response.bundles.length > 0) { 2611 2612 if(response.currentPage = 1) { 2613 this.showBundles = true; 2614 } 2615 2616 this.orderLoader = false; 2617 } else { 2618 if(!this.loadingVariants) { 2619 clearTimeout(this.orderButtonSpinner); 2620 document.querySelector('#product-order-button .product-order-form-button').style.display = "block"; 2621 document.querySelector('#product-order-button .loader').style.display = "none"; 2622 } 2623 } 2624 2625 this.bundles = this.bundles.concat(response.bundles); 2626 2627 if(response.nextPage != "") { 2628 this.lookUpBundle(productNo, response.nextPage); 2629 } else { 2630 this.loadingBundles = false; 2631 2632 if(!this.isLoggedIn) { 2633 fetch('/Admin/Public/ExtranetLogoff.aspx'); 2634 } 2635 } 2636 }) 2637 }, 2638 tempPriceUpdate() { 2639 let totalAccPrice = 0; 2640 2641 this.pickedAcc.forEach(acc => { 2642 totalAccPrice += parseFloat(acc.price.exVat.replace(',', '')) 2643 }) 2644 2645 this.priceObject.totalEx = parseFloat(this.priceObject.corePrice) + totalAccPrice; 2646 this.priceObject.vat = parseFloat(this.priceObject.totalEx) * 0.25; 2647 this.priceObject.totalInc = parseFloat(this.priceObject.totalEx) * 1.25; 2648 }, 2649 priceLookUp: async function() { 2650 let x = 1; 2651 let y = 1; 2652 let queryString = "&ICC_userId=" + this.customerNumber + "&ICC_itemId=" + this.mainProduct.id; 2653 2654 this.pickedVariants.forEach(variant => { 2655 if(variant.bundle) { 2656 queryString += "&ICC_bundle" + y + "=" + variant.bundleNo; 2657 2658 y++; 2659 } else { 2660 queryString += "&ICC_variant" + x + "=" + variant.id; 2661 queryString += "&ICC_quantity" + x + "=" + variant.qty; 2662 2663 x++; 2664 } 2665 }) 2666 2667 fetch(`@(PriceLookup)${queryString}`) 2668 .then(response => response.json()) 2669 .then(response => { 2670 this.priceObject.corePrice = parseFloat(response.totalPriceWithoutVat.replace(",", "")); 2671 this.priceObject.unitPrice = response.unitPrice; 2672 this.priceObject.totalEx = response.totalPriceWithoutVat; 2673 this.priceObject.vat = response.vat; 2674 this.priceObject.totalInc = response.totalPriceWithVat; 2675 2676 this.loadingPrice = false; 2677 }) 2678 }, 2679 toggleBundle(bundleNo) { 2680 this.toggleBundles.find(x => x.bundle == bundleNo).expanded = !this.toggleBundles.find(x => x.bundle == bundleNo).expanded; 2681 }, 2682 isExpanded(bundleNo) { 2683 return this.toggleBundles.find(x => x.bundle == bundleNo).expanded; 2684 }, 2685 getQty(variantId) { 2686 let product = this.pickedVariants.find(x => x.id == variantId || x.bundleNo == variantId); 2687 let acc = this.pickedAcc.find(x => x.id == variantId); 2688 2689 if(product || acc) { 2690 if(product) { 2691 if(product.bundle) { 2692 return 1; 2693 } 2694 2695 return product.qty; 2696 } else { 2697 return acc.qty; 2698 } 2699 } else { 2700 return 0; 2701 } 2702 }, 2703 toggleBar(productId) { 2704 let product = this.accessoriesList.find(x => x.Id == productId); // product is acc 2705 2706 if((product && product.Price.Price && product.Price.Price > 0 && !product.VariantInfo.VariantInfo) || !product) { 2707 if(this.currentMobileProduct != productId) { 2708 this.currentMobileProduct = productId; 2709 this.showMobileActionBar = true; 2710 this.showMobileActionControls = true; 2711 2712 if(this.pickedVariants.find(x => x.id == productId)) { 2713 this.currentActiveMobileButton = 'update'; 2714 this.currentMobileQty = this.pickedVariants.find(x => x.id == productId).qty; 2715 } else if(this.pickedAcc.find(x => x.id == productId)) { 2716 this.currentActiveMobileButton = 'update'; 2717 this.currentMobileQty = this.pickedAcc.find(x => x.id == productId).qty; 2718 } else { 2719 this.currentActiveMobileButton = 'add'; 2720 this.currentMobileQty = 0; 2721 } 2722 } else { 2723 this.currentMobileProduct = ''; 2724 this.showMobileActionControls = false; 2725 2726 if(this.pickedVariants.length == 0) { 2727 this.showMobileActionBar = false; 2728 } 2729 2730 if(this.showAccessories || this.accessoriesList.length == 0) { 2731 this.currentActiveMobileButton = 'order'; 2732 } else { 2733 this.currentActiveMobileButton = 'next'; 2734 } 2735 } 2736 } 2737 }, 2738 addToTempCart(amount, id) { 2739 this.loadingPrice = true; 2740 clearTimeout(this.timeOut); 2741 2742 if(this.pickedVariants.filter(x => x.id == id).length > 0) { 2743 if(amount > 0) { 2744 this.pickedVariants.find(x => x.id == id).qty = amount; 2745 } else { 2746 const index = this.pickedVariants.findIndex(x => x.id == id); 2747 this.pickedVariants.splice(index, 1); 2748 } 2749 } else { 2750 if(amount > 0) { 2751 const data = this.sortedVariants.find(x => x.id == id); 2752 2753 this.pickedVariants.push({ 2754 bundle: false, 2755 id: id, 2756 qty: parseInt(amount), 2757 length: data.size.length, 2758 width: data.size.width, 2759 thickness: data.size.thickness, 2760 variantId: data.id 2761 }); 2762 } 2763 } 2764 2765 if(this.pickedVariants.length > 0) { 2766 this.timeOut = setTimeout(() => { 2767 this.priceLookUp(); 2768 }, 1000); 2769 } else { 2770 this.priceObject.totalEx = 0; 2771 this.priceObject.totalInc = 0; 2772 this.priceObject.vat = 0; 2773 this.priceObject.unitPrice = 0; 2774 2775 this.loadingPrice = false; 2776 } 2777 }, 2778 editMobileQty($event) { 2779 this.currentMobileQty = $event.target.value 2780 }, 2781 updateMobileBasket() { 2782 let productToUpdate = this.sortedVariants.find(x => x.id == this.currentMobileProduct); 2783 2784 if(!productToUpdate) { 2785 productToUpdate = this.sortedBundles.find(x => x.bundleNo == this.currentMobileProduct); 2786 } 2787 2788 if(productToUpdate) { 2789 if(productToUpdate.bundleNo) { 2790 this.addBundleToTempCart(1, this.currentMobileProduct); 2791 } else { 2792 this.addToTempCart(this.currentMobileQty, this.currentMobileProduct); 2793 } 2794 2795 this.showMobileActionControls = false; 2796 this.currentMobileProduct = ''; 2797 2798 if(this.accessoriesList.length == 0) { 2799 this.currentActiveMobileButton = 'order'; 2800 } else { 2801 this.currentActiveMobileButton = 'next'; 2802 } 2803 } else { 2804 productToUpdate = this.accessoriesList.find(x => x.id == this.currentMobileProduct); 2805 2806 this.addAcc(this.currentMobileQty, this.currentMobileProduct); 2807 2808 this.showMobileActionControls = false; 2809 this.currentMobileProduct = ''; 2810 this.currentActiveMobileButton = 'order'; 2811 } 2812 }, 2813 addBundleToTempCart(amount, bundleNo) { 2814 this.loadingPrice = true; 2815 clearTimeout(this.timeOut); 2816 2817 if(amount > 0) { 2818 this.pickedVariants.push({ 2819 bundle: true, 2820 bundleNo: bundleNo, 2821 productsInBundle: this.sortedBundles.filter(x => x.bundleNo == bundleNo)[0].productsInBundle 2822 }) 2823 } else { 2824 const index = this.pickedVariants.findIndex(x => x.bundleNo == bundleNo); 2825 this.pickedVariants.splice(index, 1); 2826 } 2827 2828 if(this.pickedVariants.length > 0) { 2829 this.timeOut = setTimeout(() => { 2830 this.priceLookUp(); 2831 }, 1000); 2832 } else { 2833 this.priceObject.totalEx = 0; 2834 this.priceObject.totalInc = 0; 2835 this.priceObject.vat = 0; 2836 this.priceObject.unitPrice = 0; 2837 2838 this.loadingPrice = false; 2839 } 2840 }, 2841 updateAccButton(amount, productNumber) { 2842 if((amount > 0 && (!this.pickedAcc.find(x => x.id == productNumber) || amount != this.pickedAcc.find(x => x.id == productNumber).qty)) || (this.pickedAcc.find(x => x.id == productNumber) && amount != this.pickedAcc.find(x => x.id == productNumber).qty)) { 2843 this.$refs['confirmBtn' + productNumber][0].classList.remove('disabled') 2844 } else { 2845 this.$refs['confirmBtn' + productNumber][0].classList.add('disabled') 2846 } 2847 }, 2848 addAcc(event, productNumber) { 2849 let product = this.pickedAcc.find(x => x.id == productNumber); 2850 let qty = typeof event == 'number' ? event : this.$refs['accCounter' + productNumber][0].value; 2851 2852 let queryString = `&ICC_userId=${this.customerNumber}&ICC_itemId=${productNumber}&ICC_quantity1=`; 2853 2854 if(product) { 2855 if(qty > 0) { 2856 product.qty = qty; 2857 queryString += product.qty; 2858 } else { 2859 this.pickedAcc.splice(this.pickedAcc.findIndex(x => x.id == product.id), 1); 2860 } 2861 } else { 2862 this.pickedAcc.push({ 2863 id: productNumber, 2864 name: this.accessoriesList.find(x => x.Number == productNumber).Name, 2865 name2: this.accessoriesList.find(x => x.Number == productNumber).ProductFields.Name2.Value, 2866 qty: qty, 2867 price: { 2868 exVat: 0, 2869 vat: 0, 2870 withVat: 0, 2871 unitPrice: 0 2872 } 2873 }) 2874 2875 queryString += qty; 2876 } 2877 2878 if(qty > 0) { 2879 fetch(`/Default.aspx?ID=1115${queryString}`) 2880 .then(response => response.json()) 2881 .then(response => { 2882 if(product) { 2883 product.price.exVat = response.totalPriceWithoutVat; 2884 product.price.vat = response.vat; 2885 product.price.withVat = response.totalPriceWithVat; 2886 product.price.unitPrice = response.unitPrice; 2887 } else { 2888 const newProduct = this.pickedAcc.at(-1); 2889 2890 newProduct.price.exVat = response.totalPriceWithoutVat; 2891 newProduct.price.vat = response.vat; 2892 newProduct.price.withVat = response.totalPriceWithVat; 2893 newProduct.price.unitPrice = response.unitPrice; 2894 } 2895 2896 this.tempPriceUpdate(); 2897 }) 2898 } else { 2899 this.tempPriceUpdate(); 2900 } 2901 2902 this.$refs['confirmBtn' + productNumber][0].classList.add('disabled'); 2903 }, 2904 prettyNumber(number, minDigits) { 2905 return parseFloat(number).toLocaleString(this.lang, { minimumFractionDigits: minDigits ? minDigits : 0 }) 2906 }, 2907 prettyPrice(price) { 2908 let rawPrice = price; 2909 2910 if(typeof price == "string" && (price.split(".").length - 1 > 1 || (price.indexOf(".") == 1 && price.length > 4) || (price.indexOf(".") == 2 && price.length >= 6) || (price.indexOf(".") == 3 && price.length > 6))) { 2911 // Price is over 1000 in danish format 2912 2913 rawPrice = rawPrice.replaceAll(".", ""); 2914 rawPrice = rawPrice.replaceAll(",", "."); 2915 } else if(typeof price == "string" && (price.split(",").length - 1 > 1 || (price.indexOf(",") == 1 && price.length > 4) || (price.indexOf(",") == 2 && price.length >= 6) || (price.indexOf(",") == 3 && price.length > 6))) { 2916 // Price is over 1000 in english format 2917 2918 rawPrice = rawPrice.replaceAll(",", ""); 2919 } 2920 2921 rawPrice = parseFloat(rawPrice); 2922 return rawPrice.toLocaleString(this.lang, { minimumFractionDigits: 2, maximumFractionDigits: 2 } ) 2923 }, 2924 calculateStockLevel(stock) { 2925 if(this.isLoggedIn && stock >= 0) { 2926 return stock > 500 ? '+500' : stock; 2927 } else { 2928 return stock > 100 ? '+100' : stock; 2929 } 2930 2931 return 0; 2932 }, 2933 toggleState($event) { 2934 $event.target.closest('.side-basket__product').classList.toggle('closed'); 2935 }, 2936 openForm(e) { 2937 document.querySelector('.product-modal').classList.remove('js-slide-in'); 2938 2939 const toggleTrigger = e.currentTarget; 2940 const target = document.querySelector(toggleTrigger.getAttribute('data-target')); 2941 2942 if (target) { 2943 disableScrollLock() 2944 history.back(); 2945 2946 if (!target.classList.contains('js-open')) { 2947 toggleTrigger.classList.add('js-open'); 2948 target.classList.add('js-open'); 2949 target.style.height = `${target.scrollHeight}px`; 2950 2951 if (toggleTrigger.getAttribute('data-action-scroll') != 'no-scroll') { 2952 setTimeout(function() { 2953 target.scrollIntoView({behavior: 'smooth'}); 2954 },300); 2955 } 2956 2957 if (target.classList.contains('dialog')) { 2958 document.querySelector('body').style.overflow = 'hidden'; 2959 } 2960 } else { 2961 toggleTrigger.classList.remove('js-open'); 2962 target.classList.remove('js-open'); 2963 target.style.height = null; 2964 2965 if (target.classList.contains('dialog')) { 2966 document.querySelector('body').style.overflow = null; 2967 } 2968 } 2969 } 2970 }, 2971 calculateMeters() { 2972 let meters = 0; 2973 2974 this.pickedVariants.forEach(variant => { 2975 let length = variant['length']; 2976 2977 if(this.mainProduct.sizeUnits.length.indexOf('FUß') > -1) { 2978 length = length * 304.8; 2979 } 2980 2981 let variantMeters = parseFloat(length) * variant.qty / 1000; 2982 meters += variantMeters; 2983 }) 2984 2985 return parseFloat(meters).toFixed(3); 2986 }, 2987 calculatePieces() { 2988 let pieces = 0; 2989 2990 this.pickedVariants.forEach(variant => { 2991 if(variant.bundle) { 2992 variant.productsInBundle.forEach(product => { 2993 pieces += parseInt(product.stock.units); 2994 }) 2995 } else { 2996 pieces += parseInt(variant.qty); 2997 } 2998 }); 2999 3000 return pieces; 3001 }, 3002 calculateSquareMeters() { 3003 return parseFloat(this.calculateMeters() * (this.mainProduct.width / 1000)).toFixed(3); 3004 }, 3005 calculateCubicMeters() { 3006 let cubicMeters = 0; 3007 3008 this.pickedVariants.forEach(variant => { 3009 let variantMeters = 0; 3010 3011 let lengthMultiplier = 1; 3012 let widthMultiplier = 1; 3013 let thicknessMultiplier = 1; 3014 3015 if(this.mainProduct.sizeUnits.length.indexOf('FUß') > -1) { 3016 lengthMultiplier = 304.79999025; 3017 } 3018 3019 if(this.mainProduct.sizeUnits.width.indexOf('ZOLL') > -1) { 3020 widthMultiplier = 25.39998628; 3021 } 3022 3023 if(this.mainProduct.sizeUnits.thickness.indexOf('ZOLL') > -1) { 3024 thicknessMultiplier = 25.39998628 3025 } 3026 3027 if(variant.bundle) { 3028 variant.productsInBundle.forEach(product => { 3029 variantMeters += (parseFloat(product.length.replace(",", "") * lengthMultiplier / 1000) * parseFloat(product.thickness * thicknessMultiplier / 1000) * parseFloat(product.width * widthMultiplier) / 1000) * parseInt(product.stock.units); 3030 }) 3031 } else { 3032 variantMeters += (parseFloat(variant['length'] / 1000) * parseFloat(variant['thickness'] / 1000) * parseFloat(variant['width']) / 1000) * variant.qty; 3033 } 3034 3035 cubicMeters += variantMeters; 3036 }) 3037 3038 return cubicMeters; 3039 }, 3040 calculateCubicFeet() { 3041 let cubicFeet = 0; 3042 3043 this.pickedVariants.forEach(variant => { 3044 let variantMeters = 0; 3045 3046 let lengthMultiplier = 1; 3047 let widthMultiplier = 1; 3048 let thicknessMultiplier = 1; 3049 3050 if(this.mainProduct.sizeUnits.length.indexOf('FUß') > -1) { 3051 lengthMultiplier = 304.79999025; 3052 } 3053 3054 if(this.mainProduct.sizeUnits.width.indexOf('ZOLL') > -1) { 3055 widthMultiplier = 25.39998628; 3056 } 3057 3058 if(this.mainProduct.sizeUnits.thickness.indexOf('ZOLL') > -1) { 3059 thicknessMultiplier = 25.39998628 3060 } 3061 3062 if(variant.bundle) { 3063 variant.productsInBundle.forEach(product => { 3064 let lengthMM = product.length.replace(",", "") * lengthMultiplier; 3065 let lengthM = lengthMM / 1000; 3066 let widthMM = product.width * widthMultiplier; 3067 let widthM = widthMM / 1000; 3068 let thicknessMM = product.thickness * thicknessMultiplier; 3069 let thicknessM = thicknessMM / 1000; 3070 3071 variantMeters += Number(parseFloat(lengthM * widthM * thicknessM * 35.32 * product.stock.units)) 3072 }) 3073 } else { 3074 variantMeters += ((parseFloat(variant['length'] * lengthMultiplier / 1000) * parseFloat(variant['thickness'] * thicknessMultiplier / 1000) * parseFloat(variant['width'] * widthMultiplier / 1000)) * variant.qty); 3075 } 3076 3077 cubicFeet += Number(variantMeters); 3078 }) 3079 3080 return Number(cubicFeet).toFixed(2); 3081 }, 3082 calculateTotalQTY() { 3083 let qty = 0; 3084 3085 this.pickedVariants.forEach(variant => { 3086 qty += variant.qty; 3087 }); 3088 3089 return qty; 3090 }, 3091 resetBasket() { 3092 this.showAccessories = false; 3093 this.pickedVariants = []; 3094 this.showStep1 = true; 3095 this.pickedAcc = []; 3096 this.priceObject.unitPrice = 0; 3097 this.priceObject.totalEx = 0; 3098 this.priceObject.vat = 0; 3099 this.priceObject.totalInc = 0; 3100 this.priceObject.corePrice = 0; 3101 3102 Array.from(this.$refs.counter).forEach(counter => { 3103 counter.value = 0; 3104 }); 3105 }, 3106 async addToBasket() { 3107 this.loading = true; 3108 var params = new FormData(); 3109 3110 params.append('CartCmd', "addMulti") 3111 3112 this.pickedVariants.forEach((variant, index) => { 3113 var productLoopCounter = index + 1; 3114 3115 if(variant.bundle) { 3116 params.append('ProductLoopCounter' + productLoopCounter, productLoopCounter); 3117 params.append('ProductID' + productLoopCounter, this.mainProduct.id); 3118 params.append('Quantity' + productLoopCounter, 1); 3119 params.append('EcomOrderLineFieldInput_BundleNo' + productLoopCounter, variant.bundleNo); 3120 } else { 3121 params.append('ProductLoopCounter' + productLoopCounter, productLoopCounter); 3122 params.append('ProductID' + productLoopCounter, this.mainProduct.id); 3123 params.append('VariantID' + productLoopCounter, variant.variantId); 3124 params.append('Quantity' + productLoopCounter, parseInt(variant.qty)); 3125 } 3126 }) 3127 const config = { 3128 method: 'POST', 3129 body: params 3130 } 3131 3132 this.productsAdded = this.pickedVariants.length; 3133 3134 const response = await fetch('@formAction' + '&redirect=false', config) 3135 3136 if (response.ok) { 3137 dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object. 3138 dataLayer.push({ 3139 event: "add_to_cart", 3140 ecommerce: { 3141 currency: "@GetString("Ecom:Product.Currency.Code")", 3142 value: this.priceObject.totalEx, 3143 items: this.ga4Products 3144 } 3145 }); 3146 3147 if(this.accessoriesList.length > 0) { 3148 this.showAccessories = true; 3149 this.loading = false; 3150 this.currentActiveMobileButton = 'order'; 3151 } else { 3152 window.location.href = "@formAction"; 3153 } 3154 3155 showStep1 = false; 3156 } else { 3157 console.log(response) 3158 } 3159 }, 3160 async addAccToBasket() { 3161 this.loading = true; 3162 var params = new FormData(); 3163 3164 params.append('CartCmd', "addMulti") 3165 3166 this.pickedAcc.forEach((variant, index) => { 3167 var productLoopCounter = index + 1; 3168 3169 params.append('ProductLoopCounter' + productLoopCounter, productLoopCounter); 3170 params.append('ProductID' + productLoopCounter, variant.id); 3171 params.append('Quantity' + productLoopCounter, variant.qty); 3172 }) 3173 const config = { 3174 method: 'POST', 3175 body: params 3176 } 3177 3178 const response = await fetch('@formAction' + '&redirect=false', config) 3179 3180 if (response.ok) { 3181 window.location.href = "@formAction"; 3182 } else { 3183 console.log(response) 3184 } 3185 } 3186 }, 3187 watch: { 3188 orderLoader(newValue, oldValue) { 3189 if(!newValue && !this.enquireProduct && this.isLoggedIn) { 3190 clearTimeout(this.orderButtonSpinner); 3191 document.querySelector('#product-order-button .product-order-button').style.display = "block"; 3192 document.querySelector('#product-order-button .loader').style.display = "none"; 3193 } 3194 } 3195 } 3196 }); 3197 </script> 3198 @SnippetEnd("JavaScripts") 3199 } 3200 3201 3202 3203 <script> 3204 let changingFavorite = false; 3205 const productNumber = "@productNumber"; 3206 const shouldRefreshFavorite = '@shouldRefreshFavorite' === 'true'; 3207 3208 function updateFavoriteUI(isAdding) { 3209 document.getElementById("favoriteAdd").style.display = isAdding ? "none" : ""; 3210 document.getElementById("favoriteRemove").style.display = isAdding ? "" : "none"; 3211 3212 let favorite = document.getElementsByClassName("favorite-qty")[0]; 3213 if (favorite) { 3214 let count = Number(favorite.getAttribute("data-count")); 3215 count += isAdding ? 1 : -1; 3216 count = Math.max(0, count); // Ensure count doesn't go below 0 3217 favorite.setAttribute("data-count", count); 3218 favorite.innerHTML = count; 3219 } 3220 } 3221 3222 async function changeFavoriteStatus(isAdding) { 3223 if (changingFavorite) return; 3224 changingFavorite = true; 3225 3226 const action = isAdding ? "addproducttofavoritelist" : "RemoveProductFromFavoriteList"; 3227 const url = `${location.protocol}//${location.host}?FavoriteCmd=${action}&ProductId=${productNumber}`; 3228 3229 try { 3230 const response = await fetch(url); 3231 if (!response.ok) throw new Error('Network response was not ok'); 3232 3233 if (shouldRefreshFavorite) { 3234 location.reload(); 3235 } else { 3236 updateFavoriteUI(isAdding); 3237 } 3238 } catch (error) { 3239 console.error('Error changing favorite status:', error); 3240 // Optionally, show an error message to the user 3241 } finally { 3242 changingFavorite = false; 3243 } 3244 } 3245 3246 function favoriteAdd() { 3247 changeFavoriteStatus(true); 3248 } 3249 3250 function favoriteRemove() { 3251 changeFavoriteStatus(false); 3252 } 3253 </script> 3254 3255 @functions { 3256 private string GetFileIcon(string fileUrl) 3257 { 3258 string extension = Path.GetExtension(fileUrl)?.ToLower(); 3259 3260 if (string.IsNullOrEmpty(extension)) 3261 return System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/file.svg")); 3262 3263 switch (extension) 3264 { 3265 case ".pdf": 3266 return System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/file.svg")); 3267 case ".jpg": 3268 case ".jpeg": 3269 case ".png": 3270 case ".gif": 3271 case ".webp": 3272 return @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/image.svg")); 3273 default: 3274 return System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/file.svg")); 3275 } 3276 } 3277 3278 public List<string> GetFilters(string GroupId) 3279 { 3280 List<string> filters = new List<string>(); 3281 3282 switch (GroupId) 3283 { 3284 case "group1": 3285 filters.Add("10"); 3286 filters.Add("11"); 3287 break; 3288 case "group32": 3289 filters.Add("17"); 3290 break; 3291 case "group52": 3292 filters.Add("12"); 3293 filters.Add("13"); 3294 filters.Add("14"); 3295 filters.Add("16"); 3296 break; 3297 case "group53": 3298 filters.Add("14"); 3299 break; 3300 case "group56": 3301 filters.Add("16"); 3302 break; 3303 case "group61": 3304 case "group67": 3305 filters.Add("12"); 3306 break; 3307 case "group63": 3308 filters.Add("13"); 3309 break; 3310 case "group73": 3311 filters.Add("18"); 3312 break; 3313 case "group129": 3314 filters.Add("6"); 3315 break; 3316 default: 3317 filters.Add(""); 3318 break; 3319 } 3320 3321 return filters; 3322 } 3323 3324 public string BuildFilterString(string group) { 3325 string filter = ""; 3326 List<string> ids = GetFilters(group); 3327 3328 if(group == "group126") { 3329 3330 } else if(group == "group129") { 3331 foreach(var id in ids) { 3332 filter += filter == "" ? "Types contains \""+id+"\"" : " or Types contains \""+id+"\""; 3333 } 3334 } else { 3335 foreach(var id in ids) { 3336 filter += filter == "" ? "WoodTypes contains \""+id+"\"" : " or WoodTypes contains \""+id+"\""; 3337 } 3338 } 3339 3340 return filter; 3341 } 3342 } 3343