Error executing template "Designs/Dynaplus/eCom/ProductCatalog/ProductDetail.cshtml"
System.ArgumentOutOfRangeException: Index must be within the bounds of the List.
Parameter name: index
at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
at System.Collections.Generic.List`1.Insert(Int32 index, T item)
at CompiledRazorTemplates.Dynamic.RazorEngine_c7355fd2fa0b40d59da7cc09b3eef0b2.Execute() in D:\inetpub\wwwroot\www.hoenderdaal-fasteners.nl\Files\Templates\Designs\Dynaplus\eCom\ProductCatalog\ProductDetail.cshtml:line 376
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 Dynamicweb.Content
2 @using Dynamicweb.Rendering
3 @using Dynamicweb.Ecommerce.ProductCatalog
4 @using Hoenderdaal.Application.CustomCode.EqualityComparers
5 @using Hoenderdaal.Repository.Helpers
6 @using Hoenderdaal.Models.Constants
7 @using Hoenderdaal.Application.CustomCode.Helpers
8
9 @inherits ViewModelTemplate<ProductViewModel>
10 @functions {
11 private static string SpecsTable(Hoenderdaal.Models.ViewModels.SpecificationsViewModel specificationsModel)
12 {
13 if (!specificationsModel.Specifications.Any()) return string.Empty;
14 var result = new System.Text.StringBuilder();
15 result.AppendLine($"<h3>{specificationsModel.Title}</h3>");
16 result.AppendLine("<table class=\"specs-table product-detail__table\">");
17
18 // add untranslated values to this list if you want them in the translations file
19 List<string> translatableValues = new List<string> { "yes", "no" };
20
21 foreach (var spec in specificationsModel.Specifications)
22 {
23 var label = spec.Label;
24
25 // hack for mobile display of "oppervlaktebehandeling" which makes table wider than 100%
26 label = label.Replace("behandeling", "<wbr>behandeling");
27
28 // hack for untranslated values
29 var value = spec.Value;
30 if (translatableValues.Contains(value))
31 {
32 value = Hoenderdaal.Repository.Helpers.TranslationHelper.Translate($"AUTOGEN_SpecValue_{value}", Dynamicweb.Frontend.PageView.Current().AreaID, value);
33 }
34
35 result.AppendLine($"<tr><td class=\"product-detail__table__label\">{label}:</td><td class=\"product-detail__table__value\">{value}</td></tr>");
36 }
37 result.Append("</table>");
38 return result.ToString();
39 }
40 }
41 @{
42 var groupId = Dynamicweb.Context.Current.Request["GroupID"];
43 var groupIdQueryParameter = string.IsNullOrEmpty(groupId)
44 ? string.Empty
45 : "&GroupID=" + Dynamicweb.Context.Current.Request["GroupID"];
46 var productLink = "/Default.aspx?ID=" + Pageview.Page.ID + groupIdQueryParameter + "&ProductID=" + Model.Id;
47 var ecomLanguageId = Dynamicweb.Frontend.PageView.Current().Area.EcomLanguageId;
48 var countryCode = Dynamicweb.Frontend.PageView.Current().Area.EcomCountryCode;
49 var language = Pageview.GlobalTags.GetTagByName("Global:Area.Lang").Value;
50 var currentAreaId = Dynamicweb.Frontend.PageView.Current().AreaID;
51 var currentParagaph = Dynamicweb.Frontend.PageView.Current().CurrentParagraph;
52 var paragraphId = string.Format("ProductList_{0}", currentParagaph.ID);
53 var userIsLoggedIn = Hoenderdaal.Repository.Helpers.LoginHelper.CurrentUserIsLoggedIn();
54 var moneyCulture = System.Globalization.CultureInfo.InvariantCulture;
55 var hidePrices = Hoenderdaal.Repository.Helpers.EcomHelper.HidePrices();
56 var dealersPageId = Dynamicweb.Content.Services.Pages.GetPageByNavigationTag(currentAreaId, Hoenderdaal.Models.Constants.StringConstants.NavigationTags.PAGE_Dealers)?.ID ?? 0;
57 var product = Dynamicweb.Ecommerce.Services.Products.GetProductById(Model.Id, Model.VariantId, ecomLanguageId);
58 var masterProduct = Dynamicweb.Ecommerce.Services.Products.GetProductById(Model.Id, "", ecomLanguageId);
59 var baseUrl = "/Files/Templates/Designs/" + Pageview.Layout.Design.Folder.Name.TrimEnd('/');
60 var productName = masterProduct.Name;
61 var productNumber = Model.Number;
62 var productPrice = Model.Price.PriceWithoutVat;
63 var productImages = EcomHelper.GetProductImageDetails(product, masterProduct);
64
65 int.TryParse(Hoenderdaal.Repository.Helpers.AreaHelper.GetAreaItemStringValueBySystemName("AlternativeProductsGroupId", Dynamicweb.Frontend.PageView.Current().AreaID)?.Trim(), out int alternativeProductsGroupId);
66
67 var icons = EcomHelper.GetProductImageDetails(product, masterProduct, new List<StringConstants.AssetCollections.AssetCollectionId>()
68 {
69 StringConstants.AssetCollections.AssetCollectionId.QualityMark
70 });
71 var hasIcons = icons.Any();
72 var productDownloads = EcomHelper.GetProductImageDetails(product, masterProduct, new List<StringConstants.AssetCollections.AssetCollectionId>()
73 {
74 StringConstants.AssetCollections.AssetCollectionId.DeclarationOfPerformance,
75 StringConstants.AssetCollections.AssetCollectionId.TechnicalSpecificationSheet,
76 StringConstants.AssetCollections.AssetCollectionId.Catalog,
77 StringConstants.AssetCollections.AssetCollectionId.Flyer
78 });
79 var hasProductDownloads = productDownloads?.Any() ?? false;
80 var hasSpecs = false;
81 var articleSpecifications = Hoenderdaal.Repository.Helpers.EcomHelper.GetSpecifications(Dynamicweb.Frontend.PageView.Current().AreaID, Model, Hoenderdaal.Models.Enumerations.DisplayGroup.ProductInformation);
82 if (articleSpecifications.Specifications.Any())
83 {
84 hasSpecs = true;
85 }
86 var technicalSpecifications = Hoenderdaal.Repository.Helpers.EcomHelper.GetSpecifications(Dynamicweb.Frontend.PageView.Current().AreaID, Model, Hoenderdaal.Models.Enumerations.DisplayGroup.TechnicalInformation);
87 if (technicalSpecifications.Specifications.Any())
88 {
89 hasSpecs = true;
90 }
91 var applicationFeatures = Hoenderdaal.Repository.Helpers.EcomHelper.GetSpecifications(Dynamicweb.Frontend.PageView.Current().AreaID, Model, Hoenderdaal.Models.Enumerations.DisplayGroup.ApplicationFeatures);
92 if (applicationFeatures.Specifications.Any())
93 {
94 hasSpecs = true;
95 }
96 var logisticInformation = Hoenderdaal.Repository.Helpers.EcomHelper.GetSpecifications(Dynamicweb.Frontend.PageView.Current().AreaID, Model, Hoenderdaal.Models.Enumerations.DisplayGroup.LogisticInformation);
97
98 if (logisticInformation.Specifications.Any())
99 {
100 hasSpecs = true;
101 }
102
103 var showAlternatives = false;
104 List<Dynamicweb.Ecommerce.Products.Product> alternatives = null;
105
106 var productDescription = product.LongDescription;
107 if (string.IsNullOrWhiteSpace(productDescription))
108 {
109 productDescription = masterProduct.LongDescription;
110 }
111 var hasDescription = !string.IsNullOrWhiteSpace(productDescription);
112 var productVideos = EcomHelper.GetProductDetailsVideos(masterProduct.Details);
113 var hasVideos = productVideos != null && productVideos.Any();
114 var productFaqs = Hoenderdaal.Repository.Helpers.EcomHelper.GetFaqsForProductGroup(Dynamicweb.Frontend.PageView.Current().AreaID, groupId);
115 var hasFaqs = productFaqs != null && productFaqs.Any();
116 var technicalInfo = Hoenderdaal.Repository.Helpers.EcomHelper.GetTechnicalInfoForProductGroup(Dynamicweb.Frontend.PageView.Current().AreaID, groupId);
117
118 List<string> materials = EcomHelper.GetProductField<List<string>>(Model, StringConstants.ProductFields.Material);
119 if (materials?.Any() ?? false)
120 {
121 var technicalInfoByProperyValues = Hoenderdaal.Repository.Helpers.EcomHelper.GetFaqParagraphsByProperty(Dynamicweb.Frontend.PageView.Current().AreaID, materials, faq => faq.Materials, StringConstants.NavigationTags.PAGE_TechnicalInfo);
122 if (technicalInfoByProperyValues != null)
123 {
124 if (technicalInfo == null)
125 {
126 technicalInfo = new List<Paragraph>();
127 }
128 technicalInfo = technicalInfo.Union(technicalInfoByProperyValues, new CustomGenericComparer<Paragraph>((p, p1) => p.ID == p1.ID)).OrderBy(f => f.Sort);
129 }
130
131 var faqInfoByProperyValues = Hoenderdaal.Repository.Helpers.EcomHelper.GetFaqParagraphsByProperty(Dynamicweb.Frontend.PageView.Current().AreaID, materials, faq => faq.Materials);
132 if (faqInfoByProperyValues != null)
133 {
134 if (productFaqs == null)
135 {
136 productFaqs = new List<Paragraph>();
137 }
138 productFaqs = productFaqs.Union(faqInfoByProperyValues, new CustomGenericComparer<Paragraph>((p, p1) => p.ID == p1.ID)).OrderBy(f => f.Sort);
139 }
140 }
141
142 List<string> surfaceTreatments = EcomHelper.GetProductField<List<string>>(Model, StringConstants.ProductFields.SurfaceTreatment);
143 if (surfaceTreatments?.Any() ?? false)
144 {
145 var technicalInfoByProperyValues = Hoenderdaal.Repository.Helpers.EcomHelper.GetFaqParagraphsByProperty(Dynamicweb.Frontend.PageView.Current().AreaID, surfaceTreatments, faq => faq.SurfaceTreatments, StringConstants.NavigationTags.PAGE_TechnicalInfo);
146 if (technicalInfoByProperyValues != null)
147 {
148 if (technicalInfo == null)
149 {
150 technicalInfo = new List<Paragraph>();
151 }
152 technicalInfo = technicalInfo.Union(technicalInfoByProperyValues, new CustomGenericComparer<Paragraph>((p, p1) => p.ID == p1.ID)).OrderBy(f => f.Sort);
153 }
154
155 var faqInfoByProperyValues = Hoenderdaal.Repository.Helpers.EcomHelper.GetFaqParagraphsByProperty(Dynamicweb.Frontend.PageView.Current().AreaID, surfaceTreatments, faq => faq.SurfaceTreatments);
156 if (faqInfoByProperyValues != null)
157 {
158 if (productFaqs == null)
159 {
160 productFaqs = new List<Paragraph>();
161 }
162 productFaqs = productFaqs.Union(faqInfoByProperyValues, new CustomGenericComparer<Paragraph>((p, p1) => p.ID == p1.ID)).OrderBy(f => f.Sort);
163 }
164 }
165
166 var hasTechnicalInfo = technicalInfo != null && technicalInfo.Any();
167 var relatedArticles = Dynamicweb.Ecommerce.Services.ProductRelated.GetRelations(product.Id).Where(relation => !string.Equals(relation.RelatedGroupId, alternativeProductsGroupId.ToString()));
168 var hasRelatedArticles = relatedArticles != null && relatedArticles.Any();
169 var group = Model.Groups.FirstOrDefault(g => g.Id.Equals(groupId));
170 var groupName = group?.Name ?? "";
171 if (!string.IsNullOrWhiteSpace(groupName))
172 {
173 var groupUrl = $"/Default.aspx?ID={Pageview.Page.ID}&GroupID={groupId}";
174 groupName = $"<a href=\"{groupUrl}\">{groupName}</a>";
175 }
176
177 var variants = Hoenderdaal.Repository.Helpers.EcomHelper.GetVariantsFromProductViewModel(Model);
178 var selectedVariant = variants.Variants?.FirstOrDefault(v => v.VariantId.Equals(product.VariantId));
179 var packageQuantityText = "";
180 (decimal unitPrice, int priceUnit, string unitName, decimal salesUnit) unitprice = (0, 0, "", 0);
181 if (!hidePrices)
182 {
183 unitprice = Hoenderdaal.Repository.Helpers.EcomHelper.GetUnitPrice(product.Number);
184 packageQuantityText = $"<span class=\"price\">€ {unitprice.unitPrice.ToString(moneyCulture)}</span> {Translate("ProductDetail_NettoPerPc_Netto", "netto")} {Translate("ProductDetail_NettoPerPc_Per", "per")} {unitprice.priceUnit.IntegerToStringInteger()} {Translate(string.Format("ProductDetail_Unit_{0}", unitprice.unitName), unitprice.unitName)}";
185 }
186
187 var currentVariantText = string.IsNullOrEmpty(product.VariantId) ? " " : $"{Translate("ProductDetail_Variant", "Variant")}: {EcomHelper.GetVariantText(product, masterProduct.Prices, Dynamicweb.Frontend.PageView.Current().AreaID, true, language)}";
188 string previousPageUrl = null;
189 string previousPageName = string.Empty;
190 if (string.IsNullOrEmpty(previousPageUrl))
191 {
192 var assortmentPage = Dynamicweb.Content.Services.Pages.GetPageByNavigationTag(currentAreaId, Hoenderdaal.Models.Constants.StringConstants.NavigationTags.PAGE_SearchResults);
193 var assortmentPageId = assortmentPage?.ID ?? 0;
194 previousPageUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(assortmentPageId);
195 previousPageName = assortmentPage?.GetDisplayName() ?? "?";
196 }
197 }
198 <div class="container-fluid product-detail" id="@paragraphId">
199 @if (!string.IsNullOrEmpty(previousPageUrl))
200 {
201 <div class="product-detail__row">
202 <div class="container">
203 @{
204 var breadcrumbHomepageUrl = "/";
205 var breadcrumbDivider = " / ";
206 var breadcrumb = $"<div class='breadcrumb breadcrumb--less'><a href=\"{breadcrumbHomepageUrl}\">{Translate("Breadcrumb_Home", "Homepage")}</a> {breadcrumbDivider} <a href=\"{previousPageUrl}\">{previousPageName}</a> {breadcrumbDivider} {productName}</div>";
207 @breadcrumb
208 }
209 </div>
210 </div>
211 }
212 <div class="product-detail__row mobile-only">
213 <div class="container">
214 <h1 class="product-title">@productName</h1>
215 <div class="product-variantinfo">
216 @currentVariantText
217 </div>
218 </div>
219 </div>
220 <div class="product-detail__row">
221 <div class="container">
222 <div class="product-detail__variant">
223 @if (productImages.Any())
224 {
225 <div class="product-detail__variant__slider">
226 <div class="product-detail__variant__slider__large">
227 @foreach (var image in productImages)
228 {
229 var imageLargeUrl = $"/Admin/Public/GetImage.ashx?Image={image.Value}&altFmImage_path=/Files/Templates/Designs/Hoenderdaal/Images/ecom/Product_NoImage.png&Crop=5&Format=webp&Fillcanvas=1&Height=396&Width=555&Quality=95&Background=FFFFFF";
230 var imageFullUrl = $"/Admin/Public/GetImage.ashx?Image={image.Value}&altFmImage_path=/Files/Templates/Designs/Hoenderdaal/Images/ecom/Product_NoImage.png&Crop=5&Format=webp&Fillcanvas=1&Height=1000&Width=1000&Quality=95&Background=FFFFFF";
231 <div class="product-detail__variant__slider__large__container">
232 <img class="product-detail__variant__slider__large__container__image" src="@imageLargeUrl" loading="lazy" decoding="async" />
233 <div class="product-detail__variant__slider__large__container__zoom">
234 <a href="@imageFullUrl" data-lightbox="@image.Value"></a>
235 </div>
236 </div>
237 }
238 </div>
239 @if (productImages.Count > 1)
240 {
241 var largeClass = productImages.Count > 3 ? "product-detail__variant__slider__thumbnails--large" : "";
242 <div class="product-detail__variant__slider__thumbnails @largeClass">
243 @foreach (var image in productImages)
244 {
245 <div class="product-detail__variant__slider__thumbnails__container">
246 @{
247 var imageUrl = $"/Admin/Public/GetImage.ashx?Image={image.Value}&altFmImage_path=/Files/Templates/Designs/Hoenderdaal/Images/ecom/Product_NoImage.png&Crop=5&Format=webp&Fillcanvas=1&Height=128&Width=180&Quality=95&Background=FFFFFF";
248 <img class="product-detail__variant__slider__thumbnails__container__image" src="@imageUrl" loading="lazy" decoding="async" />
249 }
250 </div>
251 }
252 </div>
253 }
254 </div>
255 }
256
257 <div class="product-detail__variant__details">
258
259 <h1 class="product-detail__variant__details__title product-title hide-on-mobile">@productName</h1>
260
261 <div class="product-detail__variant__details__info product-variantinfo hide-on-mobile">
262 @currentVariantText
263 </div>
264
265 <div class="product-detail__variant__details__actions">
266 @if (dealersPageId > 0)
267 {
268 <a href="@Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(dealersPageId)" class="red">@Translate("ProductDetail_Button_Dealers", "Find a dealer")</a>
269 }
270 @if (hasDescription)
271 {
272 <a href="#product-description">@Translate("ProductDetail_Button_MoreInfo", "More info")</a>
273 }
274 else
275 {
276 <a href="#more-info">@Translate("ProductDetail_Button_MoreInfo", "More info")</a>
277 }
278 </div>
279 <div class="product-detail__variant__details__variants">
280 @RenderVariants(variants, productLink, true, countryCode)
281 </div>
282 </div>
283 </div>
284 </div>
285 </div>
286 <div class="product-detail__anchorbar">
287 <div class="container">
288 @if (hasDescription)
289 {
290 <a href="#product-description" class="product-detail__anchorbar__anchor">
291 @Translate("ProductDetail_Anchor_Description", "Description")
292 </a>
293 }
294
295 @if (hasSpecs)
296 {
297 <a href="#product-specifications" class="product-detail__anchorbar__anchor">
298 @Translate("ProductDetail_Anchor_Specifications", "Specifications")
299 </a>
300 }
301
302 @if (showAlternatives)
303 {
304 <a href="#product-alternatives" class="product-detail__anchorbar__anchor">
305 @Translate("ProductDetail_Anchor_AlternativeProducts", "Alternative products")
306 </a>
307 }
308
309 @if (hasTechnicalInfo)
310 {
311 <a href="#product-technical-info" class="product-detail__anchorbar__anchor">
312 @Translate("ProductDetail_Anchor_TechnicalInfo", "Technical info")
313 </a>
314 }
315
316 @if (hasFaqs)
317 {
318 <a href="#product-faqs" class="product-detail__anchorbar__anchor">
319 @Translate("ProductDetail_Anchor_Faqs", "FAQ")
320 </a>
321 }
322
323 @if (hasRelatedArticles)
324 {
325 <a href="#product-relatedarticles" class="product-detail__anchorbar__anchor">
326 @Translate("ProductDetail_Anchor_RelatedArticles", "Related articles")
327 </a>
328 }
329
330 @if (hasIcons)
331 {
332 <a href="#product-icons" class="product-detail__anchorbar__anchor">
333 @Translate("ProductDetail_Anchor_Icons", "Quality marks and applications")
334 </a>
335 }
336
337 @if (hasVideos)
338 {
339 <a href="#product-video" class="product-detail__anchorbar__anchor">
340 @Translate("ProductDetail_Anchor_Video", "Video")
341 </a>
342 }
343
344 @if (hasProductDownloads)
345 {
346 <a href="#product-downloads" class="product-detail__anchorbar__anchor">
347 @Translate("ProductDetail_Anchor_Downloads", "Downloads")
348 </a>
349 }
350 </div>
351 </div>
352
353 <div class="product-detail__content">
354 <div class="container">
355 <div class="product-detail__content__left">
356 <a id="more-info"></a>
357 @if (hasDescription)
358 {
359 <div class="product-detail__content__item">
360 <a id="product-description"></a>
361 <h2>@Translate("ProductDetail_Anchor_Description", "Description")</h2>
362 @productDescription.MakeParagraph()
363 </div>
364 }
365
366 @if (hasSpecs)
367 {
368 <div class="product-detail__content__item">
369 <a id="product-specifications"></a>
370 <h2>@Translate("ProductDetail_Anchor_Specifications", "Specifications")</h2>
371 @SpecsTable(articleSpecifications)
372 @SpecsTable(technicalSpecifications)
373 @SpecsTable(applicationFeatures)
374 @{
375 // add outerbox
376 logisticInformation.Specifications.Insert(1, new Hoenderdaal.Models.ViewModels.SpecificationViewModel
377 {
378 Label = Translate("ProductDetail_Specification_OuterBox", "Outer box"),
379 Value = ProductHelper.GetOuterboxQuantity(selectedVariant).DoubleToStringInteger() + " " + Translate("SpecificationPostfix_Pieces", "pieces"),
380 });
381 }
382
383 @SpecsTable(logisticInformation)
384 </div>
385 }
386
387 @if (showAlternatives)
388 {
389 <div class="product-detail__content__item">
390 <a id="product-alternatives"></a>
391 <h2>@Translate("ProductDetail_Anchor_AlternativeProducts", "Alternative products")</h2>
392 <div class="row">
393 @foreach (var alternative in alternatives)
394 {
395 var alternativeProduct = Dynamicweb.Ecommerce.Services.Products.GetProductById(alternative.Id, alternative.VariantId, ecomLanguageId);
396 var alternativeInfo = Hoenderdaal.Repository.Helpers.EcomHelper.GetAlternativeProductViewModel(alternativeProduct, userIsLoggedIn, Dynamicweb.Frontend.PageView.Current().Area, Pageview.Page.ID);
397 var alternativeProductImage = $"/Admin/Public/GetImage.ashx?Image=/Files/Templates/Designs/Hoenderdaal/Images/ecom/Product_NoImage.png&Crop=5&Format=webp&Fillcanvas=true&Height=95&Width=95&Quality=95";
398 if (alternativeProduct != null)
399 {
400 var alternativeMaster = Dynamicweb.Ecommerce.Services.Products.GetProductById(alternativeProduct.Id, "", ecomLanguageId);
401 var alternativeImage = EcomHelper.GetProductImage(alternativeProduct, alternativeMaster);
402 if (!string.IsNullOrEmpty(alternativeImage?.Value))
403 {
404 alternativeProductImage = $"/Admin/Public/GetImage.ashx?Image={alternativeImage.Value.Replace(" ", "%20")}&altFmImage_path=/Files/Templates/Designs/Hoenderdaal/Images/ecom/Product_NoImage.png&Crop=5&Format=webp&Fillcanvas=1&Height=95&Width=95&Quality=95&Background=FFFFFF";
405 }
406 }
407 <div class="col-sm-12 col-md-6">
408 <a href="@alternativeInfo.Link">
409 <div class="alternative-top">
410 <div><img src="@alternativeProductImage" loading="lazy" decoding="async" alt="Alternative @alternativeInfo.ProductName.JsEncode()" /></div>
411 <div class="alternative-content">
412 @alternativeInfo.Brand<br />
413 @if (alternativeInfo.StockInfo.HasValue)
414 {
415
416 <div class="product-detail__variant__details__quantity-stock__stock product-detail__variant__details__quantity-stock__stock--@alternativeInfo.StockInfo.Value.stockTextClass">
417 <div class="product-detail__variant__details__quantity-stock__stock__status-light @alternativeInfo.StockInfo.Value.stockColorClass" title="@alternativeInfo.StockInfo.Value.stockText"></div> @alternativeInfo.StockInfo.Value.stockText
418 </div>
419 }
420 </div>
421 </div>
422 <div>
423 @alternativeInfo.ProductNumber<br />@alternativeInfo.ProductName @alternativeInfo.Specifications<br />@alternativeInfo.Content
424 </div>
425 </a>
426 </div>
427 }
428 </div>
429 </div>
430 }
431
432 @if (hasTechnicalInfo)
433 {
434 <div class="product-detail__content__item faqlist">
435 <a id="product-technical-info"></a>
436 <h2 class="product-detail">@Translate("ProductDetail_Title_TechnicalInfo", "Technical info")</h2>
437 <div>
438 @foreach (var faq in technicalInfo)
439 {
440 var faqId = string.Format("TI_{0}", faq.ID);
441 var collapseId = string.Format("Collapse_TI_{0}", faq.ID);
442 var image = Hoenderdaal.Repository.Helpers.ItemHelper.GetItemValue<string>(faq.Item, "Image");
443 var question = Hoenderdaal.Repository.Helpers.ItemHelper.GetItemValue<string>(faq.Item, "Question");
444 var answer = Hoenderdaal.Repository.Helpers.ItemHelper.GetItemValue<string>(faq.Item, "Answer");
445
446 <div class="faqlist__accordion" id="@collapseId">
447 <button type="button" class="faqlist__accordion__button" onclick="toggle('#@collapseId')" data-toggle="collapse" data-target="#@collapseId">
448 <div class="faqlist__accordion__button__question">
449 @question
450 </div>
451 <div class="faqlist__accordion__button__icon">
452 <?xml version="1.0" encoding="UTF-8"?>
453 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="26pt" height="26pt" viewBox="0 0 26 26" version="1.1">
454 <g id="surface220560">
455 <path style=" stroke:none;fill-rule:nonzero;fill:rgb(17.647059%,25.098039%,30.588235%);fill-opacity:1;" d="M 13 15.40625 L 21.765625 6.820312 C 22.15625 6.4375 22.78125 6.441406 23.171875 6.828125 L 24.707031 8.363281 C 25.097656 8.757812 25.097656 9.390625 24.703125 9.78125 L 13.707031 20.707031 C 13.511719 20.902344 13.257812 21 13 21 C 12.742188 21 12.488281 20.902344 12.292969 20.707031 L 1.296875 9.78125 C 0.902344 9.390625 0.902344 8.757812 1.292969 8.363281 L 2.828125 6.828125 C 3.21875 6.441406 3.84375 6.4375 4.234375 6.820312 Z M 13 15.40625 "/>
456 </g>
457 </svg>
458
459 </div>
460 </button>
461 <div id="@collapseId" class="faqlist__accordion__content" aria-labelledby="@faqId">
462 <div>
463 @if (!string.IsNullOrWhiteSpace(image) && image.Length > 10)
464 {
465 <img src="@image" loading="lazy" decoding="async" />
466 }
467 @answer
468 </div>
469 </div>
470 </div>
471 }
472 </div>
473 </div>
474 }
475
476 @if (hasFaqs)
477 {
478 <div class="product-detail__content__item faqlist">
479 <a id="product-faqs"></a>
480 <h2 class="product-detail">@Translate("ProductDetail_Title_Faqs", "FAQ")</h2>
481 <div>
482 @foreach (var faq in productFaqs)
483 {
484 var faqId = string.Format("Faq_{0}", faq.ID);
485 var collapseId = string.Format("Collapse_{0}", faq.ID);
486 var image = Hoenderdaal.Repository.Helpers.ItemHelper.GetItemValue<string>(faq.Item, "Image");
487 var question = Hoenderdaal.Repository.Helpers.ItemHelper.GetItemValue<string>(faq.Item, "Question");
488 var answer = Hoenderdaal.Repository.Helpers.ItemHelper.GetItemValue<string>(faq.Item, "Answer");
489 <div class="faqlist__accordion" id="@collapseId">
490 <button type="button" class="faqlist__accordion__button" onclick="toggle('#@collapseId')" data-toggle="collapse" data-target="#@collapseId">
491 <div class="faqlist__accordion__button__question">
492 @question
493 </div>
494 <div class="faqlist__accordion__button__icon">
495 <?xml version="1.0" encoding="UTF-8"?>
496 <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="26pt" height="26pt" viewBox="0 0 26 26" version="1.1">
497 <g id="surface220560">
498 <path style=" stroke:none;fill-rule:nonzero;fill:rgb(17.647059%,25.098039%,30.588235%);fill-opacity:1;" d="M 13 15.40625 L 21.765625 6.820312 C 22.15625 6.4375 22.78125 6.441406 23.171875 6.828125 L 24.707031 8.363281 C 25.097656 8.757812 25.097656 9.390625 24.703125 9.78125 L 13.707031 20.707031 C 13.511719 20.902344 13.257812 21 13 21 C 12.742188 21 12.488281 20.902344 12.292969 20.707031 L 1.296875 9.78125 C 0.902344 9.390625 0.902344 8.757812 1.292969 8.363281 L 2.828125 6.828125 C 3.21875 6.441406 3.84375 6.4375 4.234375 6.820312 Z M 13 15.40625 "/>
499 </g>
500 </svg>
501
502 </div>
503 </button>
504 <div id="@collapseId" class="faqlist__accordion__content" aria-labelledby="@faqId">
505 <div>
506 @if (!string.IsNullOrWhiteSpace(image) && image.Length > 10)
507 {
508 <img src="@image" loading="lazy" decoding="async" />
509 }
510 @answer
511 </div>
512 </div>
513 </div>
514 }
515 </div>
516 </div>
517 }
518
519 @if (hasRelatedArticles)
520 {
521 <div class="product-detail__content__item" id="product-relatedarticles">
522 <h2>@Translate("ProductDetail_Anchor_RelatedArticles", "Related articles")</h2>
523 <div class="row">
524 @foreach (var related in relatedArticles)
525 {
526
527 var relatedProduct = Dynamicweb.Ecommerce.Services.Products.GetProductById(related.RelatedProductId, related.RelatedProductVariantId, ecomLanguageId);
528 if (relatedProduct != null)
529 {
530 var relatedProductGroupId = relatedProduct.Groups.FirstOrDefault(g => !g.IsTopGroup)?.Id ?? "";
531 if (!string.IsNullOrWhiteSpace(relatedProductGroupId))
532 {
533 var relatedProductUrl = $"/Default.aspx?ID={Dynamicweb.Frontend.PageView.Current().Page.ID}&GroupID={relatedProductGroupId}&ProductID={relatedProduct.Id}&VariantID={relatedProduct.VariantId}";
534 var relatedProductMainImage = relatedProduct.ImageLarge;
535 if (!string.IsNullOrWhiteSpace(relatedProductMainImage))
536 {
537 relatedProductMainImage = $"/Admin/Public/GetImage.ashx?Image={relatedProductMainImage}&altFmImage_path=/Files/Templates/Designs/Hoenderdaal/Images/ecom/Product_NoImage.png&Crop=5&Format=webp&Fillcanvas=1&Height=95&Width=95&Quality=95&Background=FFFFFF";
538 }
539 else
540 {
541 relatedProductMainImage = $"/Admin/Public/GetImage.ashx?Image=/Files/Templates/Designs/Hoenderdaal/Images/ecom/Product_NoImage.png&Crop=5&Format=webp&Fillcanvas=true&Height=95&Width=95&Quality=95";
542 }
543 <div class="col">
544 <a href="@relatedProductUrl">
545 <img src="@relatedProductMainImage" loading="lazy" decoding="async" alt="Related article @relatedProduct.Name.JsEncode()" />
546 <br />
547 @relatedProduct.Name
548 </a>
549 </div>
550 }
551 }
552 }
553 </div>
554 </div>
555 }
556 </div>
557
558 @if (hasIcons || hasVideos || hasProductDownloads)
559 {
560 <div class="product-detail__content__right">
561
562 @if (hasIcons)
563 {
564 <div class="product-detail__content__item">
565 <a id="product-icons"></a>
566 <h2>@Translate("ProductDetail_Anchor_Icons", "Quality marks and applications")</h2>
567 <div class="product-detail__qualitymark">
568 @foreach (var image in icons)
569 {
570 <img src="@image.Value" class="quality-mark" alt="quality mark icon" loading="lazy" decoding="async" />
571 }
572 </div>
573 </div>
574 }
575
576 @if (hasVideos)
577 {
578 <div class="product-detail__content__item">
579 <a id="product-video"></a>
580 <h2>@Translate("ProductDetail_Anchor_Video", "Video")</h2>
581 <div class="text-component__videos">
582 @foreach (var video in productVideos)
583 {
584 var productVideo = video.Value;
585 var youtubeId = Hoenderdaal.Repository.Helpers.StringHelpers.GetYoutubeIdFromUrl(productVideo);
586 <div class="text-component__videos__video">
587 <iframe src="https://youtube.com/embed/@youtubeId" frameborder="0" allow="encrypted-media; picture-in-picture" title="@video.Name.JsEncode()" loading="lazy" allowfullscreen></iframe>
588 </div>
589 }
590 </div>
591 </div>
592 }
593
594 @if (hasProductDownloads)
595 {
596 <div class="product-detail__content__item">
597 <a id="product-downloads"></a>
598 <h2>@Translate("ProductDetail_Anchor_Downloads", "Downloads")</h2>
599 <div class="product-detail__downloads">
600 @foreach (var download in productDownloads)
601 {
602 var downloadInfo = EcomHelper.GetDownloadInfo(Dynamicweb.Frontend.PageView.Current().AreaID, download);
603 if (downloadInfo != null)
604 {
605 <a href="@downloadInfo.Link" class="download" download="@downloadInfo.Name" target="_blank">
606 <div class="download__icon">
607 <img src="@($"{baseUrl}/Images/icons/download.svg")" alt="download icon" loading="lazy" decoding="async" />
608 </div>
609 <div class="download__name">
610 @downloadInfo.Name (@downloadInfo.Size)
611 </div>
612 <div class="download__downloadtext">@Translate("Download", "Download")</div>
613 </a>
614 }
615 else
616 {
617 var downloadName = download.Value;
618 if (downloadName.StartsWith("/") && downloadName.Contains('.'))
619 {
620 // Get the file name with extension
621 string fileNameWithExtension = System.IO.Path.GetFileName(downloadName);
622
623 // Extract the name (without extension)
624 string name = System.IO.Path.GetFileNameWithoutExtension(fileNameWithExtension);
625
626 // Extract the document type (extension without the dot)
627 string type = System.IO.Path.GetExtension(fileNameWithExtension).TrimStart('.');
628
629 downloadName = $"<div class='source'><div>{name}</div><div>.{type}</div></div>";
630 }
631
632 <a href="@download.Value" class="download" target="_blank">
633 <div class="download__icon">
634 <img src="@($"{baseUrl}/Images/icons/download.svg")" alt="download icon" loading="lazy" decoding="async" />
635 </div>
636 <div class="download__name">
637 @downloadName
638 </div>
639 <div class="download__downloadtext">@Translate("Download", "Download")</div>
640 </a>
641 }
642 }
643 </div>
644 </div>
645 }
646
647 </div>
648 }
649
650 </div>
651 </div>
652
653 <!-- Modal -->
654 <div class="modal modal-v2 modal--dynaplus-dark fade" id="variant-modal" data-backdrop="static">
655 <div class="modal__content">
656 <div class="modal__content__header">
657 <div class="modal__content__header__title">@Translate("ProductDetail_Variants", "Variants")</div>
658 <div class="modal__content__header__close">
659 <a href="#" type="button" class="btn btn--modalclose modal__content__header__close__link"><abbr title="@Translate("Modal_Close", "Close")"></abbr></a>
660 </div>
661 </div>
662 <div class="modal__content__body">
663 @RenderVariants(variants, productLink, false, countryCode)
664 </div>
665 </div>
666 </div>
667
668 </div>
669
670 @helper RenderVariants(Hoenderdaal.Models.ViewModels.ProductVariantsViewModel variants, string baseProductLink, bool collapse, string countryCode)
671 {
672 if (variants?.Variants != null && variants.Variants.Any())
673 {
674 var sortedVariants = variants.Variants.OrderBy(variant => Hoenderdaal.Repository.Helpers.EcomHelper.GetVariantSortValueAsString(variant, countryCode));
675
676 collapse = collapse && sortedVariants.Count() > 6;
677 var bodyClass = collapse ? "variant-table-body variant-table-body--collapsed" : "variant-table-body";
678 <div class="variant-table">
679 <div class="variant-table-row variant-table-header">
680 <div class="variant-table-row-item variant-table-row-item--size">@Translate("ProductDetail_Variants_Size", "Size")</div>
681 <div class="variant-table-row-item variant-table-row-item--threadlength">@Translate("ProductDetail_Variants_ThreadLength", "Thread length")</div>
682 <div class="variant-table-row-item variant-table-row-item--content">@Translate("ProductDetail_Variants_Content", "Content")</div>
683 <div class="variant-table-row-item variant-table-row-item--sku">@Translate("ProductDetail_Variants_ArticleNumber", "Article number")</div>
684 <div class="variant-table-row-item variant-table-row-item--drive">@Translate("ProductDetail_Variants_Drive", "Drive")</div>
685 </div>
686 <div class="@bodyClass">
687 @foreach (var variant in sortedVariants)
688 {
689 var link = $"{baseProductLink}&VariantID={variant.VariantId}";
690 var variantValues = EcomHelper.GetDisplayValues(variant.Number);
691 <a href="@link" class="variant-table-row">
692
693 @if (string.IsNullOrEmpty(variantValues.Value1) && string.IsNullOrEmpty(variantValues.Value1))
694 {
695 <div class="variant-table-row-item variant-table-row-item--full" data-header="@Translate("ProductDetail_Variants_Size", "Size")">@variant.Name</div>
696 }
697 else
698 {
699 var variantSize = $"{variantValues.Value1} x {variantValues.Value2}";
700 var packageQuantityValue = Hoenderdaal.Repository.Helpers.EcomHelper.GetProductField<int>(variant, Hoenderdaal.Models.Constants.StringConstants.ProductFields.PackageQuantity);
701 var packageQuantity = packageQuantityValue > 0 ? packageQuantityValue.ToString() : "";
702
703 <div class="variant-table-row-item variant-table-row-item--size" data-header="@Translate("ProductDetail_Variants_Size", "Size")">@variantSize</div>
704 <div class="variant-table-row-item variant-table-row-item--threadlength" data-header="@Translate("ProductDetail_Variants_ThreadLength", "Thread length")">@variantValues.Value3</div>
705 <div class="variant-table-row-item variant-table-row-item--content" data-header="@Translate("ProductDetail_Variants_Content", "Content")">@packageQuantity</div>
706 <div class="variant-table-row-item variant-table-row-item--sku" data-header="@Translate("ProductDetail_Variants_ArticleNumber", "Article number")">@variantValues.ProductNumber</div>
707 <div class="variant-table-row-item variant-table-row-item--drive" data-header="@Translate("ProductDetail_Variants_Drive", "Drive")">
708 @if (!string.IsNullOrEmpty(variantValues.Value4))
709 {
710 <span class="@variantValues.Value4.ToLower()">
711 @variantValues.Value4
712 </span>
713 }
714 </div>
715 }
716 </a>
717 }
718 </div>
719
720 @if (collapse)
721 {
722 <div class="variant-table-footer">
723 <button class="show-more-button" onclick="showVariants()">@Translate("ProductDetail_Variants_ShowMore", "Show more")</button>
724 </div>
725 }
726 </div>
727
728
729 }
730 }
731
732 @if (hasTechnicalInfo || hasFaqs)
733 {
734 @SnippetStart("JavaScriptBottom")
735 <script>
736 function toggle(paragraphId) {
737 // toggle clicked faq paragraph
738 var paragraph = $(paragraphId);
739 paragraph.toggleClass("faqlist__accordion--expanded");
740
741 // close any other open faq paragraph
742 $(".faqlist__accordion:not(" + paragraphId + ")").removeClass("faqlist__accordion--expanded");
743
744 // scroll to selected faq paragraph
745 if (paragraph.hasClass('faqlist__accordion--expanded')) {
746 $('body,html').animate({
747 scrollTop: $(paragraphId).offset().top - 150
748 }, 800);
749 }
750 }
751 </script>
752 @SnippetEnd("JavaScriptBottom")
753 }
754 @SnippetStart("JavaScriptBottom")
755 <script>
756
757 var largeOptions = {
758 slidesToShow: 1,
759 slidesToScroll: 1,
760 arrows: true,
761 fade: true,
762 mobileFirst: true,
763 infinite: false,
764 //centerMode: true,
765 asNavFor: '.product-detail__variant__slider__thumbnails'
766 }
767
768 var thumbnailOptions = {
769 slidesToShow: @productImages.Count > 4 ? 4 : @productImages.Count,
770 slidesToScroll: 1,
771 mobileFirst: true,
772 centerMode: false,
773 infinite: false,
774 arrows: true,
775 variableWidth: false,
776 asNavFor: '.product-detail__variant__slider__large',
777 focusOnSelect: true
778 }
779
780 var debounce = (func, delay) => {
781 let inDebounce
782 return function () {
783 const context = this
784 const args = arguments
785 clearTimeout(inDebounce)
786 inDebounce = setTimeout(() => func.apply(context, args), delay)
787 }
788 }
789
790 function showVariants() {
791 Modal.OpenModal("variant-modal");
792 }
793
794 $(window).resize( debounce(() => {
795 var largeSlider = $('.product-detail__variant__slider__large');
796 largeSlider.slick('unslick');
797 largeSlider.slick(largeOptions);
798
799 var thumbnailSlider = $('.product-detail__variant__slider__thumbnails');
800 thumbnailSlider.slick('unslick');
801 thumbnailSlider.slick(thumbnailOptions);
802 }, 100));
803
804 </script>
805 @SnippetEnd("JavaScriptBottom")
806 @SnippetStart("JavaScriptDocReady")
807 $('.product-detail__variant__slider__thumbnails').slick(thumbnailOptions);
808 $('.product-detail__variant__slider__large').slick(largeOptions);
809 @SnippetEnd("JavaScriptDocReady")
810