Error executing template "Designs/Swift/Catalog_PDFview.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
   at CompiledRazorTemplates.Dynamic.RazorEngine_f6298f95a5f44b91b3d3e6259b4c2004.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/css/catalog-styles.min.css?@cssStyleFileInfo.LastWriteTime.Ticks" as="style"> 119 <link rel="preload" href="/Files/Templates/Designs/Swift/Assets/js/scripts.js?@jsMainFileInfo.LastWriteTime.Ticks" as="script"> 120 121 <link rel="shortcut icon" href="@favicon"> 122 <link rel="apple-touch-icon" href="/Files/Templates/Designs/Swift/Assets/Images/logo_transparent.png"> 123 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 124 <meta name="googlebot-news" content="nosnippet"> 125 126 @Model.MetaTags 127 128 <title>@Model.Title</title> 129 130 @* Bootstrap + Swift stylesheet *@ 131 <link href="/Files/Templates/Designs/Swift/Assets/css/styles.css?@cssStyleFileInfo.LastWriteTime.Ticks" rel="stylesheet" media="all" type="text/css"> 132 133 @if (disableWideBreakpoints != "disableBoth") 134 { 135 <style> 136 @@media ( min-width: 1600px ) { 137 .container-xxl, 138 .container-xl, 139 .container-lg, 140 .container-md, 141 .container-sm, 142 .container { 143 max-width: 1520px; 144 } 145 } 146 </style> 147 148 if (disableWideBreakpoints != "disableUltraWideOnly") 149 { 150 <style> 151 @@media ( min-width: 1920px ) { 152 .container-xxl, 153 .container-xl, 154 .container-lg, 155 .container-md, 156 .container-sm, 157 .container { 158 max-width: 1820px; 159 } 160 } 161 </style> 162 } 163 } 164 165 @* Branding and Themes min stylesheet *@ 166 <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"> 167 <script src="/Files/Templates/Designs/Swift/Assets/js/scripts.js?@jsFileInfo.LastWriteTime.Ticks"></script> 168 <script src="/Files/Templates/Designs/Swift/Assets/js/main.js?@jsFileInfo.LastWriteTime.Ticks"></script> 169 <script src="/Files/Templates/Designs/Swift/Assets/js/modular.min.js?@jsModularFileInfo.LastWriteTime.Ticks"></script> 170 <script type="module"> 171 swift.Scroll.hideHeadersOnScroll(); 172 swift.Scroll.handleAlternativeTheme(); 173 </script> 174 175 @if (!string.IsNullOrWhiteSpace(customHeaderInclude)) 176 { 177 @RenderPartial($"Components/Custom/{customHeaderInclude}") 178 } 179 <style> 180 header a { 181 text-decoration: none!important; 182 } 183 184 .text-decoration-underline-hover:where(:hover) { 185 text-decoration: none !important; 186 } 187 </style> 188 </head> 189 190 <body class="@(BrandStyling) @(masterTheme)"> 191 192 @if (PageIsPDF == false) 193 { 194 var IsCatalogPage = Pageview.Page.ItemType == "Swift_CatalogPage"; 195 <header class="page-header d-none d-lg-block bg-white @headerCssClass top-0@(responsiveClassDesktop)" id="page-header-desktop"> 196 @if (headerDesktopLink != null) 197 { 198 @RenderGrid((int)headerDesktopLink) 199 } 200 </header> 201 } 202 203 @if (PageIsPDF == false) 204 { 205 <header class="page-header d-lg-none bg-white @headerCssClass top-0@(responsiveClassMobile)" id="page-header-mobile"> 206 @if (headerMobileLink != null) 207 { 208 @RenderGrid((int)headerMobileLink) 209 } 210 </header> 211 } 212 213 <div data-intersect></div> 214 215 <main id="content"> 216 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.PageViewModel> 217 @using Dynamicweb.Ecommerce.ProductCatalog 218 @using Dynamicweb.Frontend 219 @using Dynamicweb.Security.UserManagement 220 @using Oase.Backend.Models 221 222 @Model.Grid("Grid", "Grid", "default:true;sort:1", "Page") 223 @{ 224 225 // Request parameters 226 var request = Dynamicweb.Context.Current?.Request; 227 string? ProductId = request?.QueryString["pp"]; 228 string? Username = request?.QueryString["user"]; 229 string? Brandname = request?.QueryString["brand"]; 230 string? Season = request?.QueryString["season"]; 231 string Prices = $"{request?.QueryString["prices"]}"; 232 233 var SeasonParse = int.TryParse(Season, out int SeasonInt); 234 235 // Catalog User 236 var FindUser = UserManagementServices.Users.GetUserByUserName(Username); 237 var CurrentCatalogUser = new CatalogUserItem(FindUser); 238 239 var cultureInfo = System.Globalization.CultureInfo.GetCultureInfo(CurrentCatalogUser.Culture); 240 241 // Custom - Clearance Price 242 var ClearancePriceString = request?.QueryString["ClearancePrice"]; 243 double.TryParse(ClearancePriceString?.Replace(",", "."), out double ClearancePrice); 244 // Custom - Clearance Discount 245 var ClearanceDiscountString = request?.QueryString["ClearanceDiscount"]; 246 double.TryParse(ClearanceDiscountString, out double ClearanceDiscount); 247 // Custom - Stock Quantity 248 var StockQtyString = request?.QueryString["StockQty"]; 249 double.TryParse(StockQtyString, out double StockQty); 250 251 252 // Product model 253 ProductViewModel product = null; 254 string? TypeOfTent = null; 255 string? SleepsPeople = null; 256 string[]? SpecificationList = null; 257 string[]? FeatureList = null; 258 List<string> Dimensions = new(); 259 List<ProductViewModel> OptionalExtras = new(); 260 261 if (!string.IsNullOrWhiteSpace(ProductId)) 262 { 263 var ProductIdParsed = int.TryParse(ProductId, out int ProductIdInt); 264 if (ProductIdParsed) 265 { 266 product = new Oase.Backend.Services.CatalogService().GetPerfionProductWithId(ProductIdInt, CurrentCatalogUser); 267 TypeOfTent = product?.ProductFields.GetValueOrDefault("TypeOfTent")?.ToString(); 268 SleepsPeople = product?.ProductFields.GetValueOrDefault("Sleeps")?.ToString(); 269 270 271 var OtherSpecifications = product?.ProductFields.GetValueOrDefault("OtherSpecifications")?.ToString(); 272 SpecificationList = OtherSpecifications?.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); 273 274 var AllFeatures = product?.ProductFields.GetValueOrDefault("Featurelist")?.ToString(); 275 FeatureList = AllFeatures?.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries); 276 277 // Get all Dimension images 278 string[] DimensionImages = new string[] { "Image061", "Image062", "Image063", "Image064", "Image065" }; 279 foreach (var DimensionImage in DimensionImages) 280 { 281 var ImageString = product?.ProductFields.GetValueOrDefault(DimensionImage)?.ToString(); 282 if(!string.IsNullOrEmpty(ImageString)) 283 { 284 Dimensions.Add(ImageString); 285 } 286 } 287 288 // Optional extras 289 var OptionalExtrasString = product?.ProductFields.GetValueOrDefault("OptionalExtra")?.ToString(); 290 if(!string.IsNullOrWhiteSpace(OptionalExtrasString)) 291 { 292 var OptionalExtrasList = OptionalExtrasString?.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries); 293 foreach (var ItemIdString in OptionalExtrasList) 294 { 295 var ItemIdParsed = int.TryParse(ItemIdString, out int ItemId); 296 if(ItemIdParsed) 297 { 298 var ItemAsProduct = new Oase.Backend.Services.CatalogService().GetPerfionProductWithId(ItemId, CurrentCatalogUser); 299 if(ItemAsProduct != null) 300 { 301 OptionalExtras.Add(ItemAsProduct); 302 } 303 } 304 } 305 } 306 } 307 } 308 309 // Add space after code if DKK 310 var PriceSymbol = CurrentCatalogUser.Currency == "DKK" ? $"{product?.Price.CurrencyCode} " : product?.Price.CurrencyCode; 311 312 string ProductSplash = product.ProductFields.GetValueOrDefault("OutletcampingSplash")?.Value.ToString(); 313 } 314 <link rel="stylesheet" type="text/css" href="https://use.typekit.net/tjm2nvx.css"> 315 <link rel="stylesheet" href="https://use.typekit.net/xee3ogm.css"> 316 <link rel="stylesheet" href="https://use.typekit.net/grh8ljx.css"> 317 <style> 318 body { 319 width: 100vw; 320 height: 100vh; 321 overflow:hidden; 322 } 323 .pdf-wrap { 324 width: 1456px; 325 height: 1030px; 326 overflow: hidden 327 } 328 .leader-dots { 329 border-bottom: 1px dashed currentColor; 330 } 331 /* EASYCAMP */ 332 .brand-easycamp .easycamp-logo { 333 display: inline!important; 334 } 335 .brand-easycamp .robens-border { 336 border: 1px solid #c35d1d; 337 } 338 .brand-easycamp .prices-color { 339 color: #c35d1d; 340 } 341 /* OUTWELL */ 342 .brand-outwell .outwell-logo { 343 display: inline!important; 344 } 345 .brand-outwell .robens-border { 346 border: 1px solid #2a3075; 347 } 348 .brand-outwell .prices-color { 349 color: #2a3075; 350 } 351 .brand-outwell.season-2026 .robens-border { 352 border: 1px solid #22384a; 353 } 354 .brand-outwell.season-2026 .prices-color { 355 color: #22384a; 356 } 357 /* ROBENS */ 358 .brand-robens .robens-logo { 359 display: inline!important; 360 } 361 .brand-robens .robens-border { 362 border: 1px solid #dc0814; 363 } 364 .brand-robens .prices-color { 365 color: #dc0814; 366 } 367 </style> 368 <div class="pdf-wrap"> 369 370 @* Top part *@ 371 <div class="row g-0" style="height:50%;"> 372 <div class="col-4 h-100 p-4"> 373 <div class="d-flex flex-column h-100 w-100 overflow-hidden"> 374 @* Logos - toggle display based on current brand *@ 375 <div class="w-100" style="height:100px"> 376 <img src="/Files/Images/Logos/robens.png" class="robens-logo mb-3 d-none" width="200" /> 377 <img src="/Files/Images/Logos/easycamp.png" class="easycamp-logo d-none" width="200" /> 378 @if (SeasonInt <= 2025) 379 { 380 <img src="/Files/Images/Logos/outwell.png" class="outwell-logo d-none" width="200" /> 381 } else 382 { 383 <img src="/Files/Images/Logos/outwell_2026.png" class="outwell-logo d-none" width="200" /> 384 } 385 </div> 386 387 <h2 class="item_swift_productheader">@product?.Name</h2> 388 <p class="mb-2">@(TypeOfTent) @(SleepsPeople != "" ? $"- Persons {SleepsPeople}" : "")</p> 389 390 <div class="row g-0 w-75 prices-color"> 391 @if (Prices.Contains("rrp") && product?.PriceInformative.Price > 0) 392 { 393 <div class="col-12 d-flex"> 394 RRP 395 <div class="leader-dots flex-fill"></div> 396 @String.Format(cultureInfo, "{0}{1:N2}", PriceSymbol, product?.PriceInformative.Price) 397 </div> 398 } 399 @if (Prices.Contains("siap") && product?.PriceBeforeDiscount.Price > 0) 400 { 401 var SiapTitle = SeasonInt > 2025 ? "Market price" : "SIAP"; 402 <div class="col-12 d-flex"> 403 @(SiapTitle) 404 <div class="leader-dots flex-fill"></div> 405 @String.Format(cultureInfo, "{0}{1:N2}", PriceSymbol, product?.PriceBeforeDiscount.Price) 406 </div> 407 } 408 @if (Prices.Contains("price") && product?.Price.Price > 0) 409 { 410 <div class="col-12 d-flex"> 411 Net price 412 <div class="leader-dots flex-fill"></div> 413 @String.Format(cultureInfo, "{0}{1:N2}", PriceSymbol, product?.Price.Price) 414 </div> 415 } 416 @if(ClearancePrice > 0) 417 { 418 <div class="col-12 d-flex"> 419 Clearance Price @product?.Price.CurrencyCode 420 <div class="leader-dots flex-fill"></div> 421 @String.Format(cultureInfo, "{0}{1:N2}", PriceSymbol, ClearancePrice) 422 </div> 423 } 424 @if(StockQty > 0) 425 { 426 <div class="col-12 d-flex"> 427 Stock Quantity 428 <div class="leader-dots flex-fill"></div> 429 @StockQty 430 </div> 431 } 432 </div> 433 434 <p style="height:200px;"> 435 @{ 436 var FirstDimensionsImage = Dimensions.FirstOrDefault(); 437 if (FirstDimensionsImage != null && (ClearancePrice == 0 || StockQty == 0)) 438 { 439 <img src="@($"/Admin/Public/GetImage.ashx?image=/files/images/products/{FirstDimensionsImage}.jpg&width=1200&format=webp")" class="img-fluid" style="max-height:85%;" /> 440 } 441 } 442 </p> 443 </div> 444 </div> 445 446 <div class="col-8 h-100 p-4"> 447 <div class="container w-100 h-100 position-relative"> 448 @{ 449 string largeImagePath = $"/Admin/Public/GetImage.ashx?image={product?.DefaultImage.Value}&width=1200&format=webp"; 450 } 451 <img src="@largeImagePath" loading="eager" class="" style="max-width:100%;max-height:100%;" /> 452 <div class="position-absolute top-0 end-0 fs-6 fs-lg-5 lh-1 m-2"> 453 @if (string.IsNullOrWhiteSpace(ProductSplash) == false && ProductSplash != "CONT.") 454 { 455 string BadgeBackground = "transparent"; 456 string BadgeTextColor = "#ffffff"; 457 string BadgeFontWeight = "fw-normal"; 458 string BadgeFontStyle = ""; 459 <span class="badge rounded-0 @(BadgeFontWeight) @(BadgeFontStyle)" style="background-color:@(BadgeBackground);color:@(BadgeTextColor);">@ProductSplash</span> 460 } 461 </div> 462 </div> 463 </div> 464 </div> 465 466 @* Bottom part *@ 467 <div class="row g-0" style="height:50%;"> 468 469 <div class="col-4 ps-4 pb-4 pe-2 h-100"> 470 <div class="w-100 h-100 p-3 robens-border small overflow-hidden"> 471 @if (SpecificationList != null) 472 { 473 <h6 class="paragraph-title border-0">Specifications</h6> 474 <div class="row small"> 475 @foreach (var item in SpecificationList) 476 { 477 var ListItem = item.Split(","); 478 <div class="col-4">@ListItem[0]</div> 479 <div class="col-8">@ListItem[1]</div> 480 } 481 </div> 482 } 483 484 </div> 485 </div> 486 487 <div class="col-3 pb-4 px-2 h-100"> 488 <div class="w-100 h-100 p-3 robens-border small overflow-hidden"> 489 @if (FeatureList != null) 490 { 491 <h6 class="paragraph-title border-0">Features</h6> 492 <ul class="ps-3 small"> 493 @foreach (var item in FeatureList) 494 { 495 <li>@item</li> 496 } 497 </ul> 498 } 499 500 </div> 501 </div> 502 503 <div class="col-3 pb-4 px-2 h-100"> 504 @if(OptionalExtras.Any()) 505 { 506 <div class="w-100 h-100 p-3 robens-border overflow-hidden"> 507 <h6 class="paragraph-title border-0">Optional extras</h6> 508 <div class="row gx-3 gy-2"> 509 @foreach (var OptionalExtra in OptionalExtras) 510 { 511 string extraImagePath = $"/Admin/Public/GetImage.ashx?image={OptionalExtra?.DefaultImage.Value}&width=1200&format=webp"; 512 <div class="col-4"> 513 <img src="@extraImagePath" loading="eager" class="" style="max-width:100%;max-height:100%;" /> 514 </div> 515 <div class="col-8 small"> 516 <h6 class="m-0 small">@OptionalExtra?.Name</h6> 517 <p class="small">Art.No: @OptionalExtra?.ProductFields.GetValueOrDefault("ProductArtNo")</p> 518 <div class="row g-0 w-75 small"> 519 @if (OptionalExtra?.PriceInformative.Price > 0) 520 { 521 <div class="col-12 d-flex"> 522 RRP 523 <div class="leader-dots flex-fill"></div> 524 @String.Format(cultureInfo, "{0}{1:N2}", PriceSymbol, OptionalExtra?.PriceInformative.Price) 525 </div> 526 } 527 </div> 528 </div> 529 } 530 </div> 531 </div> 532 } 533 </div> 534 535 <div class="col-2 ps-2 pe-4 h-100"> 536 <div class="w-100 h-100 overflow-hidden"> 537 @if(Dimensions.Any()) 538 { 539 int SkipImages = 1; 540 int TakeImages = 2; 541 if (ClearancePrice > 0 || StockQty > 0) 542 { 543 SkipImages = 0; 544 TakeImages = 3; 545 } 546 547 <div class="row g-0"> 548 @foreach (var ItemImage in Dimensions.Skip(SkipImages).Take(TakeImages)) 549 { 550 string ItemImagePath = $"/Admin/Public/GetImage.ashx?image=/files/images/products/{ItemImage}.jpg&width=1200&format=webp"; 551 <div class="col-12 overflow-hidden text-center"> 552 <img src="@ItemImagePath" loading="eager" class="mb-3" style="max-width:100%;max-height:140px;" /> 553 </div> 554 } 555 </div> 556 } 557 </div> 558 </div> 559 </div> 560 561 </div> 562 </main> 563 564 @if (footerDesktopLink != null) 565 { 566 @RenderGrid((int)footerDesktopLink) 567 } 568 569 <link rel="stylesheet" href="/Files/Templates/Designs/Swift/Assets/css/lysboks.min.css" /> 570 <script src="/Files/Templates/Designs/Swift/Assets/js/lysboks.min.js"></script> 571 <link rel="stylesheet" href="/Files/Templates/Designs/Swift/Assets/css/glightbox.min.css" /> 572 <script src="/Files/Templates/Designs/Swift/Assets/js/glightbox.min.js"></script> 573 <script> 574 var lightbox = GLightbox({ 575 touchNavigation: true, 576 loop: false, 577 autoplayVideos: true, 578 videosWidth: '100vw', 579 plyr: { 580 config: { 581 quality: 720, 582 muted: false, 583 volume: 100, 584 youtube: { noCookie: true, rel: 0, showinfo: 0, iv_load_policy: 3, modestbranding: 1 } 585 } 586 } 587 }); 588 </script> 589 590 @* Render any offcanvas menu here *@ 591 @RenderSnippet("offcanvas") 592 593 @* Toast *@ 594 <div aria-live="polite" aria-atomic="true"> 595 <div class="toast-container position-fixed end-0 p-3" style="top:50px;z-index:1500;"> 596 <div id="notificationToast" class="toast rounded-6 overflow-hidden" role="alert" aria-live="assertive" aria-atomic="true" data-bs-delay="1000"> 597 <div class="toast-body d-flex align-items-end gap-3 theme theme-gray bg-dark text-light"> 598 @ReadFile("/Files/Templates/Designs/Swift/Assets/icons/check.svg") 599 <div id="notificationToast_Text" class=""></div> 600 </div> 601 </div> 602 </div> 603 </div> 604 605 </body> 606 607 </html> 608