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