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