Log in to view your wholesale prices.

Error executing template "Designs/TheGift_generated/eCom/Product/pdp.cshtml"
System.FormatException: Input string was not in a correct format.
   at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal)
   at System.Number.ParseDecimal(String value, NumberStyles options, NumberFormatInfo numfmt)
   at CompiledRazorTemplates.Dynamic.RazorEngine_50a89714197b4dae93af51adaeb43401.Execute() in D:\dynamicweb.net\Solutions\TheGift\Production\files\Templates\Designs\TheGift_generated\eCom\Product\pdp.cshtml:line 359
   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 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 2 @using Dynamicweb; 3 @using Bluedesk.DynamicWeb.ItemTypes.Pages; 4 @using Bluedesk.DynamicWeb.ItemTypes.BaseSolution; 5 @using Bluedesk.Tools.DynamicWeb.ExtensionMethods; 6 @using System.Linq; 7 @using System.Globalization; 8 @using Dynamicweb.Content; 9 @using Bluedesk.DynamicWeb.ItemTypes.Settings.Configuration; 10 11 @{ 12 string listId = "product_detail"; 13 string listName = "Product detail"; 14 } 15 16 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 17 @using System.Globalization; 18 @using Dynamicweb; 19 @using Dynamicweb.Content.Items; 20 @using Bluedesk.DynamicWeb.ItemTypes.BaseSolution; 21 @using Bluedesk.DynamicWeb.ItemTypes.Settings.Configuration; 22 23 @using TheGift.Tools; 24 @using TheGift.Tools.Models; 25 26 @{ 27 28 var colorService = new ColorSwatchService(); 29 30 /*** Masterconfig ***/ 31 32 var master_configuration = Dynamicweb.Services.Pages.GetPageByNavigationTag(Pageview.AreaID, "MasterConfiguration"); 33 MasterConfig mc = master_configuration.Item.ToCodeFirstItem<MasterConfig>(); 34 35 EcomConfig EcommerceConfiguration = mc.EcomConfiguration; 36 ProductDetailConfig ProductDetailConfiguration = mc.ProductDetailConfiguration; 37 ProductOverviewConfig ProductOverviewConfiguration = mc.ProductOverviewConfiguration; 38 39 /*** BEGIN Webshop Variables ***/ 40 41 WebshopPage webshopPage = Pageview.Page.Item.ToCodeFirstItem<WebshopPage>(); 42 43 /*** END Webshop Variables ***/ 44 45 /*** BEGIN Configuration Bools ***/ 46 47 bool FormattedBool = EcommerceConfiguration.FormattedPrices; 48 bool WithVATBool = Pageview.Area.EcomPricesWithVat == "True"; 49 bool pricesWithoutVatForUsers = EcommerceConfiguration.ShowPricesWithoutVatForUsers; 50 bool pricesWithoutVatForValidVat = EcommerceConfiguration.ShowPricesWithoutVatWhenValidVatNumber; 51 if (pricesWithoutVatForUsers && !pricesWithoutVatForValidVat && Pageview.User != null) 52 { 53 WithVATBool = false; 54 } 55 if (pricesWithoutVatForValidVat && Pageview.User != null && !string.IsNullOrWhiteSpace(Pageview.User.VatRegNumber)) 56 { 57 WithVATBool = false; 58 } 59 WithVATBool = Pageview.User != null ? Pageview.Area.EcomPricesWithVat == "True" : true; 60 61 bool hidePricesForGuests = EcommerceConfiguration.HidePricesForGuests; 62 bool hideShoppingCartForGuests = EcommerceConfiguration.HideShoppingCartForGuests; 63 bool hideStockForGuests = EcommerceConfiguration.HideStockForGuests; 64 bool hideZeroPrices = EcommerceConfiguration.HideZeroPrices; 65 bool enableAddToCartForZeroPrices = EcommerceConfiguration.AddToCartAllowZeroPrices; 66 bool enableAddToCartForOutOfStock = EcommerceConfiguration.AddToCartAllowOutOfStock; 67 string stockFormat = EcommerceConfiguration.StockFormat; 68 bool showReviewTab = ProductDetailConfiguration.ShowProductDetailReviewTab; 69 bool allowBackInStockNotifications = EcommerceConfiguration.AllowBackInStockNotifications; 70 bool allowBackInStockNotificationsForGuests = EcommerceConfiguration.AllowBackInStockNotificationsForGuests; 71 bool displayBackInStockNotifications = !allowBackInStockNotificationsForGuests && Pageview.User == null ? false : allowBackInStockNotifications; 72 73 74 /*** END Configuration Bools ***/ 75 var p = Dynamicweb.Ecommerce.Services.Products.GetProductById(GetString("Ecom:Product.ID"), GetString("Ecom:Product.VariantID"), Pageview.Area.EcomLanguageId); 76 77 System.Web.HttpContext.Current.Session["productid"] = GetString("Ecom:Product.ID"); 78 79 var designRoot = "/Files/Templates/Designs/" + Pageview.Area.Layout.Design.Name; 80 81 bool isVariant = !string.IsNullOrWhiteSpace(GetString("Ecom:Product.VariantID")); 82 bool hasVariants = !isVariant ? GetLoop("VariantCombinations").Count > 0 : false; 83 84 string Manufacturer = GetString("Ecom:Manufacturer.Name"); 85 86 string productName = GetString("Ecom:Product.Name"); 87 System.Web.HttpContext.Current.Items["CurrentProductName"] = productName; 88 89 string productNumber = GetString("Ecom:Product.Number"); 90 91 string productShortDescription = GetString("Ecom:Product.ShortDescription"); 92 string productLongDescription = GetString("Ecom:Product.LongDescription"); 93 bool hasShortDescription = !string.IsNullOrWhiteSpace(productShortDescription); 94 bool hasLongDescription = !string.IsNullOrWhiteSpace(productLongDescription); 95 96 bool hasSpecifications = GetLoop("ProductCategories").Any(c => c.GetLoop("ProductCategoryFields").Any(d => !string.IsNullOrWhiteSpace(d.GetString("Ecom:Product.CategoryField.Value")))); 97 98 string manufacturerLogo = GetString("Ecom:Manufacturer.Logo"); 99 100 string price = GetString("Ecom:Product.Price"); 101 string priceFormatted = GetString("Ecom:Product.Price.PriceFormatted"); 102 bool hasDiscount = GetBoolean("Ecom:Product.HaveDiscount"); 103 bool pricezero = GetBoolean("Ecom:Product.Price.IsZero"); 104 105 string WithVATSuffix = WithVATBool ? "WithVAT" : "WithoutVAT"; 106 string FormattedSuffix = FormattedBool ? "Formatted" : ""; 107 108 string ProductdetailPriceSuffixWithVAT = Translate("Productdetail.Price.Suffix.WithVAT", "Incl. VAT"); 109 string ProductdetailPriceSuffixWithoutVAT = Translate("Productdetail.Price.Suffix.WithoutVAT", "Excl. VAT"); 110 111 string ProductdetailPriceSuffix = WithVATBool ? ProductdetailPriceSuffixWithVAT : ProductdetailPriceSuffixWithoutVAT; 112 113 string discountPriceValue = GetString("Ecom:Product.Discount.Price"); 114 double yourProfitValue = GetDouble("Ecom:Product.Discount.TotalAmount.Price.Value"); 115 string yourProfitValueFormatted = GetString("Ecom:Product.Discount.TotalAmount.Price" + WithVATSuffix + FormattedSuffix); 116 117 double productRating = GetDouble("Comments.Rating"); 118 var Comments = GetLoop("Comments"); 119 int productRatingCount = GetInteger("Comments.Count"); 120 var productReviewPercentage = (productRating * 10) * 2 + "%"; 121 122 double stockSize = hasVariants ? GetLoop("VariantInfos").Sum(item => item.GetDouble("Stock")) : GetDouble("Ecom:Product.Stock"); 123 string stock = stockSize.ToString(stockSize % 1 == 0 ? "N0" : "N2", CultureInfo.GetCultureInfo(Pageview.Area.CultureInfo.TwoLetterISOLanguageName)) ?? ""; 124 string stockText = GetString("Ecom:Product:Stock.Text"); 125 bool neverOutOfStock = GetBoolean("Ecom:Product.NeverOutOfStock"); 126 bool inStock = stockSize > 0 || neverOutOfStock; 127 string stockStateClass = inStock ? "pdp-stockstate--instock" : "pdp-stockstate--outofstock"; 128 129 string productTagline = GetString("Ecom:Product:Field.ProductTagline.Value"); 130 string productTaglineInfo = GetString("Ecom:Product:Field.ProductTaglineInfo.Value"); 131 string productDetailPageTagline = webshopPage.Tagline; 132 string productDetailPageTaglineInfo = webshopPage.TaglineInfo; 133 134 var productDetailUSPList = webshopPage.ProductDetailUSPs; 135 136 string defaultImage = Dynamicweb.Ecommerce.Services.ProductImages.GetImagePath(p); 137 var productid = GetString("Ecom:Product.ID"); 138 string groupid = GetString("Ecom:Product.PrimaryOrFirstGroupID"); 139 string productVariantId = GetString("Ecom:Product.VariantID"); 140 string addToCartLink = string.Format("/products/checkout?CartCmd=Add&ProductID={0}&Redirect=/products/checkout", productid); 141 List<LoopItem> detailImages = GetLoop("Details"); 142 List<LoopItem> productDiscounts = GetLoop("ProductDiscounts"); 143 List<string> productImages = new List<string>(); 144 145 if (!string.IsNullOrWhiteSpace(defaultImage)) 146 { 147 productImages.Add(defaultImage); 148 } 149 150 string YoutubeProductVideo = GetString("Ecom:Product:Field.YoutubeProductVideo.Value"); 151 bool hasYoutubeVideo = !string.IsNullOrWhiteSpace(YoutubeProductVideo) && YoutubeProductVideo != "Files/"; 152 153 var PerfionThumbnails = GetString("Ecom:Product:Field.AdditionalImages.Value").Split(';'); 154 155 foreach (var PerfionImage in PerfionThumbnails) 156 { 157 if (!string.IsNullOrWhiteSpace(PerfionImage)) 158 { 159 productImages.Add("Files/" + PerfionImage); 160 } 161 } 162 163 /* foreach (var image in detailImages) 164 { 165 var isImageAsset = image.GetString("Ecom:Product:Detail.Image"); 166 var link = image.GetString("Ecom:Product:Detail.Image.Clean"); 167 if (!string.IsNullOrWhiteSpace(isImageAsset) && !string.IsNullOrWhiteSpace(link) && !productImages.Contains(link)) 168 { 169 productImages.Add(link); 170 } 171 } */ 172 173 System.Web.HttpContext.Current.Items["productDetailActive"] = true; 174 175 var productNumberLabel = Translate("Productdetail.ArticleNumber.Prefix", "Artikelnummer:"); 176 var originalPriceLabel = Translate("Productdetail.OriginalPriceLabel", "Adviesprijs"); 177 var yourProfitLabel = Translate("Productdetail.YourProfitLabel", "Uw voordeel:"); 178 var inShoppingCartLabel = Translate("ProductDetail.InShoppingCart", "In winkelwagen"); 179 180 // Payment provider logo's 181 var selectedPaymentLogos = Pageview.Area.Item["FooterPaymentLogos"]; 182 183 bool enableShoppingCart = hideShoppingCartForGuests && Pageview.User == null ? false : Pageview.Area.Item["ConfigModuleShoppingCart"] != null ? (bool)Pageview.Area.Item["ConfigModuleShoppingCart"] : false; 184 bool displayPrice = hidePricesForGuests ? Pageview.User != null : true; 185 bool enableProductCompare = Pageview.Area.Item["ConfigModuleProductCompare"] != null ? (bool)Pageview.Area.Item["ConfigModuleProductCompare"] : false; 186 bool enableProductFavorites = Pageview.User == null ? false : Pageview.Area.Item["ConfigModuleFavoriteLists"] != null ? (bool)Pageview.Area.Item["ConfigModuleFavoriteLists"] : false; 187 bool enableProductStock = hideStockForGuests ? Pageview.User != null : true; 188 189 bool enableProductShoppingCart = enableShoppingCart; 190 if (!enableAddToCartForZeroPrices && pricezero) 191 { 192 enableProductShoppingCart = false; 193 } 194 if (!enableAddToCartForOutOfStock && !inStock) 195 { 196 enableProductShoppingCart = false; 197 } 198 if (GetBoolean("Ecom:Product.Discontinued")) 199 { 200 enableProductShoppingCart = false; 201 } 202 203 string specifier = "G"; 204 CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US"); 205 206 string originalProductPrice = GetString("Ecom:Product.Price.Price" + WithVATSuffix + FormattedSuffix); 207 string discountProductPrice = GetString("Ecom:Product.Discount.Price.Price" + WithVATSuffix + FormattedSuffix); 208 double discountProductPriceDouble = GetDouble("Ecom:Product.Discount.Price.Price"); 209 string gtmPrice = GetDouble("Ecom:Product.Price.Price.Value").ToString(specifier, culture); 210 string gtmDiscount = GetDouble("Ecom:Product.Discount.TotalAmount.Price.Value").ToString(specifier, culture); 211 string gtmValue = GetDouble("Ecom:Product.Discount.Price.Value").ToString(specifier, culture); 212 double discountPercentage = Math.Round((((GetDouble("Ecom:Product.Discount.Price.Price" + WithVATSuffix) - GetDouble("Ecom:Product.Price.Price" + WithVATSuffix)) / GetDouble("Ecom:Product.Price.Price" + WithVATSuffix)) * 100)); 213 214 string informativePrice = GetString("Ecom:Product.InformativePrice.Price" + WithVATSuffix + FormattedSuffix); 215 if (EcommerceConfiguration.UseInformativePriceAsFromPrice && !string.IsNullOrWhiteSpace(informativePrice)) 216 { 217 double informativePriceValue = GetDouble("Ecom:Product.InformativePrice.Price" + WithVATSuffix); 218 hasDiscount = discountProductPriceDouble < informativePriceValue; 219 if (hasDiscount) 220 { 221 originalProductPrice = informativePrice; 222 discountPercentage = Math.Round((((GetDouble("Ecom:Product.Price.Price" + WithVATSuffix) - informativePriceValue) / informativePriceValue) * 100)); 223 yourProfitValue = informativePriceValue - GetDouble("Ecom:Product.Price.Price" + WithVATSuffix); 224 yourProfitValueFormatted = WithVATBool ? new PriceInfo { PriceWithVAT = yourProfitValue }.PriceWithVATFormatted : new PriceInfo { PriceWithoutVAT = yourProfitValue }.PriceWithoutVATFormatted; 225 } 226 } 227 228 string retailPrice = ""; 229 bool displayRetailPrice = EcommerceConfiguration.DisplayRetailPrice; 230 if (EcommerceConfiguration.DisplayRetailPriceForUsers && Pageview.User == null) 231 { 232 displayRetailPrice = false; 233 } 234 if (displayRetailPrice) 235 { 236 string priceFieldName = EcommerceConfiguration.RetailPriceField; 237 if (EcommerceConfiguration.RetailPriceIsDbPrice) 238 { 239 PriceRaw customerPriceRaw = new PriceRaw(GetDouble("Ecom:Product.DBPrice"), Dynamicweb.Ecommerce.Common.Context.Currency); 240 PriceCalculated customerPrice = new Dynamicweb.Ecommerce.Prices.PriceCalculated(customerPriceRaw); 241 if (customerPrice.Price > 0) 242 { 243 retailPrice = customerPrice.PriceWithVATFormatted; 244 } 245 } 246 else if (!string.IsNullOrWhiteSpace(priceFieldName)) 247 { 248 double customerPriceValue = GetDouble($"Ecom:Product:Field.{priceFieldName}.Value"); 249 if (customerPriceValue > 0) 250 { 251 retailPrice = WithVATBool ? new PriceInfo { PriceWithVAT = customerPriceValue }.PriceWithVATFormatted : new PriceInfo { PriceWithoutVAT = customerPriceValue }.PriceWithoutVATFormatted; 252 } 253 } 254 } 255 256 PriceInfo minPrice = null; 257 string minPriceFormatted = ""; 258 PriceInfo maxPrice = null; 259 string maxPriceFormatted = ""; 260 261 if (hasVariants) 262 { 263 minPrice = p.VariantCombinations.OrderBy(c => c.Product.Price.Price).FirstOrDefault().Product.Price; 264 maxPrice = p.VariantCombinations.OrderByDescending(c => c.Product.Price.Price).FirstOrDefault().Product.Price; 265 minPriceFormatted = WithVATBool ? minPrice.PriceWithVATFormatted : minPrice.PriceWithoutVATFormatted; 266 maxPriceFormatted = WithVATBool ? maxPrice.PriceWithVATFormatted : maxPrice.PriceWithoutVATFormatted; 267 } 268 269 bool displayProductPrice = displayPrice; 270 if (hideZeroPrices && pricezero) 271 { 272 if ((hasVariants && !isVariant)) 273 { 274 if (minPrice.Price == 0 && maxPrice.Price == 0) 275 { 276 displayProductPrice = false; 277 } 278 } 279 else 280 { 281 displayProductPrice = false; 282 } 283 } 284 285 string productRibbon = GetString("Ecom:Product:Field.Product_Ribbon.Value"); 286 string productRibbonStyle = GetString("Ecom:Product:Field.RibbonStyle.Value"); 287 288 int QuotePageID = Bluedesk.Tools.DynamicWeb.Generic.PageHelper.GetPageIDByNavigationTag("QuoteForm", Pageview.AreaID); 289 var buttonIconClass = Pageview.Area.Item["Global_button_icon"] != null ? Pageview.Area.Item["Global_button_icon"].ToString().Replace("+", " ") : "fal fa-arrow-right"; 290 var minimumQuantity = GetDouble("Ecom:Product.PurchaseMinimumQuantity") == 0 ? 1 : GetDouble("Ecom:Product.PurchaseMinimumQuantity"); 291 var quantityStep = GetDouble("Ecom:Product.PurchaseQuantityStep") == 0 ? 1 : GetDouble("Ecom:Product.PurchaseQuantityStep"); 292 293 var productCategory = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(GetString("Ecom:Product.PrimaryOrFirstGroupID")); 294 string productCategoryName = productCategory != null ? productCategory.Name.Replace("''", "\\\"").Replace("'", "") : ""; 295 296 string AddToCartButtonBackgroundColor = EcommerceConfiguration.AddToCartButtonBackgroundColor; 297 if (!string.IsNullOrWhiteSpace(AddToCartButtonBackgroundColor)) 298 { 299 AddToCartButtonBackgroundColor = !AddToCartButtonBackgroundColor.Contains("#") ? colorService.GetHexColor(Pageview.AreaID, AddToCartButtonBackgroundColor) : AddToCartButtonBackgroundColor; 300 } 301 302 bool enableSpecificationsSidebar = !ProductDetailConfiguration.ShowSpecificationsAsTab; 303 304 bool showTitleAboveInfoBlock = ProductDetailConfiguration.ShowTitleAboveInfoBlock; 305 bool showVariantInfo = ProductDetailConfiguration.ShowVariantInfo; 306 } 307 308 @* TheGift Specific *@ 309 @{ 310 if (Pageview.User != null) 311 { 312 var detailService = new Dynamicweb.Ecommerce.Products.DetailService(); 313 var productAssets = p != null ? detailService.GetDetails(p, "", true).OrderBy(i => i.Value).ToList() : null; 314 if (productAssets != null) 315 { 316 var noSfeerAssets = productAssets.Where(a => a.GroupId != 4).ToList(); 317 int assetsIndex = 0; 318 int targetIndex = noSfeerAssets.Count == 0 ? 0 : 1; 319 foreach (var item in productAssets.ToList()) 320 { 321 if (item.GroupId == 4) 322 { 323 var sfeerItem = productAssets[assetsIndex]; 324 productAssets.RemoveAt(assetsIndex); 325 productAssets.Insert(targetIndex, item); 326 targetIndex++; 327 } 328 assetsIndex++; 329 } 330 foreach (var image in productAssets) 331 { 332 var link = image.Value; 333 if (!string.IsNullOrWhiteSpace(link) && !productImages.Contains(link)) 334 { 335 productImages.Add(link); 336 } 337 } 338 } 339 } 340 341 int productShortDescriptionCount = productShortDescription.Count(); 342 bool hasReadMore = false; 343 if (productShortDescriptionCount > 250) 344 { 345 hasReadMore = true; 346 productShortDescription = productShortDescription.Substring(0, 250) + "..."; 347 } 348 349 List<FutureStockModel> expectedStock = TheGift.Tools.FutureStock.GetFutureStock(productid); 350 bool hasExpectedStock = expectedStock.Count > 0; 351 352 if (!enableAddToCartForOutOfStock && hasExpectedStock) 353 { 354 enableProductShoppingCart = true; 355 } 356 stockStateClass = inStock ? "pdp-stockstate--instock" : hasExpectedStock ? "pdp-stockstate--temporaryoutofstock" : "pdp-stockstate--outofstock"; 357 358 string quantityInOptions = !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.QtyInOptions.Value")) ? GetString("Ecom:Product:Field.QtyInOptions.Value") : "0"; 359 decimal quantityDecimal = decimal.Parse(quantityInOptions); 360 string quantityInOptionsFormatted = ((int)quantityDecimal).ToString(quantityDecimal % 1 == 0 ? "N0" : "N2", System.Globalization.CultureInfo.GetCultureInfo(Pageview.Area.CultureInfo.TwoLetterISOLanguageName)); 361 362 var wrongGroupIds = new[] { 2022, 2014 }; 363 bool isInWrongGroup = Pageview.User?.Groups.Any(group => wrongGroupIds.Contains(group.ID)) ?? false; 364 } 365 366 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 367 @using Dynamicweb; 368 @using Bluedesk.DynamicWeb.ItemTypes.Pages; 369 @using Bluedesk.Tools.DynamicWeb.ExtensionMethods; 370 @using System.Linq; 371 @using System.Globalization; 372 @using Dynamicweb.Content; 373 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites; 374 375 @using TheGift.Tools; 376 @using TheGift.Tools.Models; 377 378 @helper RenderInfoContentElement(string ItemName, string header, string anchor) 379 { 380 if (!string.IsNullOrWhiteSpace(GetString(ItemName))) 381 { 382 <section id="@anchor" class="pdp-specifications-info__wrapper"> 383 @if(!string.IsNullOrWhiteSpace(header)) 384 { 385 <h2 class="pdp-specifications-info__header">@header</h2> 386 } 387 <section class="pdp-specifications-info__body"> 388 @GetString(ItemName) 389 </section> 390 <div class="pdp-specifications-info__showmore"> 391 <section class="pdp-specifications-info__showmoregradient"></section> 392 <button class="pdp-button pdp-button--inline pdp-button--showmore" type="button">@Translate("Productdetail.Info.ShowMore", "Show more")</button> 393 <button class="pdp-button pdp-button--inline pdp-button--showless" type="button" style="display: none;">@Translate("Productdetail.Info.ShowLess", "Show less")</button> 394 </div> 395 </section> 396 } 397 } 398 399 @helper RenderLongDescription() 400 { 401 string longDescription = GetString("Ecom:Product.LongDescription"); 402 string YoutubeProductVideo = GetString("Ecom:Product:Field.YoutubeProductVideo.Value"); 403 bool hasYoutubeVideo = !string.IsNullOrWhiteSpace(YoutubeProductVideo) && YoutubeProductVideo != "Files/"; 404 405 if (!string.IsNullOrWhiteSpace(longDescription)) 406 { 407 <section id="longdescription" class="pdp-specifications-info__wrapper"> 408 <section class="pdp-specifications-info__body"> 409 <div class="pdp-specifications-info__content"> 410 @longDescription 411 </div> 412 @if(hasYoutubeVideo) 413 { 414 <div class="pdp-specifications-info__video"> 415 <lite-youtube videoid="@YoutubeProductVideo" params="controls=1&loop=0&playlist=@YoutubeProductVideo&playsinline=1&modestbranding=1&mute=0&rel=0&enablejsapi=1& origin=@Dynamicweb.Environment.Helpers.LinkHelper.GetHttpDomain()&disablekb=0"></lite-youtube> 416 </div> 417 } 418 </section> 419 <div class="pdp-specifications-info__showmore"> 420 <section class="pdp-specifications-info__showmoregradient"></section> 421 <button class="pdp-button pdp-button--inline pdp-button--showmore" type="button">@Translate("Productdetail.Info.ShowMore", "Show more")</button> 422 <button class="pdp-button pdp-button--inline pdp-button--showless" type="button" style="display: none;">@Translate("Productdetail.Info.ShowLess", "Show less")</button> 423 </div> 424 </section> 425 } 426 } 427 428 @helper renderReview(string name, string body, int Rating) 429 { 430 <li class="pdp-review__list-item"> 431 <div class="pdp-review-item__header"> 432 <p class="pdp-review-item__name">@name</p> 433 @renderReviewIndicator("#DB3125", Rating, "row", false, 0) 434 </div> 435 <p class="pdp-review-item__comment">@body</p> 436 </li> 437 } 438 439 @helper renderProduct(LoopItem product, string productdetailPriceSuffix, string WithVATSuffix, string FormattedSuffix, bool enableShoppingCart, bool displayPrice, bool enableProductStock, string listid, string listname, EcomConfig EcommerceConfiguration) 440 { 441 bool hideZeroPrices = EcommerceConfiguration.HideZeroPrices; 442 bool enableAddToCartForZeroPrices = EcommerceConfiguration.AddToCartAllowZeroPrices; 443 bool enableAddToCartForOutOfStock = EcommerceConfiguration.AddToCartAllowOutOfStock; 444 string stockFormat = EcommerceConfiguration.StockFormat; 445 446 int productDetailPageId = GetPageIdByNavigationTag("ProductOverview"); 447 448 string productName = product.GetString("Ecom:Product.Name"); 449 string productNumber = product.GetString("Ecom:Product.Number"); 450 string productId = product.GetString("Ecom:Product.ID"); 451 string productVariantId = product.GetString("Ecom:Product.VariantID"); 452 string productURL = product.GetString("Ecom:Product.VariantLinkGroup.Clean").Replace($"ID={Pageview.ID.ToString()}&", $"ID={productDetailPageId.ToString()}&"); 453 var p = Dynamicweb.Ecommerce.Services.Products.GetProductById(productId, productVariantId, Pageview.Area.EcomLanguageId); 454 string productImage = Dynamicweb.Ecommerce.Services.ProductImages.GetImagePath(p); 455 456 bool isVariant = !string.IsNullOrWhiteSpace(p.VariantId); 457 bool hasVariants = !isVariant ? product.GetLoop("VariantCombinations").Count > 0 : false; 458 459 var productCategory = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(product.GetString("Ecom:Product.PrimaryOrFirstGroupID")); 460 string productCategoryName = productCategory != null ? productCategory.Name.Replace("''", "\\\"").Replace("'", "") : ""; 461 string productManufacturer = product.GetString("Ecom:Manufacturer.Name"); 462 463 string specifier = "G"; 464 CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US"); 465 466 string gtmDiscount = product.GetDouble("Ecom:Product.Discount.TotalAmount.Price.Value").ToString(specifier, culture); 467 string gtmPrice = product.GetDouble("Ecom:Product.Price.Price.Value").ToString(specifier, culture); 468 469 bool isPriceZero = product.GetDouble("Ecom:Product.Price.Price.Value") <= 0; 470 bool hasDiscount = product.GetBoolean("Ecom:Product.HaveDiscount"); 471 string OriginalPrice = product.GetString("Ecom:Product.Price.Price" + WithVATSuffix + FormattedSuffix); 472 string Price = product.GetString("Ecom:Product.Discount.Price.Price" + WithVATSuffix + FormattedSuffix); 473 string yourDiscount = product.GetString("Ecom:Product.Discount.TotalAmount.Price" + WithVATSuffix + FormattedSuffix); 474 475 string informativePrice = product.GetString("Ecom:Product.InformativePrice.Price" + WithVATSuffix + FormattedSuffix); 476 if (EcommerceConfiguration.UseInformativePriceAsFromPrice && !string.IsNullOrWhiteSpace(informativePrice)) 477 { 478 double informativePriceValue = product.GetDouble("Ecom:Product.InformativePrice.Price"); 479 hasDiscount = product.GetDouble("Ecom:Product.Price.Price") < informativePriceValue; 480 if (hasDiscount) 481 { 482 OriginalPrice = product.GetString("Ecom:Product.InformativePrice.Price" + WithVATSuffix + FormattedSuffix); 483 double yourProfitValue = product.GetDouble("Ecom:Product.InformativePrice.Price" + WithVATSuffix) - product.GetDouble("Ecom:Product.Price.Price" + WithVATSuffix); 484 yourDiscount = WithVATSuffix == "WithVAT" ? new PriceInfo { PriceWithVAT = yourProfitValue }.PriceWithVATFormatted : new PriceInfo { PriceWithoutVAT = yourProfitValue }.PriceWithoutVATFormatted; 485 } 486 } 487 488 string retailPrice = ""; 489 bool displayRetailPrice = EcommerceConfiguration.DisplayRetailPrice; 490 if(EcommerceConfiguration.DisplayRetailPriceForUsers && Pageview.User == null) { 491 displayRetailPrice = false; 492 } 493 if(displayRetailPrice) { 494 string priceFieldName = EcommerceConfiguration.RetailPriceField; 495 if(EcommerceConfiguration.RetailPriceIsDbPrice) { 496 PriceRaw customerPriceRaw = new PriceRaw(product.GetDouble("Ecom:Product.DBPrice"), Dynamicweb.Ecommerce.Common.Context.Currency); 497 PriceCalculated customerPrice = new Dynamicweb.Ecommerce.Prices.PriceCalculated(customerPriceRaw); 498 if(customerPrice.Price > 0) { 499 retailPrice = customerPrice.PriceWithVATFormatted; 500 } 501 } else if(!string.IsNullOrWhiteSpace(priceFieldName)) { 502 double customerPriceValue = product.GetDouble($"Ecom:Product:Field.{priceFieldName}.Value"); 503 if(customerPriceValue > 0) { 504 retailPrice = WithVATSuffix == "WithVAT" ? new PriceInfo { PriceWithVAT = customerPriceValue }.PriceWithVATFormatted : new PriceInfo { PriceWithoutVAT = customerPriceValue }.PriceWithoutVATFormatted; 505 } 506 } 507 } 508 509 PriceInfo minPrice = null; 510 string minPriceFormatted = ""; 511 PriceInfo maxPrice = null; 512 string maxPriceFormatted = ""; 513 514 if(hasVariants) 515 { 516 minPrice = p.VariantCombinations.OrderBy(c => c.Product.Price.Price).FirstOrDefault().Product.Price; 517 maxPrice = p.VariantCombinations.OrderByDescending(c => c.Product.Price.Price).FirstOrDefault().Product.Price; 518 minPriceFormatted = WithVATSuffix == "WithVAT" ? minPrice.PriceWithVATFormatted : minPrice.PriceWithoutVATFormatted; 519 maxPriceFormatted = WithVATSuffix == "WithVAT" ? maxPrice.PriceWithVATFormatted : maxPrice.PriceWithoutVATFormatted; 520 } 521 522 double stockSize = hasVariants ? product.GetLoop("VariantInfos").Sum(item => item.GetDouble("Stock")) : product.GetDouble("Ecom:Product.Stock"); 523 string stockText = product.GetString("Ecom:Product:Stock.Text"); 524 string stockSizeFormatted = stockSize.ToString(stockSize % 1 == 0 ? "N0" : "N2", System.Globalization.CultureInfo.GetCultureInfo(Pageview.Area.CultureInfo.TwoLetterISOLanguageName)); 525 bool neverOutOfStock = product.GetBoolean("Ecom:Product.NeverOutOfStock"); 526 bool isInStock = stockSize > 0 || neverOutOfStock; 527 string stockClass = isInStock ? "products-module__product-stock-state--instock" : "products-module__product-stock-state--outofstock"; 528 529 int productRating = product.GetInteger("Ecom:Product.Rating"); 530 int productCommentCount = product.GetInteger("Ecom:Product.CommentCount"); 531 string productRibbon = product.GetString("Ecom:Product:Field.Product_Ribbon.Value"); 532 string productRibbonStyle = product.GetString("Ecom:Product:Field.RibbonStyle.Value"); 533 534 string yourProfitLabel = Translate("Productdetail.YourProfitLabel", "Uw voordeel:"); 535 536 string manufacturerName = product.GetString("Ecom:Manufacturer.Name"); 537 538 bool enableProductShoppingCart = enableShoppingCart; 539 if(!enableAddToCartForZeroPrices && isPriceZero) { 540 enableProductShoppingCart = false; 541 } 542 if(!enableAddToCartForOutOfStock && !isInStock) { 543 enableProductShoppingCart = false; 544 } 545 if(product.GetBoolean("Ecom:Product.Discontinued")) { 546 enableProductShoppingCart = false; 547 } 548 549 bool displayProductPrice = displayPrice; 550 if(hideZeroPrices && isPriceZero) 551 { 552 if((hasVariants && !isVariant)) 553 { 554 if(minPrice.Price == 0 && maxPrice.Price == 0) 555 { 556 displayProductPrice = false; 557 } 558 } else { 559 displayProductPrice = false; 560 } 561 } 562 563 bool hasExpectedStock = TheGift.Tools.FutureStock.HasFutureStock(productId); 564 stockClass = isInStock ? "products-module__product-stock-state--instock" : hasExpectedStock ? "products-module__product-stock-state--temporaryoutofstock": "products-module__product-stock-state--outofstock"; 565 566 var detailService = new Dynamicweb.Ecommerce.Products.DetailService(); 567 var productAssets = detailService.GetDetails(p, "", true).OrderBy(i => i.Value).ToList(); 568 string alternativeProductImage = productAssets.Count > 0 ? productAssets[0].Value : ""; 569 570 <li class="products-module__slider-cell"> 571 <div class="products-module products-module__slider-cell__innerwrapper"> 572 573 @if (!string.IsNullOrWhiteSpace(productRibbon)) 574 { 575 <p class="products-module__ribbon products-module__ribbon--@productRibbonStyle"> 576 <span class="products-module__ribbon-text">@productRibbon</span> 577 </p> 578 } 579 580 <figure class="products-module__image-container"> 581 @if(!string.IsNullOrWhiteSpace(productImage)) 582 { 583 <img src="/Admin/Public/GetImage.ashx?Image=@productImage&Crop=7&Format=webp&Quality=90&Compression=80&Height=200" alt="@productName" loading="lazy" height="200" width="300" /> 584 } 585 @if(!string.IsNullOrWhiteSpace(alternativeProductImage)) 586 { 587 <img class="products-module__image-container--extra" style="display: none;" src="/Admin/Public/GetImage.ashx?Image=@alternativeProductImage&Crop=7&Format=webp&Quality=90&Compression=80&Height=200" alt="@productName" loading="lazy" height="200" width="300" /> 588 } 589 </figure> 590 591 <h3 class="products-module__product-name"> 592 @if(!string.IsNullOrWhiteSpace(manufacturerName)) 593 { 594 <span class="products-module__product-name--manufacturer">@manufacturerName</span> 595 } 596 <span class="products-module__product-name--product">@productName</span> 597 </h3> 598 599 @if(!string.IsNullOrWhiteSpace(productNumber)) 600 { 601 <p class="products-module__product-article-number">@productNumber</p> 602 } 603 604 @if (productCommentCount > 0) 605 { 606 <div class="products-module__product-review-indicator"> 607 @renderReviewIndicator("gold", productRating, "row", true, productCommentCount) 608 </div> 609 } 610 611 <div class="products-module__product-spacer"></div> 612 613 @if(hasVariants) 614 { 615 <div class="products-module__product-variant-info"> 616 @if(product.GetLoop("VariantCombinations").Count == 1) 617 { 618 <p>@string.Format(Translate("ProductBlockVariantInfo.VariantCount.Single", "{0} variant"), product.GetLoop("VariantCombinations").Count)</p> 619 } 620 else 621 { 622 <p>@string.Format(Translate("ProductBlockVariantInfo.VariantCount.Multiple", "{0} variants"), product.GetLoop("VariantCombinations").Count)</p> 623 } 624 </div> 625 } 626 627 @if (displayPrice && displayProductPrice) 628 { 629 <div class="products-module__product-price-container"> 630 @if(hasVariants) { 631 <section class="pdp-price__subcontainer"> 632 @if(minPrice != maxPrice) 633 { 634 <span class="products-module__product-price">@minPriceFormatted - @maxPriceFormatted</span> 635 } 636 else 637 { 638 <span class="products-module__product-price">@minPriceFormatted</span> 639 } 640 <span class="products-module__product-price-suffix">@productdetailPriceSuffix</span> 641 </section> 642 } 643 else 644 { 645 if (hasDiscount) 646 { 647 <span class="products-module__product-price-original">@OriginalPrice</span> 648 <span class="products-module__product-price">@Price</span> 649 } 650 else 651 { 652 <span class="products-module__product-price">@OriginalPrice</span> 653 } 654 <span class="products-module__product-price-suffix">@productdetailPriceSuffix</span> 655 <p class="products-module__product-customer-price"> 656 @if(Pageview.User != null) 657 { 658 if(!string.IsNullOrWhiteSpace(retailPrice)) 659 { 660 @String.Format(Translate("ProductBlockTitle.AVP", "AVP: {0}"), retailPrice); 661 } 662 } 663 else 664 { 665 @Translate("ProductBlockTitle.AVP.Guests", "Adviesprijs"); 666 } 667 </p> 668 if (hasDiscount) 669 { 670 <p class="products-module__product-your-discount">@Translate("Productdetail.YourProfitLabel", "Uw voordeel:") @yourDiscount</p> 671 } 672 } 673 </div> 674 } 675 676 <section class="products-module__product-actions"> 677 @if(enableProductStock && !string.IsNullOrWhiteSpace(stockFormat)) 678 { 679 if(stockFormat == "text") { 680 if (!string.IsNullOrWhiteSpace(stockText)) 681 { 682 <p class="products-module__product-stock-state @stockClass">@string.Format(stockText, stockSize)</p> 683 } 684 } 685 else 686 { 687 string translationTag = ""; 688 if(neverOutOfStock) 689 { 690 translationTag = Translate("ProductBlockStockInfo.AmountInStock", "In stock"); 691 } 692 else if(stockSize == 1) 693 { 694 translationTag = Translate("ProductBlockStockInfo.AmountInStockSingle", "{0} product in stock"); 695 } 696 else if(stockSize > 1) 697 { 698 translationTag = Translate("ProductBlockStockInfo.AmountInStockMultiple", "{0} products in stock"); 699 } 700 else if(!isInStock && hasExpectedStock) 701 { 702 translationTag = Translate("ProductBlockStockInfo.AmountOutOfStockButExpected", "Soon back in stock"); 703 } 704 else if(!isInStock) 705 { 706 translationTag = Translate("ProductBlockStockInfo.AmountOutOfStock", "Out of stock"); 707 } 708 <p class="products-module__product-stock-state @stockClass">@string.Format(translationTag, stockSizeFormatted)</p> 709 710 if (stockSize > 0 && hasExpectedStock) 711 { 712 <p class="products-module__product-stock-state products-module__product-stock-state__stock-info--expected">@Translate("ProductBlockStockInfo.InStockAndExpected", "More in stock soon")</p> 713 } 714 } 715 } 716 717 @if(!hasVariants && enableShoppingCart && enableProductShoppingCart) 718 { 719 <add-to-cart 720 class="app-addtocart" 721 data-prodid="@productId" 722 data-variantid="@productVariantId" 723 data-show-text="False" 724 data-min-quantity="@product.GetDouble("Ecom:Product.PurchaseMinimumQuantity")" 725 data-step="@product.GetDouble("Ecom:Product.PurchaseQuantityStep")" 726 data-list-id="@listid" 727 data-list-name="@listname"> 728 </add-to-cart> 729 } 730 </section> 731 <a href="@productURL" class="products-module__link" data-id="@productId" data-variantid="@productVariantId" data-number="@productNumber" data-name='@productName.Replace("''", "\\\"").Replace("'", "")' data-position="@product.GetInteger("Products.LoopCounter")" data-brand='@productManufacturer.Replace("''", "\\\"").Replace("'", "")' data-category='@productCategoryName' data-listid="@listid" data-listname="@listname" data-gtmprice="@gtmPrice" data-gtmdiscount="@gtmDiscount" aria-label="@productName"></a> 732 </div> 733 </li> 734 } 735 736 @helper renderProductListEnhancedEcom(string listId, string listName, List<LoopItem> products, string WithVATSuffix) { 737 string specifier = "G"; 738 CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US"); 739 740 <text> 741 <script> 742 if(window.dataLayer) { 743 dataLayer.push({ 744 'event': 'view_item_list', 745 'ecommerce': { 746 'item_list_id': '@listId', 747 'item_list_name': '@listName', 748 'items': [ 749 @foreach (var product in products) { 750 var relCategoryObject = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(product.GetString("Ecom:Product.PrimaryOrFirstGroupID")); 751 string relCategoryName = relCategoryObject != null ? relCategoryObject.Name.Replace("''", "\\\"").Replace("'", "") : ""; 752 <text> 753 { 754 'item_id': '@product.GetString("Ecom:Product.ID")', 755 'item_name': '@product.GetString("Ecom:Product.Name").Replace("''", "\\\"").Replace("'", "")', 756 'item_number': '@product.GetString("Ecom:Product.Number")', 757 'discount': @product.GetDouble("Ecom:Product.Discount.TotalAmount.Price.Value").ToString(specifier, culture), 758 'index': @product.GetInteger("Products.LoopCounter"), 759 'item_brand': '@product.GetString("Ecom:Manufacturer.Name").Replace("''", "\\\"").Replace("'", "")', 760 'item_category': '@relCategoryName', 761 'item_list_id': '@listId', 762 'item_list_name': '@listName', 763 'item_variant': '@product.GetString("Ecom:Product.VariantID")', 764 'price': @product.GetDouble("Ecom:Product.Price.Price.Value").ToString(specifier, culture), 765 'quantity': 1, 766 }, 767 </text> 768 } 769 ] 770 } 771 }); 772 } 773 </script> 774 </text> 775 } 776 777 @helper renderReviewIndicator(string progressbarColor, int value, string flexDirection, bool boolReviews, int CommentCount) 778 { 779 int ratingPercentage = (100 / 5) * value; 780 781 <section style="display: flex; flex-direction: @flexDirection; position: relative; align-items: center;"> 782 <div class="reviews__indicator" style="display: flex; flex-direction: row;"> 783 <div class="reviews__indicator-progressbar" style="width: @ratingPercentage.ToString()%; background-color: @progressbarColor;"></div> 784 <ul class="reviews__indicator-star-list"> 785 <li class="reviews__indicator-star-list-item"></li> 786 <li class="reviews__indicator-star-list-item"></li> 787 <li class="reviews__indicator-star-list-item"></li> 788 <li class="reviews__indicator-star-list-item"></li> 789 <li class="reviews__indicator-star-list-item"></li> 790 </ul> 791 </div> 792 <span style="display: flex; position: relative; font-size: 12px; line-height: initial;"> 793 @value / 5 794 @if (boolReviews) 795 {<text>(@CommentCount reviews)</text>} 796 </span> 797 </section> 798 799 } 800 801 @helper RenderProductUSP(string USP, string USPInfo) 802 { 803 if (!string.IsNullOrWhiteSpace(USP)) 804 { 805 <li class="pdp-usplist__item pdp-usplist__item--product"> 806 <i class="fal fa-check"></i> 807 @USP 808 @if (!string.IsNullOrWhiteSpace(USPInfo)) 809 { 810 <span data-tippy-content="@USPInfo" class="pdp-usplist__infoicon"> 811 <i class="fal fa-info-circle"></i> 812 </span> 813 } 814 </li> 815 } 816 } 817 818 @helper RenderProductDetailUSP(string USP, string USPInfo) 819 { 820 if (!string.IsNullOrWhiteSpace(USP)) 821 { 822 <li class="pdp-usplist__item pdp-usplist__item--page"> 823 <i class="fal fa-dot-circle"></i> 824 @USP 825 @if (!string.IsNullOrWhiteSpace(USPInfo)) 826 { 827 <span data-tippy-content="@USPInfo" class="pdp-usplist__infoicon"> 828 <i class="fal fa-info-circle"></i> 829 </span> 830 } 831 </li> 832 } 833 } 834 835 @helper RenderProductModuleUSP(string USP, string USPInfo) 836 { 837 if (!string.IsNullOrWhiteSpace(USP)) 838 { 839 <li class="pdp-usplist__item pdp-usplist__item--product"> 840 <i class="fal fa-check"></i> 841 @USP 842 @if (!string.IsNullOrWhiteSpace(USPInfo)) 843 { 844 <span data-tippy-content="@USPInfo" class="pdp-usplist__infoicon"> 845 <i class="fal fa-info-circle"></i> 846 </span> 847 } 848 </li> 849 } 850 } 851 852 @* @helper RenderPDPTabs() 853 { 854 string downloadProductsheet = GetString("Ecom:Product:Field.DownloadProductsheet.Clean"); 855 856 <div class="pdp-specifications-navigation__container"> 857 858 <nav class="pdp-specifications-navigation"> 859 <a href="#information" class="pdp-specifications-navigation__tab pdp-specifications-navigation__tab--active">Productinformatie</a> 860 <a href="javascript:void(0);" class="pdp-specifications-navigation__tab toggle-of-canvas-menu" data-offcanvas-target="specifications">Specificaties</a> 861 @if (GetLoop("Comments").Count > 0) 862 { 863 <a href="#reviews" class="pdp-specifications-navigation__tab">Reviews</a> 864 } 865 </nav> 866 867 @if (!string.IsNullOrWhiteSpace(downloadProductsheet)) 868 { 869 <a class="productdetails__download btn btn--small ml-auto" href="@downloadProductsheet" target="_blank"> 870 <span class="btn__text">@Translate("Productdetail.DownloadProductsheetLabel", "Download productsheet")</span> 871 <i class="btn__icon fal fa-file-pdf"></i> 872 </a> 873 } 874 875 </div> 876 } *@ 877 878 @helper renderProductCompareButton(string productId, string name, string thumbnail) { 879 string title = Translate("ProductBlockAction.Compare", "Add to product compare"); 880 881 <button class="po-block__action-btn po-block__action-btn--compare" aria-label="@title" title="@title" data-prodid="@productId" data-imgsmall="@thumbnail" data-prodname="@name"> 882 <i class="fas fa-exchange-alt"></i> 883 </button> 884 } 885 886 @helper renderProductFavoriteButton(string productId, string variantId, string productName, string productNumber, string gtmDiscount, string gtmPrice, string gtmValue, string productCategoryName, string productManufacturer, string listId, string listName) { 887 if(string.IsNullOrWhiteSpace(variantId)) { 888 variantId = ""; 889 } 890 891 string title = Translate("ProductBlockAction.Wishlist", "Add to wish list"); 892 bool isActive = Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites.UserExtensions.IsProductInAnyFavoriteList(Pageview.User, productId, variantId); 893 894 <button class="po-block__action-btn po-block__action-btn--wishlist" aria-label="@title" title="@title" data-productid="@productId" data-variantid="@variantId" data-offcanvas-target="favoritelist" data-productid="@productId" data-variantid="@variantId" data-number="@productNumber" data-name='@productName' data-position="1" data-brand='@productManufacturer' data-category='@productCategoryName' data-listid="@listId" data-listname="@listName" data-gtmprice="@gtmPrice" data-gtmdiscount="@gtmDiscount" data-gtmvalue="@gtmValue" data-currencycode="@Dynamicweb.Ecommerce.Common.Context.Currency.Code"> 895 @if(isActive) { 896 <i class="fas fa-heart"></i> 897 } else { 898 <i class="far fa-heart"></i> 899 } 900 </button> 901 902 } 903 904 @helper renderReviewDistrubition(List<LoopItem> comments) { 905 int totalCount = comments.Count; 906 @renderReviewDistrubitionLine(5, comments.Count(c => c.GetInteger("Rating") == 5), totalCount) 907 @renderReviewDistrubitionLine(4, comments.Count(c => c.GetInteger("Rating") == 4), totalCount) 908 @renderReviewDistrubitionLine(3, comments.Count(c => c.GetInteger("Rating") == 3), totalCount) 909 @renderReviewDistrubitionLine(2, comments.Count(c => c.GetInteger("Rating") == 2), totalCount) 910 @renderReviewDistrubitionLine(1, comments.Count(c => c.GetInteger("Rating") == 1), totalCount) 911 } 912 913 @helper renderReviewDistrubitionLine(int stars, int amount, int total) { 914 double progress = ((double)amount / (double)total) * 100; 915 916 <div class="review-distribution"> 917 <span class="review-distribution__star"> 918 @for (int i = 0; i < stars; i++) 919 { 920 <i class="fa fa-star" aria-hidden="true"></i> 921 922 } 923 </span> 924 <div class="review-distribution__progress"> 925 <div class="review-distribution__progress-background"></div> 926 <div class="review-distribution__progress-fill" style="width: @progress%;"></div> 927 </div> 928 <span class="review-distribution__count">@amount reviews</span> 929 </div> 930 } 931 932 @helper RenderPdpTabs(bool showReviewTab, bool enableSpecificationsSidebar) { 933 string productLongDescription = GetString("Ecom:Product.LongDescription"); 934 string downloadProductsheet = GetString("Ecom:Product:Field.DownloadProductsheet.Clean"); 935 string[] prestatieGroups = { "Binnenverlichting", "Buitenverlichting", "Earbuds", "Hoofdtelefoons", "LED", "Lucifers_Aanstekers", "Opladers_Kabels", "Overige_Buitenverlichting", "Powerbanks", "Product_prestaties", "Smartwatches", "Speakers", "Elektrisch_Textiel", "Elektrische_Diffusers", "Elektronica", "Elektronica_Accessoires", "Keuken_Elektronica", "Ref_elektronica" }; 936 937 bool hasDescription = !string.IsNullOrWhiteSpace(productLongDescription); 938 var specificationGroups = GetLoop("ProductCategories").Where(c => !prestatieGroups.Contains(c.GetString("Ecom:Product.Category.ID"))); 939 var prestatiesGroups = GetLoop("ProductCategories").Where(c => prestatieGroups.Contains(c.GetString("Ecom:Product.Category.ID"))); 940 bool hasSpecifications = specificationGroups.Any(c => c.GetLoop("ProductCategoryFields").Any(d => !string.IsNullOrWhiteSpace(d.GetString("Ecom:Product.CategoryField.Value")))); 941 bool hasPrestaties = prestatiesGroups.Any(c => c.GetLoop("ProductCategoryFields").Any(d => !string.IsNullOrWhiteSpace(d.GetString("Ecom:Product.CategoryField.Value")))); 942 bool hasReviews = showReviewTab; 943 bool hasDownloads = true; 944 bool hasProductSheet = !string.IsNullOrWhiteSpace(downloadProductsheet); 945 946 string activeItem = hasSpecifications ? "specifications" : "description"; 947 if(Dynamicweb.Context.Current.Request["reviewcmd"] != null && Dynamicweb.Context.Current.Request["reviewcmd"] == "created") { 948 activeItem = "reviews"; 949 } 950 951 if(Pageview.User == null) { 952 hasSpecifications = false; 953 hasPrestaties = false; 954 hasDownloads = false; 955 hasDescription = true; 956 activeItem = "description"; 957 } 958 959 960 961 if(hasDescription || hasReviews || hasDownloads) 962 { 963 <section id="pdp-tabs" class="pdp-tabs"> 964 <div class="container pdp-tabs__container"> 965 966 <div class="pdp-tabs__navigation"> 967 @if(hasSpecifications) 968 { 969 <a class='pdp-tabnavigation__item @(activeItem == "specifications" ? "pdp-tabnavigation__item--active" : "")' href="javascript:void(0);" data-tab="specifications"> 970 @Translate("Productdetail.SpecificationsTab", "Specifications") 971 </a> 972 } 973 @if(hasPrestaties) 974 { 975 <a class='pdp-tabnavigation__item @(activeItem == "prestaties" ? "pdp-tabnavigation__item--active" : "")' href="javascript:void(0);" data-tab="prestaties"> 976 @Translate("Productdetail.PrestatiesTab", "Prestaties") 977 </a> 978 } 979 @if(hasDescription) 980 { 981 <a class='pdp-tabnavigation__item @(activeItem == "description" ? "pdp-tabnavigation__item--active" : "")' href="javascript:void(0);" data-tab="description"> 982 @Translate("Productdetail.DescriptionTab", "Description") 983 </a> 984 } 985 @if(hasReviews) 986 { 987 <a class='pdp-tabnavigation__item @(activeItem == "reviews" ? "pdp-tabnavigation__item--active" : "")' href="javascript:void(0);" data-tab="reviews"> 988 @Translate("Productdetail.ReviewsTab", "Reviews") 989 </a> 990 } 991 @if(hasDownloads) 992 { 993 <a class="pdp-tabnavigation__item" href="javascript:void(0);" data-tab="downloads"> 994 @Translate("Productdetail.DownloadsTab", "Downloads") 995 </a> 996 } 997 @if(hasProductSheet) 998 { 999 <a class="pdp-tabnavigation__productsheetdownload btn btn__secondary" href="@downloadProductsheet" target="_blank"> 1000 <span class="btn__text">@Translate("Productdetail.DownloadProductsheetLabel", "Download productsheet")</span> 1001 <i class="btn__icon fal fa-file-pdf"></i> 1002 </a> 1003 } 1004 </div> 1005 1006 <div class="pdp-tabs__content"> 1007 @if(hasSpecifications) 1008 { 1009 Regex valueRegex = new Regex(@"\[(.+?)\]", RegexOptions.IgnoreCase); 1010 string[] hiddenFields = {"ProductPromidataMediaGallery", "ProductImagesDownload", "ProductLeaflets", "ProductManuals"}; 1011 Dictionary<string, string> customFields = new Dictionary<string, string>(); 1012 1013 if(!string.IsNullOrWhiteSpace(GetString("Ecom:Product.EAN"))) 1014 { 1015 customFields["EAN artikel"] = GetString("Ecom:Product.EAN"); 1016 } 1017 1018 if(!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductMaterialen.Value"))) 1019 { 1020 customFields["Materiaal"] = GetString("Ecom:Product:Field.ProductMaterialen.Value"); 1021 } 1022 1023 if(!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductKleurenList.Label"))) 1024 { 1025 foreach(var i in GetLoop("CustomFields")) { 1026 <!-- This loop needs to be included in order to get the labels instead of values --> 1027 var fixToGetLabels = GetLoop("ProductKleurenList.Options"); 1028 } 1029 customFields["Kleur"] = GetString("Ecom:Product:Field.ProductKleurenList.Label"); 1030 } 1031 1032 foreach(LoopItem categorie in specificationGroups) 1033 { 1034 1035 if(categorie.GetLoop("ProductCategoryFields").Any(d => !string.IsNullOrWhiteSpace(d.GetString("Ecom:Product.CategoryField.Value")))) 1036 { 1037 foreach (var categoryField in categorie.GetLoop("ProductCategoryFields")) 1038 { 1039 if (!hiddenFields.Contains(categoryField.GetString("Ecom:Product.CategoryField.TemplateTag")) && !string.IsNullOrWhiteSpace(categoryField.GetString("Ecom:Product.CategoryField.Value")) && categoryField.GetString("Ecom:Product.CategoryField.Value") != "False" && categoryField.GetString("Ecom:Product.CategoryField.Value") != "0") 1040 { 1041 if(categoryField.GetString("Ecom:Product.CategoryField.TemplateTag") == "ProductInhoud") 1042 { 1043 string inhoudLabel = categoryField.GetString("Ecom:Product.CategoryField.Label") + "[ml]"; 1044 int inhoudValue = categoryField.GetInteger("Ecom:Product.CategoryField.Value"); 1045 if(inhoudValue >= 1000) { 1046 inhoudLabel = categoryField.GetString("Ecom:Product.CategoryField.Label") + "[L]"; 1047 inhoudValue = categoryField.GetInteger("Ecom:Product.CategoryField.Value") / 1000; 1048 } 1049 customFields[inhoudLabel] = inhoudValue.ToString(); 1050 } 1051 else 1052 { 1053 customFields[categoryField.GetString("Ecom:Product.CategoryField.Label")] = categoryField.GetString("Ecom:Product.CategoryField.Value"); 1054 } 1055 } 1056 } 1057 1058 } 1059 } 1060 1061 <a class='pdp-tabnavigation__item pdp-tabnavigation__item--mobile @(activeItem == "specifications" ? "pdp-tabnavigation__item--active" : "")' href="javascript:void(0);" data-tab="specifications"> 1062 @Translate("Productdetail.SpecificationsTab", "Specifications") 1063 </a> 1064 <section class='pdp-tabcontent__item @(activeItem == "specifications" ? "pdp-tabcontent__item--active" : "")' data-tab-content="specifications"> 1065 <div class="pdp-specifications-info__body" style="flex-direction: column;"> 1066 <section class="product-specifications__category"> 1067 <ul class="product-specifications__list"> 1068 @foreach (KeyValuePair<string, string> customField in customFields.OrderBy(s => s.Key)) 1069 { 1070 Match unitMatch = valueRegex.Match(customField.Key); 1071 string unit = unitMatch.Groups[1].Value; 1072 string value = customField.Value; 1073 1074 <li class="product-specifications__list-item"> 1075 <span class="product-specifications__label">@valueRegex.Replace(customField.Key, "")</span> 1076 <span class="product-specifications__value"> 1077 @if(value == "True") 1078 { 1079 <i class="fas fa-check" title="@Translate(value, value)"></i> 1080 } else { 1081 @value @unit 1082 } 1083 </span> 1084 </li> 1085 } 1086 </ul> 1087 </section> 1088 </div> 1089 </section> 1090 } 1091 @if(hasPrestaties) 1092 { 1093 Regex valueRegex = new Regex(@"\[(.+?)\]", RegexOptions.IgnoreCase); 1094 string[] hiddenFields = {}; 1095 Dictionary<string, string> customFields = new Dictionary<string, string>(); 1096 1097 foreach(LoopItem categorie in prestatiesGroups) 1098 { 1099 if(categorie.GetLoop("ProductCategoryFields").Any(d => !string.IsNullOrWhiteSpace(d.GetString("Ecom:Product.CategoryField.Value")))) 1100 { 1101 foreach (var categoryField in categorie.GetLoop("ProductCategoryFields")) 1102 { 1103 if (!hiddenFields.Contains(categoryField.GetString("Ecom:Product.CategoryField.Label")) && !string.IsNullOrWhiteSpace(categoryField.GetString("Ecom:Product.CategoryField.Value")) && categoryField.GetString("Ecom:Product.CategoryField.Value") != "False") 1104 { 1105 customFields[categoryField.GetString("Ecom:Product.CategoryField.Label")] = categoryField.GetString("Ecom:Product.CategoryField.Value"); 1106 } 1107 } 1108 } 1109 } 1110 1111 <a class='pdp-tabnavigation__item pdp-tabnavigation__item--mobile @(activeItem == "prestaties" ? "pdp-tabnavigation__item--active" : "")' href="javascript:void(0);" data-tab="prestaties"> 1112 @Translate("Productdetail.PrestatiesTab", "Prestaties") 1113 </a> 1114 <section class='pdp-tabcontent__item @(activeItem == "prestaties" ? "pdp-tabcontent__item--active" : "")' data-tab-content="prestaties"> 1115 <div class="pdp-specifications-info__body" style="flex-direction: column;"> 1116 <section class="product-specifications__category"> 1117 <ul class="product-specifications__list"> 1118 @foreach (KeyValuePair<string, string> customField in customFields.OrderBy(s => s.Key)) 1119 { 1120 Match unitMatch = valueRegex.Match(customField.Key); 1121 string unit = unitMatch.Groups[1].Value; 1122 string value = customField.Value; 1123 1124 <li class="product-specifications__list-item"> 1125 <span class="product-specifications__label">@valueRegex.Replace(customField.Key, "")</span> 1126 <span class="product-specifications__value"> 1127 @if(value == "True") 1128 { 1129 <i class="fas fa-check" title="@Translate(value, value)"></i> 1130 } else { 1131 @value @unit 1132 } 1133 </span> 1134 </li> 1135 } 1136 </ul> 1137 </section> 1138 </div> 1139 </section> 1140 } 1141 @if(hasDescription) 1142 { 1143 <a class='pdp-tabnavigation__item pdp-tabnavigation__item--mobile @(activeItem == "description" ? "pdp-tabnavigation__item--active" : "")' href="javascript:void(0);" data-tab="description"> 1144 @Translate("Productdetail.DescriptionTab", "Description") 1145 </a> 1146 <section class='pdp-tabcontent__item @(activeItem == "description" ? "pdp-tabcontent__item--active" : "")' data-tab-content="description"> 1147 @RenderLongDescription() 1148 </section> 1149 } 1150 @if(hasReviews) 1151 { 1152 <a class='pdp-tabnavigation__item pdp-tabnavigation__item--mobile @(activeItem == "reviews" ? "pdp-tabnavigation__item--active" : "")' href="javascript:void(0);" data-tab="reviews"> 1153 @Translate("Productdetail.ReviewsTab", "Reviews") 1154 </a> 1155 <section class='pdp-tabcontent__item @(activeItem == "reviews" ? "pdp-tabcontent__item--active" : "")' data-tab-content="reviews"> 1156 <div class="pdp-specifications-info__body"> 1157 @RenderSnippet("PdpReviews") 1158 </div> 1159 </section> 1160 } 1161 @if(hasDownloads) 1162 { 1163 string imagesDownload = GetString("Ecom:Product.CategoryField.ProductProperties.ProductImagesDownload.Value.Clean"); 1164 string leafletDownload = GetString("Ecom:Product.CategoryField.ProductProperties.ProductLeaflets.Value.Clean"); 1165 string manualDownload = GetString("Ecom:Product.CategoryField.ProductProperties.ProductManuals.Value.Clean"); 1166 1167 <a class="pdp-tabnavigation__item pdp-tabnavigation__item--mobile" href="javascript:void(0);" data-tab="downloads"> 1168 @Translate("Productdetail.DownloadsTab", "Downloads") 1169 </a> 1170 <section class="pdp-tabcontent__item" data-tab-content="downloads"> 1171 <div class="pdp-specifications-info__body"> 1172 <h3>@Translate("Productdetail.DownloadsTabTitle", "Downloads")</h3> 1173 1174 <div> 1175 @if(!string.IsNullOrWhiteSpace(GetString("Ecom:Product.Number"))) 1176 { 1177 string fileName = GetString("Ecom:Product.Number") + ".zip"; 1178 var absolutePath = System.Web.HttpContext.Current.Server.MapPath("~/Files/files/Downloads/Images/" + fileName); 1179 if(System.IO.File.Exists(absolutePath)) 1180 { 1181 <p> 1182 <a href="/Files/files/Downloads/Images/@fileName" download target="_blank" class="download__item"> 1183 <i class="fas fa-download"></i> 1184 <span>@Translate("Productdetail.Downloads.Zip", "Product- en sfeerafbeeldingen")</span> 1185 </a> 1186 </p> 1187 } 1188 } 1189 @if(!string.IsNullOrWhiteSpace(leafletDownload)) 1190 { 1191 var absolutePath = System.Web.HttpContext.Current.Server.MapPath("~/Files/files/Downloads/Leaflets/" + leafletDownload); 1192 if(System.IO.File.Exists(absolutePath)) 1193 { 1194 <p> 1195 <a href="/Files/files/Downloads/Leaflets/@leafletDownload" download target="_blank" class="download__item"> 1196 <i class="fas fa-download"></i> 1197 <span>@Translate("Productdetail.Downloads.Leaflet", "Product leaflet")</span> 1198 </a> 1199 </p> 1200 } 1201 } 1202 @if(!string.IsNullOrWhiteSpace(manualDownload)) 1203 { 1204 var absolutePath = System.Web.HttpContext.Current.Server.MapPath("~/Files/files/Downloads/Handleidingen/" + manualDownload); 1205 if(System.IO.File.Exists(absolutePath)) 1206 { 1207 <p> 1208 <a href="/Files/files/Downloads/Handleidingen/@manualDownload" download target="_blank" class="download__item"> 1209 <i class="fas fa-download"></i> 1210 <span>@Translate("Productdetail.Downloads.Manual", "Handleiding")</span> 1211 </a> 1212 </p> 1213 } 1214 } 1215 </div> 1216 </div> 1217 </section> 1218 } 1219 </div> 1220 1221 </div> 1222 </section> 1223 } 1224 } 1225 1226 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 1227 @using Dynamicweb; 1228 @using Bluedesk.DynamicWeb.ItemTypes.Pages; 1229 @using Bluedesk.DynamicWeb.ItemTypes.BaseSolution; 1230 @using Bluedesk.Tools.DynamicWeb.ExtensionMethods; 1231 @using System.Linq; 1232 @using Dynamicweb.Content; 1233 @using Dynamicweb.Ecommerce.Prices; 1234 @using System.Text.RegularExpressions; 1235 1236 @SnippetStart("PdpHeader") 1237 <header class="pdp-header container"> 1238 @if(!showTitleAboveInfoBlock) 1239 { 1240 <h1 class="pdp-header__title"> 1241 @if(!string.IsNullOrWhiteSpace(Manufacturer)) 1242 { 1243 <span class="pdp-header__manufacturer">@Manufacturer </span> 1244 } 1245 <span class="pdp-header__productname">@productName</span> 1246 </h1> 1247 } 1248 1249 @* Snippet PdpReviewIndicator *@ 1250 @if(GetInteger("Comments.TotalCount") > 0) { 1251 <div class="pdp-review-indicator"> 1252 @renderReviewIndicator("#86C440", GetInteger("Comments.Rating"), "row", true, GetInteger("Comments.Count")) 1253 </div> 1254 } 1255 </header> 1256 @SnippetEnd("PdpHeader") 1257 1258 @SnippetStart("PdpInfo") 1259 <section class="pdp-info"> 1260 @* Snippet PdpActionButtons *@ 1261 @if (enableProductCompare || enableProductFavorites) 1262 { 1263 <div class="pdp-actions po-block__actions"> 1264 @if (enableProductCompare) 1265 { 1266 @renderProductCompareButton(productid, productName, defaultImage) 1267 } 1268 @if (enableProductFavorites) 1269 { 1270 @renderProductFavoriteButton(productid, productVariantId, productName, productNumber, gtmDiscount, gtmPrice, gtmValue, productCategoryName, Manufacturer, listId, listName) 1271 } 1272 </div> 1273 } 1274 1275 @if(showTitleAboveInfoBlock) 1276 { 1277 <h1 class="pdp-header__title"> 1278 @if(!string.IsNullOrWhiteSpace(Manufacturer)) 1279 { 1280 <span class="pdp-header__manufacturer">@Manufacturer </span> 1281 } 1282 <span class="pdp-header__productname">@productName</span> 1283 </h1> 1284 } 1285 1286 1287 @* Snippet PdpProductNumber *@ 1288 @if (!string.IsNullOrWhiteSpace(productNumber)) 1289 { 1290 <p class="pdp-articlenumber"> 1291 @Translate("Productdetail.ArticleNumber.Prefix", "Article number:") @productNumber 1292 </p> 1293 } 1294 1295 @if(showVariantInfo && hasVariants) 1296 { 1297 <p class="pdp-variant-info"> 1298 <span class="pdp-variant-info__label"><strong>@Translate("ProductDetailVariantInfo.Variants", "Variants:")</strong></span> 1299 <span class="pdp-variant-info__amount"><strong>@Translate("ProductDetailVariantInfo.Amount", "Amount")</strong> @GetLoop("VariantCombinations").Count</span> 1300 <span class="pdp-variant-info__amountinstock"><strong>@Translate("ProductDetailVariantInfo.InStock", "In stock")</strong> @GetLoop("VariantCombinations").Where(p => p.GetDouble("Ecom:Product.Stock") != null && p.GetDouble("Ecom:Product.Stock") > 0).ToList().Count</span> 1301 </p> 1302 } 1303 1304 @* Snippet PdpPriceBlock *@ 1305 @if(displayPrice && displayProductPrice) 1306 { 1307 <div class="pdp-price__container"> 1308 @if(hasVariants) { 1309 <section class="pdp-price__subcontainer"> 1310 @if(minPrice != maxPrice) 1311 { 1312 <span class="pdp-price">@minPriceFormatted - @maxPriceFormatted</span> 1313 } 1314 else 1315 { 1316 <span class="pdp-price">@minPriceFormatted</span> 1317 } 1318 <span class="pdp-price--suffix">@ProductdetailPriceSuffix</span> 1319 </section> 1320 } 1321 else 1322 { 1323 1324 if (hasDiscount) 1325 { 1326 <p class="pdp-price--original">@originalProductPrice</p> 1327 <section class="pdp-price__subcontainer"> 1328 <span class="pdp-price">@discountProductPrice</span> 1329 <span class="pdp-price--suffix">@ProductdetailPriceSuffix</span> 1330 <span class="pdp-price__percentage">@discountPercentage%</span> 1331 </section> 1332 } 1333 else 1334 { 1335 <section class="pdp-price__subcontainer"> 1336 <span class="pdp-price">@originalProductPrice</span> 1337 <span class="pdp-price--suffix">@ProductdetailPriceSuffix</span> 1338 </section> 1339 } 1340 } 1341 <p class="pdp-price--consumer"> 1342 @if(Pageview.User != null) 1343 { 1344 if(!string.IsNullOrWhiteSpace(retailPrice)) 1345 { 1346 @String.Format(Translate("ProductBlockTitle.AVP", "AVP: {0}"), retailPrice); 1347 } 1348 } 1349 else 1350 { 1351 @Translate("ProductBlockTitle.AVP.Guests", "Adviesprijs"); 1352 } 1353 </p> 1354 @if (yourProfitValue > 0) 1355 { 1356 <p class="pdp-price__yourprofit"> 1357 @Translate("Productdetail.YourProfitLabel", "Your profit:") @yourProfitValueFormatted 1358 </p> 1359 } 1360 </div> 1361 } 1362 1363 @* Snippet PdpStockState *@ 1364 @if(enableProductStock && !string.IsNullOrWhiteSpace(stockFormat)) 1365 { 1366 <div class="pdp-stockstate__wrapper"> 1367 @if (stockFormat == "text") 1368 { 1369 if (!string.IsNullOrWhiteSpace(stockText)) 1370 { 1371 <p class="pdp-stockstate @stockStateClass">@string.Format(stockText, stock)</p> 1372 } 1373 } 1374 else 1375 { 1376 string translationTag = ""; 1377 if (neverOutOfStock) 1378 { 1379 translationTag = Translate("ProductBlockStockInfo.AmountInStock", "In stock"); 1380 } 1381 else if (stockSize == 1) 1382 { 1383 translationTag = Translate("ProductBlockStockInfo.AmountInStockSingle", "{0} product in stock"); 1384 } 1385 else if (stockSize > 1) 1386 { 1387 translationTag = Translate("ProductBlockStockInfo.AmountInStockMultiple", "{0} products in stock"); 1388 } 1389 else if (!inStock && hasExpectedStock) 1390 { 1391 translationTag = Translate("ProductBlockStockInfo.AmountOutOfStockButExpected", "Soon back in stock"); 1392 } 1393 else if (!inStock) 1394 { 1395 translationTag = Translate("ProductBlockStockInfoPDP.AmountOutOfStock", "Out of stock"); 1396 } 1397 1398 <p class="pdp-stockstate @stockStateClass">@string.Format(translationTag, stock)</p> 1399 } 1400 1401 @if (!isInWrongGroup) 1402 { 1403 if (!string.IsNullOrWhiteSpace(quantityInOptionsFormatted) && quantityInOptionsFormatted != "0") 1404 { 1405 <p class="pdp-quantityinoptions">@String.Format(Translate("PDP.QuantityInOptions", "+ {0} In optie. Mogelijk binnenkort meer beschikbaar"), quantityInOptionsFormatted)</p> 1406 } 1407 <p class="expected-stock__subtext">@Translate("Productdetail.ExpectedStockSubText", "Is de vrije beschikbaarheid niet toereikend voor uw aanvraag? Neem contact met ons op voor de mogelijkheden.")</p> 1408 } 1409 1410 @if (hasExpectedStock) 1411 { 1412 double total = stockSize; 1413 1414 <div class="pdp-expected-stock"> 1415 <h3 class="expected-stock__title">@Translate("Productdetail.ExpectedStock", "Expected stock")</h3> 1416 <div class="expected-stock__table"> 1417 <div class="expected-stock__head expected-stock__row"> 1418 <p>@Translate("Productdetail.ExpectedDate", "Expected date")</p> 1419 <p>@Translate("Productdetail.Amount", "Amount")</p> 1420 <p>@Translate("Productdetail.Total", "Total")</p> 1421 </div> 1422 <div class="expected-stock__row"> 1423 <p>@Translate("Productdetail.Today", "Today")</p> 1424 <p>@Translate("Productdetail.NA", "N/A")</p> 1425 <p>@stock</p> 1426 </div> 1427 @foreach (FutureStockModel stockItem in expectedStock) 1428 { 1429 total += stockItem.Balance; 1430 1431 <div class="expected-stock__row"> 1432 <p>@stockItem.ExpectedReceiptDate.ToString("dd-MM-yyyy")</p> 1433 <p>@stockItem.Balance.ToString("N0", CultureInfo.GetCultureInfo(Pageview.Area.CultureInfo.TwoLetterISOLanguageName))</p> 1434 <p>@total.ToString("N0", CultureInfo.GetCultureInfo(Pageview.Area.CultureInfo.TwoLetterISOLanguageName))</p> 1435 </div> 1436 } 1437 </div> 1438 1439 </div> 1440 } 1441 1442 </div> 1443 } 1444 1445 @* Snippet BackInStockNotification *@ 1446 @if(!inStock && displayBackInStockNotifications) 1447 { 1448 bool notificationRegistered = GetBoolean("Ecom:Product.NotificationRegistered"); 1449 1450 <div class="pdp-back-in-stock-notification"> 1451 @if(!notificationRegistered && Pageview.User != null) 1452 { 1453 <a class="btn default-btn" href="/Default.aspx?ID=@GetString("Ecom:Product:Page.ID")&ProductID=@GetString("Ecom:Product.ID")&VariantID=@GetString("Ecom:Product.VariantID")&LanguageID=@GetString("Ecom:Product.LanguageID")&CartCmd=createnotificationforthisproduct"> 1454 <span class="btn__text">@Translate("BackInStock.EmailMe", "Email me when back in stock")</span> 1455 <i class="btn__icon far fa-bell"></i> 1456 </a> 1457 } else if (!notificationRegistered && (Pageview.User == null && allowBackInStockNotificationsForGuests)) { 1458 <form name='@GetString("Ecom:Product.ID")' id='NotificationForm_@GetString("Ecom:Product.ID")' method='post' class="no-validate default-contact-form" action='/Default.aspx?ID=@GetString("Ecom:Product:Page.ID")'> 1459 <h3 class="pdp-back-in-stock-notification__header">@Translate("BackInStock.FormTitle", "Email me when back in stock")</h3> 1460 <input type="hidden" name="ProductID" id="ProductID" value='@GetString("Ecom:Product.ID")' /> 1461 <input type="hidden" name="VariantID" id="VariantID" value='@GetString("Ecom:Product.VariantID")' /> 1462 <input type="hidden" name="LanguageID" id="LanguageID" value='@GetString("Ecom:Product.LanguageID")' /> 1463 <input type="hidden" name="CartCmd" id="CartCmd" value="createnotificationforthisproduct" /> 1464 1465 <section class="form__item"> 1466 <div class="input__group form-item__input form-group"> 1467 <label class="form__input-label input__label" for="NotificationEmail">@Translate("BackInStock.Email", "Email *")</label> 1468 <input name="NotificationEmail" 1469 type="email" 1470 class="form__input input input--text" 1471 id="NotificationEmail" 1472 required /> 1473 </div> 1474 </section> 1475 1476 <button type="submit" class="btn default-btn"> 1477 <span class="btn__text">@Translate("BackInStock.CreateNotification", "Create notification")</span> 1478 <i class="btn__icon far fa-bell"></i> 1479 </button> 1480 </form> 1481 } else if (notificationRegistered && Pageview.User != null) { 1482 <p class="pdp-back-in-stock-notification__success"> 1483 <i class="far fa-bell-on"></i> 1484 @Translate("BackInStock.NotificationSuccess", "You will be notified when this product is back in stock.") 1485 </p> 1486 } else if (notificationRegistered && (Pageview.User == null && allowBackInStockNotificationsForGuests)) { 1487 <p class="pdp-back-in-stock-notification__success"> 1488 <i class="far fa-bell-on"></i> 1489 @Translate("BackInStock.NotificationSuccess", "You will be notified when this product is back in stock.") 1490 </p> 1491 } 1492 </div> 1493 } 1494 1495 @* Snippet PdpTaglines *@ 1496 @if (!string.IsNullOrWhiteSpace(productTagline) || !string.IsNullOrWhiteSpace(productDetailPageTagline)) 1497 { 1498 <div class="pdp-tagline__container"> 1499 @if (!string.IsNullOrWhiteSpace(productTagline)) 1500 { 1501 <p class="pdp-tagline"> 1502 @productTagline 1503 @if (!string.IsNullOrWhiteSpace(productTaglineInfo)) 1504 { 1505 <span class="pdp-tagline__infoicon" data-tippy-content="@productTaglineInfo"> 1506 <i class="fal fa-info-circle"></i> 1507 </span> 1508 } 1509 </p> 1510 } 1511 1512 @if (!string.IsNullOrWhiteSpace(productDetailPageTagline)) 1513 { 1514 <p class="pdp-tagline"> 1515 @productDetailPageTagline 1516 @if (!string.IsNullOrWhiteSpace(productDetailPageTaglineInfo)) 1517 { 1518 <span class="pdp-tagline__infoicon" data-tippy-content="@productDetailPageTaglineInfo"> 1519 <i class="fal fa-info-circle"></i> 1520 </span> 1521 } 1522 </p> 1523 } 1524 </div> 1525 } 1526 1527 @* Snippet PdpVariantSelector *@ 1528 @if (GetLoop("VariantGroups").Count > 0){ 1529 string pageId = GetGlobalValue("Global:Page.ID").ToString(); 1530 string variantSelection = productVariantId.Replace(".", ","); 1531 1532 var variantCombinationsObject = new List<Array>(); 1533 foreach (LoopItem variantcomb in GetLoop("VariantStockCombinations")) 1534 { 1535 string[] combinations = variantcomb.GetString("Ecom:VariantStockCombination.VariantID").Split('.'); 1536 variantCombinationsObject.Add(combinations); 1537 } 1538 string combinationsJson = Newtonsoft.Json.JsonConvert.SerializeObject(variantCombinationsObject).Replace("\"", "\'"); 1539 1540 var variantGroupsObject = new List<List<String>>(); 1541 foreach (LoopItem variantGroup in GetLoop("VariantGroups")) 1542 { 1543 var variantsObject = new List<String>(); 1544 foreach (LoopItem variantOption in variantGroup.GetLoop("VariantAvailableOptions")) 1545 { 1546 variantsObject.Add(variantOption.GetString("Ecom:VariantOption.ID")); 1547 } 1548 variantGroupsObject.Add(variantsObject); 1549 } 1550 string variantsJson = Newtonsoft.Json.JsonConvert.SerializeObject(variantGroupsObject).Replace("\"", "\'"); 1551 1552 <div class="pdp-variants"> 1553 <div class="product-variants__wrapper"> 1554 <div class="js-variants" data-total-variant-groups="@GetLoop("VariantGroups").Count" data-combinations="@combinationsJson" data-variants="@variantsJson" data-current-page-variant="@variantSelection" data-variant-selections="@variantSelection" data-page-id="@pageId" data-product-id="@productid" data-group-id="@groupid"> 1555 @foreach (LoopItem variantGroup in GetLoop("VariantGroups")) 1556 { 1557 bool containsImage = variantGroup.GetLoop("VariantAvailableOptions").Any(v => !string.IsNullOrEmpty(v.GetString("Ecom:VariantOption.ImgSmall.Clean"))); 1558 string groupId = variantGroup.GetString("Ecom:VariantGroup.ID"); 1559 1560 <div class="product-variants__block product-variants__block--@groupId"> 1561 @if (containsImage) 1562 { 1563 if(!string.IsNullOrWhiteSpace(variantGroup.GetString("Ecom:VariantGroup.Label"))) 1564 { 1565 <p class="product-variants__title">@variantGroup.GetString("Ecom:VariantGroup.Label")</p> 1566 } 1567 1568 <div class="product-variants__options-wrapper"> 1569 @foreach (LoopItem variantOption in variantGroup.GetLoop("VariantAvailableOptions")) 1570 { 1571 string selected = variantOption.GetBoolean("Ecom:VariantOption.Selected") ? "product-variants__btn--checked" : ""; 1572 1573 if (!string.IsNullOrEmpty(variantOption.GetString("Ecom:VariantOption.ImgSmall.Clean"))) 1574 { 1575 string variantImage = "/Files/" + variantOption.GetString("Ecom:VariantOption.ImgSmall.Clean"); 1576 1577 <div data-variant-id="@variantOption.GetString("Ecom:VariantOption.ID")" data-variant-group="@groupId" class="js-variant-option product-variants__btn product-variants__btn--image @selected"> 1578 <img src="@variantImage" alt="@variantOption.GetString("Ecom:VariantOption.Name")" title="@variantOption.GetString("Ecom:VariantOption.Name")" /> 1579 </div> 1580 } 1581 else 1582 { 1583 <button type="button" data-variant-id="@variantOption.GetString("Ecom:VariantOption.ID")" data-variant-group="@groupId" class="js-variant-option product-variants__btn @selected">@variantOption.GetString("Ecom:VariantOption.Name")</button> 1584 } 1585 } 1586 </div> 1587 } 1588 else 1589 { 1590 if(!string.IsNullOrWhiteSpace(variantGroup.GetString("Ecom:VariantGroup.Name"))) 1591 { 1592 <p class="product-variants__title">@variantGroup.GetString("Ecom:VariantGroup.Name")</p> 1593 } 1594 1595 <div class="product-variants__dropdown"> 1596 <div class="product-variants__dropdown-wrapper"> 1597 <button class="product-variants__toggle"> 1598 <span data-original="@Translate(string.Format("VariantDropdown.Placeholder.{0}", variantGroup.GetString("Ecom:VariantGroup.Name")), "Select your option")">@Translate(string.Format("VariantDropdown.Placeholder.{0}", variantGroup.GetString("Ecom:VariantGroup.Name")), "Select your option")</span> 1599 <i class="fal fa-chevron-down"></i> 1600 </button> 1601 <div class="product-variants__options-wrapper product-variants__options-wrapper--dropdown product-variants__dropdown-options-wrapper"> 1602 @foreach (LoopItem variantOption in variantGroup.GetLoop("VariantAvailableOptions")) 1603 { 1604 string selected = variantOption.GetBoolean("Ecom:VariantOption.Selected") ? "product-variants__btn--checked" : ""; 1605 1606 <button type="button" data-variant-id="@variantOption.GetString("Ecom:VariantOption.ID")" data-variant-group="@groupId" class="js-variant-option product-variants__btn--dropdown @selected"> 1607 @variantOption.GetString("Ecom:VariantOption.Name") 1608 </button> 1609 } 1610 </div> 1611 </div> 1612 </div> 1613 } 1614 </div> 1615 } 1616 </div> 1617 </div> 1618 </div> 1619 } 1620 1621 @* Snippet PdpVolumePrices *@ 1622 @if(displayPrice && displayProductPrice && GetLoop("Product.Prices").Any()) { 1623 var priceList = new List<object>(); 1624 1625 foreach (LoopItem volumePrice in GetLoop("Product.Prices")) 1626 { 1627 int volumePriceQuantity = volumePrice.GetInteger("Ecom:Product.Prices.Quantity"); 1628 1629 if (volumePriceQuantity != 0) 1630 { 1631 double newVolumePrice = Math.Round(volumePrice.GetDouble("Ecom:Product.Prices.Price" + WithVATSuffix) * volumePrice.GetDouble("Ecom:Product.Prices.Quantity"), 2); 1632 string newVolumePriceFormatted = WithVATBool ? new PriceInfo { PriceWithVAT = newVolumePrice }.PriceWithVATFormatted : new PriceInfo { PriceWithoutVAT = newVolumePrice }.PriceWithoutVATFormatted; 1633 1634 double diffVolumePrice = Math.Round((discountProductPriceDouble - volumePrice.GetDouble("Ecom:Product.Prices.Price")) * volumePrice.GetDouble("Ecom:Product.Prices.Quantity"), 2); 1635 string diffVolumePriceFormatted = WithVATBool ? new PriceInfo { PriceWithVAT = diffVolumePrice }.PriceWithVATFormatted : new PriceInfo { PriceWithoutVAT = diffVolumePrice }.PriceWithoutVATFormatted; 1636 1637 if(!FormattedBool) 1638 { 1639 newVolumePriceFormatted = newVolumePriceFormatted.Replace(volumePrice.GetString("Ecom:Product.Prices.Currency.Symbol"), ""); 1640 diffVolumePriceFormatted = diffVolumePriceFormatted.Replace(volumePrice.GetString("Ecom:Product.Prices.Currency.Symbol"), ""); 1641 } 1642 1643 var priceObj = new 1644 { 1645 discountId = volumePrice.GetValue("Product.Prices.LoopCounter"), 1646 newVolumePriceFormatted = newVolumePriceFormatted, 1647 diffVolumePriceFormatted = diffVolumePriceFormatted, 1648 quantity = volumePriceQuantity 1649 }; 1650 priceList.Add(priceObj); 1651 } 1652 } 1653 1654 string pricesJson = Newtonsoft.Json.JsonConvert.SerializeObject(priceList); 1655 1656 <div class="app-volumeprices" data-price-list='@pricesJson'></div> 1657 } 1658 1659 @* Snippet PdpAddToCartBox *@ 1660 @if (!hasVariants && enableShoppingCart && enableProductShoppingCart) { 1661 <div class="pdp-add-to-cart pdp-add-to-cart__container"> 1662 <add-to-cart class="app-addtocart" 1663 data-prodid="@productid" 1664 data-variantid="@productVariantId" 1665 data-min-quantity="@minimumQuantity" 1666 data-step="@quantityStep" 1667 data-list-id="product_detail" 1668 data-list-name="Product detail"> 1669 <a class="pdp__info-btn--add-to-shoppingcart">@inShoppingCartLabel<i class="btn__icon fal fa-shopping-cart"></i></a> 1670 </add-to-cart> 1671 </div> 1672 } 1673 else 1674 { 1675 if (QuotePageID > 0) 1676 { 1677 <div class="pdp-request-quote__container"> 1678 <a href="/Default.aspx?ID=@QuotePageID&ProdID=@productid&VarID=@productVariantId" class="btn product-detailpage__info-btn--request-quote"> 1679 <span class="btn__text">@Translate("ProductDetail.QuoteButton.Text", "Vraag een offerte aan")</span> 1680 <i class="btn__icon @buttonIconClass"></i> 1681 </a> 1682 </div> 1683 } 1684 1685 if(!string.IsNullOrWhiteSpace(GetString("Ecom:Product.ReplacementProductId"))) 1686 { 1687 var repProd = Dynamicweb.Ecommerce.Services.Products.GetProductById(GetString("Ecom:Product.ReplacementProductId"), GetString("Ecom:Product.ReplacementVariantId"), Pageview.Area.EcomLanguageId); 1688 1689 if(repProd != null) 1690 { 1691 int productDetailPageId = GetPageIdByNavigationTag("ProductOverview"); 1692 string productUrl = $"Default.aspx?ID={productDetailPageId}&GroupID={repProd.DefaultGroup.Id}&ProductID={repProd.Id}"; 1693 if(!string.IsNullOrWhiteSpace(repProd.VariantId)) 1694 { 1695 productUrl = $"{productUrl}?VariantID={repProd.VariantId}"; 1696 } 1697 1698 <div class="pdp-replacement__container"> 1699 @if(!string.IsNullOrWhiteSpace(Translate("ProductDetail.ReplacementProd.Intro"))) 1700 { 1701 <p class="pdp-replacement__intro">@string.Format(Translate("ProductDetail.ReplacementProd.Intro", "This product is not available anymore. Please take a look at our selected replacement product."), repProd.Name)</p> 1702 } 1703 <a href="@productUrl" class="btn default-btn"> 1704 <span class="btn__text">@string.Format(Translate("ProductDetail.ReplacementProd.Btn", "View replacement product"), repProd.Name)</span> 1705 <i class="btn__icon @buttonIconClass"></i> 1706 </a> 1707 </div> 1708 } 1709 } 1710 } 1711 1712 @* TheGift Sample Request Button *@ 1713 @{ 1714 bool enableSampleRequest = Pageview.User != null; 1715 int sampleRequestPageId = GetPageIdByNavigationTag("SampleRequest"); 1716 1717 if (enableSampleRequest && sampleRequestPageId > 0) 1718 { 1719 <div class="pdp-sample-request__container"> 1720 <a href="/Default.aspx?ID=@sampleRequestPageId&Productnummers=@productNumber" class="btn"> 1721 <span class="btn__text">@Translate("ProductDetail.SampleRequestButton.Text", "Vraag een sample aan")</span> 1722 <i class="btn__icon @buttonIconClass"></i> 1723 </a> 1724 </div> 1725 } 1726 } 1727 1728 @* TheGift VariantSelector *@ 1729 @foreach(var variantRelatedGroups in GetLoop("ProductRelatedGroups").Where(gr => gr.GetString("Ecom:Product:RelatedGroup.GroupID") == "RELGRP7")) { 1730 string relatedGroupName = variantRelatedGroups.GetString("Ecom:Product:RelatedGroup.Name"); 1731 1732 if(variantRelatedGroups.GetLoop("Products").Any()) 1733 { 1734 <section class="variantgroup"> 1735 <h3 class="variantgroup__title">@Translate("ProductRelatedGroup." + relatedGroupName.Replace(" ", ""), relatedGroupName)</h3> 1736 <div class="variantgroup__products"> 1737 @foreach (var varProduct in variantRelatedGroups.GetLoop("Products")) 1738 { 1739 string image = varProduct.GetString("Ecom:Product.ImageDefault.Clean"); 1740 1741 <a href="@varProduct.GetString("Ecom:Product.VariantLinkGroup.Clean")" class="variantgroup__item" title="@varProduct.GetString("Ecom:Product.Name")"> 1742 <img src="/Admin/Public/GetImage.ashx?Image=@image&Crop=5&Format=webp&Quality=90&Compression=80&width=150&height=150" alt="@varProduct.GetString("Ecom:Product.Name")" class="variantgroup__image" width="75" height="75" /> 1743 </a> 1744 } 1745 </div> 1746 </section> 1747 } 1748 } 1749 1750 @* Snippet PdpPaymentLogos *@ 1751 @if(selectedPaymentLogos != null) 1752 { 1753 <section class="pdp-paymentlogos paymentlogos--small"> 1754 <div class="footer-paymentoptions" data-paymentmethods="@selectedPaymentLogos"></div> 1755 </section> 1756 } 1757 1758 @* Snippet PdpPageUspList *@ 1759 @if (productDetailUSPList.Any()) 1760 { 1761 <ul class="pdp-usplist"> 1762 @foreach (ProductDetailUSP item in productDetailUSPList) 1763 { 1764 @RenderProductDetailUSP(item.USP, item.Tooltip) 1765 } 1766 </ul> 1767 } 1768 1769 @* Snippet PdpProductUspList *@ 1770 @if (!string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductUSP1.Value")) || !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductUSP2.Value")) || !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductUSP3.Value")) || !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductUSP4.Value")) || !string.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProductUSP5.Value"))) 1771 { 1772 <ul class="pdp-usplist"> 1773 @RenderProductUSP(GetString("Ecom:Product:Field.ProductUSP1.Value"), GetString("Ecom:Product:Field.ProductUSP1Info.Value")) 1774 @RenderProductUSP(GetString("Ecom:Product:Field.ProductUSP2.Value"), GetString("Ecom:Product:Field.ProductUSP2Info.Value")) 1775 @RenderProductUSP(GetString("Ecom:Product:Field.ProductUSP3.Value"), GetString("Ecom:Product:Field.ProductUSP3Info.Value")) 1776 @RenderProductUSP(GetString("Ecom:Product:Field.ProductUSP4.Value"), GetString("Ecom:Product:Field.ProductUSP4Info.Value")) 1777 @RenderProductUSP(GetString("Ecom:Product:Field.ProductUSP5.Value"), GetString("Ecom:Product:Field.ProductUSP5Info.Value")) 1778 </ul> 1779 } 1780 1781 @* Snippet PdpProductShortDescription *@ 1782 @if (hasShortDescription) 1783 { 1784 <div class="pdp-short-description"> 1785 @productShortDescription 1786 </div> 1787 } 1788 1789 @* @if((hasShortDescription && hasLongDescription) || hasSpecifications) 1790 { 1791 <div class="pdp-action-buttons"> 1792 @if (hasShortDescription && hasLongDescription) 1793 { 1794 <p class="pdp-short-description__readmore"> 1795 <a href="#pdp-tabs">@Translate("Productdetail.Readmore", "Read more")</a> 1796 </p> 1797 } 1798 @if (hasSpecifications && enableSpecificationsSidebar) 1799 { 1800 <a href="javascript:void(0);" class="toggle-of-canvas-menu" data-offcanvas-target="specifications"> 1801 @Translate("Productdetail.ViewSpecifications", "View product specifications") 1802 </a> 1803 } 1804 </div> 1805 } *@ 1806 1807 @* Snippet LoginForMoreInfo *@ 1808 @if(Pageview.User == null) 1809 { 1810 int createAccountPageId = GetPageIdByNavigationTag("CreateAccount"); 1811 1812 <div class="pdp-login-seemore"> 1813 <p>@string.Format(Translate("Productdetail.LoginToseeMore", "Do you want to see more information about this product? {0}Login{1} or {2}create an account{3}."), "<a href='#' class='topmenu__link--login'>", "</a>", String.Format("<a href='/Default.aspx?ID={0}'>", createAccountPageId), "</a>")</p> 1814 </div> 1815 } 1816 1817 </section> 1818 @SnippetEnd("PdpInfo") 1819 1820 @SnippetStart("PdpImage") 1821 <section class="product-detailpage__images pdp__images-container"> 1822 1823 <section class="productimages pdp__images"> 1824 1825 @{ 1826 int videoThumbPosition = 1; 1827 var SetViaThumbInt = 0; 1828 var SetViaImageInt = 0; 1829 string panoramicPhoto = GetString("Ecom:Product:Field.PanoramicPhoto"); 1830 } 1831 1832 @if ((productImages.Count > 1 || hasYoutubeVideo || !string.IsNullOrWhiteSpace(panoramicPhoto)) && Pageview.User != null) 1833 { 1834 1835 if (videoThumbPosition > productImages.Count) 1836 { 1837 videoThumbPosition = productImages.Count; 1838 } 1839 1840 <section class="productimages__wrapper"> 1841 1842 @if (!string.IsNullOrWhiteSpace(productRibbon)) 1843 { 1844 <p class="product-detailpage__ribbon product-detailpage__ribbon--big product-detailpage__ribbon--@productRibbonStyle"><span>@productRibbon</span></p> 1845 } 1846 1847 @if (!string.IsNullOrWhiteSpace(manufacturerLogo)) 1848 { 1849 <img src="/Admin/Public/GetImage.ashx?Image=/Files/@manufacturerLogo&Crop=7&Format=webp&Quality=90&Compression=80&width=150" class="manufacturer__logo" alt="Manufacturer" width="150" height="50" /> 1850 } 1851 1852 <div class="productimages__carousel-big hidden"> 1853 @if (!string.IsNullOrWhiteSpace(panoramicPhoto)) 1854 { 1855 <div class="productimages__item"> 1856 <iframe src="@panoramicPhoto" width="100%" height="500" scrolling="no" marginwidth="0" marginheight="0" frameborder="0" vspace="0" hspace="0" allow="fullscreen" webkitallowfullscreen mozallowfullscreen allowfullscreen> 1857 Could not load @panoramicPhoto 1858 </iframe> 1859 </div> 1860 } 1861 1862 @foreach (var image in productImages) 1863 { 1864 1865 SetViaImageInt++; 1866 1867 <div class="productimages__item"> 1868 <img class="w-auto" src="/Admin/Public/GetImage.ashx?Image=@image&Format=webp&Quality=-1&width=800&height=800" alt="@productName" /> 1869 </div> 1870 1871 if (SetViaImageInt == videoThumbPosition && hasYoutubeVideo) 1872 { 1873 <div class="productimages__item"> 1874 <div class="productimages__carousel__video-container"> 1875 <lite-youtube videoid="@YoutubeProductVideo" params="controls=1&loop=0&playlist=@YoutubeProductVideo&playsinline=1&modestbranding=1&mute=0&rel=0&enablejsapi=1& origin=@Dynamicweb.Environment.Helpers.LinkHelper.GetHttpDomain()&disablekb=0"></lite-youtube> 1876 </div> 1877 </div> 1878 } 1879 } 1880 1881 </div> 1882 1883 </section> 1884 1885 <div class="productimages__carousel-thumbnails hidden"> 1886 @if (!string.IsNullOrWhiteSpace(panoramicPhoto)) 1887 { 1888 <div class="productimages__thumbnail"> 1889 <img class="product-image" src="/Admin/Public/GetImage.ashx?Image=/Files/Images/Products/360.png&Crop=5&Format=webp&Quality=90&Compression=80&width=200&height=200" alt="Thumbnail @productName" width="100" height="100" /> 1890 </div> 1891 } 1892 @foreach (var image in productImages) 1893 { 1894 SetViaThumbInt++; 1895 1896 <div class="productimages__thumbnail"> 1897 <img class="product-image" src="/Admin/Public/GetImage.ashx?Image=@image&Crop=5&Format=webp&Quality=90&Compression=80&width=200&height=200" alt="Thumbnail @productName" width="100" height="100" /> 1898 </div> 1899 1900 if (SetViaThumbInt == videoThumbPosition && hasYoutubeVideo) 1901 { 1902 <div class="productimages__thumbnail productimages__thumbnail--video"> 1903 <img src="https://i.ytimg.com/vi_webp/@YoutubeProductVideo/hqdefault.webp" alt="Video preview @productName" width="100" height="100" /> 1904 <span class="video-icon"> 1905 <i class="fas fa-play"></i> 1906 </span> 1907 </div> 1908 } 1909 } 1910 </div> 1911 } 1912 else if (productImages.Count == 1) 1913 { 1914 var img = productImages.First(); 1915 img = !string.IsNullOrWhiteSpace(img) ? $"/Admin/Public/GetImage.ashx?Image={img}&Format=webp&Quality=-1&width=800&height=800" : "https://via.placeholder.com/800x400/?text=No%20Image%20Found"; 1916 1917 <div id="productimages__big" class="productimages__big"> 1918 @if (!string.IsNullOrWhiteSpace(productRibbon)) 1919 { 1920 <p class="product-detailpage__ribbon product-detailpage__ribbon--big product-detailpage__ribbon--@productRibbonStyle"><span>@productRibbon</span></p> 1921 } 1922 @if (!string.IsNullOrWhiteSpace(manufacturerLogo)) 1923 { 1924 <img src="/Admin/Public/GetImage.ashx?Image=/Files/@manufacturerLogo&Crop=7&Format=webp&Quality=90&Compression=80&width=150" class="manufacturer__logo" alt="Manufacturer" width="150" height="50" /> 1925 } 1926 <img id="product-image" class="product-image" src="@img" alt="@productName" /> 1927 </div> 1928 } 1929 </section> 1930 </section> 1931 @SnippetEnd("PdpImage") 1932 1933 @SnippetStart("PdpHelp") 1934 @if (!string.IsNullOrWhiteSpace(webshopPage.HelpBannerHeader) || !string.IsNullOrWhiteSpace(webshopPage.HelpBannerBody) || !string.IsNullOrWhiteSpace(webshopPage.HelpBannerBody)) 1935 { 1936 <section class="pdp-paragraph pdp-paragraph__container"> 1937 <div class="container pdp-paragraph__innerwrapper"> 1938 @if(!string.IsNullOrWhiteSpace(webshopPage.HelpBannerImage)) 1939 { 1940 <figure class="pdp-paragraph__image"> 1941 <img src="/Admin/Public/GetImage.ashx?Image=@webshopPage.HelpBannerImage&Crop=7&Format=webp&Quality=90&Compression=80&Width=600" alt="" loading="lazy" width="600" height="300" /> 1942 </figure> 1943 } 1944 <div class="pdp-paragraph__body"> 1945 @if(!string.IsNullOrWhiteSpace(webshopPage.HelpBannerHeader)) 1946 { 1947 <h2 class="pdp-paragraph__header">@webshopPage.HelpBannerHeader</h2> 1948 } 1949 <div class="pdp-paragraph__content"> 1950 @webshopPage.HelpBannerBody 1951 </div> 1952 @if(!string.IsNullOrWhiteSpace(webshopPage.HelpBannerCTAButtonLink)) 1953 { 1954 <a href="@webshopPage.HelpBannerCTAButtonLink" class="btn default-btn"> 1955 <span class="btn__text">@webshopPage.HelpBannerCTAButtonLabel</span> 1956 <i class="btn__icon fa-chevron-right"></i> 1957 </a> 1958 } 1959 </div> 1960 </div> 1961 </section> 1962 } 1963 @SnippetEnd("PdpHelp") 1964 1965 @SnippetStart("PdpBanner") 1966 @if (!string.IsNullOrWhiteSpace(webshopPage.BannerHeader) || !string.IsNullOrWhiteSpace(webshopPage.BannerBody) || !string.IsNullOrWhiteSpace(webshopPage.BannerCTAButtonLabel)) 1967 { 1968 <section class="pdp-paragraph pdp-paragraph--image-right pdp-paragraph__container"> 1969 <div class="container pdp-paragraph__innerwrapper"> 1970 @if(!string.IsNullOrWhiteSpace(webshopPage.BannerImage)) 1971 { 1972 <figure class="pdp-paragraph__image"> 1973 <img src="/Admin/Public/GetImage.ashx?Image=@webshopPage.BannerImage&Crop=7&Format=webp&Quality=90&Compression=80&Width=600" alt="" loading="lazy" width="600" height="300" /> 1974 </figure> 1975 } 1976 <div class="pdp-paragraph__body"> 1977 @if(!string.IsNullOrWhiteSpace(webshopPage.HelpBannerImage)) 1978 { 1979 <h2 class="pdp-paragraph__header">@webshopPage.BannerHeader</h2> 1980 } 1981 <div class="pdp-paragraph__content"> 1982 @if(!string.IsNullOrWhiteSpace(webshopPage.BannerBody)) { 1983 @webshopPage.BannerBody 1984 } 1985 </div> 1986 @if(!string.IsNullOrWhiteSpace(webshopPage.BannerCTAButtonLink)) { 1987 <a href="@webshopPage.BannerCTAButtonLink" class="btn default-btn"> 1988 <span class="btn__text">@webshopPage.BannerCTAButtonLabel</span> 1989 <i class="btn__icon fa-chevron-right"></i> 1990 </a> 1991 } 1992 </div> 1993 </div> 1994 </section> 1995 } 1996 @SnippetEnd("PdpBanner") 1997 1998 @* @SnippetStart("PdpSpecification") 1999 <section class="pdp-specifications" id="specs"> 2000 <div class="container pdp-specifications__container"> 2001 2002 @RenderPDPTabs() 2003 2004 <div class="pdp-specifications__innerwrapper"> 2005 <div class="pdp-specifications__long-description" id="information"> 2006 @RenderInfoContentElement("Ecom:Product.LongDescription", "Productinformatie", "productinformation") 2007 @RenderInfoContentElement("Ecom:Product:Field.ProductDetailSpecs", "Specificaties", "specifications") 2008 @RenderInfoContentElement("Ecom:Product:Field.ProductDetailDownloads", "Downloads", "downloads") 2009 </div> 2010 @if (hasYoutubeVideo) 2011 { 2012 <div class="pdp-specifications__video-wrapper"> 2013 <iframe class="pdp-specifications__video" width="100%" height="315" src="https://www.youtube.com/embed/@YoutubeProductVideo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> 2014 </div> 2015 } 2016 </div> 2017 </div> 2018 </section> 2019 @SnippetEnd("PdpSpecification") *@ 2020 2021 @SnippetStart("PdpReviews") 2022 <div id="reviews" class="review-paragraph"> 2023 <div class="container review-paragraph__container"> 2024 <div class="review-paragraph__summary"> 2025 <div class="review-summary__column"> 2026 <h2 class="review-summary__title">@Translate("Reviews.Title", "Customer reviews")</h2> 2027 <div class="review-summary__container"> 2028 <div class="review-summary__indicator"> 2029 @renderReviewIndicator("#86C440", GetInteger("Comments.Rating"), "row", true, GetInteger("Comments.TotalCount")) 2030 </div> 2031 </div> 2032 </div> 2033 2034 <div class="review-summary__column"> 2035 @renderReviewDistrubition(GetLoop("Comments")) 2036 </div> 2037 2038 <div class="review-summary__column"> 2039 @if(Dynamicweb.Context.Current.Request["reviewcmd"] != null && Dynamicweb.Context.Current.Request["reviewcmd"] == "created") { 2040 <h2 class="review-summary__title">@Translate("Reviews.Submitted.Title", "Thanks for your review")</h2> 2041 <p>@Translate("Reviews.Submitted.Text", "Thank you for submitting your review.")</p> 2042 } else { 2043 <h2 class="review-summary__title">@Translate("Reviews.Create.Text", "Write a review")</h2> 2044 <p>@Translate("Reviews.Create.Text", "Share your thoughts about this product with other customers.")</p> 2045 <div class="review-summary__btn-wrapper"> 2046 <a href="#" class="btn btn__primary toggle-of-canvas-menu" data-offcanvas-target="reviewcreate"> 2047 <span class="btn__text">@Translate("Reviews.Create.Button", "Write a review")</span> 2048 <i class="btn__icon fal fa-plus"></i> 2049 </a> 2050 </div> 2051 } 2052 </div> 2053 </div> 2054 <div class="review-paragraph__comments"> 2055 @foreach (LoopItem Comment in GetLoop("Comments.Newfirst").Take(3)) 2056 { 2057 <section class="review-comment__list-item"> 2058 <div class="review-comment__header"> 2059 <div class="review-comment__rating-wrapper"> 2060 @renderReviewIndicator("#86C440", Comment.GetInteger("Rating"), "row", false, 0) 2061 </div> 2062 </div> 2063 <div class="review-comment__message"> 2064 <p>@Comment.GetString("Text")</p> 2065 </div> 2066 <p class="review-comment__meta">@Comment.GetString("Name") | @Comment.GetDate("CreatedDate").ToString("d MMMM yyyy")</p> 2067 </section> 2068 } 2069 @if(GetLoop("Comments.Newfirst").Count > 3) { 2070 <a href="#" class="toggle-of-canvas-menu" data-offcanvas-target="reviews">@string.Format(Translate("Reviews.ReadMore", "Read all {0} reviews"), GetLoop("Comments.Newfirst").Count)</a> 2071 } 2072 </div> 2073 </div> 2074 </div> 2075 @SnippetEnd("PdpReviews") 2076 2077 @SnippetStart("PdpRelatedGroups") 2078 @foreach (var relatedGroup in GetLoop("ProductRelatedGroups")) 2079 { 2080 string relatedGroupId = relatedGroup.GetString("Ecom:Product:RelatedGroup.GroupID"); 2081 string relatedGroupName = relatedGroup.GetString("Ecom:Product:RelatedGroup.Name"); 2082 int RelatedGroupsInt = relatedGroup.GetLoop("Products").Count; 2083 string ClassIgniteCarousel = RelatedGroupsInt > 4 ? "products-module__container--carousel" : ""; 2084 2085 if (relatedGroupId != "RELGRP6" && relatedGroupId != "RELGRP7") 2086 { 2087 <section class="products-module__container @ClassIgniteCarousel" id="@relatedGroupId"> 2088 <div class="container"> 2089 2090 <header class="products-module__header"> 2091 <h2 class="products-module__title">@Translate("ProductRelatedGroup." + relatedGroupName.Replace(" ", ""), relatedGroupName)</h2> 2092 </header> 2093 2094 <ul class="products-module__slider"> 2095 @foreach (var Product in relatedGroup.GetLoop("Products")) 2096 { 2097 @renderProduct(Product, ProductdetailPriceSuffix, WithVATSuffix, FormattedSuffix, enableShoppingCart, displayPrice, enableProductStock, relatedGroupId, relatedGroupName, EcommerceConfiguration); 2098 } 2099 </ul> 2100 2101 </div> 2102 </section> 2103 2104 @renderProductListEnhancedEcom(relatedGroupId, relatedGroupName, relatedGroup.GetLoop("Products"), WithVATSuffix); 2105 } 2106 } 2107 @SnippetEnd("PdpRelatedGroups") 2108 2109 @SnippetStart("PdpSecondaryInfo") 2110 <section class="pdp-secondary-info"> 2111 <div class="container pdp-secondary-info__container"> 2112 <div class="pdp-secondary-info__column"> 2113 @if(!string.IsNullOrWhiteSpace(defaultImage)) 2114 { 2115 <figure class="pdp-secondary-info__image-container"> 2116 <img class="pdp-secondary-info__image" src="/Admin/Public/GetImage.ashx?Image=@defaultImage&Crop=7&Format=webp&Quality=90&Compression=80&Height=400" alt="@productName" loading="lazy" height="400" width="600" /> 2117 </figure> 2118 } 2119 </div> 2120 <div class="pdp-secondary-info__column"> 2121 2122 <h2 class="pdp-header__title"> 2123 @if (!string.IsNullOrWhiteSpace(Manufacturer)) 2124 { 2125 <span class="pdp-header__manufacturer">@Manufacturer</span> 2126 } 2127 <span class="pdp-header__productname"> 2128 @productName 2129 </span> 2130 </h2> 2131 2132 @if (GetInteger("Comments.TotalCount") > 0) 2133 { 2134 <div class="pdp-review-indicator"> 2135 @renderReviewIndicator("#86C440", GetInteger("Comments.Rating"), "row", true, GetInteger("Comments.Count")) 2136 </div> 2137 } 2138 2139 @if (!string.IsNullOrWhiteSpace(productNumber)) 2140 { 2141 <p class="pdp-articlenumber"> 2142 @Translate("Productdetail.ArticleNumber.Prefix", "Article number:") @productNumber 2143 </p> 2144 } 2145 2146 @if(enableProductStock && !string.IsNullOrWhiteSpace(stockFormat)) 2147 { 2148 <div class="pdp-stockstate__wrapper"> 2149 @if(stockFormat == "text") { 2150 if (!string.IsNullOrWhiteSpace(stockText)) 2151 { 2152 <p class="pdp-stockstate @stockStateClass">@string.Format(stockText, stock)</p> 2153 } 2154 } 2155 else 2156 { 2157 string translationTag = ""; 2158 if(neverOutOfStock) 2159 { 2160 translationTag = Translate("ProductBlockStockInfo.AmountInStock", "In stock"); 2161 } 2162 else if(stockSize == 1) 2163 { 2164 translationTag = Translate("ProductBlockStockInfo.AmountInStockSingle", "{0} product in stock"); 2165 } 2166 else if(stockSize > 1) 2167 { 2168 translationTag = Translate("ProductBlockStockInfo.AmountInStockMultiple", "{0} products in stock"); 2169 } 2170 else if(!inStock) 2171 { 2172 translationTag = Translate("ProductBlockStockInfoPDP.AmountOutOfStock", "Out of stock"); 2173 } 2174 2175 <p class="pdp-stockstate @stockStateClass">@string.Format(translationTag, stock)</p> 2176 } 2177 </div> 2178 } 2179 2180 @if (!string.IsNullOrWhiteSpace(productTagline) || !string.IsNullOrWhiteSpace(productDetailPageTagline)) 2181 { 2182 <div class="pdp-tagline__container"> 2183 @if (!string.IsNullOrWhiteSpace(productTagline)) 2184 { 2185 <p class="pdp-tagline"> 2186 @productTagline 2187 @if (!string.IsNullOrWhiteSpace(productTaglineInfo)) 2188 { 2189 <span class="pdp-tagline__infoicon" data-tippy-content="@productTaglineInfo"> 2190 <i class="fal fa-info-circle"></i> 2191 </span> 2192 } 2193 </p> 2194 } 2195 2196 @if (!string.IsNullOrWhiteSpace(productDetailPageTagline)) 2197 { 2198 <p class="pdp-tagline"> 2199 @productDetailPageTagline 2200 @if (!string.IsNullOrWhiteSpace(productDetailPageTaglineInfo)) 2201 { 2202 <span class="pdp-tagline__infoicon" data-tippy-content="@productDetailPageTaglineInfo"> 2203 <i class="fal fa-info-circle"></i> 2204 </span> 2205 } 2206 </p> 2207 } 2208 </div> 2209 } 2210 </div> 2211 <div class="pdp-secondary-info__column"> 2212 2213 @if (displayPrice && displayProductPrice) 2214 { 2215 <div class="pdp-price__container"> 2216 @if(hasVariants) { 2217 <section class="pdp-price__subcontainer"> 2218 @if(minPrice != maxPrice) 2219 { 2220 <span class="pdp-price">@minPriceFormatted - @maxPriceFormatted</span> 2221 } 2222 else 2223 { 2224 <span class="pdp-price">@minPriceFormatted</span> 2225 } 2226 <span class="pdp-price--suffix">@ProductdetailPriceSuffix</span> 2227 </section> 2228 } else { 2229 if (hasDiscount) 2230 { 2231 <p class="pdp-price--original">@originalProductPrice</p> 2232 <section class="pdp-price__subcontainer"> 2233 <span class="pdp-price">@discountProductPrice</span> 2234 <span class="pdp-price--suffix">@ProductdetailPriceSuffix</span> 2235 <span class="pdp-price__percentage">@discountPercentage%</span> 2236 </section> 2237 } 2238 else 2239 { 2240 <section class="pdp-price__subcontainer"> 2241 <span class="pdp-price">@originalProductPrice</span> 2242 <span class="pdp-price--suffix">@ProductdetailPriceSuffix</span> 2243 </section> 2244 } 2245 if (!string.IsNullOrWhiteSpace(retailPrice)) 2246 { 2247 <p class="pdp-price__retail-price"> 2248 @String.Format(Translate("ProductBlockTitle.RetailPrice", "Retail price: {0}"), retailPrice) 2249 </p> 2250 } 2251 if (yourProfitValue > 0) 2252 { 2253 <p class="pdp-price__yourprofit"> 2254 @Translate("Productdetail.YourProfitLabel", "Your profit:") @yourProfitValueFormatted 2255 </p> 2256 } 2257 } 2258 </div> 2259 } 2260 2261 @if (GetLoop("VariantGroups").Count > 0){ 2262 string pageId = GetGlobalValue("Global:Page.ID").ToString(); 2263 string variantSelection = productVariantId.Replace(".", ","); 2264 2265 var variantCombinationsObject = new List<Array>(); 2266 foreach (LoopItem variantcomb in GetLoop("VariantStockCombinations")) 2267 { 2268 string[] combinations = variantcomb.GetString("Ecom:VariantStockCombination.VariantID").Split('.'); 2269 variantCombinationsObject.Add(combinations); 2270 } 2271 string combinationsJson = Newtonsoft.Json.JsonConvert.SerializeObject(variantCombinationsObject).Replace("\"", "\'"); 2272 2273 var variantGroupsObject = new List<List<String>>(); 2274 foreach (LoopItem variantGroup in GetLoop("VariantGroups")) 2275 { 2276 var variantsObject = new List<String>(); 2277 foreach (LoopItem variantOption in variantGroup.GetLoop("VariantAvailableOptions")) 2278 { 2279 variantsObject.Add(variantOption.GetString("Ecom:VariantOption.ID")); 2280 } 2281 variantGroupsObject.Add(variantsObject); 2282 } 2283 string variantsJson = Newtonsoft.Json.JsonConvert.SerializeObject(variantGroupsObject).Replace("\"", "\'"); 2284 2285 <div class="pdp-variants"> 2286 <div class="product-variants__wrapper"> 2287 <div class="js-variants" data-total-variant-groups="@GetLoop("VariantGroups").Count" data-combinations="@combinationsJson" data-variants="@variantsJson" data-current-page-variant="@variantSelection" data-variant-selections="@variantSelection" data-page-id="@pageId" data-product-id="@productid" data-group-id="@groupid"> 2288 @foreach (LoopItem variantGroup in GetLoop("VariantGroups")) 2289 { 2290 bool containsImage = variantGroup.GetLoop("VariantAvailableOptions").Any(v => !string.IsNullOrEmpty(v.GetString("Ecom:VariantOption.ImgSmall.Clean"))); 2291 string groupId = variantGroup.GetString("Ecom:VariantGroup.ID"); 2292 2293 <div class="product-variants__block product-variants__block--@groupId"> 2294 @if (containsImage) 2295 { 2296 if(!string.IsNullOrWhiteSpace(variantGroup.GetString("Ecom:VariantGroup.Label"))) 2297 { 2298 <p class="product-variants__title">@variantGroup.GetString("Ecom:VariantGroup.Label")</p> 2299 } 2300 2301 <div class="product-variants__options-wrapper"> 2302 @foreach (LoopItem variantOption in variantGroup.GetLoop("VariantAvailableOptions")) 2303 { 2304 string selected = variantOption.GetBoolean("Ecom:VariantOption.Selected") ? "product-variants__btn--checked" : ""; 2305 2306 if (!string.IsNullOrEmpty(variantOption.GetString("Ecom:VariantOption.ImgSmall.Clean"))) 2307 { 2308 string variantImage = "/Files/" + variantOption.GetString("Ecom:VariantOption.ImgSmall.Clean"); 2309 <div data-variant-id="@variantOption.GetString("Ecom:VariantOption.ID")" data-variant-group="@groupId" class="js-variant-option product-variants__btn product-variants__btn--image @selected"> 2310 <img src="@variantImage" alt="@variantOption.GetString("Ecom:VariantOption.Name")" title="@variantOption.GetString("Ecom:VariantOption.Name")" /> 2311 </div> 2312 } 2313 else 2314 { 2315 <button type="button" data-variant-id="@variantOption.GetString("Ecom:VariantOption.ID")" data-variant-group="@groupId" class="js-variant-option product-variants__btn @selected">@variantOption.GetString("Ecom:VariantOption.Name")</button> 2316 } 2317 } 2318 </div> 2319 } 2320 else 2321 { 2322 if(!string.IsNullOrWhiteSpace(variantGroup.GetString("Ecom:VariantGroup.Name"))) 2323 { 2324 <p class="product-variants__title">@variantGroup.GetString("Ecom:VariantGroup.Name")</p> 2325 } 2326 2327 <div class="product-variants__dropdown"> 2328 <div class="product-variants__dropdown-wrapper"> 2329 2330 <button class="product-variants__toggle"> 2331 <span data-original="@Translate(string.Format("VariantDropdown.Placeholder.{0}", variantGroup.GetString("Ecom:VariantGroup.Name")), "Select your option")">@Translate(string.Format("VariantDropdown.Placeholder.{0}", variantGroup.GetString("Ecom:VariantGroup.Name")), "Select your option")</span> 2332 <i class="fal fa-chevron-down"></i> 2333 </button> 2334 <div class="product-variants__options-wrapper product-variants__options-wrapper--dropdown product-variants__dropdown-options-wrapper"> 2335 @foreach (LoopItem variantOption in variantGroup.GetLoop("VariantAvailableOptions")) { 2336 string selected = variantOption.GetBoolean("Ecom:VariantOption.Selected") ? "product-variants__btn--checked" : ""; 2337 2338 <button type="button" data-variant-id="@variantOption.GetString("Ecom:VariantOption.ID")" data-variant-group="@groupId" class="js-variant-option product-variants__btn--dropdown @selected"> 2339 @variantOption.GetString("Ecom:VariantOption.Name") 2340 </button> 2341 } 2342 </div> 2343 </div> 2344 </div> 2345 } 2346 </div> 2347 } 2348 </div> 2349 </div> 2350 </div> 2351 } 2352 2353 @if (!hasVariants && enableShoppingCart && enableProductShoppingCart) 2354 { 2355 <div class="pdp-add-to-cart pdp-add-to-cart__container"> 2356 <add-to-cart class="app-addtocart" 2357 data-prodid="@productid" 2358 data-variantid="@productVariantId" 2359 data-min-quantity="@minimumQuantity" 2360 data-step="@quantityStep" 2361 data-list-id="product_detail" 2362 data-list-name="Product detail"> 2363 <a class="pdp__info-btn--add-to-shoppingcart">@inShoppingCartLabel<i class="btn__icon fal fa-shopping-cart"></i></a> 2364 </add-to-cart> 2365 </div> 2366 } 2367 else 2368 { 2369 if (QuotePageID > 0) 2370 { 2371 <div class="pdp-request-quote__container"> 2372 <a href="/Default.aspx?ID=@QuotePageID&ProdID=@productid&VarID=@productVariantId" class="btn product-detailpage__info-btn--request-quote"> 2373 <span class="btn__text">@Translate("ProductDetail.QuoteButton.Text", "Vraag een offerte aan")</span> 2374 <i class="btn__icon @buttonIconClass"></i> 2375 </a> 2376 </div> 2377 } 2378 2379 if(!string.IsNullOrWhiteSpace(GetString("Ecom:Product.ReplacementProductId"))) 2380 { 2381 var repProd = Dynamicweb.Ecommerce.Services.Products.GetProductById(GetString("Ecom:Product.ReplacementProductId"), GetString("Ecom:Product.ReplacementVariantId"), Pageview.Area.EcomLanguageId); 2382 2383 if(repProd != null) 2384 { 2385 int productDetailPageId = GetPageIdByNavigationTag("ProductOverview"); 2386 string productUrl = $"Default.aspx?ID={productDetailPageId}&GroupID={repProd.DefaultGroup.Id}&ProductID={repProd.Id}"; 2387 if(!string.IsNullOrWhiteSpace(repProd.VariantId)) 2388 { 2389 productUrl = $"{productUrl}?VariantID={repProd.VariantId}"; 2390 } 2391 2392 <div class="pdp-replacement__container"> 2393 @if(!string.IsNullOrWhiteSpace(Translate("ProductDetail.ReplacementProd.Intro"))) 2394 { 2395 <p class="pdp-replacement__intro">@string.Format(Translate("ProductDetail.ReplacementProd.Intro", "This product is not available anymore. Please take a look at our selected replacement product."), repProd.Name)</p> 2396 } 2397 <a href="@productUrl" class="btn default-btn"> 2398 <span class="btn__text">@string.Format(Translate("ProductDetail.ReplacementProd.Btn", "View replacement product"), repProd.Name)</span> 2399 <i class="btn__icon @buttonIconClass"></i> 2400 </a> 2401 </div> 2402 } 2403 } 2404 } 2405 2406 @if(selectedPaymentLogos != null) 2407 { 2408 <section class="pdp-paymentlogos paymentlogos--small"> 2409 <div class="footer-paymentoptions" data-paymentmethods="@selectedPaymentLogos"></div> 2410 </section> 2411 } 2412 </div> 2413 </div> 2414 </section> 2415 @SnippetEnd("PdpSecondaryInfo") 2416 2417 @SnippetStart("PdpRecentViewedProducts") 2418 @if (GetInteger("eCom:Related.YouHaveSeenTheseProducts.Count") > 0) 2419 { 2420 var groupTitle = Translate("Productdetail.RecentlyViewedArticles", "Recently viewed articles"); 2421 var recentlyViewedProducts = GetLoop("eCom:Related.YouHaveSeenTheseProducts"); 2422 recentlyViewedProducts.Reverse(); 2423 recentlyViewedProducts = recentlyViewedProducts.Where(p => !string.IsNullOrWhiteSpace(p.GetString("Ecom:Product.ID")) && p.GetString("Ecom:Product.ID") != productid).Take(4).ToList(); 2424 2425 if(recentlyViewedProducts.Count > 0) 2426 { 2427 <section class="products-module__container"> 2428 <div class="container"> 2429 2430 <header class="products-module__header"> 2431 <h2 class="products-module__title">@groupTitle</h2> 2432 </header> 2433 2434 <ul class="products-module__slider"> 2435 @foreach (var Product in recentlyViewedProducts) 2436 { 2437 @renderProduct(Product, ProductdetailPriceSuffix, WithVATSuffix, FormattedSuffix, enableShoppingCart, displayPrice, enableProductStock, "recently_viewed_articles", groupTitle, EcommerceConfiguration); 2438 } 2439 </ul> 2440 2441 </div> 2442 </section> 2443 2444 @renderProductListEnhancedEcom("recently_viewed_articles", groupTitle, recentlyViewedProducts, WithVATSuffix); 2445 } 2446 } 2447 @SnippetEnd("PdpRecentViewedProducts") 2448 2449 <!-- ***** END PDP ***** --> 2450 2451 @SnippetStart("ProductDetailHeaderDesktop") 2452 <!-- BEGIN Stickymenu --> 2453 <section class="stickymenu__replaceable"> 2454 2455 <div class="stickymenu_product-info-wrapper"> 2456 @if (!string.IsNullOrWhiteSpace(defaultImage)) 2457 { 2458 <figure class="stickymenu__product-image"> 2459 <img src="/Admin/Public/GetImage.ashx?Image=@defaultImage&Crop=5&Format=webp&Quality=90&Compression=80&Height=60&Width=60" alt="@productName" width="60" height="60"> 2460 </figure> 2461 } 2462 <div class="stickymenu_product-info"> 2463 <p class="stickymenu__product-name"> 2464 @if(!string.IsNullOrWhiteSpace(Manufacturer)) 2465 { 2466 <span class="stickymenu__product-name--manufacturer">@Manufacturer </span> 2467 } 2468 <span class="stickymenu__product-name--product">@productName</span> 2469 </p> 2470 2471 @if (displayPrice && displayProductPrice) 2472 { 2473 if (hasDiscount) 2474 { 2475 <div class="stickymenu_product__price__wrapper"> 2476 <span class="stickymenu_product__price--old">@originalProductPrice</span> 2477 <span class="stickymenu_product__price--current">@discountProductPrice</span> 2478 <span class="stickymenu_product__price--suffix">@ProductdetailPriceSuffix</span> 2479 <span class="stickymenu_product__price--profit">@Translate("Productdetail.YourProfitLabel", "Your profit:") @yourProfitValueFormatted</span> 2480 </div> 2481 } 2482 else 2483 { 2484 <div class="stickymenu_product__price__wrapper"> 2485 <span class="stickymenu_product__price--current">@originalProductPrice</span> 2486 <span class="stickymenu_product__price--suffix">@ProductdetailPriceSuffix</span> 2487 </div> 2488 } 2489 <p class="pdp-price--consumer"> 2490 @if(Pageview.User != null) 2491 { 2492 if(!string.IsNullOrWhiteSpace(retailPrice)) 2493 { 2494 @String.Format(Translate("ProductBlockTitle.AVP", "AVP: {0}"), retailPrice); 2495 } 2496 } 2497 else 2498 { 2499 @Translate("ProductBlockTitle.AVP.Guests", "Adviesprijs"); 2500 } 2501 </p> 2502 } 2503 2504 </div> 2505 </div> 2506 2507 @if (enableShoppingCart && enableProductShoppingCart) 2508 { 2509 <add-to-cart class="app-addtocart" 2510 data-prodid="@productid" 2511 data-variantid="@productVariantId" 2512 data-min-quantity="@minimumQuantity" 2513 data-step="@quantityStep" 2514 data-list-id="product_detail" 2515 data-list-name="Product detail"> 2516 <!-- Fall Back button for Add to Cart--> 2517 <a class="pdp__add-to-cart-btn">@inShoppingCartLabel <i class="btn__icon fal fa-shopping-cart"></i></a> 2518 </add-to-cart> 2519 } 2520 else 2521 { 2522 if (QuotePageID > 0) 2523 { 2524 <div class="py-4"> 2525 <a href="/Default.aspx?ID=@QuotePageID&ProdID=@productid&VarID=@productVariantId" class="btn product-detailpage__info-btn--request-quote"> 2526 <span class="btn__text">@Translate("ProductDetail.QuoteButton.Text", "Vraag een offerte aan")</span> 2527 <i class="btn__icon @buttonIconClass"></i> 2528 </a> 2529 </div> 2530 } 2531 } 2532 2533 </section> 2534 <!-- END Stickymenu --> 2535 @SnippetEnd("ProductDetailHeaderDesktop") 2536 2537 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>> 2538 @using Dynamicweb; 2539 @using Bluedesk.DynamicWeb.ItemTypes.Pages; 2540 @using Bluedesk.Tools.DynamicWeb.ExtensionMethods; 2541 @using System.Linq; 2542 @using Dynamicweb.Content; 2543 2544 @{ 2545 2546 } 2547 2548 <style> 2549 .products-module__product-btn--add-to-shoppingcart { 2550 background-color: @AddToCartButtonBackgroundColor; 2551 } 2552 </style> 2553 2554 2555 @using System.Text.RegularExpressions; 2556 2557 @if(enableSpecificationsSidebar) 2558 { 2559 <div class="offcanvas__backdrop" name="specifications"></div> 2560 2561 <aside class="offcanvas" name="specifications"> 2562 <header class="offcanvas__header"> 2563 <span class="offcanvas__title">@Translate("OffCanvasMenu.SpecificationsTitle", "Specifications")</span> 2564 <button class="offcanvas__close" aria-label="@Translate("OffCanvasMenu.Close", "Close menu")"> 2565 <i class="fal fa-times"></i> 2566 </button> 2567 </header> 2568 <div class="offcanvas__body"> 2569 @foreach(LoopItem categorie in GetLoop("ProductCategories")) 2570 { 2571 string prodCategoryId = categorie.GetString("Ecom:Product.Category.ID"); 2572 string prodCategoryName = categorie.GetString("Ecom:Product.Category.Name"); 2573 Regex valueRegex = new Regex(@"\[(.+?)\]", RegexOptions.IgnoreCase); 2574 2575 if(categorie.GetLoop("ProductCategoryFields").Any(d => !string.IsNullOrWhiteSpace(d.GetString("Ecom:Product.CategoryField.Value")))) 2576 { 2577 <section class="product-specifications__category"> 2578 <button class="product-specifications__header product-specifications__toggle" aria-selected="true" aria-controls="@prodCategoryId"> 2579 <p class="product-specifications__title">@Translate("SpecificationsList." + prodCategoryName.Replace(" ", ""), prodCategoryName)</p> 2580 <i class="fal fa-chevron-down"></i> 2581 </button> 2582 <ul id="@prodCategoryId" class="product-specifications__list"> 2583 @foreach (var categoryField in categorie.GetLoop("ProductCategoryFields")) 2584 { 2585 if (!string.IsNullOrWhiteSpace(categoryField.GetString("Ecom:Product.CategoryField.Value"))) 2586 { 2587 Match unitMatch = valueRegex.Match(categoryField.GetString("Ecom:Product.CategoryField.Label")); 2588 string unit = unitMatch.Groups[1].Value; 2589 2590 <li class="product-specifications__list-item"> 2591 <span class="product-specifications__label">@valueRegex.Replace(categoryField.GetString("Ecom:Product.CategoryField.Label"), "")</span> 2592 <span class="product-specifications__value">@categoryField.GetString("Ecom:Product.CategoryField.Value") @unit</span> 2593 </li> 2594 } 2595 } 2596 </ul> 2597 </section> 2598 } 2599 } 2600 2601 </div> 2602 </aside> 2603 } 2604 @using System.Text.RegularExpressions; 2605 2606 <div class="offcanvas__backdrop" name="reviews"></div> 2607 2608 <aside class="offcanvas" name="reviews"> 2609 <header class="offcanvas__header"> 2610 <span class="offcanvas__title">@Translate("OffCanvasMenu.ReviewTitle", "Customer reviews") (@GetLoop("Comments.Newfirst").Count)</span> 2611 <button class="offcanvas__close" aria-label="@Translate("OffCanvasMenu.Close", "Close menu")"> 2612 <i class="fal fa-times"></i> 2613 </button> 2614 </header> 2615 <div class="offcanvas__body"> 2616 @foreach (LoopItem Comment in GetLoop("Comments.Newfirst")) 2617 { 2618 <section class="review-comment__list-item"> 2619 <div class="review-comment__header"> 2620 <div class="review-comment__rating-wrapper"> 2621 @renderReviewIndicator("#86C440", Comment.GetInteger("Rating"), "row", false, 0) 2622 </div> 2623 </div> 2624 <div class="review-comment__message"> 2625 <p>@Comment.GetString("Text")</p> 2626 </div> 2627 <p class="review-comment__meta">@Comment.GetString("Name") | @Comment.GetDate("CreatedDate").ToString("d MMMM yyyy")</p> 2628 </section> 2629 } 2630 </div> 2631 </aside> 2632 @using System.Text.RegularExpressions; 2633 2634 @{ 2635 @* var pageView = Dynamicweb.Frontend.PageView.Current(); *@ 2636 var isLoggedIn = Dynamicweb.Security.UserManagement.User.IsExtranetUserLoggedIn(); 2637 var currentUser = Dynamicweb.Security.UserManagement.User.GetCurrentExtranetUser(); 2638 var ecomLanguageId = GetString("Ecom:Product.LanguageID"); 2639 2640 string namePrefill = currentUser != null ? currentUser.Name : ""; 2641 string emailPrefill = currentUser != null ? currentUser.Email : ""; 2642 } 2643 2644 <script> 2645 var errorMessagesJSON = 2646 { 2647 'StrErrorMsgTxtName': { 2648 '_default' : '@Translate("form.error.text", "It looks like this field is empty or too short")', 2649 '_validate' : '@Translate("form.error.text.notvalid", "It looks like this field is not valid")' 2650 }, 2651 'StrErrorMsgTxtEmail': { 2652 '_default' : '@Translate("form.error.emaildefault", "It seems that your e-mail address has not been completed")', 2653 '_validate' : '@Translate("form.error.emailnotvalid", "It seems that this is not a valid e-mail address")' 2654 } 2655 }; 2656 </script> 2657 2658 <div class="offcanvas__backdrop" name="reviewcreate"></div> 2659 2660 <aside class="offcanvas" name="reviewcreate"> 2661 2662 <header class="offcanvas__header"> 2663 <span class="offcanvas__title">@Translate("OffCanvasMenu.ReviewCreateTitle", "Rate this product")</span> 2664 <button class="offcanvas__close" aria-label="@Translate("OffCanvasMenu.Close", "Close menu")"> 2665 <i class="fal fa-times"></i> 2666 </button> 2667 </header> 2668 2669 <div class="offcanvas__body"> 2670 <form method="post" action="/Default.aspx?ID=@Pageview.ID" id="commentform" class="default-contact-form"> 2671 <input type="hidden" name="Comment.Command" id="Comment.Command" value="create" /> 2672 <input type="hidden" name="Comment.Active" value="false" /> 2673 <input type="hidden" name="Comment.ItemType" value="ecomProduct" /> 2674 <input type="hidden" name="Comment.ItemID" value="@productid" /> 2675 <input type="hidden" name="Comment.LangID" value="@ecomLanguageId" /> 2676 <input type="hidden" name="Comment.Continue" value="@Pageview.SearchFriendlyUrl?reviewcmd=created#pdp-tabs" /> 2677 2678 <div class="flex flex-row flex-wrap"> 2679 <div class="w-full"> 2680 @renderDefaultField("Name", "Review.Form", true, "Comment.Name", namePrefill, "text", 2681 new Dictionary<string, string>() { 2682 { "erroralias", "StrErrorMsgTxtName" }, 2683 { "verplicht", "verplicht" }, 2684 { "minchar", "1" }, 2685 } 2686 ) 2687 </div> 2688 </div> 2689 2690 <div class="flex flex-row flex-wrap"> 2691 <div class="w-full"> 2692 @renderDefaultField("Email", "Review.Form", true, "Comment.Email", emailPrefill, "email", 2693 new Dictionary<string, string>() { 2694 { "erroralias", "StrErrorMsgTxtEmail" }, 2695 { "verplicht", "verplicht" }, 2696 { "minchar", "1" }, 2697 } 2698 ) 2699 </div> 2700 </div> 2701 2702 <div class="flex flex-row flex-wrap"> 2703 <div class="w-full"> 2704 @renderStarRatingField("Rating", "Review.Form", true, "Comment.Rating") 2705 </div> 2706 </div> 2707 2708 <div class="flex flex-row flex-wrap"> 2709 <div class="w-full"> 2710 @renderTextArea("Comment", "Review.Form", true, "Comment.Text", "", 2711 new Dictionary<string, string>() { 2712 { "erroralias", "StrErrorMsgTxtName" }, 2713 { "verplicht", "verplicht" }, 2714 { "minchar", "1" }, 2715 { "rows", "10" } 2716 } 2717 ) 2718 </div> 2719 </div> 2720 2721 </form> 2722 </div> 2723 2724 <footer class="offcanvas__footer"> 2725 <button type="submit" form="commentform" class="btn btn__primary"> 2726 <span class="btn__text">@Translate("Review.Form.Submit", "Submit review")</span> 2727 <i class="far fa-chevron-right btn__icon"></i> 2728 </button> 2729 </footer> 2730 2731 </aside> 2732 2733 @helper renderInputLabel(string prefix, string label, bool isRequired, string inputId) { 2734 var labelCode = string.Format("{0}.{1}", prefix, label.Replace(" ", "_")); 2735 <label class="form__input-label" for="@inputId">@Translate(labelCode, label) @(isRequired ? "*" : "")</label> 2736 } 2737 2738 @helper renderDefaultField(string label, string labelPrefix, bool isRequired, string fieldId, string fieldValue, string fieldType, Dictionary<string, string> customattributes) 2739 { 2740 string attrs = ""; 2741 foreach (var item in customattributes) 2742 { 2743 attrs += item.Key + "=\"" + item.Value + "\" "; 2744 } 2745 2746 <section class="form__item form__item--default"> 2747 <div class="input__group form-item__input"> 2748 @renderInputLabel(labelPrefix, label, isRequired, fieldId) 2749 <input 2750 name="@fieldId" 2751 id="@fieldId" 2752 class="input form__input" 2753 type="@fieldType" 2754 value="@fieldValue" 2755 @attrs 2756 /> 2757 <i class="fal fa-check checkmark"></i> 2758 <i class="fal fa-times cross"></i> 2759 </div> 2760 <div class="errormessage"></div> 2761 </section> 2762 } 2763 2764 @helper renderTextArea(string label, string labelPrefix, bool isRequired, string fieldId, string fieldValue, Dictionary<string, string> customattributes) 2765 { 2766 string attrs = ""; 2767 foreach (var item in customattributes) 2768 { 2769 attrs += item.Key + "=\"" + item.Value + "\" "; 2770 } 2771 2772 <section class="form__item"> 2773 <div class="input__group form-item__input form-group"> 2774 @renderInputLabel(labelPrefix, label, isRequired, fieldId) 2775 <textarea class="input--text form__input form__input--textarea" 2776 id="@fieldId" 2777 name="@fieldId" 2778 @attrs 2779 >@fieldValue</textarea> 2780 </div> 2781 <div class="errormessage"></div> 2782 </section> 2783 } 2784 2785 @helper renderStarRatingField(string label, string labelPrefix, bool isRequired, string fieldId) { 2786 <section class="form__item form__item--default form__item--starrating"> 2787 @renderInputLabel(labelPrefix, label, isRequired, fieldId) 2788 <div class="star-rating"> 2789 <div class="star-rating__wrap"> 2790 <input class="star-rating__input" id="star-rating-5" type="radio" name="@fieldId" value="5"> 2791 <label class="star-rating__ico far fa-star" for="star-rating-5" title="5 out of 5 stars"></label> 2792 <input class="star-rating__input" id="star-rating-4" type="radio" name="@fieldId" value="4"> 2793 <label class="star-rating__ico far fa-star" for="star-rating-4" title="4 out of 5 stars"></label> 2794 <input class="star-rating__input" id="star-rating-3" type="radio" name="@fieldId" value="3"> 2795 <label class="star-rating__ico far fa-star" for="star-rating-3" title="3 out of 5 stars"></label> 2796 <input class="star-rating__input" id="star-rating-2" type="radio" name="@fieldId" value="2"> 2797 <label class="star-rating__ico far fa-star" for="star-rating-2" title="2 out of 5 stars"></label> 2798 <input class="star-rating__input" id="star-rating-1" type="radio" name="@fieldId" value="1"> 2799 <label class="star-rating__ico far fa-star" for="star-rating-1" title="1 out of 5 stars"></label> 2800 </div> 2801 </div> 2802 <div class="errormessage"></div> 2803 </section> 2804 } 2805 2806 2807 <script> 2808 window.globals.productId = '@productid'; 2809 window.globals.productVariantId = '@productVariantId'; 2810 </script> 2811 2812 @if(enableShoppingCart && enableProductShoppingCart) 2813 { 2814 <section class="w-full stickymenu__replaceable__wrapper"> 2815 <div class="container"> 2816 @RenderSnippet("ProductDetailHeaderDesktop") 2817 </div> 2818 </section> 2819 2820 <script> 2821 window.addEventListener('DOMContentLoaded', function (event) { 2822 var stickyAddToCartEl = document.querySelector('.stickymenu__replaceable__wrapper'); 2823 var pdpAddToCartElement = document.querySelector('.pdp-add-to-cart'); 2824 var scrollToTopEl = document.querySelector('#scroll-to-top'); 2825 2826 if(pdpAddToCartElement) { 2827 var observer = new IntersectionObserver((entries, observer) => { 2828 entries.forEach(entry => { 2829 if(entry.intersectionRatio != 1 && window.scrollY >= entry.boundingClientRect.top) { 2830 // Out of view 2831 stickyAddToCartEl.classList.add('stickymenu__replaceable__wrapper--active'); 2832 scrollToTopEl.classList.add('scroll-to-top--shown--lg'); 2833 scrollToTopEl.style.setProperty('--sticky-bottom-offset', stickyAddToCartEl.offsetHeight + 'px'); 2834 document.body.style.paddingBottom = stickyAddToCartEl.offsetHeight + 'px'; 2835 } else { 2836 // In view 2837 stickyAddToCartEl.classList.remove('stickymenu__replaceable__wrapper--active'); 2838 scrollToTopEl.classList.remove('scroll-to-top--shown--lg'); 2839 scrollToTopEl.style.setProperty('--sticky-bottom-offset', '0px'); 2840 document.body.style.paddingBottom = null; 2841 } 2842 }); 2843 }, { 2844 threshold: 1 2845 }); 2846 2847 observer.observe(pdpAddToCartElement); 2848 } 2849 2850 }); 2851 </script> 2852 } 2853 2854 <main class="pdp"> 2855 2856 <div class="container pdp-info__container"> 2857 <div class="pdp-info__imagecolumn"> 2858 @RenderSnippet("PdpHeader") 2859 @RenderSnippet("PdpImage") 2860 </div> 2861 @RenderSnippet("PdpInfo") 2862 </div> 2863 2864 @RenderPdpTabs(showReviewTab, enableSpecificationsSidebar) 2865 @* @RenderSnippet("PdpSpecification") *@ 2866 @* @RenderSnippet("PdpBanner") *@ 2867 @* @RenderSnippet("PdpReviews") *@ 2868 @RenderSnippet("PdpRelatedGroups") 2869 @RenderSnippet("PdpHelp") 2870 @if(enableShoppingCart && enableProductShoppingCart) 2871 { 2872 @* @RenderSnippet("PdpSecondaryInfo"); *@ 2873 } 2874 @RenderSnippet("PdpRecentViewedProducts") 2875 2876 </main> 2877 2878 @using System.Globalization; 2879 2880 @{ 2881 var httpdomain = Dynamicweb.Environment.Helpers.LinkHelper.GetHttpDomain(); 2882 var productAvailability = GetInteger("Ecom:Product.Stock") > 0 ? "https://schema.org/InStock" : "https://schema.org/OutOfStock"; 2883 double productPrice = GetBoolean("Ecom:Product.HaveDiscount") ? GetDouble("Ecom:Product.Discount.Price.PriceWithVAT.Value") : GetDouble("Ecom:Product.Price.PriceWithVAT.Value"); 2884 2885 //Dynamicweb gives the values with comma notation, while google expects a dot notation and then converts it into a comma notation on frontend 2886 string formattedProductPrice = Math.Round(productPrice, 2, MidpointRounding.AwayFromZero).ToString(specifier, culture); 2887 2888 //Rating is set at the website setting, not product setting. If it's not defined, the rating won't be rendered in the review 2889 double rating = GetDouble("Ecom:Product.Rating"); 2890 bool renderRating = rating > 0; 2891 2892 //Must be at least 1, or reviews won't be rendered 2893 int reviewCount = Math.Max(GetInteger("Comments.RepliesCount"), 1); 2894 } 2895 2896 <script type="application/ld+json"> 2897 { 2898 "@@context": "https://schema.org/", 2899 "@@type": "Product", 2900 "name": "@GetString("Ecom:Product.Name")", 2901 "image": "@httpdomain@GetString("Ecom:Product.ImageLarge.Clean")", 2902 "description": "@GetString("Ecom:Product.ShortDescription.Raw")", 2903 "sku": "@GetString("Ecom:Product:Field.EAN.Value.Clean")", 2904 "mpn": "@GetString("Ecom:Product.ID")", 2905 "url": "@httpdomain/@GetString("Ecom:Product.VariantLinkGroup.Clean")", 2906 @if(!string.IsNullOrWhiteSpace(GetString("Ecom:Manufacturer.Name"))) 2907 { 2908 <text> 2909 "brand": { 2910 "@@type": "Brand", 2911 "name": "@GetString("Ecom:Manufacturer.Name")" 2912 }, 2913 </text> 2914 } 2915 @if(renderRating) 2916 { 2917 <text> 2918 "aggregateRating": { 2919 "@@type": "AggregateRating", 2920 "ratingValue": "@rating", 2921 "reviewCount": "@reviewCount" 2922 }, 2923 </text> 2924 } 2925 "offers": { 2926 "@@type": "Offer", 2927 "availability": "@productAvailability", 2928 "itemCondition": "https://schema.org/NewCondition", 2929 "price": "@formattedProductPrice", 2930 "priceCurrency": "@GetString("Ecom:Product.Price.Currency.Code")", 2931 "url": "@httpdomain/@GetString("Ecom:Product.VariantLinkGroup.Clean")" 2932 } 2933 } 2934 </script> 2935 2936 2937 @SnippetStart("DataLayer") 2938 @{ 2939 var categoryObject = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(GetString("Ecom:Product.PrimaryOrFirstGroupID")); 2940 string categoryName = categoryObject != null ? categoryObject.Name : ""; 2941 } 2942 2943 <script> 2944 if(window.dataLayer) { 2945 dataLayer.push({ 2946 'event': 'view_item', 2947 'ecommerce': { 2948 'currency': '@Dynamicweb.Ecommerce.Common.Context.Currency.Code', 2949 'value': @GetDouble("Ecom:Product.Discount.Price.Price.Value").ToString(specifier, culture), 2950 'items': [{ 2951 'item_id': '@GetString("Ecom:Product.ID")', 2952 'item_name': '@GetString("Ecom:Product.Name").Replace("''", "\\\"").Replace("'", "")', 2953 'item_number': '@GetString("Ecom:Product.Number")', 2954 'discount': @GetDouble("Ecom:Product.Discount.TotalAmount.Price.Value").ToString(specifier, culture), 2955 'index': 1, 2956 'item_brand': '@GetString("Ecom:Manufacturer.Name").Replace("''", "\\\"").Replace("'", "")', 2957 'item_category': '@categoryName.Replace("''", "\\\"").Replace("'", "")', 2958 'item_list_id': '@listId', 2959 'item_list_name': '@listName', 2960 'item_variant': '@GetString("Ecom:Product.VariantID")', 2961 'price': @GetDouble("Ecom:Product.Price.Price.Value").ToString(specifier, culture), 2962 'quantity': 1 2963 }] 2964 } 2965 }); 2966 } 2967 </script> 2968 2969 @SnippetEnd("DataLayer") 2970 2971 2972 @SnippetStart("ProductDetailHeaderMobile") 2973 <!-- BEGIN Stickymenu --> 2974 <section class="stickymenu__replaceable flex flex-1 md:justify-between items-center"> 2975 <div class="stickymenu_product-info-wrapper flex items-center mr-auto"> 2976 @if (!string.IsNullOrWhiteSpace(defaultImage)) 2977 { 2978 <div class="stickymenu__product-image mr-2" style="background-image: url('@defaultImage');"></div> 2979 } 2980 <div class="flex flex-col leading-tight"> 2981 <p class="h5 sm:text-l md:text-xl font-bold m-0 stickymenu__product-name">@productName</p> 2982 2983 @if (displayPrice) 2984 { 2985 if (!pricezero) 2986 { 2987 if (hasDiscount) 2988 { 2989 <div class="stickymenu_product__price__wrapper flex items-center"> 2990 <span class="stickymenu_product__price--old">@priceFormatted</span> 2991 <span class="stickymenu_product__price--current">@discountPriceValue</span> 2992 <span class="stickymenu_product__price--profit">@yourProfitLabel @yourProfitValue</span> 2993 </div> 2994 } 2995 else 2996 { 2997 <p>@priceFormatted</p> 2998 } 2999 } 3000 } 3001 3002 </div> 3003 </div> 3004 </section> 3005 <!-- END Stickymenu --> 3006 3007 @SnippetEnd("ProductDetailHeaderMobile") 3008 3009
To Top