Skip to main content
Error executing template "Designs/Keflico/eCom/Product/Product-pdf.cshtml"
System.InvalidOperationException: Sequence contains no elements
   at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
   at CompiledRazorTemplates.Dynamic.RazorEngine_21872e880ac941cf8d7ac227ec4b3f50.Execute() in D:\dynamicweb.net\Solutions\keflico.live\Files\Templates\Designs\Keflico\eCom\Product\Product-pdf.cshtml:line 330
   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 10 @{ 11 System.Web.HttpCookie lastProductIdCookie = new System.Web.HttpCookie("LastProductIdCookie"); 12 lastProductIdCookie.Value = GetString("Ecom:Product.ID"); 13 lastProductIdCookie.Expires = DateTime.MinValue; 14 HttpContext.Current.Response.Cookies.Add(lastProductIdCookie); 15 16 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()); 17 var productViewmodel = Dynamicweb.Ecommerce.ProductCatalog.ViewModelFactory.CreateView(viewmodelSettings, GetString("Ecom:Product.ID"), GetString("Ecom:Product.VariantId"), ""); 18 var combination = new VariantCombination(GetString("Ecom:Product.ID")); 19 IList<VariantCombination> productVariants = new List<VariantCombination>(); 20 var singleVariant = new VariantCombination(); 21 22 if(combination != null && combination.Product != null) { 23 productVariants = combination.Product.VariantCombinations; 24 } 25 26 if(productVariants.Count == 1) { 27 singleVariant = productVariants.FirstOrDefault(); 28 } 29 30 string CartPage = Pageview.Area.Item["CartPage"].ToString(); 31 string cartCount = GetGlobalValue("Global:eCommerce.Order.OrderLines.TotalProductQuantity"); 32 33 if(string.IsNullOrWhiteSpace(cartCount)) { 34 cartCount = "0"; 35 } 36 37 string EcomPage = Pageview.Area.Item["EcomPage"].ToString(); 38 string VariantsLookup = "/" + Pageview.Area.Item["Variantslookup"].ToString(); 39 string BundleLookup = "/" + Pageview.Area.Item["Bundlelookup"].ToString(); 40 string PriceLookup = "/" + Pageview.Area.Item["Pricelookup"].ToString(); 41 string productNumber = GetString("Ecom:Product.Number"); 42 string dbNumber = GetString("Ecom:Product:Field.ProductDbNumber.Value"); 43 string productId = GetString("Ecom:Product.ID"); 44 string productName = GetString("Ecom:Product.Name"); 45 string primaryGroup = GetString("Ecom:Product.PrimaryGroupID").ToLower(); 46 string formAction = "/" + CartPage; 47 string priceCurrencySymbol = GetString("Ecom:Product.Currency.Symbol"); 48 string loopCounter = GetString("Ecom:Product.LoopCounter"); 49 string inputName = "Quantity" + loopCounter; 50 var categories = GetLoop("AssociatedGroups"); 51 var mainCategory = (LoopItem)null; 52 var productLayout = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductLayout")) ? GetString("Ecom:Product:Field.ProductLayout") : primaryGroup; 53 54 string labelCode = GetString("Ecom:Product:Field.ProductPurchaseCode"); 55 56 var breadcrumbs = new List<LoopItem>(); 57 58 foreach(var cat in categories) { 59 if(cat.GetString("Ecom:Group.ID").ToLower() == primaryGroup) { 60 mainCategory = cat; 61 } 62 } 63 64 if(mainCategory != null) { 65 breadcrumbs.Add(mainCategory); 66 67 foreach(var child in mainCategory.GetLoop("Childgroups")) { 68 foreach(var category in categories) { 69 if(category.GetString("Ecom:Group.ID").ToLower() == child.GetString("Ecom:Group.ID").ToLower()) { 70 breadcrumbs.Add(category); 71 } 72 } 73 } 74 } 75 76 bool enquireProduct = false; 77 bool isSunDryProduct = System.Convert.ToBoolean(GetString("Ecom:Product:Field.ProductSundryItem")); 78 bool isBundleOnly = System.Convert.ToBoolean(GetString("Ecom:Product:Field.BundleOnly")); 79 80 var totalStock = GetDouble("Ecom:Product.Stock"); 81 totalStock += GetDouble("Ecom:Product:Field.StockSea.Value"); 82 83 int totalStockWareHouse = GetInteger("Ecom:Product.Stock"); 84 int totalStockSea = GetInteger("Ecom:Product:Field.StockSea.Value"); 85 86 if(primaryGroup != "group32") { 87 totalStock += GetDouble("Ecom:Product:Field.ProductPieceOnPurchase.Value"); 88 totalStockSea += GetInteger("Ecom:Product:Field.ProductPieceOnPurchase.Value"); 89 } 90 91 bool isSingleVariant = GetInteger("Ecom:Product.VariantCount") == 1 ? true : false; 92 93 double singleVariantStock = 0; 94 int singleVariantStockSea = 0; 95 96 if(isSingleVariant) { 97 singleVariantStock = singleVariant.Product.UnitStock; 98 singleVariantStockSea = !String.IsNullOrWhiteSpace(singleVariant.Product.ProductFieldValues.GetProductFieldValue("ProductPieceOnPurchase").Value.ToString()) ? Convert.ToInt32(singleVariant.Product.ProductFieldValues.GetProductFieldValue("ProductPieceOnPurchase").Value.ToString().Replace(",", "")) : 0; 99 100 totalStock += singleVariantStock; 101 totalStock += singleVariantStockSea; 102 } 103 104 string halfParcelAmount = GetString("Ecom:Product:Field.ProductNumberPerHalfPackage.Value.Raw"); 105 string completeParcelAmount = GetString("Ecom:Product:Field.ProductNumberPerPackage"); 106 107 string standardPrice = GetString("Ecom:Product.Price.PriceWithoutVAT"); 108 string standardPriceJS = !string.IsNullOrWhiteSpace(GetString("Ecom:Product.Discount.Price.PriceWithoutVAT.Value")) ? GetString("Ecom:Product.Discount.Price.PriceWithoutVAT.Value").Replace(",", ".") : "0"; 109 string halfPrice = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceHalfParcel")) ? GetString("Ecom:Product:Field.ProductPriceHalfParcel").Replace(".","-").Replace(",", ".").Replace("-", ",") : "0"; 110 string fullPrice = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceCompleteParcel")) ? GetString("Ecom:Product:Field.ProductPriceCompleteParcel").Replace(".","-").Replace(",", ".").Replace("-", ",") : "0"; 111 string bundlePrice = GetString("Ecom:Product:Field.ProductPriceBundle.Value").Replace(",", "-").Replace(".", ",").Replace("-", "."); 112 string bundlePriceJS = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceBundle.Value")) ? GetString("Ecom:Product:Field.ProductPriceBundle.Value").Replace(",", "") : "0"; 113 string above100Price = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceAbove100SQM")) ? GetString("Ecom:Product:Field.ProductPriceAbove100SQM") : "0"; 114 string above100PriceJS = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceAbove100SQM.Value.Raw")) ? GetString("Ecom:Product:Field.ProductPriceAbove100SQM.Value.Raw").Replace(",", ".") : "0"; 115 string above3000Price = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductPriceAbove3000M.Value")) ? GetString("Ecom:Product:Field.ProductPriceAbove3000M.Value").Replace(".", ",") : "0"; 116 117 string salesUnit = GetString("Ecom:Product.DefaultUnitName").ToLower(); 118 string salesUnitCode = GetString("Ecom:Product:Field.ProductLengthUnitCode"); 119 120 string configuratorUrl = GetString("Ecom:Product:Field.ProductConfiguratorUrl"); 121 122 string fscLink = Pageview.Area.Item["FSC"].ToString(); 123 string pefcLink = Pageview.Area.Item["PEFC"].ToString(); 124 string ecoNordicLink = Pageview.Area.Item["EcoNordic"].ToString(); 125 string cradleToCradleLink = Pageview.Area.Item["CradleToCradle"].ToString(); 126 127 bool hasNewDownloads = false; 128 bool hasDownloads = false; 129 130 foreach(var fileCat in GetLoop("ImageCategories")) { 131 132 if(fileCat.GetString("Category.SystemName").StartsWith("docView_") && fileCat.GetLoop("Category.Images").Count() > 0) { 133 hasNewDownloads = true; 134 hasDownloads = false; 135 break; 136 } 137 else if(fileCat.GetString("Category.SystemName") != "Images" && fileCat.GetLoop("Category.Images").Count() > 0) { 138 hasDownloads = true; 139 } 140 } 141 142 bool isloggedin = false; 143 144 if (!string.IsNullOrWhiteSpace(GetGlobalValue("Global:Extranet.UserName").ToString())) { 145 isloggedin = true; 146 } 147 148 if(GetString("Ecom:Product:Field.EcomProduct").Contains("forespoerg")) { 149 enquireProduct = true; 150 } 151 152 if(isSunDryProduct) { 153 enquireProduct = true; 154 } 155 156 if(labelCode.ToLower() == "skaffe" || labelCode.ToLower() == "relatordre") { 157 enquireProduct = true; 158 } 159 } 160 161 @SnippetStart("PageTemplate") 162 product-page 163 @SnippetEnd("PageTemplate") 164 165 <article class="module module-sand background-transparent product-details product-details-pdf"> 166 <div class="product-details__info"> 167 <div class="product-details__info-labels-pdf"> 168 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductCertification")) && GetString("Ecom:Product:Field.ProductCertification") != "-1") 169 { 170 string certification = GetString("Ecom:Product:Field.ProductCertification").ToLower(); 171 if (certification.Contains("pefc")) 172 { 173 <a href="@pefcLink" target="_blank"> 174 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/pefc-logo.svg"))) 175 { 176 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/pefc-logo.svg")) 177 } 178 </a> 179 } 180 else if (certification.Contains("fsc")) 181 { 182 <a href="@fscLink" target="_blank"> 183 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/fsc-logo.svg"))) 184 { 185 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/fsc-logo.svg")) 186 } 187 </a> 188 } 189 } 190 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductNordicEcoLabel")) && GetString("Ecom:Product:Field.ProductNordicEcoLabel") != "-1") 191 { 192 <a href="@ecoNordicLink" target="_blank"> 193 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/svanemaerket-logo.svg"))) 194 { 195 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/svanemaerket-logo.svg")) 196 } 197 </a> 198 } 199 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductCradleToCradle.Value")) && GetString("Ecom:Product:Field.ProductCradleToCradle.Value") != "-1" && GetString("Ecom:Product:Field.ProductCradleToCradle.Value") != "N/A") 200 { 201 <a href="@cradleToCradleLink" target="_blank"> 202 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/cradle-logo.svg"))) 203 { 204 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/cradle-logo.svg")) 205 } 206 </a> 207 } 208 </div> 209 </div> 210 <div class="product-details__info-meta"> 211 <ul class="product-details__info-meta-list"> 212 <li id="productNumber">@Translate("Translate_Product_Page_ProductNumber"): @productNumber</li> 213 @if (!string.IsNullOrWhiteSpace(dbNumber)) 214 { 215 <li>@GetString("Ecom:Product:Field.ProductDbNumber.Name"): @dbNumber</li> 216 } 217 </ul> 218 </div> 219 <h1 id="productName" class="product-details__info-title-pdf">@productName</h1> 220 <div class="product-details__info-size-pdf">@GetString("Ecom:Product:Field.Name2")</div> 221 </div> 222 223 <div> 224 @switch (labelCode.ToLower()) 225 { 226 case "prøver": 227 <div class="product-details__label-code label-code label-code--samples">@Translate("LabelCode_" + labelCode.ToLower().Replace("ø", "oe"))</div> 228 break; 229 case "skaffe": 230 case "relatordre": 231 <div class="product-details__label-code label-code">@Translate("LabelCode_" + labelCode.ToLower())</div> 232 break; 233 default: 234 break; 235 } 236 <div class="product-details-pdf__media"> 237 238 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product.ImageDefault.Clean"))) 239 { 240 <picture class="product-details__image-wrap"> 241 <img src="@GetString("Ecom:Product.ImageDefault.Clean")" alt="@productName" loading="lazy"> 242 </picture> 243 } 244 @foreach (var category in GetLoop("ImageCategories")) 245 { 246 if (category.GetString("Category.SystemName") == "Images") 247 { 248 foreach (var image in category.GetLoop("Category.Images")) 249 { 250 <picture class="product-details__image-wrap"> 251 <img src="@image.GetString("Ecom:Product:Detail.Image.Clean")" alt="@productName" loading="lazy"> 252 </picture> 253 } 254 } 255 } 256 257 </div> 258 </div> 259 <span class="disclaimer">(@Translate("Translate_Product_Page_ImageDisclaimer"))</span> 260 </article> 261 262 263 264 <article class="module module-sand background-transparent product-description product-description-pdf"> 265 <div class="tabs"> 266 267 <div id="description" class="tab__content tab__content--active" role="tabpanel" aria-hidden="false" aria-labelledby="description-tab"> 268 <h2>@Translate("Translate_Product_Page_ProductDescription")</h2> 269 <div class="tab__content-wrapper"> 270 <div class="rich-text"> 271 <div class="large-text">@GetString("Ecom:Product.LongDescription")</div> 272 @GetString("Ecom:Product:Field.ProductDescriptionShortLongText.Value") 273 </div> 274 @if (hasDownloads) 275 { 276 <div class="tab__content-sidebar"> 277 <strong>@Translate("Translate__Product_Page_Downloads")</strong> 278 <ul class="tab__content-list"> 279 @foreach (var fileCat in GetLoop("ImageCategories")) 280 { 281 if (fileCat.GetString("Category.SystemName") != "Images") 282 { 283 foreach (var file in fileCat.GetLoop("Category.Images")) 284 { 285 string fileLink = file.GetString("Ecom:Product:Detail.Image.Clean"); 286 string fileName = file.GetString("Ecom:Product:Detail.Name"); 287 288 if (String.IsNullOrWhiteSpace(fileName)) 289 { 290 fileName = Path.GetFileNameWithoutExtension(fileLink); 291 } 292 293 <li> 294 <a class="swap-link" href="@fileLink" data-link="@fileLink" target="_blank"> 295 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/file.svg"))) 296 { 297 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/file.svg")) 298 } 299 @fileName.Replace("-", " ").Replace("_", " ") 300 </a> 301 </li> 302 } 303 } 304 } 305 306 </ul> 307 </div> 308 } 309 </div> 310 </div> 311 <div id="technical" class="tab__content technical-specs tab__content--active" role="tabpanel" aria-hidden="false" aria-labelledby="technical-tab"> 312 <div class="rich-text"> 313 <h2>@Translate("Translate_Product_Page_TechnicalSpecifications")</h2> 314 315 @{ 316 CategoryFieldViewModel specs = productViewmodel.FieldDisplayGroups.Values.Where(x => x.Id.ToLower() == "technicalspecifications").FirstOrDefault(); 317 CategoryFieldViewModel specsWithInfo = productViewmodel.FieldDisplayGroups.Values.Where(x => x.Id.ToLower() == "technicalspecificationswithinfo").FirstOrDefault(); 318 } 319 320 @if (specs != null) 321 { 322 <ul class="product-description__technical-list"> 323 @foreach (var field in specs.Fields.Values) 324 { 325 if (field.Type.ToLower() == "list") 326 { 327 List<FieldOptionValueViewModel> fieldValue = (List<FieldOptionValueViewModel>)field.Value; 328 int i = 0; 329 330 if (fieldValue.First().Value.ToString() != "-1" && fieldValue.Where(x => x.Name.ToLower() == "ingen").Count() == 0) 331 { 332 <li class="product-description__technical-list-item"> 333 <div class="key">@field.Name</div> 334 <div class="value"> 335 <span class="line"> 336 @foreach (FieldOptionValueViewModel info in fieldValue) 337 { 338 if (i > 0) 339 { 340 @(", " + info.Name) 341 } 342 else 343 { 344 @info.Name 345 } 346 i++; 347 } 348 </span> 349 </div> 350 </li> 351 } 352 } 353 else 354 { 355 if (field.Value.ToString() != "0" && field.Value.ToString().ToLower() != "ingen") 356 { 357 <li class="product-description__technical-list-item"> 358 @if (field.Name.ToLower() == "number") 359 { 360 <div class="key">@Translate("Translate_General_ProductNumber")</div> 361 } 362 else if (field.Name.ToLower() == "weight") 363 { 364 <div class="key">@Translate("Translate_General_Weight")</div> 365 } 366 else 367 { 368 <div class="key">@field.Name</div> 369 } 370 <div class="value"> 371 <span class="line">@field.Value</span> 372 </div> 373 </li> 374 } 375 } 376 } 377 </ul> 378 } 379 @if (specsWithInfo != null) 380 { 381 <ul class="product-description__technical-list"> 382 @foreach (var field in specsWithInfo.Fields.Values) 383 { 384 if (field.Type.ToLower() == "list") 385 { 386 List<FieldOptionValueViewModel> fieldValue = (List<FieldOptionValueViewModel>)field.Value; 387 int i = 0; 388 389 if (fieldValue.First().Value.ToString() != "-1" && fieldValue.Where(x => x.Name.ToLower() == "ingen").Count() == 0) 390 { 391 <li class="product-description__technical-list-item"> 392 <div class="key">@field.Name</div> 393 <div class="value"> 394 <span class="line with-toolip"> 395 @foreach (FieldOptionValueViewModel info in fieldValue) 396 { 397 if (i > 0) 398 { 399 @(", " + info.Name) 400 } 401 else 402 { 403 @info.Name 404 } 405 i++; 406 } 407 <span class="tooltip"> 408 <span class="tooltip__icon"> 409 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 410 { 411 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 412 } 413 </span> 414 <span class="tooltip__text tooltip__text--left"> 415 <p>@Translate("Translate_Product_Spec_Info_" + field.SystemName)</p> 416 </span> 417 </span> 418 </span> 419 </div> 420 </li> 421 } 422 } 423 else 424 { 425 if (field.Value.ToString() != "0" && field.Value.ToString().ToLower() != "ingen") 426 { 427 <li class="product-description__technical-list-item"> 428 <div class="key">@field.Name</div> 429 <div class="value"> 430 <span class="line with-tooltip"> 431 @if (field.SystemName.ToLower() == "productdensity") 432 { 433 @field.Value.ToString().Replace(",", ".") 434 } 435 else 436 { 437 @field.Value 438 } 439 <span class="tooltip"> 440 <span class="tooltip__icon"> 441 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg"))) 442 { 443 @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/info.svg")) 444 } 445 </span> 446 <span class="tooltip__text tooltip__text--left"> 447 <p>@Translate("Translate_Product_Spec_Info_" + field.SystemName)</p> 448 </span> 449 </span> 450 </span> 451 </div> 452 </li> 453 } 454 } 455 } 456 </ul> 457 } 458 </div> 459 </div> 460 @if (hasNewDownloads) 461 { 462 <div id="documents" class="tab__content documents tab__content--active" role="tabpanel" aria-hidden="false" aria-labelledby="documents-tab"> 463 <div class="rich-text"> 464 <h2>@Translate("Translate_Product_Page_Documents")</h2> 465 466 <ul class="tab__content-list product-description__documents-list"> 467 @foreach (var fileCat in GetLoop("ImageCategories")) 468 { 469 if (fileCat.GetString("Category.SystemName").StartsWith("docView_") && fileCat.GetLoop("Category.Images").Count() > 0) 470 { 471 <li class="product-description__documents-list-item"> 472 <p class="product-description__documents-list-item-title">@fileCat.GetString("Category.Name")</p> 473 @foreach (var file in fileCat.GetLoop("Category.Images")) 474 { 475 string fileLink = file.GetString("Ecom:Product:Detail.Image.Clean"); 476 string fileName = file.GetString("Ecom:Product:Detail.Name"); 477 478 if (String.IsNullOrWhiteSpace(fileName)) 479 { 480 fileName = Path.GetFileNameWithoutExtension(fileLink); 481 } 482 483 if (!String.IsNullOrEmpty(fileLink)) 484 { 485 <a class="swap-link" href="@fileLink" data-link="@fileLink" target="_blank"> 486 @GetFileIcon(fileLink) 487 @fileName.Replace("-", " ").Replace("_", " ") 488 </a> 489 } 490 } 491 </li> 492 } 493 } 494 </ul> 495 </div> 496 </div> 497 } 498 499 <div id="more-info" class="tab__content more-info-content tab__content--active" role="tabpanel" aria-hidden="false" aria-labelledby="more-info-tab"> 500 <div class="rich-text"> 501 <h2>@Translate("Translate_Product_Page_MoreInformation")</h2> 502 @{ 503 CategoryFieldViewModel moreInfo = productViewmodel.FieldDisplayGroups.Values.Where(x => x.Id.ToLower() == "yderlig_information").FirstOrDefault(); 504 505 if (moreInfo != null && moreInfo.Fields != null) 506 { 507 var groups = moreInfo.Fields.Values; 508 509 foreach (var group in groups) 510 { 511 <strong>@Translate("Translate_ProductPage_" + group.SystemName)</strong> 512 @group.Value 513 } 514 } 515 } 516 </div> 517 </div> 518 </div> 519 520 </article> 521 522 @RenderSnippet("EnquireProductForm") 523 524 525 @SnippetEnd("Javascripts") 526 527 @if(productLayout != "group32" && productLayout != "group73" && productLayout != "group1") { 528 @SnippetStart("JavaScripts") 529 <script> 530 const orderButton = document.getElementById("product-order-button"); 531 532 if(orderButton) { 533 const form = orderButton.closest('form'); 534 535 orderButton.addEventListener('click', (event) => { 536 event.preventDefault(); 537 538 const formData = new FormData(form); 539 const qty = parseInt(formData.get('Quantity1')) 540 541 const price = @GetString("Ecom:Product.Discount.Price.PriceWithoutVAT.Value").Replace(",", "."); 542 const halfPrice = @GetString("Ecom:Product:Field.ProductPriceHalfParcel").Replace(",", ""); 543 const fullPrice = @GetString("Ecom:Product:Field.ProductPriceCompleteParcel").Replace(",", ""); 544 545 const halfParcel = @halfParcelAmount; 546 const fullParcel = @completeParcelAmount; 547 548 const salesUnit = "@salesUnit"; 549 const productLength = @GetString("Ecom:Product:Field.ProductLengthSale.Value.Raw"); 550 const lengthUnit = "@GetString("Ecom:Product:Field.ProductLengthUnitCode.Value")"; 551 552 let value = 0; 553 554 addToValue(qty) 555 556 function addToValue(remainingQty) { 557 if(salesUnit == "m") { 558 value += remainingQty * (productLength/1000) * price; 559 } else { 560 value += remainingQty * price; 561 } 562 } 563 564 dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object. 565 dataLayer.push({ 566 event: "add_to_cart", 567 ecommerce: { 568 currency: "@GetString("Ecom:Product.Currency.Code")", 569 value: value, 570 items: [ 571 { 572 item_id: "@productNumber", 573 item_name: "@productName", 574 quantity: qty 575 576 } 577 ] 578 } 579 }); 580 form.submit() 581 }); 582 } 583 </script> 584 @SnippetEnd("Javascripts") 585 } 586 587 @if(productLayout == "group32" || productLayout == "group73" || productLayout == "group1") { 588 <div id="orderFlowApp" class="product-modal" :class="{ 'not-logged-in': !isLoggedIn, 'upsell js-slide-in' : showAccessories || showStep1 }"> 589 <div v-if="showAccessories" class="product-modal__content product-modal__upsell-section"> 590 <div class="product-modal__content-top"> 591 <a href="/" class="navigation__logo"> 592 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/keflico_logo.svg"))) 593 { 594 <text>@System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/keflico_logo.svg"))</text> 595 } 596 </a> 597 <a href="@CartPage" class="navigation__cart"> 598 <svg xmlns="http://www.w3.org/2000/svg" width="21.773" height="19.513" viewBox="0 0 21.773 19.513"> 599 <g transform="translate(0.75 0.75)"> 600 <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"/> 601 <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"/> 602 <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"/> 603 <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"/> 604 <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"/> 605 <line x2="11.102" transform="translate(6.374 13.37)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 606 </g> 607 </svg> 608 <span class="cart-qty" data-count="@cartCount">@cartCount</span> 609 </a> 610 <a href="#" class="navigation__return product-modal__back-link" @@click="showAccessories = false"> 611 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20.006 12.445"> 612 <g transform="translate(-421.494 -889.275)"> 613 <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"/> 614 <line x2="17" transform="translate(423.5 895.5)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="2" /> 615 </g> 616 </svg> 617 </a> 618 <div v-if="accessoriesList.length > 0" class="breadcrumbs breadcrumbs--no-seperator"> 619 <ul class="breadcrumbs__list"> 620 <li class="breadcrumbs__item">1. Vælg produkter</li> 621 <li class="breadcrumbs__item active">2. Vælg tilbehør</li> 622 </ul> 623 </div> 624 </div> 625 <div class="upsell-section__top"> 626 <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> 627 <h2 class="upsell-section__subtitle">@Translate("Translate_OrderFlow_UpsellReminder")</h2> 628 </div> 629 <div class="upsell-section__card-list"> 630 631 <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)"> 632 <div class="counter-amount" v-if="getQty(product.Number) > 0">{{ getQty(product.Number) }}</div> 633 <div class="upsell__content-info"> 634 <div class="upsell__content-info-media"> 635 <a :href="'/@(EcomPage)&ProductID=' + product.Number" target="_blank"> 636 <img :src="'/admin/public/getimage.ashx?Image=' + product.DefaultImage.Value + '&width=175&height=175&Crop=0&Compression=100'" :alt="product.Name" loading="lazy"> 637 </a> 638 </div> 639 <div class="upsell__content-info-container"> 640 <div class="product-details__info-meta"> 641 <ul class="product-details__info-meta-list"> 642 <li>@Translate("Translate_Product_Page_ProductNumber") {{ product.Number }}</li> 643 @if(!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductDbNumber.Value"))) { 644 <li>@GetString("Ecom:Product:Field.ProductDbNumber.Name") {{ product.EAN }}</li> 645 } 646 </ul> 647 </div> 648 <div class="product-details__info-title">{{ product.Name }}</div> 649 <div class="product-details__info-size">{{ product.ProductFields.Name2.Value }}</div> 650 <div class="product-details__info-quantity"> 651 <div class="info-quantity" v-if="product.ProductFields.ProductNumberPerPackage.Value && product.ProductFields.ProductNumberPerPackage.Value > 0">{{ product.ProductFields.ProductNumberPerPackage.Value }} @Translate("Translate_General_Pieces")</div> 652 <div class="info-price" v-if="product.Price.Price">{{ prettyPrice(product.Price.Price) }} @priceCurrencySymbol</div> 653 </div> 654 <div class="product-details__info-actions" v-if="product.Price.Price && product.Price.Price > 0 && !product.VariantInfo.VariantInfo"> 655 <vue-counter :ref="'accCounter' + product.Number" :min="0" @@valueupdate="updateAccButton($event, product.Number)"></vue-counter> 656 <a href="#" class="confirm disabled" :ref="'confirmBtn' + product.Number" @@click="addAcc($event, product.Number)"> 657 <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"> 658 <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"/> 659 </svg> 660 <svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21.773 19.513"> 661 <g transform="translate(-87.571 -246.25)"> 662 <g transform="translate(88.321 247)"> 663 <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"/> 664 <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"/> 665 <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"/> 666 <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"/> 667 <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"/> 668 <line x2="11.102" transform="translate(6.374 13.37)" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 669 </g> 670 </g> 671 </svg> 672 </a> 673 </div> 674 </div> 675 </div> 676 </div> 677 </div> 678 <div class="notification-bar"> 679 <div class="notification-bar__text"> 680 @Translate("Translate_OrderFlow_HelpText") 681 <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> 682 </div> 683 </div> 684 <div class="product-modal__content-bottom"></div> 685 </div> 686 <div v-if="!showAccessories" class="product-modal__content"> 687 <div class="product-modal__content-top"> 688 <a href="/" class="navigation__logo"> 689 @if (System.IO.File.Exists(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/keflico_logo.svg"))) 690 { 691 <text>@System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/keflico_logo.svg"))</text> 692 } 693 </a> 694 <a href="@CartPage" class="navigation__cart"> 695 <svg xmlns="http://www.w3.org/2000/svg" width="21.773" height="19.513" viewBox="0 0 21.773 19.513"> 696 <g transform="translate(0.75 0.75)"> 697 <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"/> 698 <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"/> 699 <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"/> 700 <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"/> 701 <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"/> 702 <line x2="11.102" transform="translate(6.374 13.37)" fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/> 703 </g> 704 </svg> 705 <span class="cart-qty" data-count="@cartCount">@cartCount</span> 706 </a> 707 <a href="#" class="navigation__return product-modal__back-link" @@click="showAccessories = false"> 708 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20.006 12.445"> 709 <g transform="translate(-421.494 -889.275)"> 710 <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"/> 711 <line x2="17" transform="translate(423.5 895.5)" fill="none" stroke="#000" stroke-linecap="round" stroke-width="2" /> 712 </g> 713 </svg> 714 </a> 715 <div v-if="accessoriesList.length > 0" class="breadcrumbs breadcrumbs--no-seperator"> 716 <ul class="breadcrumbs__list"> 717 <li class="breadcrumbs__item active">1. Vælg produkter</li> 718 <li class="breadcrumbs__item">2. Vælg tilbehør</li> 719 </ul> 720 </div> 721 </div> 722 <div class="product-modal__content-info"> 723 <div class="product-modal__content-info-media"> 724 <picture v-if="mainProduct.image"> 725 <img :src="mainProduct.image" :alt="mainProduct.name" loading="lazy"> 726 </picture> 727 </div> 728 <div class="product-modal__content-info-container"> 729 <div class="product-details__info-meta"> 730 <ul class="product-details__info-meta-list"> 731 <li>{{ mainProduct.number }}</li> 732 @if(!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductDbNumber.Value"))) { 733 <li>{{ mainProduct.dbNumber }}</li> 734 } 735 </ul> 736 </div> 737 <div class="product-details__info-title">{{ mainProduct.name }}</div> 738 <div class="product-details__info-size"> 739 @if(primaryGroup == "group1") { 740 <text>{{ mainProduct.size }}</text> 741 } else { 742 <div class="product-details__info-size-thickness"> 743 <strong>@Translate("Translate_OrderFlow_Thickness")</strong> 744 {{ mainProduct.thickness }} {{ thicknessUnit }} 745 </div> 746 <div class="product-details__info-size-width"> 747 <strong>@Translate("Translate_OrderFlow_Width")</strong> 748 {{ mainProduct.width }} {{ widthUnit }} 749 </div> 750 } 751 </div> 752 </div> 753 </div> 754 <div class="product-modal__content-product-stock"> 755 @if(primaryGroup == "group1") { 756 <div class="tabs"> 757 <div v-if="bundles.length > 0" class="tabs__nav"> 758 <ul role="tablist" class="tabs__list" data-action="tabs"> 759 <li role="none" class="tabs__item"> 760 @Translate("Translate_OrderFlow_Choose_ProductType") 761 </li> 762 <li role="none" class="tabs__item"> 763 <a href="#helbundt" @@click.prevent="showBundles = true;" class="tab" :class="{ 'tab--active': showBundles }" role="tab" id="helbundt-tab" aria-controls="helbundt" aria-selected="true"> 764 <span>@Translate("Translate_OrderFlow_Bundle")</span> 765 </a> 766 </li> 767 @if(!isBundleOnly) { 768 <li role="none" class="tabs__item"> 769 <a href="#anbrud" @@click.prevent="showBundles = false;" class="tab" :class="{ 'tab--active': !showBundles }" role="tab" id="anbrud-tab" aria-controls="anbrud" aria-selected="false"> 770 <span>@Translate("Translate_OrderFlow_Pieces")</span> 771 </a> 772 </li> 773 } 774 </ul> 775 </div> 776 <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"> 777 <div class="product-modal__stock"> 778 <div class="product-modal__stock-line product-modal__stock-line--header bundle-header"> 779 <div class="product-modal__stock-cell mobile-cell filler"></div> 780 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_BundleNo")</div> 781 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Thickness")</div> 782 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Width")</div> 783 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Length")</div> 784 <div v-if="!enquireProduct" class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_InStock")</div> 785 <div v-if="!enquireProduct" class="product-modal__stock-cell"> 786 @Translate("Translate_OrderFlow_OnWayHome"): 787 <span v-html="onTheWayHome"></span> 788 </div> 789 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted">@Translate("Translate_OrderFlow_ChooseAmount")</div> 790 </div> 791 <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)"> 792 <div class="stock-bundle__header"> 793 <div class="product-modal__stock-cell mobile-cell"> 794 <div class="cell-radio counter-amount bundle-amount" v-html="getQty(bundle.bundleNo) > 0 ? getQty(bundle.bundleNo) : ''" :data-id="bundle.bundleNo"></div> 795 </div> 796 <div class="product-modal__stock-cell"> 797 <span class="bundle-toggle" @@click="toggleBundle(bundle.bundleNo)"> 798 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12.446 7.223"> 799 <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"/> 800 </svg> 801 </span> 802 {{ bundle.bundleNo }} 803 </div> 804 <div class="product-modal__stock-cell mobile-cell"></div> 805 <div class="product-modal__stock-cell mobile-cell"></div> 806 </div> 807 <div class="stock-bundle__body"> 808 <div class="stock-bundle__body-columns"> 809 <div class="stock-bundle__cell"> 810 @Translate("Translate_OrderFlow_Dimensions") 811 </div> 812 <div class="stock-bundle__cell"> 813 @Translate("Translate_OrderFlow_InStock") 814 </div> 815 </div> 816 <div v-for="(product, index) in bundle.productsInBundle" class="stock-bundle__stick"> 817 <div class="stock-bundle__cell mobile-cell"> 818 <div class="cell-stack"> 819 @Translate("Translate_OrderFlow_Thickness_Short") {{ product.thickness }} {{ thicknessUnit }} 820 </div> 821 <div class="cell-stack"> 822 @Translate("Translate_OrderFlow_Width_Short") {{ product.width }} {{ widthUnit }} 823 </div> 824 <div class="cell-stack"> 825 @Translate("Translate_OrderFlow_Length_Short") {{ product.length.replace(",", "") }} {{ lenghtUnit }} 826 </div> 827 </div> 828 <div class="stock-bundle__cell desktop-cell">{{ product.thickness }} {{ thicknessUnit }}</div> 829 <div class="stock-bundle__cell desktop-cell">{{ product.width }} {{ widthUnit }}</div> 830 <div class="stock-bundle__cell desktop-cell">{{ product.length.replace(",", "") }} {{ lenghtUnit }}</div> 831 <div v-if="!enquireProduct" class="stock-bundle__cell">{{ product.stock.units > 500 ? '+500' : product.stock.units }} @Translate("Translate_General_Pieces")</div> 832 <div v-if="!enquireProduct" class="stock-bundle__cell desktop-cell"></div> 833 <div v-if="isLoggedIn && !enquireProduct" class="stock-bundle__cell product-modal__stock-cell--highlighted desktop-cell"> 834 <vue-counter v-if="index == 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" :max="1" @@valueupdate="addBundleToTempCart($event, bundle.bundleNo)" /> 835 </div> 836 </div> 837 </div> 838 </div> 839 <div v-if="loadingBundles" class="bundle-loading"> 840 <span class="loader"></span> 841 </div> 842 </div> 843 </div> 844 @if(!isBundleOnly) { 845 <div id="anbrud" class="tab__content" :class="{ 'tab__content--active': !showBundles }" role="tabpanel" aria-hidden="false" aria-labelledby="anbrud-tab"> 846 <div class="product-modal__stock"> 847 <div class="product-modal__stock-line product-modal__stock-line--header"> 848 <div class="product-modal__stock-cell filler mobile-cell" v-if="isLoggedIn && !enquireProduct"></div> 849 <div class="product-modal__stock-cell mobile-cell">@Translate("Translate_OrderFlow_Dimension")</div> 850 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Thickness")</div> 851 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Width")</div> 852 <div class="product-modal__stock-cell desktop-cell">@Translate("Translate_OrderFlow_Length")</div> 853 <div v-if="!enquireProduct" class="product-modal__stock-cell">@Translate("Translate_OrderFlow_InStock")</div> 854 <div v-if="!enquireProduct" class="product-modal__stock-cell"> 855 @Translate("Translate_OrderFlow_OnWayHome"): 856 <span v-html="onTheWayHome"></span> 857 </div> 858 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted">@Translate("Translate_OrderFlow_ChooseAmount")</div> 859 </div> 860 <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"> 861 <div class="product-modal__stock-cell quantity" v-if="isLoggedIn && !enquireProduct"> 862 <div class="cell-radio counter-amount" :data-id="variant.id" v-html="getQty(variant.id) > 0 ? getQty(variant.id) : ''"></div> 863 </div> 864 <div class="product-modal__stock-cell cell-stack"> 865 <span class="stack">@Translate("Translate_OrderFlow_Thickness_Short") {{ variant.size.thickness }} {{ thicknessUnit }}</span> 866 <span class="stack">@Translate("Translate_OrderFlow_Width_Short") {{ variant.size.width }} {{ widthUnit }}</span> 867 <span class="stack">@Translate("Translate_OrderFlow_Length_Short") {{ variant.size.length }} {{ lenghtUnit }}</span> 868 </div> 869 <div class="product-modal__stock-cell desktop-cell">{{ variant.size.thickness }} {{ thicknessUnit }}</div> 870 <div class="product-modal__stock-cell desktop-cell">{{ variant.size.width }} {{ widthUnit }}</div> 871 <div class="product-modal__stock-cell desktop-cell">{{ variant.size.length }} {{ lenghtUnit }}</div> 872 <div v-if="!enquireProduct" class="product-modal__stock-cell">{{ calculateStockLevel(variant.stock.warehouse) }} @Translate("Translate_General_Pieces") </div> 873 <div v-if="!enquireProduct" class="product-modal__stock-cell"></div> 874 <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}"> 875 <vue-counter v-if="variant.stock.warehouse + variant.stock.purchase > 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" @@valueupdate="addToTempCart($event, variant.id)" /> 876 </div> 877 </div> 878 </div> 879 </div> 880 } 881 </div> 882 } else { 883 <div class="product-modal__stock"> 884 <div class="product-modal__stock-line product-modal__stock-line--header product-modal__stock-line--terrace"> 885 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell filler mobile-cell"></div> 886 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_Length")</div> 887 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_InStock")</div> 888 <div class="product-modal__stock-cell">@Translate("Translate_OrderFlow_OnWayHome")</div> 889 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell product-modal__stock-cell--highlighted">@Translate("Translate_OrderFlow_ChooseAmount")</div> 890 </div> 891 <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"> 892 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__stock-cell quantity"> 893 <div class="cell-radio counter-amount" :data-id="variant.id" v-html="getQty(variant.id) > 0 ? getQty(variant.id) : ''"></div> 894 </div> 895 <div class="product-modal__stock-cell">{{ variant.size.length }} {{ lenghtUnit }}</div> 896 <div class="product-modal__stock-cell">{{ calculateStockLevel(variant.stock.warehouse) }}</div> 897 @if(primaryGroup == "group73") { 898 <div class="product-modal__stock-cell">{{ variant.stock.purchase > 0 ? "@Translate("Translate_General_Yes")" : "@Translate("Translate_General_No")" }}</div> 899 } else { 900 <div class="product-modal__stock-cell">{{ calculateStockLevel(variant.stock.sea) }}</div> 901 } 902 903 @if(primaryGroup == "group32") { 904 <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}"> 905 <vue-counter v-if="variant.stock.warehouse + variant.stock.sea > 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" @@valueupdate="addToTempCart($event, variant.id)" /> 906 </div> 907 } else { 908 <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}"> 909 <vue-counter v-if="variant.stock.warehouse + variant.stock.purchase > 0 && isLoggedIn && !enquireProduct" ref="counter" :min="0" @@valueupdate="addToTempCart($event, variant.id)" /> 910 </div> 911 } 912 </div> 913 </div> 914 } 915 </div> 916 <div class="notification-bar"> 917 <div class="notification-bar__text"> 918 @Translate("Translate_OrderFlow_HelpText") 919 <a href="#product-inquire-form" id="product-orderflow-inquire-button" data-action="open-content" data-target="#enquireForm">@Translate("Translate_Product_Page_EnquireButton")</a> 920 </div> 921 </div> 922 <div class="product-modal__content-bottom"></div> 923 </div> 924 <div class="product-modal__side"> 925 <div class="product-modal__side-top"> 926 <a href="#" class="product-modal__back-link" @@click="showAccessories = false"> 927 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20.006 12.445"> 928 <g transform="translate(-421.494 -889.275)"> 929 <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"/> 930 <line x2="17" transform="translate(423.5 895.5)" fill="none" stroke="#fff" stroke-linecap="round" stroke-width="2" /> 931 </g> 932 </svg> 933 @Translate("Translate_ProductFlow_Back") 934 </a> 935 <div class="product-modal__basket-icon"> 936 <span class="icon"></span> 937 <span class="badge"></span> 938 </div> 939 </div> 940 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__side-basket" :class="{ 'popup--active': showMobileBasket }"> 941 <div v-if="pickedVariants.length > 0" class="side-basket__product"> 942 <div class="side-basket__product-head"> 943 <div class="product-name">{{ mainProduct.name }}</div> 944 <div class="product-price"> 945 @if(primaryGroup == "group1") { 946 <span v-if="!loadingPrice">{{ prettyPrice(priceObject.unitPrice) }} @priceCurrencySymbol / {{ mainProduct.prices.unit.label }}</span> 947 <span v-if="loadingPrice" class="loader"></span> 948 } else { 949 <span v-if="!loadingPrice">{{ prettyPrice(priceObject.unitPrice) }} @priceCurrencySymbol / @Translate("Translate_General_RunningMeters")</span> 950 <span v-if="loadingPrice" class="loader"></span> 951 } 952 </div> 953 <div class="product-toggle" @@click="toggleState"> 954 <span class="close">@Translate("Translate_General_Close")</span> 955 <span class="open">@Translate("Translate_General_Open")</span> 956 </div> 957 </div> 958 <div v-for="(line, index) in sortedPickedVariants" class="side-basket__product-variant"> 959 @if(primaryGroup == "group1") { 960 <template v-if="line.bundle"> 961 <div class="size size__header"> 962 <strong>@Translate("Translate_OrderFlow_BundleNo") {{ line.bundleNo }}</strong> 963 </div> 964 <template v-for="variant in line.productsInBundle"> 965 <div class="size"> 966 <template v-if="lenghtUnit == 'mm'"> 967 {{ variant.thickness.replace(".", ",") }} x {{ variant.width }} x {{ variant.length.replace(",", "") }}mm 968 </template> 969 <template v-else> 970 {{ variant.thickness.replace(".", ",") }}{{ thicknessUnit }} x {{ variant.width }}{{ widthUnit }} x {{ variant.length.replace(",", "") }} {{ lenghtUnit }} 971 </template> 972 </div> 973 <div class="quantity">{{ prettyNumber(variant.stock.units) }} @Translate("Translate_General_Pieces")</div> 974 </template> 975 </template> 976 <template v-else> 977 <div class="size size__header"> 978 <strong>@Translate("Translate_OrderFlow_Pieces")</strong> 979 </div> 980 <div class="size">{{ line.thickness }} x {{ line.width }} x {{ line.length }}mm</div> 981 <div class="quantity">{{ prettyNumber(line.qty) }} @Translate("Translate_General_Pieces")</div> 982 </template> 983 } else { 984 <div class="size">{{ mainProduct.thickness }} x {{ mainProduct.width }} x {{ line.length }}mm</div> 985 <div class="quantity">{{ prettyNumber(line.qty) }} @Translate("Translate_General_Pieces")</div> 986 } 987 </div> 988 <div class="side-basket__product-total"> 989 <div class="product-meters"> 990 @Translate("Translate_General_InTotal") 991 <span v-if="mainProduct.prices.unit.label == 'm²' || mainProduct.prices.unit.label == 'm'">{{ prettyNumber(calculateMeters()) }} {{ mainProduct.prices.unit.label }}</span> 992 <span v-else>{{ prettyNumber(calculatePieces()) }} @Translate("Translate_General_Pieces")</span> 993 </div> 994 <div class="product-square-meters"> 995 @Translate("Translate_General_TranslatesTo") 996 <span v-if="mainProduct.prices.unit.label == 'm²' || mainProduct.prices.unit.label == 'm'">{{ prettyNumber(calculateSquareMeters()) }} m<sup>2</sup></span> 997 <span v-else-if="mainProduct.prices.unit.label == 'm³'">{{ prettyNumber(calculateCubicMeters(), 3) }} m<sup>3</sup></span> 998 <span v-else-if="mainProduct.prices.unit.label == 'kbf'">{{ prettyNumber(calculateCubicFeet()) }} kbf</span> 999 </div> 1000 </div> 1001 </div> 1002 <div v-for="product in pickedAcc" class="side-basket__product"> 1003 <div class="side-basket__product-head"> 1004 <div class="product-name">{{ product.name }}</div> 1005 <div class="product-price">{{ prettyPrice(product.price.unitPrice) }}</div> 1006 <div class="product-toggle" @@click="toggleState"> 1007 <span class="close">@Translate("Translate_General_Close")</span> 1008 <span class="open">@Translate("Translate_General_Open")</span> 1009 </div> 1010 </div> 1011 <div class="side-basket__product-variant"> 1012 <div class="size">{{ product.name2 }}</div> 1013 <div class="quantity">{{ product.qty }} @Translate("Translate_General_Pieces")</div> 1014 </div> 1015 </div> 1016 <div class="side-basket__summary"> 1017 <div class="price-raw"> 1018 @Translate("Translate_General_PriceWithoutVat") 1019 <span v-if="!loadingPrice">{{ prettyPrice(priceObject.totalEx) }} @priceCurrencySymbol</span> 1020 <span v-if="loadingPrice" class="loader"></span> 1021 </div> 1022 <div class="vat"> 1023 @Translate("Translate_General_Vat") 1024 <span v-if="!loadingPrice">{{ prettyPrice(priceObject.vat) }} @priceCurrencySymbol</span> 1025 <span v-if="loadingPrice" class="loader"></span> 1026 </div> 1027 <div class="price-vat"> 1028 @Translate("Translate_General_PriceWithVat") 1029 <span v-if="!loadingPrice">{{ prettyPrice(priceObject.totalInc) }} @priceCurrencySymbol</span> 1030 <span v-if="loadingPrice" class="loader"></span> 1031 </div> 1032 </div> 1033 1034 <div v-if="isLoggedIn && !enquireProduct" class="product-modal__floating-cart"> 1035 <div class="floating-cart__bar" :class="{ 'active-bar': showMobileActionBar }"> 1036 <div v-if="showMobileActionControls" class="product-modal__stock-control floating-cart__stock-control counter"> 1037 <button id="amount-subtract" @@click="currentMobileQty > 0 ? currentMobileQty-- : currentMobileQty = 0" class="substract"><span>−</span></button> 1038 <input id="amount-input" @@keyup="editMobileQty" class="amount" pattern="[0-9.]+" type="tel" data-min="0" min="0" :value="currentMobileQty"> 1039 <button id="amount-add" @@click="currentMobileQty++" class="add"><span>+</span></button> 1040 </div> 1041 <div v-if="!showMobileActionControls" @@click="showMobileBasket = !showMobileBasket" class="floating-cart__toggle cart-toggle"> 1042 <span v-if="!showMobileBasket" class="cart-toggle--open">Se kurv</span> 1043 <span v-if="showMobileBasket" class="cart-toggle--close">Luk kurv</span> 1044 </div> 1045 <div class="floating-cart__btn-wrapper"> 1046 <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> 1047 <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> 1048 <button v-if="currentActiveMobileButton == 'next'" class="btn btn-secondary floating-cart__btn btn--next" @@click="addToBasket"> 1049 <template v-if="!loading">Gå videre</template> 1050 <span v-if="loading" class="loader"></span> 1051 </button> 1052 <button v-if="currentActiveMobileButton == 'order' && !showAccessories" class="btn btn-secondary floating-cart__btn btn--order" @@click="addToBasket"> 1053 <template v-if="!loading">Gå til bestilling</template> 1054 <span v-if="loading" class="loader"></span> 1055 </button> 1056 <button v-if="currentActiveMobileButton == 'order' && showAccessories" class="btn btn-secondary floating-cart__btn btn--order" @@click="addAccToBasket"> 1057 <template v-if="!loading">Gå til bestilling</template> 1058 <span v-if="loading" class="loader"></span> 1059 </button> 1060 </div> 1061 </div> 1062 </div> 1063 1064 <div class="side-basket__actions"> 1065 <template v-if="!showAccessories"> 1066 <button type="button" @@click="resetBasket" class="btn btn-link btn-link--underlined" :class="{ disabled: pickedVariants.length == 0 }">@Translate("Translate_General_EmptyCart")</button> 1067 <button type="button" :class="{ disabled: pickedVariants.length == 0 }" @@click="addToBasket" class="btn btn-secondary"> 1068 <template v-if="!loading">@Translate("Translate_General_AddToCart")</template> 1069 <span v-if="loading" class="loader"></span> 1070 </button> 1071 </template> 1072 <template v-else> 1073 <a href="@CartPage" class="btn btn-link btn-link--underlined">@Translate("Translate_General_GoToCheckout")</a> 1074 <button type="button" @@click="addAccToBasket" class="btn btn-secondary" :class="{ disabled: pickedAcc.length == 0}"> 1075 <template v-if="!loading">@Translate("Translate_OrderFlow_AddAccessories")</template> 1076 <span v-if="loading" class="loader"></span> 1077 </button> 1078 </template> 1079 </div> 1080 </div> 1081 </div> 1082 </div> 1083 1084 1085 @SnippetStart("JavaScripts") 1086 <script> 1087 dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object. 1088 dataLayer.push({ 1089 event: "view_item", 1090 ecommerce: { 1091 items: [ 1092 { 1093 item_id: "@productNumber", 1094 item_name: "@productName", 1095 } 1096 ] 1097 } 1098 }); 1099 1100 </script> 1101 <script async> 1102 const vueCounter = { 1103 name: "VueCounter", 1104 props: ['min', 'max'], 1105 data: () => { 1106 return { 1107 value: 0, 1108 hasError: false, 1109 } 1110 }, 1111 methods: { 1112 substract() { 1113 if(this.min || this.min === 0) { 1114 if(this.value > this.min) { 1115 this.value-- 1116 } else { 1117 this.handleError(); 1118 } 1119 } else { 1120 this.value-- 1121 } 1122 }, 1123 add() { 1124 if(this.max) { 1125 if(this.value < this.max) { 1126 this.value++ 1127 } else { 1128 this.handleError(); 1129 } 1130 } else { 1131 this.value++ 1132 } 1133 }, 1134 handleError() { 1135 this.hasError = true; 1136 setTimeout(() => { 1137 this.hasError = false; 1138 }, 1000); 1139 } 1140 }, 1141 watch: { 1142 value(newValue, oldValue) { 1143 this.$emit('valueupdate', newValue) 1144 } 1145 }, 1146 template: `<div class="vue-counter" :class="hasError ? 'counter--error' : ''" > 1147 <a class="counter__sub" @@click="substract">-</a> 1148 <input class="counter__value" type="tel" v-model="value" :disabled="value == max ? true : false"> 1149 <a class="counter__add" @@click="add">+</a> 1150 </div>` 1151 } 1152 1153 new Vue({ 1154 el: '#orderFlowApp', 1155 name: 'Bestillings flow', 1156 components: { 1157 'vue-counter': vueCounter 1158 }, 1159 computed: { 1160 lenghtUnit() { 1161 return this.sizeUnitValues[this.mainProduct.sizeUnits.length]; 1162 }, 1163 widthUnit() { 1164 return this.sizeUnitValues[this.mainProduct.sizeUnits.width]; 1165 }, 1166 thicknessUnit() { 1167 return this.sizeUnitValues[this.mainProduct.sizeUnits.thickness]; 1168 }, 1169 sortedVariants() { 1170 return this.variants.sort((a,b) => { 1171 if (a.length !== b.length) { 1172 return a.length - b.length; 1173 } else if (a.width !== b.width) { 1174 return a.width - b.width; 1175 } else { 1176 return a.thickness - b.thickness; 1177 } 1178 }).filter(x => x.size.length > 0); 1179 }, 1180 sortedBundles() { 1181 let newBundleList = []; 1182 1183 if(this.bundles.length > 0) { 1184 this.bundles.forEach(bundle => { 1185 const index = newBundleList.findIndex(x => x.bundleNo == bundle.bundleNo); 1186 1187 if(parseFloat(bundle.stock.warehouse) > 0) { 1188 if(index > -1) { 1189 newBundleList[index].productsInBundle.push( 1190 { 1191 length: bundle.length, 1192 width: bundle.width, 1193 thickness: bundle.thickness, 1194 stock: bundle.stock 1195 } 1196 ) 1197 } else { 1198 this.toggleBundles.push({ 1199 bundle: bundle.bundleNo, 1200 expanded: true, 1201 }); 1202 newBundleList.push( 1203 { 1204 bundleNo: bundle.bundleNo, 1205 productsInBundle: [ 1206 { 1207 length: bundle.length, 1208 width: bundle.width, 1209 thickness: bundle.thickness, 1210 stock: bundle.stock 1211 } 1212 ] 1213 } 1214 ) 1215 } 1216 } 1217 }) 1218 } 1219 1220 return newBundleList; 1221 }, 1222 sortedPickedVariants() { 1223 return this.pickedVariants.sort((x,y) => x.bundle - y.bundle); 1224 }, 1225 ga4Products() { 1226 let list = []; 1227 1228 this.sortedPickedVariants.forEach(product => { 1229 let productObject = {}; 1230 1231 if(product.bundle) { 1232 productObject.item_name = this.mainProduct.name; 1233 productObject.item_id = product.bundleNo; 1234 productObject.productsInBundle = product.productsInBundle; 1235 } else { 1236 productObject.item_id = product.id, 1237 productObject.item_name = this.mainProduct.name, 1238 productObject.item_variant = product.variantId, 1239 productObject.quantity = product.qty, 1240 productObject.price = this.priceObject.unitPrice 1241 } 1242 1243 list.push(productObject) 1244 }) 1245 1246 return list; 1247 }, 1248 onTheWayHome() { 1249 if(this.variants.filter(x => x.stock.pieces > 0).length > 0) { 1250 return "@Translate("Translate_General_Yes")"; 1251 } 1252 1253 return "@Translate("Translate_General_No")"; 1254 }, 1255 accessoriesList() { 1256 let list = []; 1257 1258 if(this.relatedProducts && this.relatedProducts.length > 0) { 1259 this.relatedProducts.forEach(product => { 1260 let group = this.relatedGroups.find(x => x.Id == "RELGRP1"); 1261 1262 if(group && group.Products.find(x => x.ProductId == product.Id)) { 1263 list.push(product); 1264 } 1265 }) 1266 } 1267 1268 return list; 1269 } 1270 }, 1271 mounted() { 1272 this.getVariants(@(productNumber)) 1273 @if(primaryGroup == "group1") { 1274 <text> 1275 this.lookUpBundle(@(productNumber)); 1276 </text> 1277 } 1278 1279 this.getAccessories(); 1280 1281 if(!this.enquireProduct && this.isLoggedIn) { 1282 this.orderButtonSpinner = setTimeout(() => { 1283 document.querySelector('#product-order-button .loader').style.display = "inline-block"; 1284 }, 3000); 1285 } else { 1286 document.querySelector('#product-order-button .product-order-form-button').style.display = "block"; 1287 } 1288 }, 1289 data() { 1290 return { 1291 isLoggedIn: @isloggedin.ToString().ToLower(), 1292 enquireProduct: @enquireProduct.ToString().ToLower(), 1293 lang: document.querySelector('html').getAttribute('data-lang'), 1294 loading: false, 1295 loadingPrice: false, 1296 loadingBundles: false, 1297 loadingVariants: false, 1298 orderLoader: true, 1299 orderButtonSpinner: null, 1300 timeOut: null, 1301 priceObject: { 1302 unitPrice: 0, 1303 totalEx: 0, 1304 vat: 0, 1305 totalInc: 0, 1306 corePrice: 0 1307 }, 1308 customerNumber: '@GetGlobalValue("Global:Extranet.CustomerNumber")', 1309 sizeUnitValues: { 1310 'ZOLL/00': '@Translate("Translate_Product_SizeUnit_Inch")', 1311 'ZOLL/01': '@Translate("Translate_Product_SizeUnit_Inch")', 1312 'FUß/00': '@Translate("Translate_Product_SizeUnit_Foot")', 1313 'FUß/01': '@Translate("Translate_Product_SizeUnit_Foot")', 1314 'MM/00': '@Translate("Translate_Product_SizeUnit_Millimeter")', 1315 'MM/01': '@Translate("Translate_Product_SizeUnit_Millimeter")' 1316 }, 1317 mainProduct: { 1318 name: '@productName', 1319 id: @productNumber, 1320 number: '@Translate("Translate_Product_Page_ProductNumber"): @productNumber', 1321 dbNumber: '@GetString("Ecom:Product:Field.ProductDbNumber.Name"): @GetString("Ecom:Product:Field.ProductDbNumber.Value")', 1322 width: '@GetString("Ecom:Product:Field.ProductWidthSale.Value.Raw")', 1323 thickness: '@GetString("Ecom:Product:Field.ProductThicknessSale.Value.Raw")', 1324 image: '@GetString("Ecom:Product.ImageDefault.Clean")', 1325 size: @Json.Encode(GetString("Ecom:Product:Field.Name2")), 1326 prices: { 1327 unit: { 1328 code: "@salesUnitCode", 1329 label: "@salesUnit" 1330 }, 1331 standard: @standardPriceJS, 1332 above100: @above100PriceJS, 1333 bundle: @bundlePriceJS 1334 }, 1335 sizeUnits: { 1336 length: '@GetString("Ecom:Product:Field.ProductLengthUnitCode.Value")', 1337 width: '@GetString("Ecom:Product:Field.ProductWidthUnitCode.Value")', 1338 thickness: '@GetString("Ecom:Product:Field.ProductThicknessUnitCode.Value")', 1339 } 1340 }, 1341 relatedGroups: null, 1342 relatedProducts: null, 1343 variants: [], 1344 showAccessories: false, 1345 showStep1: false, 1346 showBundles: false, 1347 bundles: [], 1348 pickedVariants: [], 1349 productsAdded: 0, 1350 pickedAcc: [], 1351 currentMobileProduct: '', 1352 showMobileBasket: false, 1353 showMobileActionBar: false, 1354 showMobileActionControls: false, 1355 currentActiveMobileButton: 'none', 1356 currentMobileQty: 0, 1357 toggleBundles: [], 1358 } 1359 }, 1360 methods: { 1361 doesCookieExist(cookieName) { 1362 const cookies = document.cookie.split('; '); 1363 const cookieExists = cookies.some(cookie => cookie.startsWith(cookieName + '=')); 1364 return cookieExists; 1365 }, 1366 setCookie(name, value, days) { 1367 let expires = ""; 1368 if (days) { 1369 const date = new Date(); 1370 date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); 1371 expires = "; expires=" + date.toUTCString(); 1372 } 1373 document.cookie = name + "=" + (value || "") + expires + "; path=/"; 1374 }, 1375 deleteCookie(name) { 1376 document.cookie = name + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; 1377 }, 1378 async getAccessories() { 1379 await fetch(`/dwapi/ecommerce/products/@(productNumber)`) 1380 .then(response => response.json()) 1381 .then(response => { 1382 this.relatedGroups = response.RelatedGroups; 1383 }) 1384 1385 await fetch(`/dwapi/ecommerce/products/@(productNumber)/related`) 1386 .then(response => response.json()) 1387 .then(response => { 1388 if(response.TotalProductsCount > 0) { 1389 this.relatedProducts = response.Products; 1390 } 1391 }) 1392 .catch(error => { 1393 console.log(error) 1394 }) 1395 }, 1396 getVariants: async function(productNo, url) { 1397 this.loadingVariants = true; 1398 let requestUrl = `@(VariantsLookup)&ICC_itemId=${productNo}`; 1399 1400 if(url && url != "") { 1401 requestUrl = url; 1402 } 1403 1404 if(!this.isLoggedIn) { 1405 if(requestUrl.indexOf('username') < 0) { 1406 requestUrl += '&username=marketing@keflico.com&password=K123456'; 1407 } 1408 1409 if (!this.doesCookieExist('tempLogin')) { 1410 this.setCookie('tempLogin', true, 1); 1411 } 1412 } 1413 1414 fetch(requestUrl, { 1415 credentials: "same-origin" 1416 }) 1417 .then(response => response.json()) 1418 .then(response => { 1419 if(response.variants && response.variants.length > 0) { 1420 this.variants = this.variants.concat(response.variants); 1421 1422 this.orderLoader = false; 1423 1424 if(response.nextPage != "") { 1425 this.getVariants(productNo, response.nextPage); 1426 } else { 1427 this.loadingVariants = false; 1428 1429 if(!this.isLoggedIn) { 1430 fetch('/Admin/Public/ExtranetLogoff.aspx'); 1431 } 1432 } 1433 } else { 1434 this.loadingVariants = false; 1435 1436 if(!this.loadingBundles) { 1437 clearTimeout(this.orderButtonSpinner); 1438 document.querySelector('#product-order-button .product-order-form-button').style.display = "block"; 1439 document.querySelector('#product-order-button .loader').style.display = "none"; 1440 } 1441 } 1442 }) 1443 }, 1444 lookUpBundle: async function(productNo, url) { 1445 this.loadingBundles = true; 1446 let requestUrl = `@(BundleLookup)&ICC_itemId=${productNo}`; 1447 1448 if(url && url != "") { 1449 requestUrl = url; 1450 } 1451 1452 if(!this.isLoggedIn) { 1453 if(requestUrl.indexOf('username') < 0) { 1454 requestUrl += '&username=marketing@keflico.com&password=K123456'; 1455 } 1456 1457 if (!this.doesCookieExist('tempLogin')) { 1458 this.setCookie('tempLogin', true, 1); 1459 } 1460 } 1461 1462 fetch(requestUrl, { 1463 credentials: "same-origin" 1464 }) 1465 .then(response => response.json()) 1466 .then(response => { 1467 if(response.bundles && response.bundles.length > 0) { 1468 1469 if(response.currentPage = 1) { 1470 this.showBundles = true; 1471 } 1472 1473 this.orderLoader = false; 1474 } else { 1475 if(!this.loadingVariants) { 1476 clearTimeout(this.orderButtonSpinner); 1477 document.querySelector('#product-order-button .product-order-form-button').style.display = "block"; 1478 document.querySelector('#product-order-button .loader').style.display = "none"; 1479 } 1480 } 1481 1482 this.bundles = this.bundles.concat(response.bundles); 1483 1484 if(response.nextPage != "") { 1485 this.lookUpBundle(productNo, response.nextPage); 1486 } else { 1487 this.loadingBundles = false; 1488 1489 if(!this.isLoggedIn) { 1490 fetch('/Admin/Public/ExtranetLogoff.aspx'); 1491 } 1492 } 1493 }) 1494 }, 1495 tempPriceUpdate() { 1496 let totalAccPrice = 0; 1497 1498 this.pickedAcc.forEach(acc => { 1499 totalAccPrice += parseFloat(acc.price.exVat.replace(',', '')) 1500 }) 1501 1502 this.priceObject.totalEx = parseFloat(this.priceObject.corePrice) + totalAccPrice; 1503 this.priceObject.vat = parseFloat(this.priceObject.totalEx) * 0.25; 1504 this.priceObject.totalInc = parseFloat(this.priceObject.totalEx) * 1.25; 1505 }, 1506 priceLookUp: async function() { 1507 let x = 1; 1508 let y = 1; 1509 let queryString = "&ICC_userId=" + this.customerNumber + "&ICC_itemId=" + this.mainProduct.id; 1510 1511 this.pickedVariants.forEach(variant => { 1512 if(variant.bundle) { 1513 queryString += "&ICC_bundle" + y + "=" + variant.bundleNo; 1514 1515 y++; 1516 } else { 1517 queryString += "&ICC_variant" + x + "=" + variant.id; 1518 queryString += "&ICC_quantity" + x + "=" + variant.qty; 1519 1520 x++; 1521 } 1522 }) 1523 1524 fetch(`@(PriceLookup)${queryString}`) 1525 .then(response => response.json()) 1526 .then(response => { 1527 this.priceObject.corePrice = parseFloat(response.totalPriceWithoutVat.replace(",", "")); 1528 this.priceObject.unitPrice = response.unitPrice; 1529 this.priceObject.totalEx = response.totalPriceWithoutVat; 1530 this.priceObject.vat = response.vat; 1531 this.priceObject.totalInc = response.totalPriceWithVat; 1532 1533 this.loadingPrice = false; 1534 }) 1535 }, 1536 toggleBundle(bundleNo) { 1537 this.toggleBundles.find(x => x.bundle == bundleNo).expanded = !this.toggleBundles.find(x => x.bundle == bundleNo).expanded; 1538 }, 1539 isExpanded(bundleNo) { 1540 return this.toggleBundles.find(x => x.bundle == bundleNo).expanded; 1541 }, 1542 getQty(variantId) { 1543 let product = this.pickedVariants.find(x => x.id == variantId || x.bundleNo == variantId); 1544 let acc = this.pickedAcc.find(x => x.id == variantId); 1545 1546 if(product || acc) { 1547 if(product) { 1548 if(product.bundle) { 1549 return 1; 1550 } 1551 1552 return product.qty; 1553 } else { 1554 return acc.qty; 1555 } 1556 } else { 1557 return 0; 1558 } 1559 }, 1560 toggleBar(productId) { 1561 let product = this.accessoriesList.find(x => x.Id == productId); // product is acc 1562 1563 if((product && product.Price.Price && product.Price.Price > 0 && !product.VariantInfo.VariantInfo) || !product) { 1564 if(this.currentMobileProduct != productId) { 1565 this.currentMobileProduct = productId; 1566 this.showMobileActionBar = true; 1567 this.showMobileActionControls = true; 1568 1569 if(this.pickedVariants.find(x => x.id == productId)) { 1570 this.currentActiveMobileButton = 'update'; 1571 this.currentMobileQty = this.pickedVariants.find(x => x.id == productId).qty; 1572 } else if(this.pickedAcc.find(x => x.id == productId)) { 1573 this.currentActiveMobileButton = 'update'; 1574 this.currentMobileQty = this.pickedAcc.find(x => x.id == productId).qty; 1575 } else { 1576 this.currentActiveMobileButton = 'add'; 1577 this.currentMobileQty = 0; 1578 } 1579 } else { 1580 this.currentMobileProduct = ''; 1581 this.showMobileActionControls = false; 1582 1583 if(this.pickedVariants.length == 0) { 1584 this.showMobileActionBar = false; 1585 } 1586 1587 if(this.showAccessories || this.accessoriesList.length == 0) { 1588 this.currentActiveMobileButton = 'order'; 1589 } else { 1590 this.currentActiveMobileButton = 'next'; 1591 } 1592 } 1593 } 1594 }, 1595 addToTempCart(amount, id) { 1596 this.loadingPrice = true; 1597 clearTimeout(this.timeOut); 1598 1599 if(this.pickedVariants.filter(x => x.id == id).length > 0) { 1600 if(amount > 0) { 1601 this.pickedVariants.find(x => x.id == id).qty = amount; 1602 } else { 1603 const index = this.pickedVariants.findIndex(x => x.id == id); 1604 this.pickedVariants.splice(index, 1); 1605 } 1606 } else { 1607 if(amount > 0) { 1608 const data = this.sortedVariants.find(x => x.id == id); 1609 1610 this.pickedVariants.push({ 1611 bundle: false, 1612 id: id, 1613 qty: parseInt(amount), 1614 length: data.size.length, 1615 width: data.size.width, 1616 thickness: data.size.thickness, 1617 variantId: data.id 1618 }); 1619 } 1620 } 1621 1622 if(this.pickedVariants.length > 0) { 1623 this.timeOut = setTimeout(() => { 1624 this.priceLookUp(); 1625 }, 1000); 1626 } else { 1627 this.priceObject.totalEx = 0; 1628 this.priceObject.totalInc = 0; 1629 this.priceObject.vat = 0; 1630 this.priceObject.unitPrice = 0; 1631 1632 this.loadingPrice = false; 1633 } 1634 }, 1635 editMobileQty($event) { 1636 this.currentMobileQty = $event.target.value 1637 }, 1638 updateMobileBasket() { 1639 let productToUpdate = this.sortedVariants.find(x => x.id == this.currentMobileProduct); 1640 1641 if(!productToUpdate) { 1642 productToUpdate = this.sortedBundles.find(x => x.bundleNo == this.currentMobileProduct); 1643 } 1644 1645 if(productToUpdate) { 1646 if(productToUpdate.bundleNo) { 1647 this.addBundleToTempCart(1, this.currentMobileProduct); 1648 } else { 1649 this.addToTempCart(this.currentMobileQty, this.currentMobileProduct); 1650 } 1651 1652 this.showMobileActionControls = false; 1653 this.currentMobileProduct = ''; 1654 1655 if(this.accessoriesList.length == 0) { 1656 this.currentActiveMobileButton = 'order'; 1657 } else { 1658 this.currentActiveMobileButton = 'next'; 1659 } 1660 } else { 1661 productToUpdate = this.accessoriesList.find(x => x.id == this.currentMobileProduct); 1662 1663 this.addAcc(this.currentMobileQty, this.currentMobileProduct); 1664 1665 this.showMobileActionControls = false; 1666 this.currentMobileProduct = ''; 1667 this.currentActiveMobileButton = 'order'; 1668 } 1669 }, 1670 addBundleToTempCart(amount, bundleNo) { 1671 this.loadingPrice = true; 1672 clearTimeout(this.timeOut); 1673 1674 if(amount > 0) { 1675 this.pickedVariants.push({ 1676 bundle: true, 1677 bundleNo: bundleNo, 1678 productsInBundle: this.sortedBundles.filter(x => x.bundleNo == bundleNo)[0].productsInBundle 1679 }) 1680 } else { 1681 const index = this.pickedVariants.findIndex(x => x.bundleNo == bundleNo); 1682 this.pickedVariants.splice(index, 1); 1683 } 1684 1685 if(this.pickedVariants.length > 0) { 1686 this.timeOut = setTimeout(() => { 1687 this.priceLookUp(); 1688 }, 1000); 1689 } else { 1690 this.priceObject.totalEx = 0; 1691 this.priceObject.totalInc = 0; 1692 this.priceObject.vat = 0; 1693 this.priceObject.unitPrice = 0; 1694 1695 this.loadingPrice = false; 1696 } 1697 }, 1698 updateAccButton(amount, productNumber) { 1699 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)) { 1700 this.$refs['confirmBtn' + productNumber][0].classList.remove('disabled') 1701 } else { 1702 this.$refs['confirmBtn' + productNumber][0].classList.add('disabled') 1703 } 1704 }, 1705 addAcc(event, productNumber) { 1706 let product = this.pickedAcc.find(x => x.id == productNumber); 1707 let qty = typeof event == 'number' ? event : this.$refs['accCounter' + productNumber][0].value; 1708 1709 let queryString = `&ICC_userId=${this.customerNumber}&ICC_itemId=${productNumber}&ICC_quantity1=`; 1710 1711 if(product) { 1712 if(qty > 0) { 1713 product.qty = qty; 1714 queryString += product.qty; 1715 } else { 1716 this.pickedAcc.splice(this.pickedAcc.findIndex(x => x.id == product.id), 1); 1717 } 1718 } else { 1719 this.pickedAcc.push({ 1720 id: productNumber, 1721 name: this.accessoriesList.find(x => x.Number == productNumber).Name, 1722 name2: this.accessoriesList.find(x => x.Number == productNumber).ProductFields.Name2.Value, 1723 qty: qty, 1724 price: { 1725 exVat: 0, 1726 vat: 0, 1727 withVat: 0, 1728 unitPrice: 0 1729 } 1730 }) 1731 1732 queryString += qty; 1733 } 1734 1735 if(qty > 0) { 1736 fetch(`/Default.aspx?ID=1115${queryString}`) 1737 .then(response => response.json()) 1738 .then(response => { 1739 if(product) { 1740 product.price.exVat = response.totalPriceWithoutVat; 1741 product.price.vat = response.vat; 1742 product.price.withVat = response.totalPriceWithVat; 1743 product.price.unitPrice = response.unitPrice; 1744 } else { 1745 const newProduct = this.pickedAcc.at(-1); 1746 1747 newProduct.price.exVat = response.totalPriceWithoutVat; 1748 newProduct.price.vat = response.vat; 1749 newProduct.price.withVat = response.totalPriceWithVat; 1750 newProduct.price.unitPrice = response.unitPrice; 1751 } 1752 1753 this.tempPriceUpdate(); 1754 }) 1755 } else { 1756 this.tempPriceUpdate(); 1757 } 1758 1759 this.$refs['confirmBtn' + productNumber][0].classList.add('disabled'); 1760 }, 1761 prettyNumber(number, minDigits) { 1762 return parseFloat(number).toLocaleString(this.lang, { minimumFractionDigits: minDigits ? minDigits : 0 }) 1763 }, 1764 prettyPrice(price) { 1765 let rawPrice = price; 1766 1767 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))) { 1768 // Price is over 1000 in danish format 1769 1770 rawPrice = rawPrice.replaceAll(".", ""); 1771 rawPrice = rawPrice.replaceAll(",", "."); 1772 } 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))) { 1773 // Price is over 1000 in english format 1774 1775 rawPrice = rawPrice.replaceAll(",", ""); 1776 } 1777 1778 rawPrice = parseFloat(rawPrice); 1779 return rawPrice.toLocaleString(this.lang, { minimumFractionDigits: 2, maximumFractionDigits: 2 } ) 1780 }, 1781 calculateStockLevel(stock) { 1782 if(this.isLoggedIn && stock >= 0) { 1783 return stock > 500 ? '+500' : stock; 1784 } else { 1785 return stock > 100 ? '+100' : stock; 1786 } 1787 1788 return 0; 1789 }, 1790 toggleState($event) { 1791 $event.target.closest('.side-basket__product').classList.toggle('closed'); 1792 }, 1793 openForm(e) { 1794 document.querySelector('.product-modal').classList.remove('js-slide-in'); 1795 1796 const toggleTrigger = e.currentTarget; 1797 const target = document.querySelector(toggleTrigger.getAttribute('data-target')); 1798 1799 if (target) { 1800 disableScrollLock() 1801 history.back(); 1802 1803 if (!target.classList.contains('js-open')) { 1804 toggleTrigger.classList.add('js-open'); 1805 target.classList.add('js-open'); 1806 target.style.height = `${target.scrollHeight}px`; 1807 1808 if (toggleTrigger.getAttribute('data-action-scroll') != 'no-scroll') { 1809 setTimeout(function() { 1810 target.scrollIntoView({behavior: 'smooth'}); 1811 },300); 1812 } 1813 1814 if (target.classList.contains('dialog')) { 1815 document.querySelector('body').style.overflow = 'hidden'; 1816 } 1817 } else { 1818 toggleTrigger.classList.remove('js-open'); 1819 target.classList.remove('js-open'); 1820 target.style.height = null; 1821 1822 if (target.classList.contains('dialog')) { 1823 document.querySelector('body').style.overflow = null; 1824 } 1825 } 1826 } 1827 }, 1828 calculateMeters() { 1829 let meters = 0; 1830 1831 this.pickedVariants.forEach(variant => { 1832 let length = variant['length']; 1833 1834 if(this.mainProduct.sizeUnits.length.indexOf('FUß') > -1) { 1835 length = length * 304.8; 1836 } 1837 1838 let variantMeters = parseFloat(length) * variant.qty / 1000; 1839 meters += variantMeters; 1840 }) 1841 1842 return parseFloat(meters).toFixed(3); 1843 }, 1844 calculatePieces() { 1845 let pieces = 0; 1846 1847 this.pickedVariants.forEach(variant => { 1848 if(variant.bundle) { 1849 variant.productsInBundle.forEach(product => { 1850 pieces += parseInt(product.stock.units); 1851 }) 1852 } else { 1853 pieces += parseInt(variant.qty); 1854 } 1855 }); 1856 1857 return pieces; 1858 }, 1859 calculateSquareMeters() { 1860 return parseFloat(this.calculateMeters() * (this.mainProduct.width / 1000)).toFixed(3); 1861 }, 1862 calculateCubicMeters() { 1863 let cubicMeters = 0; 1864 1865 this.pickedVariants.forEach(variant => { 1866 let variantMeters = 0; 1867 1868 let lengthMultiplier = 1; 1869 let widthMultiplier = 1; 1870 let thicknessMultiplier = 1; 1871 1872 if(this.mainProduct.sizeUnits.length.indexOf('FUß') > -1) { 1873 lengthMultiplier = 304.79999025; 1874 } 1875 1876 if(this.mainProduct.sizeUnits.width.indexOf('ZOLL') > -1) { 1877 widthMultiplier = 25.39998628; 1878 } 1879 1880 if(this.mainProduct.sizeUnits.thickness.indexOf('ZOLL') > -1) { 1881 thicknessMultiplier = 25.39998628 1882 } 1883 1884 if(variant.bundle) { 1885 variant.productsInBundle.forEach(product => { 1886 variantMeters += (parseFloat(product.length.replace(",", "") * lengthMultiplier / 1000) * parseFloat(product.thickness * thicknessMultiplier / 1000) * parseFloat(product.width * widthMultiplier) / 1000) * parseInt(product.stock.units); 1887 }) 1888 } else { 1889 variantMeters += (parseFloat(variant['length'] / 1000) * parseFloat(variant['thickness'] / 1000) * parseFloat(variant['width']) / 1000) * variant.qty; 1890 } 1891 1892 cubicMeters += variantMeters; 1893 }) 1894 1895 return cubicMeters; 1896 }, 1897 calculateCubicFeet() { 1898 let cubicFeet = 0; 1899 1900 this.pickedVariants.forEach(variant => { 1901 let variantMeters = 0; 1902 1903 let lengthMultiplier = 1; 1904 let widthMultiplier = 1; 1905 let thicknessMultiplier = 1; 1906 1907 if(this.mainProduct.sizeUnits.length.indexOf('FUß') > -1) { 1908 lengthMultiplier = 304.79999025; 1909 } 1910 1911 if(this.mainProduct.sizeUnits.width.indexOf('ZOLL') > -1) { 1912 widthMultiplier = 25.39998628; 1913 } 1914 1915 if(this.mainProduct.sizeUnits.thickness.indexOf('ZOLL') > -1) { 1916 thicknessMultiplier = 25.39998628 1917 } 1918 1919 if(variant.bundle) { 1920 variant.productsInBundle.forEach(product => { 1921 let lengthMM = product.length.replace(",", "") * lengthMultiplier; 1922 let lengthM = lengthMM / 1000; 1923 let widthMM = product.width * widthMultiplier; 1924 let widthM = widthMM / 1000; 1925 let thicknessMM = product.thickness * thicknessMultiplier; 1926 let thicknessM = thicknessMM / 1000; 1927 1928 variantMeters += Number(parseFloat(lengthM * widthM * thicknessM * 35.32 * product.stock.units)) 1929 }) 1930 } else { 1931 variantMeters += ((parseFloat(variant['length'] * lengthMultiplier / 1000) * parseFloat(variant['thickness'] * thicknessMultiplier / 1000) * parseFloat(variant['width'] * widthMultiplier / 1000)) * variant.qty); 1932 } 1933 1934 cubicFeet += Number(variantMeters); 1935 }) 1936 1937 return Number(cubicFeet).toFixed(2); 1938 }, 1939 calculateTotalQTY() { 1940 let qty = 0; 1941 1942 this.pickedVariants.forEach(variant => { 1943 qty += variant.qty; 1944 }); 1945 1946 return qty; 1947 }, 1948 resetBasket() { 1949 this.showAccessories = false; 1950 this.pickedVariants = []; 1951 this.showStep1 = true; 1952 this.pickedAcc = []; 1953 this.priceObject.unitPrice = 0; 1954 this.priceObject.totalEx = 0; 1955 this.priceObject.vat = 0; 1956 this.priceObject.totalInc = 0; 1957 this.priceObject.corePrice = 0; 1958 1959 Array.from(this.$refs.counter).forEach(counter => { 1960 counter.value = 0; 1961 }); 1962 }, 1963 async addToBasket() { 1964 this.loading = true; 1965 var params = new FormData(); 1966 1967 params.append('CartCmd', "addMulti") 1968 1969 this.pickedVariants.forEach((variant, index) => { 1970 var productLoopCounter = index + 1; 1971 1972 if(variant.bundle) { 1973 params.append('ProductLoopCounter' + productLoopCounter, productLoopCounter); 1974 params.append('ProductID' + productLoopCounter, this.mainProduct.id); 1975 params.append('Quantity' + productLoopCounter, 1); 1976 params.append('EcomOrderLineFieldInput_BundleNo' + productLoopCounter, variant.bundleNo); 1977 } else { 1978 params.append('ProductLoopCounter' + productLoopCounter, productLoopCounter); 1979 params.append('ProductID' + productLoopCounter, this.mainProduct.id); 1980 params.append('VariantID' + productLoopCounter, variant.variantId); 1981 params.append('Quantity' + productLoopCounter, parseInt(variant.qty)); 1982 } 1983 }) 1984 const config = { 1985 method: 'POST', 1986 body: params 1987 } 1988 1989 this.productsAdded = this.pickedVariants.length; 1990 1991 const response = await fetch('@formAction' + '&redirect=false', config) 1992 1993 if (response.ok) { 1994 dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object. 1995 dataLayer.push({ 1996 event: "add_to_cart", 1997 ecommerce: { 1998 currency: "@GetString("Ecom:Product.Currency.Code")", 1999 value: this.priceObject.totalEx, 2000 items: this.ga4Products 2001 } 2002 }); 2003 2004 if(this.accessoriesList.length > 0) { 2005 this.showAccessories = true; 2006 this.loading = false; 2007 this.currentActiveMobileButton = 'order'; 2008 } else { 2009 window.location.href = "@formAction"; 2010 } 2011 2012 showStep1 = false; 2013 } else { 2014 console.log(response) 2015 } 2016 }, 2017 async addAccToBasket() { 2018 this.loading = true; 2019 var params = new FormData(); 2020 2021 params.append('CartCmd', "addMulti") 2022 2023 this.pickedAcc.forEach((variant, index) => { 2024 var productLoopCounter = index + 1; 2025 2026 params.append('ProductLoopCounter' + productLoopCounter, productLoopCounter); 2027 params.append('ProductID' + productLoopCounter, variant.id); 2028 params.append('Quantity' + productLoopCounter, variant.qty); 2029 }) 2030 const config = { 2031 method: 'POST', 2032 body: params 2033 } 2034 2035 const response = await fetch('@formAction' + '&redirect=false', config) 2036 2037 if (response.ok) { 2038 window.location.href = "@formAction"; 2039 } else { 2040 console.log(response) 2041 } 2042 } 2043 }, 2044 watch: { 2045 orderLoader(newValue, oldValue) { 2046 if(!newValue && !this.enquireProduct && this.isLoggedIn) { 2047 clearTimeout(this.orderButtonSpinner); 2048 document.querySelector('#product-order-button .product-order-button').style.display = "block"; 2049 document.querySelector('#product-order-button .loader').style.display = "none"; 2050 } 2051 } 2052 } 2053 }); 2054 </script> 2055 @SnippetEnd("JavaScripts") 2056 } 2057 2058 @functions { 2059 private string GetFileIcon(string fileUrl) 2060 { 2061 string extension = Path.GetExtension(fileUrl)?.ToLower(); 2062 2063 if (string.IsNullOrEmpty(extension)) 2064 return System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/file.svg")); 2065 2066 switch (extension) 2067 { 2068 case ".pdf": 2069 return System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/file.svg")); 2070 case ".jpg": 2071 case ".jpeg": 2072 case ".png": 2073 case ".gif": 2074 case ".webp": 2075 return @System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/image.svg")); 2076 default: 2077 return System.IO.File.ReadAllText(System.Web.HttpContext.Current.Server.MapPath("/files/templates/designs/keflico/assets/svg/product/file.svg")); 2078 } 2079 } 2080 2081 public List<string> GetFilters(string GroupId) 2082 { 2083 List<string> filters = new List<string>(); 2084 2085 switch (GroupId) 2086 { 2087 case "group1": 2088 filters.Add("10"); 2089 filters.Add("11"); 2090 break; 2091 case "group32": 2092 filters.Add("17"); 2093 break; 2094 case "group52": 2095 filters.Add("12"); 2096 filters.Add("13"); 2097 filters.Add("14"); 2098 filters.Add("16"); 2099 break; 2100 case "group53": 2101 filters.Add("14"); 2102 break; 2103 case "group56": 2104 filters.Add("16"); 2105 break; 2106 case "group61": 2107 case "group67": 2108 filters.Add("12"); 2109 break; 2110 case "group63": 2111 filters.Add("13"); 2112 break; 2113 case "group73": 2114 filters.Add("18"); 2115 break; 2116 case "group129": 2117 filters.Add("6"); 2118 break; 2119 default: 2120 filters.Add(""); 2121 break; 2122 } 2123 2124 return filters; 2125 } 2126 2127 public string BuildFilterString(string group) { 2128 string filter = ""; 2129 List<string> ids = GetFilters(group); 2130 2131 if(group == "group126") { 2132 2133 } else if(group == "group129") { 2134 foreach(var id in ids) { 2135 filter += filter == "" ? "Types contains \""+id+"\"" : " or Types contains \""+id+"\""; 2136 } 2137 } else { 2138 foreach(var id in ids) { 2139 filter += filter == "" ? "WoodTypes contains \""+id+"\"" : " or WoodTypes contains \""+id+"\""; 2140 } 2141 } 2142 2143 return filter; 2144 } 2145 }