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