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