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