Error executing template "Designs/Swift/Catalog_PDFview.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_c7c389a7e2ff496bb05548ba998274c9.ExecuteAsync()
   at RazorEngine.Templating.TemplateBase.Run(ExecuteContext context, TextWriter reader)
   at RazorEngine.Templating.RazorEngineCore.RunTemplate(ICompiledTemplate template, TextWriter writer, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.DynamicWrapperService.Run(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
   at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass23_0.<Run>b__0(TextWriter writer)
   at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
   at RazorEngine.Templating.RazorEngineServiceExtensions.Run(IRazorEngineService service, String name, Type modelType, Object model, DynamicViewBag viewBag)
   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.PageViewModel> 2 @using Dynamicweb 3 @using Dynamicweb.Environment 4 @using Dynamicweb.Security.UserManagement 5 @{ 6 string swiftVersion = ReadFile("/Files/Templates/Designs/Swift/swift_version.txt"); 7 bool renderAsResponsive = Model.Area.Item.GetString("DeviceRendering", "responsive").Equals("responsive", StringComparison.OrdinalIgnoreCase); 8 bool renderMobile = Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Mobile || Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Tablet; 9 string responsiveClassDesktop = string.Empty; 10 string responsiveClassMobile = string.Empty; 11 if (renderAsResponsive) 12 { 13 responsiveClassDesktop = " d-none d-xl-block"; 14 responsiveClassMobile = " d-block d-xl-none"; 15 } 16 17 //var CurrentUrlRaw = Dynamicweb.Context.Current?.Request.RawUrl; 18 var CurrentUrlRaw = Pageview.Page.GetBreadcrumbPath(); 19 var CurrentUrlParts = CurrentUrlRaw?.Split('/'); 20 var CurrentUrlSeason = CurrentUrlParts?.Length > 2 ? CurrentUrlParts[2] : ""; 21 CurrentUrlSeason = Dynamicweb.Context.Current?.Request?.QueryString["season"] ?? CurrentUrlSeason; 22 int.TryParse(CurrentUrlSeason, out int CurrentSeasonInt); 23 var CurrentUrlBrand = CurrentUrlParts?.Length > 3 ? CurrentUrlParts[3] : ""; 24 25 var PageIsBrandFrontpage = CurrentUrlParts?.Length == 4; 26 27 var BrandName = "oase"; 28 var BrandStyling = "brand"; 29 if (!string.IsNullOrWhiteSpace(CurrentUrlBrand)) 30 { 31 BrandName = $"{CurrentUrlBrand.Replace(" ", "").ToLower()}"; 32 BrandStyling = $"brand-{BrandName}"; 33 } 34 Dynamicweb.Context.Current?.Items.Add("CurrentBrand", BrandName); 35 36 Dynamicweb.Context.Current?.Session?.Add("username", UserContext.Current.User?.UserName); 37 Dynamicweb.Context.Current?.Session?.Add("brand", BrandName); 38 Dynamicweb.Context.Current?.Session?.Add("season", CurrentUrlSeason); 39 40 var PageIsPDF = Pageview.Page.LayoutTemplate == "Catalog_PDFview.cshtml"; 41 if (PageIsPDF) 42 { 43 BrandName = Dynamicweb.Context.Current?.Request.QueryString["brand"]; 44 BrandStyling = $"brand-{BrandName}"; 45 } 46 47 var headerDesktopLink = Model.Area.Item?.GetLink("HeaderDesktop")?.PageId ?? null; 48 var headerMobileLink = Model.Area.Item?.GetLink("HeaderMobile")?.PageId ?? null; 49 50 var footerDesktopLink = Model.Area.Item?.GetLink("FooterDesktop")?.PageId ?? null; 51 var footerMobileLink = Model.Area.Item?.GetLink("FooterMobile")?.PageId ?? null; 52 @* Branding Themes Fonts *@ 53 var brandingPageId = Model.Area.Item.GetLink("BrandingPage") != null ? Model.Area.Item.GetLink("BrandingPage").PageId : 0; 54 var themePageId = Model.Area.Item.GetLink("ThemesPage") != null ? Model.Area.Item.GetLink("ThemesPage").PageId : 0; 55 56 var disableWideBreakpoints = Model.Area?.Item?.GetRawValueString("DisableWideBreakpoints", "default"); 57 58 string customHeaderInclude = Model.Area.Item.GetFile("CustomHeaderInclude") != null ? Model.Area.Item.GetFile("CustomHeaderInclude").Name : string.Empty; 59 60 string masterTheme = !string.IsNullOrWhiteSpace(Model.Area.Item.GetRawValueString("Theme")) ? " theme " + Model.Area.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 61 62 string favicon = Model.Area.Item.GetFile("Favicon") != null ? Model.Area.Item.GetFile("Favicon").Path : "/Files/Templates/Designs/Swift/Assets/Images/favicon.png"; 63 64 string headerCssClass = "sticky-top"; 65 bool movePageBehind = false; 66 67 if (Model.PropertyItem != null) 68 { 69 headerCssClass = Model.PropertyItem.GetRawValueString("MoveThisPageBehindTheHeader", "sticky-top"); 70 movePageBehind = headerCssClass == "fixed-top" && !Pageview.IsVisualEditorMode ? true : false; 71 } 72 73 headerCssClass = headerCssClass == "" ? "sticky-top" : headerCssClass; 74 headerCssClass = Pageview.IsVisualEditorMode ? "" : headerCssClass; 75 76 var brandingPage = Dynamicweb.Content.Services.Pages?.GetPage(brandingPageId) ?? null; 77 var themesParagraphLastChanged = Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(themePageId).OrderByDescending(p => p.Audit.LastModifiedAt).FirstOrDefault(); 78 var cssLastModified = brandingPage.Audit.LastModifiedAt > themesParagraphLastChanged.Audit.LastModifiedAt ? brandingPage.Audit.LastModifiedAt : themesParagraphLastChanged.Audit.LastModifiedAt; 79 80 var cssStyleFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/css/styles.css")); 81 var jsFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/js/scripts.js")); 82 var jsMainFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/js/main.js")); 83 var jsModularFileInfo = new System.IO.FileInfo(Dynamicweb.Core.SystemInformation.MapPath("/Files/Templates/Designs/Swift/Assets/js/modular.min.js")); 84 85 // Log 86 var CurrentUsername = UserContext.Current.User?.UserName; 87 if (PageIsBrandFrontpage && (!string.IsNullOrWhiteSpace(CurrentUsername) && CurrentUsername.Contains("@oase.dk") == false)) 88 { 89 int.TryParse(CurrentUrlSeason, out int SeasonYear); 90 var catalogService = new Oase.Backend.Services.CatalogService(); 91 catalogService.WriteLogToDb(new() 92 { 93 Action = "Frontpage Catalog", 94 Username = $"{CurrentUsername}", 95 Brandname = $"{BrandName}", 96 CatalogId = SeasonYear, 97 Parm = "" 98 }); 99 } 100 101 if (CurrentSeasonInt > 0) 102 { 103 BrandStyling += $" season-{CurrentSeasonInt}"; 104 var SeasonHeader = Dynamicweb.Content.Services.Pages?.GetPageByNavigationTag(Pageview.AreaID, $"Header{CurrentUrlSeason}"); 105 headerDesktopLink = SeasonHeader?.ID; 106 var SeasonHeaderMobile = Dynamicweb.Content.Services.Pages?.GetPageByNavigationTag(Pageview.AreaID, $"HeaderMobile{CurrentUrlSeason}"); 107 headerMobileLink = SeasonHeaderMobile?.ID; 108 } 109 } 110 <!doctype html> 111 <html lang="@Pageview.Area.CultureInfo.TwoLetterISOLanguageName"> 112 <head> 113 <!-- @swiftVersion --> 114 @* Required meta tags *@ 115 <meta charset="utf-8"> 116 <meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0"> 117 <link rel="preload" href="/Files/Templates/Designs/Swift/Assets/css/styles.css?@cssStyleFileInfo.LastWriteTime.Ticks" as="style"> 118 <link rel="preload" href="/Files/Templates/Designs/Swift/Assets/js/scripts.js?@jsMainFileInfo.LastWriteTime.Ticks" as="script"> 119 120 <link rel="shortcut icon" href="@favicon"> 121 <link rel="apple-touch-icon" href="/Files/Templates/Designs/Swift/Assets/Images/logo_transparent.png"> 122 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 123 <meta name="googlebot-news" content="nosnippet"> 124 125 @Model.MetaTags 126 127 <title>@Model.Title</title> 128 129 @* Bootstrap + Swift stylesheet *@ 130 <link href="/Files/Templates/Designs/Swift/Assets/css/styles.css?@cssStyleFileInfo.LastWriteTime.Ticks" rel="stylesheet" media="all" type="text/css"> 131 132 @if (disableWideBreakpoints != "disableBoth") 133 { 134 <style> 135 @@media ( min-width: 1600px ) { 136 .container-xxl, 137 .container-xl, 138 .container-lg, 139 .container-md, 140 .container-sm, 141 .container { 142 max-width: 1520px; 143 } 144 } 145 </style> 146 147 if (disableWideBreakpoints != "disableUltraWideOnly") 148 { 149 <style> 150 @@media ( min-width: 1920px ) { 151 .container-xxl, 152 .container-xl, 153 .container-lg, 154 .container-md, 155 .container-sm, 156 .container { 157 max-width: 1820px; 158 } 159 } 160 </style> 161 } 162 } 163 164 @* Branding and Themes min stylesheet *@ 165 <link href="/Files/Templates/Designs/Swift/_parsed/Swift_css/Swift_styles_@(Model.Area.ID).min.css?@cssLastModified.Ticks" rel="stylesheet" media="all" type="text/css" data-last-modified-content="@cssLastModified"> 166 <script src="/Files/Templates/Designs/Swift/Assets/js/scripts.js?@jsFileInfo.LastWriteTime.Ticks"></script> 167 <script src="/Files/Templates/Designs/Swift/Assets/js/main.js?@jsFileInfo.LastWriteTime.Ticks"></script> 168 <script src="/Files/Templates/Designs/Swift/Assets/js/modular.min.js?@jsModularFileInfo.LastWriteTime.Ticks"></script> 169 <script type="module"> 170 swift.Scroll.hideHeadersOnScroll(); 171 swift.Scroll.handleAlternativeTheme(); 172 </script> 173 174 @if (!string.IsNullOrWhiteSpace(customHeaderInclude)) 175 { 176 @RenderPartial($"Components/Custom/{customHeaderInclude}") 177 } 178 <style> 179 header a { 180 text-decoration: none!important; 181 } 182 183 .text-decoration-underline-hover:where(:hover) { 184 text-decoration: none !important; 185 } 186 </style> 187 </head> 188 189 <body class="@(BrandStyling) @(masterTheme)"> 190 191 @if (PageIsPDF == false) 192 { 193 var IsCatalogPage = Pageview.Page.ItemType == "Swift_CatalogPage"; 194 <header class="page-header d-none d-lg-block bg-white @headerCssClass top-0@(responsiveClassDesktop)" id="page-header-desktop"> 195 @if (headerDesktopLink != null) 196 { 197 @RenderGrid((int)headerDesktopLink) 198 } 199 </header> 200 } 201 202 @if (PageIsPDF == false) 203 { 204 <header class="page-header d-lg-none bg-white @headerCssClass top-0@(responsiveClassMobile)" id="page-header-mobile"> 205 @if (headerMobileLink != null) 206 { 207 @RenderGrid((int)headerMobileLink) 208 } 209 </header> 210 } 211 212 <div data-intersect></div> 213 214 <main id="content"> 215 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel> 216 @using Dynamicweb.Ecommerce.ProductCatalog 217 @using Dynamicweb.Frontend 218 @using Dynamicweb.Security.UserManagement 219 @using Oase.Backend.Models 220 221 @Model.Grid("Grid", "Grid", "default:true;sort:1", "Page") 222 @{ 223 224 // Request parameters 225 var request = Dynamicweb.Context.Current?.Request; 226 string? ProductId = request?.QueryString["pp"]; 227 string? Username = request?.QueryString["user"]; 228 string? Brandname = request?.QueryString["brand"]; 229 string? Season = request?.QueryString["season"]; 230 string Prices = $"{request?.QueryString["prices"]}"; 231 232 var SeasonParse = int.TryParse(Season, out int SeasonInt); 233 234 // Catalog User 235 var FindUser = UserManagementServices.Users.GetUserByUserName(Username); 236 var CurrentCatalogUser = new CatalogUserItem(FindUser); 237 238 var cultureInfo = System.Globalization.CultureInfo.GetCultureInfo(CurrentCatalogUser.Culture); 239 240 // Custom - Clearance Price 241 var ClearancePriceString = request?.QueryString["ClearancePrice"]; 242 double.TryParse(ClearancePriceString?.Replace(",", "."), out double ClearancePrice); 243 // Custom - Clearance Discount 244 var ClearanceDiscountString = request?.QueryString["ClearanceDiscount"]; 245 double.TryParse(ClearanceDiscountString, out double ClearanceDiscount); 246 // Custom - Stock Quantity 247 var StockQtyString = request?.QueryString["StockQty"]; 248 double.TryParse(StockQtyString, out double StockQty); 249 250 251 // Product model 252 ProductViewModel product = null; 253 string? TypeOfTent = null; 254 string? SleepsPeople = null; 255 string[]? SpecificationList = null; 256 string[]? FeatureList = null; 257 List<string> Dimensions = new(); 258 List<ProductViewModel> OptionalExtras = new(); 259 260 if (!string.IsNullOrWhiteSpace(ProductId)) 261 { 262 var ProductIdParsed = int.TryParse(ProductId, out int ProductIdInt); 263 if (ProductIdParsed) 264 { 265 product = new Oase.Backend.Services.CatalogService().GetPerfionProductWithId(ProductIdInt, CurrentCatalogUser); 266 TypeOfTent = product?.ProductFields.GetValueOrDefault("TypeOfTent")?.ToString(); 267 SleepsPeople = product?.ProductFields.GetValueOrDefault("Sleeps")?.ToString(); 268 269 270 var OtherSpecifications = product?.ProductFields.GetValueOrDefault("OtherSpecifications")?.ToString(); 271 SpecificationList = OtherSpecifications?.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); 272 273 var AllFeatures = product?.ProductFields.GetValueOrDefault("Featurelist")?.ToString(); 274 FeatureList = AllFeatures?.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); 275 276 // Get all Dimension images 277 string[] DimensionImages = new string[] { "Image061", "Image062", "Image063", "Image064", "Image065" }; 278 foreach (var DimensionImage in DimensionImages) 279 { 280 var ImageString = product?.ProductFields.GetValueOrDefault(DimensionImage)?.ToString(); 281 if(!string.IsNullOrEmpty(ImageString)) 282 { 283 Dimensions.Add(ImageString); 284 } 285 } 286 287 // Optional extras 288 var OptionalExtrasString = product?.ProductFields.GetValueOrDefault("OptionalExtra")?.ToString(); 289 if(!string.IsNullOrWhiteSpace(OptionalExtrasString)) 290 { 291 var OptionalExtrasList = OptionalExtrasString?.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); 292 foreach (var ItemIdString in OptionalExtrasList) 293 { 294 var ItemIdParsed = int.TryParse(ItemIdString, out int ItemId); 295 if(ItemIdParsed) 296 { 297 var ItemAsProduct = new Oase.Backend.Services.CatalogService().GetPerfionProductWithId(ItemId, CurrentCatalogUser); 298 if(ItemAsProduct != null) 299 { 300 OptionalExtras.Add(ItemAsProduct); 301 } 302 } 303 } 304 } 305 } 306 } 307 308 // Add space after code if DKK 309 var PriceSymbol = CurrentCatalogUser.Currency == "DKK" ? $"{product?.Price.CurrencyCode} " : product?.Price.CurrencyCode; 310 311 string ProductSplash = product.ProductFields.GetValueOrDefault("OutletcampingSplash")?.Value.ToString(); 312 } 313 <link rel="stylesheet" type="text/css" href="https://use.typekit.net/tjm2nvx.css"> 314 <link rel="stylesheet" href="https://use.typekit.net/xee3ogm.css"> 315 <link rel="stylesheet" href="https://use.typekit.net/grh8ljx.css"> 316 <style> 317 body { 318 width: 100vw; 319 height: 100vh; 320 overflow:hidden; 321 } 322 .pdf-wrap { 323 width: 1456px; 324 height: 1030px; 325 overflow: hidden 326 } 327 .leader-dots { 328 border-bottom: 1px dashed currentColor; 329 } 330 /* EASYCAMP */ 331 .brand-easycamp .easycamp-logo { 332 display: inline!important; 333 } 334 .brand-easycamp .robens-border { 335 border: 1px solid #c35d1d; 336 } 337 .brand-easycamp .prices-color { 338 color: #c35d1d; 339 } 340 /* OUTWELL */ 341 .brand-outwell .outwell-logo { 342 display: inline!important; 343 } 344 .brand-outwell .robens-border { 345 border: 1px solid #2a3075; 346 } 347 .brand-outwell .prices-color { 348 color: #2a3075; 349 } 350 .brand-outwell.season-2026 .robens-border { 351 border: 1px solid #22384a; 352 } 353 .brand-outwell.season-2026 .prices-color { 354 color: #22384a; 355 } 356 /* ROBENS */ 357 .brand-robens .robens-logo { 358 display: inline!important; 359 } 360 .brand-robens .robens-border { 361 border: 1px solid #dc0814; 362 } 363 .brand-robens .prices-color { 364 color: #dc0814; 365 } 366 </style> 367 <div class="pdf-wrap"> 368 369 @* Top part *@ 370 <div class="row g-0" style="height:50%;"> 371 <div class="col-4 h-100 p-4"> 372 <div class="d-flex flex-column h-100 w-100 overflow-hidden"> 373 @* Logos - toggle display based on current brand *@ 374 <div class="w-100" style="height:100px"> 375 <img src="/Files/Images/Logos/robens.png" class="robens-logo mb-3 d-none" width="200" /> 376 <img src="/Files/Images/Logos/easycamp.png" class="easycamp-logo d-none" width="200" /> 377 @if (SeasonInt <= 2025) 378 { 379 <img src="/Files/Images/Logos/outwell.png" class="outwell-logo d-none" width="200" /> 380 } else 381 { 382 <img src="/Files/Images/Logos/outwell_2026.png" class="outwell-logo d-none" width="200" /> 383 } 384 </div> 385 386 <h2 class="item_swift_productheader">@product?.Name</h2> 387 <p class="mb-2">@(TypeOfTent) @(SleepsPeople != "" ? $"- Persons {SleepsPeople}" : "")</p> 388 389 <div class="row g-0 w-75 prices-color"> 390 @if (Prices.Contains("rrp") && product?.PriceInformative.Price > 0) 391 { 392 <div class="col-12 d-flex"> 393 RRP 394 <div class="leader-dots flex-fill"></div> 395 @String.Format(cultureInfo, "{0}{1:N2}", PriceSymbol, product?.PriceInformative.Price) 396 </div> 397 } 398 @if (Prices.Contains("siap") && product?.PriceBeforeDiscount.Price > 0) 399 { 400 var SiapTitle = SeasonInt > 2025 ? "Market price" : "SIAP"; 401 <div class="col-12 d-flex"> 402 @(SiapTitle) 403 <div class="leader-dots flex-fill"></div> 404 @String.Format(cultureInfo, "{0}{1:N2}", PriceSymbol, product?.PriceBeforeDiscount.Price) 405 </div> 406 } 407 @if (Prices.Contains("price") && product?.Price.Price > 0) 408 { 409 <div class="col-12 d-flex"> 410 Net price 411 <div class="leader-dots flex-fill"></div> 412 @String.Format(cultureInfo, "{0}{1:N2}", PriceSymbol, product?.Price.Price) 413 </div> 414 } 415 @if(ClearancePrice > 0) 416 { 417 <div class="col-12 d-flex"> 418 Clearance Price @product?.Price.CurrencyCode 419 <div class="leader-dots flex-fill"></div> 420 @String.Format(cultureInfo, "{0}{1:N2}", PriceSymbol, ClearancePrice) 421 </div> 422 } 423 @if(StockQty > 0) 424 { 425 <div class="col-12 d-flex"> 426 Stock Quantity 427 <div class="leader-dots flex-fill"></div> 428 @StockQty 429 </div> 430 } 431 </div> 432 433 <p style="height:200px;"> 434 @{ 435 var FirstDimensionsImage = Dimensions.FirstOrDefault(); 436 if (FirstDimensionsImage != null && (ClearancePrice == 0 || StockQty == 0)) 437 { 438 <img src="@($"/Admin/Public/GetImage.ashx?image=/files/images/products/{FirstDimensionsImage}.jpg&width=1200&format=webp")" class="img-fluid" style="max-height:85%;" /> 439 } 440 } 441 </p> 442 </div> 443 </div> 444 445 <div class="col-8 h-100 p-4"> 446 <div class="container w-100 h-100 position-relative"> 447 @{ 448 string largeImagePath = $"/Admin/Public/GetImage.ashx?image={product?.DefaultImage.Value}&width=1200&format=webp"; 449 } 450 <img src="@largeImagePath" loading="eager" class="" style="max-width:100%;max-height:100%;" /> 451 <div class="position-absolute top-0 end-0 fs-6 fs-lg-5 lh-1 m-2"> 452 @if (string.IsNullOrWhiteSpace(ProductSplash) == false && ProductSplash != "CONT.") 453 { 454 string BadgeBackground = "transparent"; 455 string BadgeTextColor = "#ffffff"; 456 string BadgeFontWeight = "fw-normal"; 457 string BadgeFontStyle = ""; 458 <span class="badge rounded-0 @(BadgeFontWeight) @(BadgeFontStyle)" style="background-color:@(BadgeBackground);color:@(BadgeTextColor);">@ProductSplash</span> 459 } 460 </div> 461 </div> 462 </div> 463 </div> 464 465 @* Bottom part *@ 466 <div class="row g-0" style="height:50%;"> 467 468 <div class="col-4 ps-4 pb-4 pe-2 h-100"> 469 <div class="w-100 h-100 p-3 robens-border small overflow-hidden"> 470 @if (SpecificationList != null) 471 { 472 <h6 class="paragraph-title border-0">Specifications</h6> 473 <div class="row small"> 474 @foreach (var item in SpecificationList) 475 { 476 var ListItem = item.Split(","); 477 <div class="col-4">@ListItem[0]</div> 478 <div class="col-8">@ListItem[1]</div> 479 } 480 </div> 481 } 482 483 </div> 484 </div> 485 486 <div class="col-3 pb-4 px-2 h-100"> 487 <div class="w-100 h-100 p-3 robens-border small overflow-hidden"> 488 @if (FeatureList != null) 489 { 490 <h6 class="paragraph-title border-0">Features</h6> 491 <ul class="ps-3 small"> 492 @foreach (var item in FeatureList) 493 { 494 <li>@item</li> 495 } 496 </ul> 497 } 498 499 </div> 500 </div> 501 502 <div class="col-3 pb-4 px-2 h-100"> 503 @if(OptionalExtras.Any()) 504 { 505 <div class="w-100 h-100 p-3 robens-border overflow-hidden"> 506 <h6 class="paragraph-title border-0">Optional extras</h6> 507 <div class="row gx-3 gy-2"> 508 @foreach (var OptionalExtra in OptionalExtras) 509 { 510 string extraImagePath = $"/Admin/Public/GetImage.ashx?image={OptionalExtra?.DefaultImage.Value}&width=1200&format=webp"; 511 <div class="col-4"> 512 <img src="@extraImagePath" loading="eager" class="" style="max-width:100%;max-height:100%;" /> 513 </div> 514 <div class="col-8 small"> 515 <h6 class="m-0 small">@OptionalExtra?.Name</h6> 516 <p class="small">Art.No: @OptionalExtra?.ProductFields.GetValueOrDefault("ProductArtNo")</p> 517 <div class="row g-0 w-75 small"> 518 @if (OptionalExtra?.PriceInformative.Price > 0) 519 { 520 <div class="col-12 d-flex"> 521 RRP 522 <div class="leader-dots flex-fill"></div> 523 @String.Format(cultureInfo, "{0}{1:N2}", PriceSymbol, OptionalExtra?.PriceInformative.Price) 524 </div> 525 } 526 </div> 527 </div> 528 } 529 </div> 530 </div> 531 } 532 </div> 533 534 <div class="col-2 ps-2 pe-4 h-100"> 535 <div class="w-100 h-100 overflow-hidden"> 536 @if(Dimensions.Any()) 537 { 538 int SkipImages = 1; 539 int TakeImages = 2; 540 if (ClearancePrice > 0 || StockQty > 0) 541 { 542 SkipImages = 0; 543 TakeImages = 3; 544 } 545 546 <div class="row g-0"> 547 @foreach (var ItemImage in Dimensions.Skip(SkipImages).Take(TakeImages)) 548 { 549 string ItemImagePath = $"/Admin/Public/GetImage.ashx?image=/files/images/products/{ItemImage}.jpg&width=1200&format=webp"; 550 <div class="col-12 overflow-hidden text-center"> 551 <img src="@ItemImagePath" loading="eager" class="mb-3" style="max-width:100%;max-height:140px;" /> 552 </div> 553 } 554 </div> 555 } 556 </div> 557 </div> 558 </div> 559 560 </div> 561 </main> 562 563 @if (footerDesktopLink != null) 564 { 565 @RenderGrid((int)footerDesktopLink) 566 } 567 568 <link rel="stylesheet" href="/Files/Templates/Designs/Swift/Assets/css/lysboks.min.css" /> 569 <script src="/Files/Templates/Designs/Swift/Assets/js/lysboks.min.js"></script> 570 <link rel="stylesheet" href="/Files/Templates/Designs/Swift/Assets/css/glightbox.min.css" /> 571 <script src="/Files/Templates/Designs/Swift/Assets/js/glightbox.min.js"></script> 572 <script> 573 var lightbox = GLightbox({ 574 touchNavigation: true, 575 loop: false, 576 autoplayVideos: true, 577 videosWidth: '100vw', 578 plyr: { 579 config: { 580 quality: 720, 581 muted: false, 582 volume: 100, 583 youtube: { noCookie: true, rel: 0, showinfo: 0, iv_load_policy: 3, modestbranding: 1 } 584 } 585 } 586 }); 587 </script> 588 589 @* Render any offcanvas menu here *@ 590 @RenderSnippet("offcanvas") 591 592 @* Toast *@ 593 <div aria-live="polite" aria-atomic="true"> 594 <div class="toast-container position-fixed end-0 p-3" style="top:50px;z-index:1500;"> 595 <div id="notificationToast" class="toast rounded-6 overflow-hidden" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="1000"> 596 <div class="toast-body d-flex align-items-end gap-3 theme theme-gray bg-dark text-light"> 597 @ReadFile("/Files/Templates/Designs/Swift/Assets/icons/check.svg") 598 <div id="notificationToast_Text" class=""></div> 599 </div> 600 </div> 601 </div> 602 </div> 603 604 </body> 605 606 </html> 607