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 : "&amp;GroupID=" + Dynamicweb.Context.Current.Request["GroupID"]; 46 var productLink = "/Default.aspx?ID=" + Pageview.Page.ID + groupIdQueryParameter + "&amp;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\">&euro; {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) ? "&nbsp;" : $"{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 = "&nbsp;/&nbsp;"; 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>&nbsp;@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