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