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_e41acea81e684f23a09149da5c9d4dca.Execute() in D:\dynamicweb.net\Solutions\Dynamicweb\bomedys.cloud.dynamicweb-cms.com\files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsInfo.cshtml:line 391 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 } 153 154 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking) 155 { 156 <script> 157 gtag("event", "view_item", { 158 currency: "@product.Price.CurrencyCode", 159 value: @product.Price.Price, 160 items: [ 161 { 162 item_id: "@product.Number", 163 item_name: "@product.Name", 164 currency: "@product.Price.CurrencyCode", 165 price: @product.Price.Price 166 } 167 ] 168 }); 169 </script> 170 } 171 172 <div class="h-100 @(contentPadding) @(theme) [email protected]()" @productInfoFeed> 173 <div class="d-flex flex-column gap-4 js-product" data-product-id="@product.Id"> 174 @if (showBadges) { 175 <div class="swift_badge-collection"> 176 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 177 </div> 178 } 179 180 <div class="d-flex flex-column gap-2"> 181 <h1 class="@titleFontSize m-0" itemprop="name">@product.Name</h1> 182 @if (!Model.Item.GetBoolean("HideProductNumber")) 183 { 184 @RenderPartial("Paragraph/Swift_ProductNumber.cshtml", Model) 185 } 186 </div> 187 188 @if (!hidePrice && !isDiscontinued) 189 { 190 @RenderPartial("Paragraph/Swift_ProductPrice.cshtml", Model, priceParms) 191 192 if (isLazyLoadingForProductInfoEnabled) 193 { 194 <div class="product-prices-container @liveInfoClass d-none" data-show-if="LiveProductInfo.product.Prices.length > 0"> 195 @if (quantityPricesLayout == "list") 196 { 197 <div class="mt-3 product-prices"> 198 <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> 199 </div> 200 } 201 else if (quantityPricesLayout == "table") 202 { 203 <div class="grid"> 204 <table class="table table-sm mt-3 g-col-12 g-col-lg-6"> 205 <thead> 206 <tr> 207 <td>@Translate("QTY")</td> 208 <td>@Translate("pr. PCS")</td> 209 </tr> 210 </thead> 211 <tbody class="product-prices"> 212 <tr class="product-prices-template"> 213 <td class="js-text-price-quantity"></td> 214 <td class="js-text-price-price"></td> 215 </tr> 216 </tbody> 217 </table> 218 </div> 219 } 220 </div> 221 } 222 else 223 { 224 if (product.Prices.Count > 0) 225 { 226 <div> 227 @if (quantityPricesLayout == "list") 228 { 229 <div class="mt-3"> 230 @foreach (PriceListViewModel quantityPrice in product.Prices) 231 { 232 string quantityLabel = Translate("PCS"); 233 string quantityPriceSuffix = quantityPrice.Quantity > 1 ? Translate("pr. PCS") : ""; 234 235 <small class="d-block opacity-75"><span>@quantityPrice.Quantity @quantityLabel</span> - <span class="fw-bold">@quantityPrice.Price.PriceFormatted @quantityPriceSuffix</span></small> 236 } 237 </div> 238 } 239 else if (quantityPricesLayout == "table") 240 { 241 <div class="grid"> 242 <table class="table table-sm mt-3 g-col-12 g-col-lg-6"> 243 <thead> 244 <tr> 245 <td>@Translate("QTY")</td> 246 <td>@Translate("pr. PCS")</td> 247 </tr> 248 </thead> 249 <tbody> 250 @foreach (PriceListViewModel quantityPrice in product.Prices) 251 { 252 <tr> 253 <td>@quantityPrice.Quantity</td> 254 <td>@quantityPrice.Price.PriceFormatted</td> 255 </tr> 256 } 257 </tbody> 258 </table> 259 </div> 260 } 261 </div> 262 } 263 } 264 } 265 266 @RenderPartial("Paragraph/Swift_ProductShortDescription.cshtml", Model) 267 268 @if (mainFeatures.Count > 0) 269 { 270 foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 271 { 272 <dl class="grid gap-0"> 273 @foreach (var field in mainFeatureGroup.Fields) 274 { 275 @RenderField(field.Value) 276 } 277 </dl> 278 } 279 } 280 281 @if (product.VariantInfo.VariantInfo != null && !Model.Item.GetBoolean("HideVariantSelector")) 282 { 283 int groupNumber = 1; 284 285 string baseUrl = $"Default.aspx?ID={GetPageIdByNavigationTag("Shop")}&GroupID={product.PrimaryOrDefaultGroup.Id}&ProductID={product.Id}"; 286 string variantUrl = ""; 287 if (!string.IsNullOrEmpty(product.VariantId)) 288 { 289 variantUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl($"Default.aspx?ID={GetPageIdByNavigationTag("Shop")}&GroupID={product.PrimaryOrDefaultGroup.Id}&ProductID={product.Id}&VariantID={product.VariantId}"); 290 } 291 292 <form class="js-variant-selector" data-combinations="@string.Join(",", product.VariantCombinations())" data-base-url="@baseUrl" data-friendly-url="@variantUrl"> 293 <input type="hidden" name="variantid" /> 294 295 @foreach (var variantGroup in product.VariantGroups()) 296 { 297 VariantGroupViewModel group = variantGroup; 298 299 <h3 class="h6">@group.Name</h3> 300 <div class="d-flex gap-2 flex-wrap js-variant-group" data-group-id="@groupNumber"> 301 @foreach (var option in group.Options) 302 { 303 string active = variantId.Contains(option.Id) ? "active" : ""; 304 305 if (!string.IsNullOrEmpty(option.Color)) 306 { 307 string contrastColor = GetContrastColor(option.Color); 308 <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> 309 } 310 else if (!string.IsNullOrEmpty(option.Color) && !string.IsNullOrEmpty(option.Image.Value)) 311 { 312 <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"> 313 <img src="/Admin/Public/GetImage.ashx?image=@(option.Image.Value)&width=42&Format=WebP&Quality=70" /> 314 </button> 315 } 316 else 317 { 318 <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"> 319 @option.Name 320 </button> 321 } 322 } 323 </div> 324 325 groupNumber++; 326 } 327 </form> 328 } 329 330 <div class="d-flex flex-row flex-nowrap gap-2"> 331 @if (!hideAddToCart) 332 { 333 <form method="post" action="@url" class="flex-fill"> 334 <input type="hidden" name="redirect" value="false" /> 335 <input type="hidden" name="ProductId" value="@product.Id" /> 336 <input type="hidden" name="ProductName" value="@product.Name" /> 337 <input type="hidden" name="ProductPrice" value="@product.Price.Price" /> 338 <input type="hidden" name="ProductCurrency" value="@product.Price.CurrencyCode" /> 339 <input type="hidden" name="ProductReferer" value="product_details_info"> 340 <input type="hidden" name="cartcmd" value="add" /> 341 342 @if (!string.IsNullOrEmpty(product.VariantId)) 343 { 344 <input type="hidden" name="VariantId" value="@product.VariantId" /> 345 } 346 @if (!Model.Item.GetBoolean("QuantitySelector")) 347 { 348 <input id="[email protected]" name="Quantity" value="@valueQty" type="hidden"> 349 <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> 350 } else { 351 <div class="input-group input-primary-button-group js-input-group d-flex flex-row flex-nowrap"> 352 <label for="Quantity_@(product.Id)" class="visually-hidden">@Translate("Quantity")</label> 353 <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)"> 354 <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> 355 </div> 356 357 if (stepQty != "1") 358 { 359 <div class="invalid-feedback d-none"> 360 @Translate("Please select a quantity that is dividable by") @stepQty 361 </div> 362 } 363 } 364 </form> 365 if (!anonymousUser && !hideFavoritesSelector) 366 { 367 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 368 } 369 } 370 else if (!anonymousUser && !hideFavoritesSelector && !isDiscontinued) 371 { 372 <div class="flex-fill" id="[email protected]"> 373 @Translate("Add to favorites") @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 374 </div> 375 } 376 377 @if (isDiscontinued && product.ReplacementProduct != null) { 378 List<ProductInfoViewModel> replacementProductList = new List<ProductInfoViewModel>(); 379 replacementProductList.Add(product.ReplacementProduct); 380 var replacementProduct = replacementProductList.GetProducts().FirstOrDefault(); 381 382 if ((product.DiscontinuedAction == 0 || product.DiscontinuedAction == 1) && product?.ReplacementProduct.ProductId != null) { 383 var parms = new Dictionary<string, object>(); 384 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto"); 385 parms.Add("fullwidth", true); 386 parms.Add("columns", Model.GridRowColumnCount); 387 388 string imagePath = replacementProduct?.DefaultImage?.Value != null ? replacementProduct.DefaultImage.Value : ""; 389 390 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop"); 391 link += $"&GroupID={replacementProduct.PrimaryOrDefaultGroup.Id}"; 392 link += $"&ProductID={replacementProduct.Id}"; 393 link += !string.IsNullOrEmpty(replacementProduct.VariantId) ? $"&VariantID={replacementProduct.VariantId}" : ""; 394 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link); 395 396 <div class="w-100"> 397 <div class="fw-bold w-100">@Translate("Sorry, this product is no longer available").</div> 398 <div>@Translate("We recommend this replacement product instead"):</div> 399 400 <a href="@link"> 401 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms) 402 </a> 403 404 <div>@replacementProduct.Name</div> 405 406 @if (!hidePrice) 407 { 408 <div class="mb-3"> 409 <div class="h4" itemprop="offers" itemscope itemtype="https://schema.org/Offer"> 410 @if (showPricesWithVat == "false" && !neverShowVat) 411 { 412 if (isLazyLoadingForProductInfoEnabled) 413 { 414 <span itemprop="price" content="" class="d-none"></span> 415 <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> 416 } 417 else 418 { 419 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted; 420 421 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 422 if (product.Price.Price != product.PriceBeforeDiscount.Price) 423 { 424 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span> 425 } 426 } 427 } 428 else 429 { 430 if (isLazyLoadingForProductInfoEnabled) 431 { 432 <span itemprop="price" content="" class="d-none"></span> 433 <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> 434 } 435 else 436 { 437 string beforePrice = product.PriceBeforeDiscount.PriceFormatted; 438 439 <span itemprop="price" content="@product.Price.Price" class="d-none"></span> 440 if (product.Price.Price != product.PriceBeforeDiscount.Price) 441 { 442 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span> 443 } 444 } 445 } 446 447 @if (showPricesWithVat == "false" && !neverShowVat) 448 { 449 if (isLazyLoadingForProductInfoEnabled) 450 { 451 <span class="text-price js-text-price"><div class="spinner-border" role="status"></div></span> 452 } 453 else 454 { 455 string price = product.Price.PriceWithoutVatFormatted; 456 if (product?.VariantInfo?.VariantInfo != null) 457 { 458 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : ""; 459 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : ""; 460 } 461 if (priceMin != priceMax) 462 { 463 price = priceMin + " - " + priceMax; 464 } 465 <span class="text-price">@price</span> 466 } 467 } 468 else 469 { 470 if (isLazyLoadingForProductInfoEnabled) 471 { 472 <span class="text-price js-text-price"><div class="spinner-border" role="status"></div></span> 473 } 474 else 475 { 476 string price = product.Price.PriceFormatted; 477 if (product?.VariantInfo?.VariantInfo != null) 478 { 479 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : ""; 480 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : ""; 481 } 482 if (priceMin != priceMax) 483 { 484 price = priceMin + " - " + priceMax; 485 } 486 <span class="text-price">@price</span> 487 } 488 } 489 </div> 490 491 @if (showPricesWithVat == "false" && !neverShowVat) 492 { 493 if (isLazyLoadingForProductInfoEnabled) 494 { 495 <small class="opacity-85 fst-normal js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></small> 496 } 497 else 498 { 499 string price = product.Price.PriceWithVatFormatted; 500 if (product?.VariantInfo?.VariantInfo != null) 501 { 502 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : ""; 503 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : ""; 504 } 505 if (priceMin != priceMax) 506 { 507 price = priceMin + " - " + priceMax; 508 } 509 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small> 510 } 511 } 512 </div> 513 } 514 515 <a href="@link" class="btn btn-primary w-100">@Translate("Go to the replacement")</a> 516 </div> 517 } 518 } 519 </div> 520 </div> 521 @if (!hideStock) 522 { 523 if (!IsNeverOutOfStock) 524 { 525 if (isLazyLoadingForProductInfoEnabled) 526 { 527 string hideStockState = string.IsNullOrEmpty(product.VariantId) && product.VariantInfo.VariantInfo != null ? "d-none" : ""; 528 <div class="js-product @liveInfoClass" data-product-id="@product.Id"> 529 <div class="mt-3 js-stock-state spinner-border @hideStockState"> 530 @if (!Model.Item.GetBoolean("HideInventory")) 531 { 532 <div class="small text-success d-none" data-show-if="LiveProductInfo.product.StockLevel > 0"> 533 <span class="js-text-stock"></span> 534 @Translate("Products available in stock") 535 </div> 536 } 537 else 538 { 539 <div class="small text-success d-none" data-show-if="LiveProductInfo.product.StockLevel > 0">@Translate("Available in stock")</div> 540 } 541 <div class="small text-danger d-none" data-show-if="LiveProductInfo.product.StockLevel <= 0">@Translate("Out of Stock")</div> 542 543 <div class="d-none" data-show-if="LiveProductInfo.product.ExpectedDelivery != null && new Date(LiveProductInfo.product.ExpectedDelivery) > new Date()"> 544 <span>@Translate("Expected back in stock:")</span> 545 <span class="js-text-expected-delivery"></span> 546 </div> 547 </div> 548 </div> 549 } 550 else 551 { 552 <div class="mt-3 js-stock-state"> 553 554 @if (product.StockLevel > 0) 555 { 556 if (!Model.Item.GetBoolean("HideInventory")) 557 { 558 <div class="small text-success">@product.StockLevel @Translate("Products available in stock")</div> 559 } 560 else 561 { 562 <div class="small text-success">@Translate("Available in stock")</div> 563 } 564 } 565 566 else 567 { 568 <div class="small text-danger">@Translate("Out of Stock")</div> 569 } 570 571 @if (hasExpectedDelivery) 572 { 573 <div> 574 <span>@Translate("Expected back in stock:")</span> 575 <span>@expectedDeliveryDate</span> 576 </div> 577 } 578 579 </div> 580 } 581 } 582 } 583 </div> 584 585 @helper RenderField(FieldValueViewModel field) 586 { 587 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 588 bool noValues = false; 589 590 if (!string.IsNullOrEmpty(fieldValue)) 591 { 592 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 593 { 594 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 595 noValues = values.Count > 0 ? false : true; 596 } 597 } 598 599 if (!string.IsNullOrEmpty(fieldValue) && noValues == false) 600 { 601 <dt class="g-col-12 g-col-sm-4 g-col-lg-12 fw-bold m-0">@field.Name</dt> 602 <dd class="g-col-12 g-col-sm-8 g-col-lg-12 mb-3"> 603 @RenderFieldValue(field) 604 </dd> 605 } 606 } 607 608 @helper RenderFieldValue(FieldValueViewModel field) 609 { 610 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 611 612 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 613 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 614 615 bool isColor = false; 616 617 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>)) 618 { 619 int valueCount = 0; 620 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>; 621 int totalValues = values.Count; 622 623 foreach (FieldOptionValueViewModel option in values) 624 { 625 if (option.Value.Substring(0, 1) == "#") 626 { 627 isColor = true; 628 } 629 630 if (!isColor) 631 { 632 @option.Name 633 } 634 else 635 { 636 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span> 637 } 638 639 if (valueCount != totalValues && valueCount < (totalValues - 1)) 640 { 641 if (isColor) 642 { 643 <text> </text> 644 } 645 else 646 { 647 <text>, </text> 648 } 649 } 650 valueCount++; 651 } 652 } 653 else 654 { 655 if (fieldValue.Substring(0, 1) == "#") 656 { 657 isColor = true; 658 } 659 660 if (!isColor) 661 { 662 @fieldValue 663 } 664 else 665 { 666 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span> 667 } 668 } 669 } 670
Product beschrijving
Mininor is een kwalitatief, duurzaam Deens babymerk en voldoet aan de EU-norm EN 1400.
Deze klassieke ronde (kersen) fopspeen is ontworpen in Denemarken en is de favoriete fopspeen van verschillende Deense ziekenhuizen.
MININOR staat bekend voor de frisse kleuren en innovatieve kwaliteit en design.
Baby's worden geboren met een sterke zuigreflex en de behoefte om te zuigen is volkomen natuurlijk. Deze behoefte blijft de eerste jaren bestaan, ook nadat er geen borst- of flesvoeding meer gegeven wordt. De fopspeen vervult een behoefte, kalmeert en geeft een gevoel van veiligheid.
- Deze ronde retro fopspeen is verkrijgbaar in twee maten (0m+ & 6m+) en 6 verschillende kleuren (Wit, Roze, Blauw, Groen, Grijs & Paars) en 2 materialen (Latex & siliconen)
- Materialen (BPA-vrij)
- Schild: Polypropyleen (PP)
- Speen: Siliconen
- Voldoet aan de EU EN-1400-normen
Voor de veiligheid van uw kind:
WAARSCHUWING!
Voor elk gebruik zorgvuldig inspecteren. Trek de fopspeen in alle richtingen. Gooi weg bij het eerste teken van schade. Gebruik alleen fopspeenkettingen die getest zijn volgens EN 12586. Bevestig nooit andere linten of koorden aan een fopspeen, uw kind kan hierdoor gewurgd worden. Laat de fopspeen niet in direct zonlicht of in de buurt van een warmtebron liggen en laat hem niet langer dan aanbevolen in een ontsmettingsmiddel liggen. ("steriliserende oplossing"), omdat dit de speen kan verzwakken. Stop de fopspeen nooit in suikerhoudende stoffen of medicijnen, dit kan gaatjes veroorzaken. Als de fopspeen vast komt te zitten in je mond, raak dan niet in paniek; de fopspeen kan niet worden ingeslikt. Door de gaten in het schild kun je zonder onderbreking blijven ademen. Haal de fopspeen zo voorzichtig mogelijk uit de mond. Om gezondheids- en veiligheidsredenen moet de fopspeen na één tot twee maanden gebruik worden vervangen. Maak de fopspeen nooit schoon met schurende schoonmaakmiddelen. Voor het eerste gebruik en voor de daaropvolgende reiniging, dompel de fopspeen gedurende 5 minuten onder in een steriliserende oplossing of kokend water en laat afkoelen; Knijp ingesloten water uit de speen. Dit om de hygiëne te waarborgen. Maak de fopspeen voor elk gebruik schoon. Als de fopspeen niet direct na sterilisatie wordt gebruikt, bewaar hem dan altijd in een droge, afgedekte container. Een kind moet de fopspeen gebruiken onder toezicht van een volwassene.
Specificaties
- Leeftijd
- 6m+
- Kleur
- Blauw
- Wat zit er in de doos
- 2x fopspeen
- Fopspeencollectie
- Mininor
- Materiaal
- Latex, PP