Error executing template "Designs/Swift/Paragraph/Swift_ProductListListView.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_e3f157f1327e48678cb9cb75622bbeea.<>c__DisplayClass6_0.<RenderProductList>b__0(TextWriter __razor_helper_writer) in E:\dynamicweb.net\Solutions\www.licscadenta.no\Files\Templates\Designs\Swift\Paragraph\Swift_ProductListListView.cshtml:line 352
   at CompiledRazorTemplates.Dynamic.RazorEngine_e3f157f1327e48678cb9cb75622bbeea.Execute() in E:\dynamicweb.net\Solutions\www.licscadenta.no\Files\Templates\Designs\Swift\Paragraph\Swift_ProductListListView.cshtml:line 54
   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.Core 5 @using System.Linq 6 @using Lic_Scadenta.CustomModules 7 8 @functions 9 { 10 11 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsLazyLoadingForProductInfoEnabled; 12 string liveInfoClass = ""; 13 string productInfoFeed = ""; 14 string showPricesWithVat = ""; 15 bool neverShowVat = false; 16 17 ProductListViewModel productList = new ProductListViewModel(); 18 } 19 20 @{ 21 if (Dynamicweb.Context.Current.Items.Contains("ProductList")) 22 { 23 productList = (ProductListViewModel)Dynamicweb.Context.Current.Items["ProductList"]; 24 } 25 26 showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower(); 27 neverShowVat = string.IsNullOrEmpty(showPricesWithVat); 28 29 if (isLazyLoadingForProductInfoEnabled) 30 { 31 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed")) 32 { 33 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString(); 34 if (!string.IsNullOrEmpty(productInfoFeed)) 35 { 36 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\""; 37 } 38 } 39 liveInfoClass = "js-live-info"; 40 } 41 42 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 43 } 44 45 @if (!string.IsNullOrEmpty(theme)) 46 { 47 <div class="h-100@(theme) item_@Model.Item.SystemName.ToLower()" @productInfoFeed> 48 @RenderProductList(productList) 49 </div> 50 } 51 else 52 { 53 <div class="item_@Model.Item.SystemName.ToLower()" @productInfoFeed> 54 @RenderProductList(productList) 55 </div> 56 } 57 58 @helper RenderProductList(ProductListViewModel productList) 59 { 60 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 61 bool anonymousUser = Pageview.User == null; 62 bool hideProductNumber = !string.IsNullOrEmpty(Model.Item.GetString("HideProductNumber")) ? Model.Item.GetBoolean("HideProductNumber") : false; 63 bool isErpConnectionDown = !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable(); 64 bool hideStock = Model.Item.GetBoolean("HideStockState") || (Pageview.AreaSettings.GetBoolean("ErpDownHideStock") && isErpConnectionDown); 65 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && isErpConnectionDown; 66 bool hideAddToCart = !string.IsNullOrEmpty(Model.Item.GetString("HideAddToCart")) ? Model.Item.GetBoolean("HideAddToCart") : false; 67 hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown ? true : hideAddToCart; 68 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false; 69 70 string detailsPageLink = Dynamicweb.Context.Current.Items["DetailsPageLink"] != null ? Dynamicweb.Context.Current.Items["DetailsPageLink"].ToString() : ""; 71 string productTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ProductTheme")) ? " theme p-2 " + Model.Item.GetRawValueString("ProductTheme").Replace(" ", "").Trim().ToLower() : ""; 72 string modalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ModalTheme")) ? " theme " + Model.Item.GetRawValueString("ModalTheme").Replace(" ", "").Trim().ToLower() : "theme light"; 73 string textPadding = !string.IsNullOrEmpty(Model.Item.GetString("ContentPadding")) ? Model.Item.GetRawValueString("ContentPadding") : "none"; 74 textPadding = textPadding == "none" ? "" : textPadding; 75 textPadding = textPadding == "small" ? "p-2 p-lg-3" : textPadding; 76 textPadding = textPadding == "large" ? "p-3 p-lg-4" : textPadding; 77 78 string groupId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("GroupID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("GroupID") : ""; 79 string url = Dynamicweb.Context.Current.Request.RawUrl; 80 81 var badgeParms = new Dictionary<string, object>(); 82 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType")); 83 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign")); 84 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign")); 85 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays")); 86 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges")); 87 88 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false; 89 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false; 90 91 var favoriteParameters = new Dictionary<string, object>(); 92 if (!anonymousUser && !hideFavoritesSelector) 93 { 94 int defaultFavoriteListId = 0; 95 96 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists(); 97 if (favoreiteLists.Count() == 1) 98 { 99 foreach (FavoriteList list in favoreiteLists) 100 { 101 defaultFavoriteListId = list.ListId; 102 } 103 } 104 105 favoriteParameters.Add("ListId", defaultFavoriteListId); 106 } 107 108 if (productList.TotalProductsCount > 0) 109 { 110 int pageSizeSetting = 30; 111 int pageSize = productList.PageSize; 112 pageSize += pageSizeSetting; 113 114 int loadedProducts = productList.PageSize > productList.TotalProductsCount ? productList.TotalProductsCount : productList.PageSize; 115 116 foreach (ProductViewModel product in productList.Products) 117 { 118 var pro = Dynamicweb.Ecommerce.Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId); 119 var NavDiscount = Helpers.DiscountProvider(pro); 120 // SDSCADENT2-43 Punchout marked products green background 121 string markedItem = ""; 122 if (Pageview.User != null) 123 { 124 markedItem = NavDiscount.SpecialPrice && Pageview.User.CustomerNumber == "10041" ? "marked-item" : ""; 125 } 126 127 var defaultGroupId = product.PrimaryOrDefaultGroup.Id; 128 var selectedDetailPage = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(defaultGroupId)?.Meta.PrimaryPage ?? string.Empty; 129 130 string link = string.IsNullOrEmpty(selectedDetailPage) ? $"{detailsPageLink}&groupid={defaultGroupId}" : selectedDetailPage; 131 link += "&productid=" + product.Id; 132 link += !string.IsNullOrEmpty(product.VariantId) ? "&variantid=" + product.VariantId : ""; 133 //string link = product.GetProductLink(GetPageIdByNavigationTag("Shop"), false); 134 135 // Custom image path 136 string img = Helpers.ProductImage(product.Id); 137 string imagePath = !string.IsNullOrEmpty(img) ? img : "/Files/Images/missing_image.jpg"; 138 imagePath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath); 139 imagePath = "/Admin/Public/GetImage.ashx?width=" + 80 + "&image=" + imagePath + "&v=2" + "&format=webp"; 140 141 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 142 ratio = ratio != "0" ? ratio : ""; 143 string ratioCssClass = ratio != "" ? " ratio" : ""; 144 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : ""; 145 146 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 147 string imageId = "ProductImage_" + product.Id + product.VariantId; 148 149 @* Alternative image *@ 150 var supportedImageFormats = new string[] { ".jpg", ".webp", ".png", ".gif" }; 151 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : ""; 152 var selectedAssetCategories = Model.Item.GetRawValueString("AlternativeImageAssets"); 153 IEnumerable<MediaViewModel> alternativeImagesList = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets); 154 155 if (alternativeImagesList.FirstOrDefault() != null) 156 { 157 alternativeImagesList = alternativeImagesList.OrderByDescending(x => x.Value.Equals(defaultImage)); 158 159 if (alternativeImagesList.First().Value == defaultImage) 160 { 161 alternativeImagesList = alternativeImagesList.Skip(1); 162 } 163 } 164 165 @* Badges *@ 166 DateTime createdDate = product.Created.Value; 167 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false; 168 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges; 169 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; 170 171 string alternativeImage = alternativeImagesList.FirstOrDefault() != null ? alternativeImagesList.FirstOrDefault().Value : ""; 172 alternativeImage = !string.IsNullOrEmpty(alternativeImage) ? "/Admin/Public/GetImage.ashx?width=" + 80 + "&image=" + alternativeImage + "&format=webp" : ""; 173 var isProductKonkurensvare = bool.Parse(product.ProductFields.FirstOrDefault(x => x.Key == "Konkurensvare").Value.Value.ToString()); 174 @* Main features *@ 175 IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList(); 176 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>(); 177 178 foreach (var selection in selectedDisplayGroups) 179 { 180 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values) 181 { 182 if (selection == group.Id) 183 { 184 mainFeatures.Add(group); 185 } 186 } 187 } 188 189 <style> 190 .short-description > p:last-child { 191 margin: 0; 192 } 193 </style> 194 195 <article class="product-list-item grid gap-2 gap-lg-3 mb-3 @(productTheme) @textPadding product @liveInfoClass @markedItem" data-product-id="@product.Id"> 196 <a href="@link" class="g-col-12 g-col-lg-7 d-flex text-decoration-none mb-2 mb-lg-0"> 197 198 @if (!Model.Item.GetBoolean("HideImage")) 199 { 200 201 202 <div class="d-flex align-items-center position-relative me-2 me-lg-3" style="min-width: 110px"> 203 @if (isProductKonkurensvare) 204 { 205 <div class="badge fast-lavpris roundedBadge"> 206 <div>FAST LAVPRIS</div> 207 </div> 208 } 209 210 <div class="@ratioCssClass m-0" style="@(ratioVariable)"> 211 <figure class="m-0 d-flex justify-content-center"> 212 <div class="d-flex justify-content-center align-items-center h-100 w-100"> 213 <img src="@imagePath" onmouseover="showBiggerImage('@imagePath');" onmouseout="hideBiggerImage()" alt="@product.Name" class="mw-100 mh-100" loading="lazy" style="object-fit: cover;" /> 214 </div> 215 </figure> 216 </div> 217 </div> 218 } 219 220 @if (showBadges) 221 { 222 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms) 223 } 224 225 <div class="flex-fill d-flex flex-column justify-content-center"> 226 227 <div class="d-flex align-items-center gap-2 w-lg-75 w-xxl-100"> 228 <h3 class="h6 mb-1"> 229 @product.Name @if (!string.IsNullOrEmpty(product.VariantName)) 230 {<text>(@product.VariantName)</text>} 231 </h3> 232 </div> 233 <div class="fs-7 opacity-75"> 234 @Translate("Manufacturer: ")@product.ProductFields.FirstOrDefault(x => x.Key == "ManufacturerField").Value.Value.ToString() 235 </div> 236 237 @if (!hideProductNumber) 238 { 239 <div class="fs-7 opacity-75">@Translate("Item no. ")@product.Number</div> 240 } 241 @if (!hideStock) 242 { 243 <div class="g-col-12 fs-7 d-block d-lg-none"> 244 @RenderStockState(product) 245 </div> 246 } 247 @if (!Model.Item.GetBoolean("HideShortDescription") && selectedDisplayGroups.Count() > 0) 248 { 249 <div class="grid gap-2 gap-lg-4 w-100"> 250 <div class="g-col-12 g-col-lg-8 fs-7 fs-7 d-none d-lg-block short-description"> 251 @product.ShortDescription 252 </div> 253 <div class="g-col-12 g-col-lg-4"> 254 @RenderMainFeatures(mainFeatures) 255 </div> 256 </div> 257 } 258 else 259 { 260 if (!Model.Item.GetBoolean("HideShortDescription")) 261 { 262 <div class="fs-7 d-none d-lg-block"> 263 @product.ShortDescription 264 </div> 265 } 266 @RenderMainFeatures(mainFeatures) 267 } 268 </div> 269 </a> 270 271 <div class="g-col-12 g-col-lg-5 d-flex flex-row gap-2 gap-lg-3 align-items-center justify-content-end"> 272 @if (!hidePrice) 273 { 274 <div class="text-end align-self-end h-100 w-100 d-flex flex-column align-items-start align-items-lg-end justify-content-center ms-2"> 275 <b>@RenderPrice(product, NavDiscount)</b> 276 </div> 277 } 278 <div class="d-flex gap-2 justify-content-end"> 279 @*@if (!hidePrice) 280 { 281 <div class="text-end d-flex h-100 flex-column align-items-center justify-content-center"> 282 <b>@RenderPrice(product)</b> 283 @if (actualPrice.PriceWithoutVAT != product.Price.PriceWithoutVat) 284 { 285 <p class="text-decoration-line-through mb-0" style="color: gray; font-size: 14px;">@actualPrice.PriceWithoutVATFormatted</p> 286 } 287 </div> 288 }*@ 289 <div class="d-flex flex-row"> 290 <div class="flex-fill d-flex align-items-center add-to-cart-div"> 291 @RenderAddToCart(product) 292 </div> 293 @if (!anonymousUser && !hideFavoritesSelector && product.VariantInfo.VariantInfo == null) 294 { 295 <div class="d-flex flex-column"> 296 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters) 297 @if (!anonymousUser && !hideStock) 298 { 299 <div class="text-end align-self-end mt-1 d-none d-lg-block stock-state"> 300 @RenderStockState(product) 301 </div> 302 } 303 </div> 304 } 305 else 306 { 307 <p class="product-list-log-in mb-0 me-2" data-bs-toggle="modal" data-bs-target="#loginModal">@Translate("Log in to shop")</p> 308 } 309 </div> 310 </div> 311 </div> 312 </article> 313 <div class="modal fade addToCartModal" id="addToCartModal-@product.Id" tabindex="-1" aria-labelledby="addToCartModalLabel" aria-hidden="true"> 314 <div class="modal-dialog modal-dialog-centered d-flex justify-content-center"> 315 <div class="modal-content"> 316 <div class="d-flex justify-content-end"> 317 <button type="button" class="btn-close p-2" data-bs-dismiss="modal" aria-label="Close"></button> 318 </div> 319 <div class="modal-body text-center py-2 d-flex flex-column align-items-center"> 320 <img src="@imagePath" alt="@product.Name" class="mw-100 mh-100 pb-3" loading="lazy" style="object-fit: cover;" /> 321 <h5>@product.Name</h5> 322 <h5>@Translate("Added to cart")!</h5> 323 </div> 324 <div class="d-flex flex-column align-items-center pb-4"> 325 <button type="button" class="btn btn-primary cart-modal-button mb-2" onclick="location.href='/cart'">@Translate("View Cart")</button> 326 <button type="button" class="btn btn-secondary cart-modal-button" data-bs-dismiss="modal">@Translate("Continue Shopping")</button> 327 </div> 328 </div> 329 </div> 330 </div> 331 } 332 333 <div id="biggerImagePlace"></div> 334 335 <div class="my-3" id="LoadMoreButton"> 336 <div class="text-center d-flex flex-column gap-3"> 337 <div class="opacity-85">@loadedProducts @Translate("out of") @productList.TotalProductsCount @Translate("products")</div> 338 @if (productList.PageCount != 1) 339 { 340 string sortBySelection = Dynamicweb.Context.Current.Request?.Form["SortBy"] ?? "SortByProductId"; 341 sortBySelection = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SortBy")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SortBy") : sortBySelection; 342 343 if (Pageview.User != null && Pageview.User.CustomerNumber == "10040") 344 { 345 sortBySelection = "-created"; 346 } 347 348 string searchQuery = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("q")) ? Dynamicweb.Context.Current.Request.QueryString.Get("q") : ""; 349 string searchLayout = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout") : ""; 350 351 <form method="get" action="@url" data-response-target-element="content" class="w-100"> 352 @foreach (FacetGroupViewModel facetGroup in productList.FacetGroups) 353 { 354 foreach (FacetViewModel facetItem in facetGroup.Facets) 355 { 356 foreach (FacetOptionViewModel facetOption in facetItem.Options) 357 { 358 if (facetOption.Selected) 359 { 360 <input type="hidden" name="@facetItem.QueryParameter" value="[@facetOption.Value]" /> 361 } 362 } 363 } 364 } 365 366 @if (productList?.Group?.Id != null) 367 { 368 <input type="hidden" name="GroupId" value="@productList.Group.Id" /> 369 } 370 371 <input type="hidden" name="PageSize" value="@pageSize" /> 372 <input type="hidden" name="SortBy" value="@sortBySelection" /> 373 <input type="hidden" name="RequestType" value="UpdateList" /> 374 375 @if (!string.IsNullOrEmpty(searchQuery)) 376 { 377 <input type="hidden" name="q" value="@searchQuery" /> 378 <input type="hidden" name="SearchLayout" value="@searchLayout" /> 379 } 380 381 @{ 382 string nextPageLink = "/Default.aspx?ID=" + Pageview.Page.ID + "&PageSize=" + pageSize + "&SortBy=" + sortBySelection; 383 384 foreach (FacetGroupViewModel facetGroup in productList.FacetGroups) 385 { 386 foreach (FacetViewModel facetItem in facetGroup.Facets) 387 { 388 foreach (FacetOptionViewModel facetOption in facetItem.Options) 389 { 390 if (facetOption.Selected) 391 { 392 nextPageLink += "&" + facetItem.QueryParameter + "=[" + facetOption.Value + "]"; 393 } 394 } 395 } 396 } 397 398 nextPageLink += productList?.Group?.Id != null ? "&GroupID=" + productList.Group.Id : ""; 399 nextPageLink += !string.IsNullOrEmpty(searchQuery) ? "&q=" + searchQuery : ""; 400 } 401 402 <a href="@nextPageLink" class="btn btn-primary" type="button" onclick="swift.ProductList.Update(event)" id="LoadMoreButton_@Model.ID">@Translate("Load more products")</a> 403 </form> 404 } 405 </div> 406 </div> 407 <div class="position-sticky bottom-0 end-0 d-flex justify-content-end align-items-end" style="z-index: 2;"> 408 <button id="backToTopButton" onclick="backToTop()" style="display: none;" class=""><img src="~/Files/Icons/chevron-up.svg" /></button> 409 </div> 410 } 411 else 412 { 413 if (!Pageview.IsVisualEditorMode) 414 { 415 <div class="alert alert-dark m-0"> 416 @Translate("We did not find anything matching your search result") 417 </div> 418 } 419 else 420 { 421 <div class="alert alert-dark m-0" role="alert"> 422 <span>@Translate("Product list: The list will be shown here, if any")</span> 423 </div> 424 } 425 } 426 } 427 428 @helper RenderMainFeatures(List<CategoryFieldViewModel> mainFeatures) 429 { 430 if (mainFeatures.Count > 0) 431 { 432 string featuresLayout = Model.Item.GetRawValueString("FeaturesLayout", "bullets"); 433 434 if (featuresLayout == "bullets") 435 { 436 <ul class="m-0 p-0 lh-1 fs-7 opacity-75" style="list-style-position: inside"> 437 @foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 438 { 439 foreach (var field in mainFeatureGroup.Fields) 440 { 441 @RenderField(field.Value) 442 } 443 } 444 </ul> 445 } 446 else 447 { 448 List<string> featuresList = new List<string>(); 449 foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures) 450 { 451 foreach (var field in mainFeatureGroup.Fields) 452 { 453 if (field.Value.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 454 { 455 List<string> options = new List<string>(); 456 foreach (FieldOptionValueViewModel option in field.Value.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 457 { 458 if (!string.IsNullOrWhiteSpace(option.Value)) 459 { 460 if (option.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 461 { 462 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 463 options.Add(colorSpan); 464 } 465 else 466 { 467 options.Add(option.Value); 468 } 469 } 470 } 471 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 472 if ((Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 473 { 474 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 475 } 476 featuresList.Add(field.Value.Name + ": " + optionsString); 477 } 478 else 479 { 480 if (!string.IsNullOrWhiteSpace(field.Value.Value.ToString())) 481 { 482 if (field.Value.Value.ToString().Contains("#") && (Translate(field.Value.Name) == Translate("Color") || Translate(field.Value.Name) == Translate("Colour"))) 483 { 484 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + field.Value.Value + "\"></span>"; 485 featuresList.Add(field.Value.Name + ": " + colorSpan); 486 } 487 else 488 { 489 featuresList.Add(field.Value.Name + ": " + field.Value.Value.ToString()); 490 } 491 } 492 } 493 } 494 } 495 string featuresString = (string.Join(", ", featuresList.Select(x => x.ToString()).ToArray())); 496 497 <div class="opacity-75 fs-7">@featuresString</div> 498 } 499 } 500 } 501 502 @helper RenderField(FieldValueViewModel field) 503 { 504 string fieldValue = field?.Value != null ? field.Value.ToString() : ""; 505 506 if (fieldValue != "") 507 { 508 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue; 509 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue; 510 511 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) 512 { 513 fieldValue = ""; 514 List<string> options = new List<string>(); 515 foreach (FieldOptionValueViewModel option in field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) 516 { 517 if (!string.IsNullOrWhiteSpace(option.Value)) 518 { 519 if (option.Value.ToString().Contains("#") && (Translate(field.Name) == Translate("Color") || Translate(field.Name) == Translate("Colour"))) 520 { 521 string colorSpan = "<span class=\"colorbox-sm\" style=\"background-color: " + option.Value + "\"></span>"; 522 options.Add(colorSpan); 523 } 524 else 525 { 526 options.Add(option.Name); 527 } 528 } 529 } 530 string optionsString = (string.Join(", ", options.Select(x => x.ToString()).ToArray())); 531 if ((Translate(field.Name) == Translate("Color") || Translate(field.Name) == Translate("Colour"))) 532 { 533 optionsString = (string.Join(" ", options.Select(x => x.ToString()).ToArray())); 534 } 535 536 fieldValue = optionsString; 537 } 538 539 if (!string.IsNullOrEmpty(fieldValue)) 540 { 541 <li>@(field.Name): @fieldValue</li> 542 } 543 } 544 } 545 546 @helper RenderStockState(ProductViewModel product) 547 { 548 bool isNeverOutOfStock = product.NeverOutOfstock; 549 // IsNeverOutOfStock for Kjeveortopedi 550 if (product.GroupPaths.FirstOrDefault() != null && product.GroupPaths.FirstOrDefault().FirstOrDefault() != null) 551 { 552 var productCategoryId = product.GroupPaths.FirstOrDefault().FirstOrDefault().Id; 553 if (productCategoryId == "2500") 554 { 555 isNeverOutOfStock = true; 556 } 557 } 558 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery > DateTime.Now; 559 string expectedDeliveryDate = product.ExpectedDelivery?.ToShortDateString() ?? ""; 560 561 string stockLevel = product.StockLevel > 100 ? "100+" : product.StockLevel.ToString(); 562 563 if (!isNeverOutOfStock) 564 { 565 if (isLazyLoadingForProductInfoEnabled) 566 { 567 <div class="js-stock-state"> 568 @if (!Model.Item.GetBoolean("HideInventory")) 569 { 570 <p class="fs-7 text-success text-lg-center m-0 d-none" data-show-if="LiveProductInfo.product.StockLevel > 0"><span class="js-text-stock"></span> @Translate("In stock")</p> 571 } 572 else 573 { 574 <p class="fs-7 text-success text-lg-center m-0 d-none" data-show-if="LiveProductInfo.product.StockLevel > 0">@Translate("In stock")</p> 575 } 576 <p class="fs-7 text-danger text-lg-center m-0 d-none" data-show-if="LiveProductInfo.product.StockLevel <= 0">@Translate("Out of Stock")</p> 577 578 <p class="d-none" data-show-if="LiveProductInfo.product.ExpectedDelivery != null && new Date(LiveProductInfo.product.ExpectedDelivery) > new Date()"> 579 <span>@Translate("Expected:")</span> 580 <span class="js-text-expected-delivery"></span> 581 </p> 582 </div> 583 } 584 else 585 { 586 if (product.StockLevel > 0) 587 { 588 if (!Model.Item.GetBoolean("HideInventory")) 589 { 590 <p class="fs-7 text-success text-lg-center m-0">@stockLevel @Translate("In stock")</p> 591 } 592 else 593 { 594 <p class="fs-7 text-success text-lg-center m-0">@Translate("In stock")</p> 595 } 596 } 597 else 598 { 599 <p class="fs-7 text-danger text-lg-center m-0">@Translate("Out of Stock")</p> 600 } 601 602 if (hasExpectedDelivery) 603 { 604 <p> 605 <span>@Translate("Expected:")</span> 606 <span>@expectedDeliveryDate</span> 607 </p> 608 } 609 } 610 } 611 } 612 613 @helper RenderAddToCart(ProductViewModel product) 614 { 615 string iconPath = "/Files/icons/"; 616 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService")); 617 if (!url.Contains("LayoutTemplate")) 618 { 619 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml"; 620 } 621 622 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", ""); 623 bool anonymousUser = Pageview.User == null; 624 625 bool hideAddToCart = !string.IsNullOrEmpty(Model.Item.GetString("HideAddToCart")) ? Model.Item.GetBoolean("HideAddToCart") : false; 626 hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable() ? true : hideAddToCart; 627 bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("QuantitySelector")) ? Model.Item.GetBoolean("QuantitySelector") : false; 628 bool IsNeverOutOfStock = product.NeverOutOfstock; 629 List<ProductInfoViewModel> replacementProductList = new List<ProductInfoViewModel>(); 630 replacementProductList.Add(product.ReplacementProduct); 631 var replacementProduct = replacementProductList.GetProducts().FirstOrDefault(); 632 var productHasReplacement = replacementProduct != null; 633 string disableAddToCart = productHasReplacement == true ? "disabled" : ""; 634 disableAddToCart = isLazyLoadingForProductInfoEnabled ? "disabled" : disableAddToCart; 635 636 if (!hideAddToCart) 637 { 638 if (product.VariantInfo.VariantInfo == null) 639 { 640 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\""; 641 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1"; 642 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty; 643 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : ""; 644 string filterProductName = product.Name.Replace(@"""", "&quot;"); 645 <form method="post" action="@url" class="d-inline-block"> 646 <input type="hidden" name="redirect" value="false" /> 647 <input type="hidden" name="ProductId" value="@product.Id" /> 648 <input type="hidden" name="cartcmd" value="add" /> 649 650 @if (!string.IsNullOrEmpty(product.VariantId)) 651 { 652 <input type="hidden" name="VariantId" value="@product.VariantId" /> 653 } 654 @if (quantitySelector) 655 { 656 <div class="input-group input-primary-button-group d-flex flex-row w-100"> 657 <label for="Quantity_@(product.Id)_@product.VariantId" class="visually-hidden">@Translate("Quantity")</label> 658 <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control" style="max-width: 100px" type="number"> 659 <button type="button" onclick="AddToCartModal(event,'@filterProductName', '@product.NeverOutOfstock.ToString()', '@product.StockLevel','@product.Id')" class="btn btn-primary flex-fill js-add-to-cart-button" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)"><span class="icon-2">@ReadFile(iconPath + "shopping-cart.svg")</span></button> 660 661 @if (stepQty != "1") 662 { 663 <div class="invalid-feedback d-none"> 664 @Translate("Please select a quantity that is dividable by") @stepQty 665 </div> 666 } 667 </div> 668 } 669 else 670 { 671 <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" type="hidden"> 672 <button type="button" onclick="AddToCartModal(event, '@filterProductName', '@product.NeverOutOfstock.ToString()', '@product.StockLevel','@product.Id')" class="btn btn-primary js-add-to-cart-button" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)"><span class="icon-2">@ReadFile(iconPath + "shopping-cart.svg")</span></button> 673 } 674 <!-- Modal --> 675 <div class="modal fade" id="staticBackdrop_@product.Id" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true"> 676 <div class="modal-dialog"> 677 <div class="modal-content"> 678 <div class="modal-header"> 679 <h5 class="modal-title" id="staticBackdropLabel"></h5> 680 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> 681 </div> 682 <div class="modal-body"> 683 684 </div> 685 <div class="modal-footer"> 686 <button type="button" id="cancelButton" class="btn btn-secondary" data-bs-dismiss="modal"></button> 687 <button type="button" id="submitModal" class="btn btn-primary" data-bs-dismiss="modal" data-bs-toggle="modal" data-bs-target="#addToCartModal-@product.Id"></button> 688 </div> 689 </div> 690 </div> 691 </div> 692 </form> 693 <button class="d-none" id="addToCartPopupButton-@product.Id" data-bs-toggle="modal" data-bs-target="#addToCartModal-@product.Id"></button> 694 } 695 else 696 { 697 string buttonWidth = quantitySelector ? "width: calc(80px + 3.5rem);" : "max-width: 3.5rem;"; 698 string buttonText = quantitySelector ? Translate("Select") : "<span class=\"icon-2\">" + @ReadFile(iconPath + "shopping-cart.svg") + "</span>"; 699 700 string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : ""; 701 variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString(); 702 703 string disableVariantSelector = isLazyLoadingForProductInfoEnabled ? "disabled" : ""; 704 705 <form action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" class="d-inline-block"> 706 <input type="hidden" name="ProductID" value="@product.Id" /> 707 <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()" /> 708 <input type="hidden" name="HideInventory" value="@Model.Item.GetBoolean("HideInventory").ToString()" /> 709 <input type="hidden" name="HideStockState" value="@Model.Item.GetBoolean("HideStockState").ToString()" /> 710 <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId" /> 711 <input type="hidden" name="ViewType" value="ModalContent" /> 712 <button type="button" onclick="swift.PageUpdater.Update(event)" class="btn btn-primary" style="@buttonWidth" @disableVariantSelector @disableVariantSelector title="@Translate("Select")" data-bs-toggle="modal" data-bs-target="#DynamicModal" id="OpenVariantSelectorModal@(product.Id)_@Pageview.CurrentParagraph.ID">@buttonText</button> 713 </form> 714 } 715 716 } 717 } 718 <script type="text/javascript"> 719 720 function AddToCartModal(event, productName, isProcurement, productStock, productId) { 721 var addToCartPopupButton = document.getElementById('addToCartPopupButton-' + productId); 722 if (isProcurement == "False" && productStock != "0") { 723 swift.Cart.Update(event); 724 addToCartPopupButton.click(); 725 } 726 else { 727 var modal = document.getElementById('staticBackdrop_' + productId); 728 729 //header 730 var headerText = modal.querySelector('#staticBackdropLabel'); 731 headerText.innerText = "@Model.Item.GetString("ModalHeaderTitle")"; 732 733 //body 734 var modalBody = modal.querySelector('.modal-body'); 735 if (isProcurement == "True") { 736 var bodyMessage = "@Model.Item.GetString("ProcurementMessage")"; 737 var rep = bodyMessage.replace("{0}", productName); 738 modalBody.innerHTML = rep; 739 } 740 if (isProcurement == "False" && productStock == "0") { 741 var bodyMessage = "@Model.Item.GetString("OutOfStockMessage")"; 742 var rep = bodyMessage.replace("{0}", productName); 743 modalBody.innerHTML = rep; 744 } 745 746 //footer 747 var cancelButton = modal.querySelector('#cancelButton'); 748 cancelButton.innerText = "@Model.Item.GetString("CancelButtonText")"; 749 var submitButton = modal.querySelector('#submitModal'); 750 submitButton.onclick = function (event) { 751 swift.Cart.Update(event); 752 addToCartPopupButton.click(); 753 }; 754 submitButton.innerText = "@Model.Item.GetString("AddToCartButtonText")"; 755 756 //show modal 757 var myModal = new bootstrap.Modal(modal); 758 myModal.show(); 759 } 760 } 761 762 //Back to top button 763 764 // When the user scrolls down 20px from the top of the document, show the button 765 window.onscroll = function () { scrollFunction() }; 766 var backToTopButton = document.getElementById('backToTopButton'); 767 768 function scrollFunction() { 769 if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) { 770 backToTopButton.style.display = "block"; 771 } else { 772 backToTopButton.style.display = "none"; 773 } 774 } 775 function backToTop() { 776 document.body.scrollTop = 0; // For Safari 777 document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera 778 } 779 780 function showBiggerImage(imagePath) { 781 var biggerImagePlace = document.getElementById('biggerImagePlace'); 782 var bigimagePath = imagePath.replace("width=80", "width=400"); 783 var imageTag = "<img id='bigProductImage' src='" + bigimagePath + "'/>"; 784 biggerImagePlace.innerHTML = imageTag; 785 } 786 787 function hideBiggerImage() { 788 var biggerImagePlace = document.getElementById('biggerImagePlace'); 789 biggerImagePlace.innerHTML = ""; 790 } 791 792 </script> 793 794 <script> 795 window.onload = function() { 796 var allowedCustomerNumbers = ["10040", "16526", "16896", "19349", "21790", "22031", "15768", "15732", "15729", "15728", "15727", "14431", "14445", "15723", "15724", "15725"]; 797 var user = '@(Pageview.User != null ? Pageview.User.CustomerNumber : null)'; 798 var hasParams = window.location.href.includes('?'); 799 if (allowedCustomerNumbers.includes(user) && !hasParams) { 800 window.location.href = window.location.href + '?SortBy=-created'; 801 } 802 }; 803 </script> 804 805 @helper RenderPrice(ProductViewModel product, NavPriceModal navPrice) 806 { 807 var discountPrice = new Dynamicweb.Ecommerce.Prices.PriceInfo { PriceWithoutVAT = navPrice.Discount, Currency = Dynamicweb.Ecommerce.Common.Context.Currency }; 808 var CustomerPrice = new Dynamicweb.Ecommerce.Prices.PriceInfo { PriceWithoutVAT = navPrice.DefaultCustomerPrice, Currency = Dynamicweb.Ecommerce.Common.Context.Currency }; 809 string beforePrice = product.Price.PriceWithoutVatFormatted; 810 string afterPrice = discountPrice.PriceWithoutVATFormatted; 811 812 <div class="text-start text-lg-end lh-1 my-auto my-lg-0"> 813 <div> 814 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span> 815 @if (product.Price.PriceWithoutVat != discountPrice.PriceWithoutVAT && discountPrice.PriceWithoutVAT > 0) 816 { 817 <div> 818 <span itemprop="price" content="@discountPrice.PriceWithoutVAT" class="d-none"></span> 819 <span class="text-price">@afterPrice</span> 820 <br /> 821 <span class="text-decoration-line-through opacity-75 text-price price-line-through">@beforePrice</span> 822 </div> 823 } 824 else if (CustomerPrice.PriceWithoutVAT > 0) 825 { 826 <div> 827 <span itemprop="price" content="@CustomerPrice.PriceWithoutVAT" class="d-none"></span> 828 <span class="text-price">@CustomerPrice.PriceWithoutVATFormatted</span> 829 <br /> 830 <span class="text-decoration-line-through opacity-75 text-price price-line-through">@beforePrice</span> 831 </div> 832 } 833 else 834 { 835 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span> 836 <span class="text-price">@product.Price.PriceWithoutVatFormatted</span> 837 } 838 </div> 839 </div> 840 } 841
Ved å trykke på "Godkjenn alle" så gir du tillatelse til at vi kan bruke informasjonskapsler som optimaliserer og forbedrer din brukeropplevelse.