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