Error executing template "/Designs/Swift/Paragraph/Swift_ProductDetailsInfo.cshtml" System.NullReferenceException: Object reference not set to an instance of an object. at CompiledRazorTemplates.Dynamic.RazorEngine_7683a96faf6c403eaab6b252e499a22e.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsInfo.cshtml:line 399 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.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using Dynamicweb.Ecommerce.ProductCatalog 3 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites 4 @using Dynamicweb.Ecommerce.Products.FieldDisplayGroups 5 @using Dynamicweb.Frontend 6 @using Dynamicweb.Core 7 @using System.Drawing 8 @using Dynamicweb.Environment 9 10 @functions { 11 //Find contrast color (white, black) 12 public static string GetContrastColor(string hexString) 13 { 14 System.Drawing.Color bg = System.Drawing.ColorTranslator.FromHtml(hexString); 15 16 int nThreshold = 105; 17 int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) + 18 (bg.B * 0.114)); 19 20 string foreColor = (255 - bgDelta < nThreshold) ? "#333" : "#fff"; 21 return foreColor; 22 } 23 } 24 25 @{ 26 string googleAnalyticsTrackingID = Pageview.AreaSettings.GetString("GoogleAnalyticsTrackingID"); 27 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID"); 28 var cookieOptInLevel = CookieManager.GetCookieOptInLevel(); 29 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical")); 30 31 ProductViewModel product = new ProductViewModel(); 32 33 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 34 { 35 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 36 } 37 38 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 39 bool anonymousUser = Pageview.User == null; 40 bool isErpConnectionDown = !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable(); 41 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown; 42 hideAddToCart = product?.VariantInfo?.VariantInfo != null && Model.Item.GetBoolean("HideVariantSelector") ? true : hideAddToCart; 43 bool hideStock = Model.Item.GetBoolean("HideStockState") || (Pageview.AreaSettings.GetBoolean("ErpDownHideStock") && isErpConnectionDown); 44 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && isErpConnectionDown; 45 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false; 46 47 bool isDiscontinued = product.Discontinued; 48 bool IsNeverOutOfStock = product.NeverOutOfstock; 49 string[] variantId = { }; 50 51 if (product?.VariantId != null) { 52 variantId = product.VariantId.Split('.'); 53 } 54 55 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : ""; 56 disableAddToCart = isDiscontinued ? "disabled" : disableAddToCart; 57 disableAddToCart = IsNeverOutOfStock ? "" : disableAddToCart; 58 59 // Does product has a expected delivery data 60 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery > DateTime.Now; 61 string expectedDeliveryDate = product.ExpectedDelivery?.ToShortDateString() ?? ""; 62 63 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 64 if (!url.Contains("LayoutTemplate")) 65 { 66 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 67 } 68 69 IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList(); 70 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>(); 71 72 foreach (var selection in selectedDisplayGroups) 73 { 74 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 75 { 76 if (selection == group.Id) 77 { 78 mainFeatures.Add(group); 79 } 80 } 81 } 82 83 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 84 85 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-6"); 86 87 string contentPadding = Model.Item.GetRawValueString("ContentPadding", ""); 88 contentPadding = contentPadding == "small" ? "p-2 p-md-3" : contentPadding; 89 contentPadding = contentPadding == "large" ? "p-4 p-md-5" : contentPadding; 90 91 string quantityPricesLayout = Model.Item.GetRawValueString("QuantityPricesLayout", "list"); 92 93 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\""; 94 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1"; 95 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty; 96 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : ""; 97 98 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 99 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 100 101 string priceMin = ""; 102 string priceMax = ""; 103 104 var favoriteParameters = new Dictionary<string, object>(); 105 if (!anonymousUser && !hideFavoritesSelector) 106 { 107 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists(); 108 int defaultFavoriteListId = 0; 109 110 if (favoreiteLists.Count() == 1) { 111 foreach (FavoriteList list in favoreiteLists) { 112 defaultFavoriteListId = list.ListId; 113 } 114 } 115 116 favoriteParameters.Add("ListId", defaultFavoriteListId); 117 } 118 119 var priceParms = new Dictionary<string, object>(); 120 priceParms.Add("theme", ""); 121 122 var badgeParms = new Dictionary<string, object>(); 123 badgeParms.Add("size", "h7"); 124 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 125 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 126 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 127 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 128 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 129 130 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 131 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 132 DateTime createdDate = product.Created.Value; 133 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 134 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 135 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 136 137 string liveInfoClass = ""; 138 string productInfoFeed = ""; 139 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsLazyLoadingForProductInfoEnabled; 140 if (isLazyLoadingForProductInfoEnabled) 141 { 142 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 143 { 144 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 145 if (!string.IsNullOrEmpty(productInfoFeed)) 146 { 147 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 148 } 149 } 150 liveInfoClass = "js-live-info"; 151 } 152 @* Product stock state *@ 153 double? currentStockLevel = product.StockLevel; 154 155 156 string stockStateLabel = !string.IsNullOrEmpty(product.StockStatus) ? product.StockStatus : ""; 157 158 string stockStateCss = currentStockLevel > 0 ? "text-success" : "text-danger"; 159 string stockStateIconCss = currentStockLevel > 0 ? "bg-success" : "bg-danger"; 160 } 161 162 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking) 163 { 164 <script> 165 gtag("event", "view_item", { 166 currency: "@product.Price.CurrencyCode", 167 value: @product.Price.Price, 168 items: [ 169 { 170 item_id: "@product.Number", 171 item_name: "@product.Name", 172 currency: "@product.Price.CurrencyCode", 173 price: @product.Price.Price 174 } 175 ] 176 }); 177 </script> 178 } 179 180 <div class="h-100 @(contentPadding) @(theme) [email protected]()" @productInfoFeed> 181 <div class="d-flex flex-column gap-4 js-product" data-product-id="@product.Id"> 182 @if (showBadges) { 183 <div class="swift_badge-collection"> 184 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 185 </div> 186 } 187 188 <div class="d-flex flex-column gap-2"> 189 <h1 class="@titleFontSize m-0" itemprop="name">@product.Name</h1> 190 @if (!Model.Item.GetBoolean("HideProductNumber")) 191 { 192 @RenderPartial("Paragraph/Swift_ProductNumber.cshtml", Model) 193 } 194 </div> 195 196 @if (!hidePrice && !isDiscontinued) 197 { 198 @RenderPartial("Paragraph/Swift_ProductPrice.cshtml", Model, priceParms) 199 200 if (isLazyLoadingForProductInfoEnabled) 201 { 202 <div class="product-prices-container @liveInfoClass d-none" data-show-if="LiveProductInfo.product.Prices.length > 0"> 203 @if (quantityPricesLayout == "list") 204 { 205 <div class="mt-3 product-prices"> 206 <small class="d-block opacity-75 product-prices-template"><span><span class="js-text-price-quantity"></span> @Translate("PCS")</span> - <span class="fw-bold"><span class="js-text-price-price"></span> <span class="d-none" data-show-if="LiveProductInfo.productPrice.Quantity > 1">@Translate("pr. PCS")</span></span></small> 207 </div> 208 } 209 else if (quantityPricesLayout == "table") 210 { 211 <div class="grid"> 212 <table class="table table-sm mt-3 g-col-12 g-col-lg-6"> 213 <thead> 214 <tr> 215 <td>@Translate("QTY")</td> 216 <td>@Translate("pr. PCS")</td> 217 </tr> 218 </thead> 219 <tbody class="product-prices"> 220 <tr class="product-prices-template"> 221 <td class="js-text-price-quantity"></td> 222 <td class="js-text-price-price"></td> 223 </tr> 224 </tbody> 225 </table> 226 </div> 227 } 228 </div> 229 } 230 else 231 { 232 if (product.Prices.Count > 0) 233 { 234 <div> 235 @if (quantityPricesLayout == "list") 236 { 237 <div class="mt-3"> 238 @foreach (PriceListViewModel quantityPrice in product.Prices) 239 { 240 string quantityLabel = Translate("PCS"); 241 string quantityPriceSuffix = quantityPrice.Quantity > 1 ? Translate("pr. PCS") : ""; 242 243 <small class="d-block opacity-75"><span>@quantityPrice.Quantity @quantityLabel</span> - <span class="fw-bold">@quantityPrice.Price.PriceFormatted @quantityPriceSuffix</span></small> 244 } 245 </div> 246 } 247 else if (quantityPricesLayout == "table") 248 { 249 <div class="grid"> 250 <table class="table table-sm mt-3 g-col-12 g-col-lg-6"> 251 <thead> 252 <tr> 253 <td>@Translate("QTY")</td> 254 <td>@Translate("pr. PCS")</td> 255 </tr> 256 </thead> 257 <tbody> 258 @foreach (PriceListViewModel quantityPrice in product.Prices) 259 { 260 <tr> 261 <td>@quantityPrice.Quantity</td> 262 <td>@quantityPrice.Price.PriceFormatted</td> 263 </tr> 264 } 265 </tbody> 266 </table> 267 </div> 268 } 269 </div> 270 } 271 } 272 } 273 274 @RenderPartial("Paragraph/Swift_ProductShortDescription.cshtml", Model) 275 276 @if (mainFeatures.Count > 0) 277 { 278 foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 279 { 280 <dl class="grid gap-0"> 281 @foreach (var field in mainFeatureGroup.Fields) 282 { 283 @RenderField(field.Value) 284 } 285 </dl> 286 } 287 } 288 289 @if (product.VariantInfo.VariantInfo != null && !Model.Item.GetBoolean("HideVariantSelector")) 290 { 291 int groupNumber = 1; 292 293 string baseUrl = $"Default.aspx?ID={GetPageIdByNavigationTag("Shop")}&GroupID={product.PrimaryOrDefaultGroup.Id}&ProductID={product.Id}"; 294 string variantUrl = ""; 295 if (!string.IsNullOrEmpty(product.VariantId)) 296 { 297 variantUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl($"Default.aspx?ID={GetPageIdByNavigationTag("Shop")}&GroupID={product.PrimaryOrDefaultGroup.Id}&ProductID={product.Id}&VariantID={product.VariantId}"); 298 } 299 300 <form class="js-variant-selector" data-combinations="@string.Join(",", product.VariantCombinations())" data-base-url="@baseUrl" data-friendly-url="@variantUrl"> 301 <input type="hidden" name="variantid" /> 302 303 @foreach (var variantGroup in product.VariantGroups()) 304 { 305 VariantGroupViewModel group = variantGroup; 306 307 <h3 class="h6">@group.Name</h3> 308 <div class="d-flex gap-2 flex-wrap js-variant-group" data-group-id="@groupNumber"> 309 @foreach (var option in group.Options) 310 { 311 string active = variantId.Contains(option.Id) ? "active" : ""; 312 313 if (!string.IsNullOrEmpty(option.Color)) 314 { 315 string contrastColor = GetContrastColor(option.Color); 316 <button type="button" class="btn colorbox rounded-circle d-inline-block variant-option border js-variant-option @active" style="background-color: @option.Color; --variantoption-check-color: @contrastColor" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id" id="@(product.Id)_@(option.Id)[email protected]"></button> 317 } 318 else if (!string.IsNullOrEmpty(option.Color) && !string.IsNullOrEmpty(option.Image.Value)) 319 { 320 <button type="button" class="btn p-0 d-inline-block variant-option border js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id"> 321 <img src="/Admin/Public/GetImage.ashx?image=@(option.Image.Value)&width=42&Format=WebP&Quality=70" /> 322 </button> 323 } 324 else 325 { 326 <button type="button" class="btn btn-secondary d-inline-block variant-option js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id"> 327 @option.Name 328 </button> 329 } 330 } 331 </div> 332 333 groupNumber++; 334 } 335 </form> 336 } 337 338 <div class="d-flex flex-row flex-nowrap gap-2"> 339 @if (!hideAddToCart) 340 { 341 <form method="post" action="@url" class="flex-fill"> 342 <input type="hidden" name="redirect" value="false" /> 343 <input type="hidden" name="ProductId" value="@product.Id" /> 344 <input type="hidden" name="ProductName" value="@product.Name" /> 345 <input type="hidden" name="ProductPrice" value="@product.Price.Price" /> 346 <input type="hidden" name="ProductCurrency" value="@product.Price.CurrencyCode" /> 347 <input type="hidden" name="ProductReferer" value="product_details_info"> 348 <input type="hidden" name="cartcmd" value="add" /> 349 350 @if (!string.IsNullOrEmpty(product.VariantId)) 351 { 352 <input type="hidden" name="VariantId" value="@product.VariantId" /> 353 } 354 @if (!Model.Item.GetBoolean("QuantitySelector")) 355 { 356 <input id="[email protected]" name="Quantity" value="@valueQty" type="hidden"> 357 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary w-100 js-add-to-cart-button @disableAddToCart" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)[email protected]">@Translate("Add to cart")</button> 358 } else { 359 <div class="input-group input-primary-button-group js-input-group d-flex flex-row flex-nowrap"> 360 <label for="Quantity_@(product.Id)" class="visually-hidden">@Translate("Quantity")</label> 361 <input id="[email protected]" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control" style="max-width: 96px; min-width:64px;" type="number" onkeydown="swift.Cart.UpdateOnEnterKey(event)"> 362 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button @disableAddToCart" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)[email protected]">@Translate("Add to cart")</button> 363 </div> 364 365 if (stepQty != "1") 366 { 367 <div class="invalid-feedback d-none"> 368 @Translate("Please select a quantity that is dividable by") @stepQty 369 </div> 370 } 371 } 372 </form> 373 if (!anonymousUser && !hideFavoritesSelector) 374 { 375 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 376 } 377 } 378 else if (!anonymousUser && !hideFavoritesSelector && !isDiscontinued) 379 { 380 <div class="flex-fill" id="[email protected]"> 381 @Translate("Add to favorites") @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 382 </div> 383 } 384 385 @if (isDiscontinued && product.ReplacementProduct != null) { 386 List<ProductInfoViewModel> replacementProductList = new List<ProductInfoViewModel>(); 387 replacementProductList.Add(product.ReplacementProduct); 388 var replacementProduct = replacementProductList.GetProducts().FirstOrDefault(); 389 390 if ((product.DiscontinuedAction == 0 || product.DiscontinuedAction == 1) && product?.ReplacementProduct.ProductId != null) { 391 var parms = new Dictionary<string, object>(); 392 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 393 parms.Add("fullwidth", true); 394 parms.Add("columns", Model.GridRowColumnCount); 395 396 string imagePath = replacementProduct?.DefaultImage?.Value != null ? replacementProduct.DefaultImage.Value : ""; 397 398 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 399 link += $"&GroupID={replacementProduct.PrimaryOrDefaultGroup.Id}"; 400 link += $"&ProductID={replacementProduct.Id}"; 401 link += !string.IsNullOrEmpty(replacementProduct.VariantId) ? $"&VariantID={replacementProduct.VariantId}" : ""; 402 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link); 403 404 <div class="w-100"> 405 <div class="fw-bold w-100">@Translate("Sorry, this product is no longer available").</div> 406 <div>@Translate("We recommend this replacement product instead"):</div> 407 408 <a href="@link"> 409 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 410 </a> 411 412 <div>@replacementProduct.Name</div> 413 414 @if (!hidePrice) 415 { 416 <div class="mb-3"> 417 <div class="h4" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 418 @if (showPricesWithVat == "false" && !neverShowVat) 419 { 420 if (isLazyLoadingForProductInfoEnabled) 421 { 422 <span itemprop="price" content="" class="d-none"></span> 423 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span> 424 } 425 else 426 { 427 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted; 428 429 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 430 if (product.Price.Price != product.PriceBeforeDiscount.Price) 431 { 432 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span> 433 } 434 } 435 } 436 else 437 { 438 if (isLazyLoadingForProductInfoEnabled) 439 { 440 <span itemprop="price" content="" class="d-none"></span> 441 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span> 442 } 443 else 444 { 445 string beforePrice = product.PriceBeforeDiscount.PriceFormatted; 446 447 <span itemprop="price" content="@product.Price.Price" class="d-none"></span> 448 if (product.Price.Price != product.PriceBeforeDiscount.Price) 449 { 450 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span> 451 } 452 } 453 } 454 455 @if (showPricesWithVat == "false" && !neverShowVat) 456 { 457 if (isLazyLoadingForProductInfoEnabled) 458 { 459 <span class="text-price js-text-price"><div class="spinner-border" role="status"></div></span> 460 } 461 else 462 { 463 string price = product.Price.PriceWithoutVatFormatted; 464 if (product?.VariantInfo?.VariantInfo != null) 465 { 466 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 467 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 468 } 469 if (priceMin != priceMax) 470 { 471 price = priceMin + " - " + priceMax; 472 } 473 <span class="text-price">@price</span> 474 } 475 } 476 else 477 { 478 if (isLazyLoadingForProductInfoEnabled) 479 { 480 <span class="text-price js-text-price"><div class="spinner-border" role="status"></div></span> 481 } 482 else 483 { 484 string price = product.Price.PriceFormatted; 485 if (product?.VariantInfo?.VariantInfo != null) 486 { 487 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 488 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 489 } 490 if (priceMin != priceMax) 491 { 492 price = priceMin + " - " + priceMax; 493 } 494 <span class="text-price">@price</span> 495 } 496 } 497 </div> 498 499 @if (showPricesWithVat == "false" && !neverShowVat) 500 { 501 if (isLazyLoadingForProductInfoEnabled) 502 { 503 <small class="opacity-85 fst-normal js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></small> 504 } 505 else 506 { 507 string price = product.Price.PriceWithVatFormatted; 508 if (product?.VariantInfo?.VariantInfo != null) 509 { 510 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 511 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 512 } 513 if (priceMin != priceMax) 514 { 515 price = priceMin + " - " + priceMax; 516 } 517 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 518 } 519 } 520 </div> 521 } 522 523 <a href="@link" class="btn btn-primary w-100">@Translate("Go to the replacement")</a> 524 </div> 525 } 526 } 527 </div> 528 </div> 529 @if (!hideStock) 530 { 531 <div class="js-stock-state [email protected]()"> 532 <div class="small"> 533 <span class="@stockStateCss">@stockStateLabel </span> 534 </div> 535 </div> 536 } 537 </div> 538 539 @helper RenderField(FieldValueViewModel field) 540 { 541 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 542 bool noValues = false; 543 544 if (!string.IsNullOrEmpty(fieldValue)) 545 { 546 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 547 { 548 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 549 noValues = values.Count > 0 ? false : true; 550 } 551 } 552 553 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 554 { 555 <dt class="g-col-12 g-col-sm-4 g-col-lg-12 fw-bold m-0">@field.Name</dt> 556 <dd class="g-col-12 g-col-sm-8 g-col-lg-12 mb-3"> 557 @RenderFieldValue(field) 558 </dd> 559 } 560 } 561 562 @helper RenderFieldValue(FieldValueViewModel field) 563 { 564 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 565 566 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 567 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 568 569 bool isColor = false; 570 571 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 572 { 573 int valueCount = 0; 574 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 575 int totalValues = values.Count; 576 577 foreach (FieldOptionValueViewModel option in values) 578 { 579 if (option.Value.Substring(0, 1) == "#") 580 { 581 isColor = true; 582 } 583 584 if (!isColor) 585 { 586 @option.Name 587 } 588 else 589 { 590 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span> 591 } 592 593 if (valueCount != totalValues && valueCount < (totalValues - 1)) 594 { 595 if (isColor) 596 { 597 <text> </text> 598 } 599 else 600 { 601 <text>, </text> 602 } 603 } 604 valueCount++; 605 } 606 } 607 else 608 { 609 if (fieldValue.Substring(0, 1) == "#") 610 { 611 isColor = true; 612 } 613 614 if (!isColor) 615 { 616 @fieldValue 617 } 618 else 619 { 620 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 621 } 622 } 623 } 624
Product beschrijving
Maak kennis met het dynamische reisbedje dat PAAL wordt genoemd.
De PAAL is veel meer dan alleen een fijne thuishaven voor je kindje, het is een onmisbare gadget voor ouders, waar je kindje zowel kan spelen en rond kan kijken, of lekker diep in dromenland kan vallen.
Stijlvol en discreet, PAAL danst prachtig door je hele huis en wordt al snel je hulpje voor jouw weekenduitstapjes. Het bevrijdende ontwerp biedt een onbelemmerd uitzicht op je kleintje en omsluit hem in een herkenbare omhelzing van warmte en liefde, waar je je ook bevindt.
Beschouw PAAL als je betrouwbare mede-avonturier, die elke ruimte verandert in een veilig toevluchtsoord voor je kleintje. In de journey van het ouderschap is PAAL die medereiziger die gemakkelijk in je reisstijl past en je altijd aan je zijde kunt houden.
Kenmerken
- Indrukwekkend makkelijk inklapbaar
- Herondek de vrijheid
- Groeit mee
- Dromerig comfort
Gebruik:
- Te gebruiken als reisbed en box die zich aanpast aan jouw baby's behoeften
- Verwijderbare verhoging biedt flexibiliteit voor kleinere baby's
- Klapt compact in om makkelijk mee te nemen
- Klapt moeiteloos uit zodat jij in slechts seconden klaar bent, thuis of onderweg
- Matrashoes is volledig wasbaar waardoor je deze snel weer schoon kunt krijgen
Veiligheid:
- Klemvergrendeling beveiligd de ritssluiting van het zijpaneel
- Klittenband verbergt de ritssluiting van de verhoging
- Gewatteerde hoeken zorgen voor extra bescherming voor actieve peuters
- Stevig aluminium frame zorgt ervoor dat je baby veilig kan slapen en spelen
Comfort:
- Ventilerend matrasinlegje is ondersteunend en zorgt voor een vlakke ondergrond met optimale luchtcirculatie
- Hoeslaken van GOTS gecertificeerd organisch katoen voor optimaal comfort
- Schanieren die 1 kant op klappen verminderen kieren in het matras voor een gladder slaapoppervlak
- De optie van een gelijkvloerse matraspositie biedt de voordelen van een stevig slaapoppervlak
- Zijpanelen van mesh voor een onbelemmerd zicht op je kleintje en extra ventilatie
Luxe details:
- Eigentijds ontwerp en premium kleuren zorgen ervoor dat de ruimte van je baby een aanvulling is op die van jou
- Klapt eenvoudig in met één druk op de knop, zelfs met de verhoging
- Zijpaneel met ritssluiting voor kruipplezier en directe interactie met baby
- Lichtgewicht en gemaakt om te reizen met een handige reistas met handvatten
- Inclusief een insectennet voor bescherming tegen alles met vleugels
Awards en certificeringen:
- Winnaar iF Design Award 2025
- Winnaar Europese Product Design Award 2024
- De GOTS-certificering is een internationale norm die garandeert dat textiel niet alleen
- biologisch is, maar ook op een milieuvriendelijke en sociaal verantwoorde manier is geproduceerd
Product specificaties
Aanbevolen gebruik: Geboorte tot 15 kg of 86 cm
Gewicht: 9.4 kg
AFMETINGEN: 104 L x 66 B x 72 H cm
AFMETINGEN INGEKLAPT: 38 L x 31 B x 73 H cm
Specificaties
- Wat zit er in de doos
- Reisbed, Insectennethoes, Matras, Biologisch katoenen laken, Reistas
- Bestel beleid
- Order
- Video
- https://www.youtube.com/@NunaGlobal/videos
GPSR EU-Regulation
- EU Authorised Representative - Address
- Van der Valk Boumanweg 178 C, 2352 JD Leiedorp, Nederland
- EU Authorised Representative - Email
- [email protected]
- Manufacturer information - Address
- Van der Valk Boumanweg 178 C, 2352 JD Leiedorp, Nederland
- Manufacturer information - Trade name
- Nuna International B.V.