Error executing template "Designs/Swift/Paragraph/Swift_ProductListGridView_EA.cshtml"
System.NullReferenceException: Objektreferencen er ikke indstillet til en forekomst af et objekt.
ved CompiledRazorTemplates.Dynamic.RazorEngine_a8b57e1b6cc545cb96d7494cdc76764d.Execute() i C:\inetpub\wwwroot\plus-prod\Files\Templates\Designs\Swift\Paragraph\Swift_ProductListGridView_EA.cshtml:linje 2013
ved RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
ved RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
ved RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
ved RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
ved Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
ved Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
ved Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel>
2 @using System
3 @using System.Collections.Generic
4 @using Dynamicweb.Ecommerce.ProductCatalog
5 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites
6 @using System.Linq
7 @using Dynamicweb.Core
8 @using Dynamicweb.Ecommerce
9 @using Dynamicweb.Environment
10 @using Plus.CustomModules.ExcelGenerator.Operations
11 @using Plus.CustomModules.Helpers
12
13 @functions
14 {
15 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsLazyLoadingForProductInfoEnabled;
16 string liveInfoClass = "";
17 string productInfoFeed = "";
18
19 string showPricesWithVat = "";
20 bool neverShowVat = false;
21
22 bool isDetailPage = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("ProductID"));
23
24 ProductListViewModel productList = new ProductListViewModel();
25 }
26
27 @{
28 if (Dynamicweb.Context.Current.Items.Contains("ProductList"))
29 {
30 productList = (ProductListViewModel)Dynamicweb.Context.Current.Items["ProductList"];
31 }
32
33 showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower();
34 neverShowVat = string.IsNullOrEmpty(showPricesWithVat);
35
36 if (isLazyLoadingForProductInfoEnabled)
37 {
38 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed"))
39 {
40 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString();
41 if (!string.IsNullOrEmpty(productInfoFeed))
42 {
43 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\"";
44 }
45 }
46 liveInfoClass = "js-live-info";
47 }
48
49 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
50 string themePadding = theme != string.Empty ? "p-3" : string.Empty;
51 var CartId = Dynamicweb.Ecommerce.Common.Context.Cart == null ? "" : Dynamicweb.Ecommerce.Common.Context.Cart.Id;
52 var CartValue = "";
53 var CartUrl = "";
54 var order = Dynamicweb.Ecommerce.Services.Orders.GetById(CartId);
55
56 }
57
58 @if (!isDetailPage)
59 {
60 var productCount = productList.Group != null ? Dynamicweb.Ecommerce.Services.ProductGroups?.GetGroup(productList.Group.Id).Products.Count : 1;
61
62 FacetsInfo.GetFacetsInfo(productList, out var facetsFound, out var selectedFacetsCount);
63
64 //var group = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(productList.Group.Id);
65 //var productCount = group.Products.Count;
66
67 if (!string.IsNullOrEmpty(theme))
68 {
69 if (productCount > 0 || selectedFacetsCount > 0)
70 {
71 <div class="h-100@(theme) @themePadding item_@Model.Item.SystemName.ToLower()" @productInfoFeed>
72 @{@RenderProductList()}
73 </div>
74 }
75
76 }
77 else
78 {
79 if (productCount > 0)
80 {
81 <div class="p-3 item_@Model.Item.SystemName.ToLower() @productCount" @productInfoFeed>
82 @{@RenderProductList()}
83 </div>
84 }
85
86 }
87 }
88
89 @helper RenderProductList()
90 {
91 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
92 bool anonymousUser = Pageview.User == null;
93 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable();
94
95 string productTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ProductTheme")) ? " theme " + Model.Item.GetRawValueString("ProductTheme").Replace(" ", "").Trim().ToLower() : "";
96 string productThemePadding = productTheme != string.Empty ? "p-3" : string.Empty;
97
98 string url = Dynamicweb.Context.Current.Request.RawUrl;
99 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false;
100 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Model.Item.GetString("ShowFavoritesSelectorMasterProduct")) ? Model.Item.GetBoolean("ShowFavoritesSelectorMasterProduct") : false;
101 string staticVariantsLayout = Model.Item.GetRawValueString("StaticVariantsLayout", "hide");
102
103 string groupId = productList?.Group?.Id != null ? productList.Group.Id : "";
104
105 var badgeParms = new Dictionary<string, object>();
106 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType"));
107 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign"));
108 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign"));
109 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays"));
110 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges"));
111
112 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false;
113 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false;
114
115 string googleAnalyticsTrackingID = Pageview.AreaSettings.GetString("GoogleAnalyticsTrackingID");
116 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID");
117 var cookieOptInLevel = CookieManager.GetCookieOptInLevel();
118 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical"));
119 var bagdeItems = Model.Item?.GetItems("Bagdes") ?? Enumerable.Empty<Dynamicweb.Frontend.ItemViewModel>().ToList();
120 var mobileGrid = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GridLayoutMobile", "grid-1")) ? Model.Item.GetRawValueString("GridLayoutMobile", "grid-1") : "grid-1";
121
122 var favoriteParameters = new Dictionary<string, object>();
123 if (!anonymousUser && !hideFavoritesSelector)
124 {
125 int defaultFavoriteListId = 0;
126
127 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists();
128 if (favoreiteLists.Count() == 1)
129 {
130 foreach (FavoriteList list in favoreiteLists)
131 {
132 defaultFavoriteListId = list.ListId;
133 }
134 }
135
136 favoriteParameters.Add("ListId", defaultFavoriteListId);
137 }
138
139 if (productList.TotalProductsCount > 0)
140 {
141 if (Pageview.IsVisualEditorMode)
142 {
143 <div class="alert alert-dark m-0" role="alert">
144 <span>@Translate("Product list: The list will be shown here, if any")</span>
145 </div>
146 }
147 else
148 {
149
150 int pageSizeSetting = 30;
151 int pageSize = productList.PageSize;
152 pageSize += pageSizeSetting;
153
154 int loadedProducts = productList.PageSize > productList.TotalProductsCount ? productList.TotalProductsCount : productList.PageSize;
155
156 //indsæt check på Grid colums felt på mobile når itemtype er opdateret.
157
158 <div class="grid @mobileGrid grid-md-2 grid-lg-3 grid-xl-4">
159
160 @foreach (ProductViewModel product in productList.Products)
161 {
162 string variantIdForLink = !string.IsNullOrEmpty(product.VariantId) ? $"&VariantID={product.VariantId}" : "";
163 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(product.DefaultVariantId) ? $"&VariantID={product.DefaultVariantId}" : variantIdForLink;
164
165 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop");
166 link += $"&GroupID={product.PrimaryOrDefaultGroup.Id}";
167 link += $"&ProductID={product.Id}";
168 link += variantIdForLink;
169 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link);
170
171 string imagePath = product?.DefaultImage?.Value ?? "";
172 imagePath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath);
173
174 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", "");
175 ratio = ratio != "0" ? ratio : "";
176 string ratioCssClass = ratio != "" ? " ratio" : "";
177 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
178
179 string imagePathXs = "/Admin/Public/GetImage.ashx?width=" + 480 + "&image=" + imagePath + "&format=webp";
180 string imagePathS = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp";
181 string imagePathFallBack = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp";
182
183 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : "";
184 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
185 string imageOutlineStyle = imageTheme == string.Empty ? "style=\"border: 1px solid transparent\"" : string.Empty;
186
187 string imageId = "ProductImage_" + product.Id + product.VariantId;
188 string priceId = "ProductPrice_" + product.Id + product.VariantId;@* Alternative image *@ var supportedImageFormats = new string[] { ".jpg", ".webp", ".png", ".gif" };
189 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : "";
190 var selectedAssetCategories = Model.Item.GetRawValueString("AlternativeImageAssets");
191 IEnumerable<MediaViewModel> alternativeImagesList = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets);
192
193 if (alternativeImagesList.FirstOrDefault() != null)
194 {
195 alternativeImagesList = alternativeImagesList.OrderByDescending(x => x.Value.Equals(defaultImage));
196
197 if (alternativeImagesList.First().Value == defaultImage)
198 {
199 alternativeImagesList = alternativeImagesList.Skip(1);
200 }
201 }
202
203 string alternativeImage = alternativeImagesList.FirstOrDefault() != null ? alternativeImagesList.FirstOrDefault().Value : "";
204 alternativeImage = !string.IsNullOrEmpty(alternativeImage) ? "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + alternativeImage + "&format=webp" : ""; @* Badges *@ DateTime createdDate = product.Created.Value;
205 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false;
206 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges;
207 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges; @* Main features *@ IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList();
208 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>();
209
210 foreach (var selection in selectedDisplayGroups)
211 {
212 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values)
213 {
214 if (selection == group.Id)
215 {
216 mainFeatures.Add(group);
217 }
218 }
219 }
220
221 FieldValueViewModel pageContent;
222 product.ProductFields.TryGetValue("EFA_PageContentID", out pageContent);
223 var pageContentID = !string.IsNullOrEmpty(pageContent.Value.ToString()) ? Convert.ToInt32(pageContent.Value) : 0;
224
225 if (pageContentID != 0)
226 {
227 var paragraphs = Dynamicweb.Content.Services.Paragraphs.GetParagraphsByPageId(pageContentID);
228
229 if (paragraphs.Count() <= 1)
230 {
231 <article class="position-relative@(productTheme) product-list-item js-product @liveInfoClass" data-product-id="@product.Id">
232 @{
233 foreach (var paragraph in paragraphs)
234 {
235 <div class="d-block h-100 align-items-stretch p-3">
236 @RenderParagraphContent(paragraph.ID)
237 </div>
238 };
239 }
240 </article>
241 }
242 }
243 else
244 {
245 <article class="position-relative@(productTheme) product-list-item js-product @liveInfoClass" data-product-id="@product.Id" itemscope itemtype="https://schema.org/Product">
246 @if (!anonymousUser)
247 {
248 if (!hideFavoritesSelector && product.VariantInfo.VariantInfo == null)
249 {
250 <div class="position-absolute top-0 end-0 my-3" style="z-index: 2">
251 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters)
252 </div>
253 }
254 else if (showFavoritesSelectorMasterProduct)
255 {
256 <div class="position-absolute top-0 end-0 my-3" style="z-index: 2">
257 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters)
258 </div>
259 }
260 }
261
262 @if (showBadges)
263 {
264 <div class="position-absolute top-0 left-0 p-1 p-lg-2 ps-0 ps-lg-0" style="z-index: 2">
265 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)
266 </div>
267 }
268
269 <div class="d-flex flex-column d-block h-100 align-items-stretch">
270 @{
271 string clickProductLink = string.Empty;
272 if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking)
273 {
274 clickProductLink = "onclick=\"return clickProductLink('" + product.Id + "', '" + product.Name + "', '" + product.VariantName + "', '" + product.Price.CurrencyCode + "', '" + product.Price.Price + "')\"";
275 }
276 }
277 <a href="@link" class="text-decoration-none d-flex flex-column j h-100" @clickProductLink>
278 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking)
279 {
280 <script>
281 function clickProductLink(productId, productName, productVariant, productCurrency, productPrice) {
282 if (typeof gtag !== "undefined") {
283 gtag("event", "select_item", {
284 item_list_id: "product_list_gridview",
285 item_list_name: "Product list (Gridview)",
286 items: [
287 {
288 item_id: productId,
289 item_name: productName,
290 currency: productCurrency,
291 item_list_id: "product_list_gridview",
292 item_list_name: "Product list (Gridview)",
293 item_variant: productVariant,
294 price: productPrice
295 }
296 ]
297 });
298 }
299 }
300 </script>
301 }
302
303 <div class="px-3 order-2">
304 <div class="flex-grow-1">
305 <h3 class="h6 mb-0 text-break" itemprop="name">
306 @product.Name
307 @if (!string.IsNullOrEmpty(product.VariantName))
308 {<text>(@product.VariantName)</text>}
309 </h3>
310 @if (!Model.Item.GetBoolean("HideProductNumber"))
311 {
312 <p class="fs-7 opacity-85 mb-2">@product.Number</p>
313 }
314 @if (mainFeatures.Count > 0)
315 {
316 <ul class="p-0 lh-sm opacity-75" style="list-style-position: inside">
317 @foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures)
318 {
319 foreach (var fieldViewModel in mainFeatureGroup.Fields)
320 {
321 var field = fieldViewModel.Value;
322 string fieldValue = field?.Value is object ? field.Value.ToString() : "";
323
324 if (fieldValue != "")
325 {
326 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue;
327 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue;
328
329 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
330 {
331 fieldValue = "";
332
333 foreach (FieldOptionValueViewModel option in field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>)
334 {
335 fieldValue = option.Name;
336 }
337 }
338
339 bool isColor = false;
340 if (fieldValue.Contains("#") && (Translate(field.Name) == Translate("Color") || Translate(field.Name) == Translate("Colour")))
341 {
342 isColor = true;
343 }
344
345 if (!string.IsNullOrEmpty(fieldValue))
346 {
347 if (!isColor)
348 {
349 <li>@(field.Name): @fieldValue</li>
350 }
351 else
352 {
353 <li class="position-relative">
354 <span class="colorbox-sm" style="background-color: @fieldValue"></span>
355 </li>
356 }
357 }
358 }
359 }
360 }
361 </ul>
362
363 }
364 </div>
365
366 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "swatches")
367 {
368 var optionCount = product.VariantInfo.VariantInfo.Count();
369 var showMaxVariants = 5;
370
371 <div class="d-flex flex-row gap-1 align-items-center">
372 @foreach (VariantInfoViewModel variant in product.VariantInfo.VariantInfo.Take(showMaxVariants))
373 {
374 <span class="colorbox colorbox-sm rounded-circle border me-1" style="background-color: @variant.OptionColor"></span>
375
376 }
377 @if (optionCount > showMaxVariants)
378 {
379 int left = optionCount - showMaxVariants;
380 <span class="ms-2">+@left</span>
381
382 }
383 </div>
384 }
385 </div>
386
387 <div class="overflow-hidden order-1 @(imageTheme) pb-2">
388 <div class="d-flex justify-content-center align-items-center">
389 @{
390 FieldValueViewModel plusDesignerIndikator;
391 product.ProductFields.TryGetValue("PlusDesignerLinkEA", out plusDesignerIndikator);
392 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "target=\"_blank\"" : string.Empty;
393 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "rel=\"noopener\"" : string.Empty;
394
395 if (!String.IsNullOrEmpty(plusDesignerIndikator.Value.ToString()))
396 {
397 <div class="plus-designer__container" style="position: absolute; left: 0; margin-bottom: 55%; background: #fff; border: 1px solid #228B64; border-left: none; z-index: 100; width: 75%; max-width: 200px; ">
398 <object>
399 <a href="@plusDesignerIndikator.Value" @target @rel class="text-decoration-none">
400 <div class="plus-designer__inner p-1 ps-3">
401 <p class="m-0 opacity-75 fs-7">Try me in</p>
402 <div class="plus-designer__image-container d-flex w-100">
403 <img class="" src="/Files/Templates/Designs/Swift/Assets/Images/plusdesignerlogo.svg" alt="Alternate Text" style="width:90%" />
404 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right" style="width:10%"><polyline points="9 18 15 12 9 6"></polyline></svg>
405 </div>
406 </div>
407 </a>
408 </object>
409 </div>
410
411 }
412 FieldValueViewModel plusBadge;
413 product.ProductFields.TryGetValue("Art", out plusBadge);
414 FieldValueViewModel fscBadge;
415 product.ProductFields.TryGetValue("FSC", out fscBadge);
416 FieldValueViewModel extraBadgeListObject;
417 product.ProductFields.TryGetValue("ExtraPlusBadges", out extraBadgeListObject);
418 FieldValueViewModel fscBadgeLink;
419 product.ProductFields.TryGetValue("FSC_link", out fscBadgeLink);
420 FieldValueViewModel pefcBadgeLink;
421 product.ProductFields.TryGetValue("PEFC_link", out pefcBadgeLink);
422 <div></div>
423 if (!String.IsNullOrEmpty(plusBadge.Value.ToString()) || !String.IsNullOrEmpty(fscBadge.Value.ToString()) || extraBadgeListObject.Value != null)
424 {
425 foreach (var badge in bagdeItems)
426 {
427 if (fscBadge.Value.ToString().Contains(badge.GetString("Title")))
428 {
429
430 <div class="plus-badge d-flex justify-content-center w-100 fsc-badge-list efa-mod ">
431 <object class="w-100">
432 <div class="d-flex justify-content-start w-75 h-75 efa-mod m-auto">
433 @if (fscBadge.Value.ToString().Contains("FSC"))
434 {
435 <a href="@fscBadgeLink.Value.ToString()" target="_blank" class="w-25"><img class="plus-badge_img fsc-badge_img w-100 efa-mod" srcset="@badge.GetString("Image")" src="" alt="@fscBadge.Value.ToString()" /></a>
436 }
437 @if (fscBadge.Value.ToString().Contains("PEFC"))
438 {
439 <a href="@pefcBadgeLink.Value.ToString()" target="_blank" class="w-25"><img class="plus-badge_img fsc-badge_img w-100 efa-mod" srcset="@badge.GetString("Image")" src="" alt="@fscBadge.Value.ToString()" /></a>
440 }
441 </div>
442 </object>
443 </div>
444 }
445 var badgeTitle = badge.GetString("Title");
446
447 switch (product.LanguageId)
448 {
449 case "LANG3":
450 {
451 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && badgeTitle.EndsWith("_DE"))
452 {
453 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString())
454 }
455 break;
456 }
457 case "LANG4":
458 {
459 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && badgeTitle.EndsWith("_SE"))
460 {
461 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString())
462 }
463 break;
464 }
465 case "LANG5":
466 {
467 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && badgeTitle.EndsWith("_NO"))
468 {
469 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString())
470 }
471 break;
472 }
473 default:
474 {
475 if (badgeTitle.StartsWith(plusBadge.Value.ToString()) && !badgeTitle.EndsWith("_NO") && !badgeTitle.EndsWith("_SE") && !badgeTitle.EndsWith("_DE"))
476 {
477 @RenderBadge(badge.GetString("Image"), plusBadge.Value.ToString())
478 }
479 break;
480 }
481 }
482
483 if (extraBadgeListObject.Value is List<FieldOptionValueViewModel>)
484 {
485 var extraBadgeList = (List<FieldOptionValueViewModel>)extraBadgeListObject.Value;
486 foreach (var extraBadge in extraBadgeList)
487 {
488 if (badge.GetString("Title") == extraBadge.Value.ToString())
489 {
490 <div class="plus-badge d-flex justify-content-center w-100 efa-mod">
491 <div class="d-flex justify-content-start w-75 h-75">
492 <img class="extra-plus-badge_img efa-mod" srcset="@System.Web.HttpUtility.UrlPathEncode(badge.GetString("Image"))" src="" alt="@extraBadge.Value.ToString()" />
493 </div>
494 </div>
495 }
496 }
497 }
498 }
499 }
500
501 }
502 @if (string.IsNullOrEmpty(alternativeImage))
503 {
504 <img id="@imageId" itemprop="image"
505 srcset="
506 @imagePathXs 480w,
507 @imagePathS 640w"
508 sizes="(min-width: 992px) 33vw, 50vw"
509 src="@imagePathFallBack"
510 loading="lazy"
511 decoding="async"
512 class="w-75 mw-100 mh-100 @imageThemePadding"
513 alt="@product.Name">
514 }
515 else
516 {
517 <img id="@imageId" itemprop="image"
518 src="@imagePathFallBack"
519 loading="lazy"
520 decoding="async"
521 class="w-75 mw-100 mh-100 @imageThemePadding"
522 alt="@product.Name"
523 onmouseover="this.src='@alternativeImage'"
524 onmouseout="this.src='@imagePathFallBack'">
525
526 }
527 </div>
528
529 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "images")
530 {
531 int variantGroupCount = 0;
532 int showMaxVariantGroups = 2;
533 int showMaxVariants = 3;
534 var productVariantTheme = productTheme != "" ? productTheme : "bg-white";
535
536 <div class="position-relative">
537 <div id="StaticVariants_@product.Id" class="static-variants w-100 d-none d-lg-block position-absolute left-0 bottom-0 @productTheme" style="pointer-events: none;">
538
539 @foreach (var variantGroup in product.VariantGroups())
540 {
541 int variantsCount = 0;
542
543 <div class="d-flex gap-2 mb-2">
544 @foreach (var variant in variantGroup.Options)
545 {
546 if (variantGroupCount < showMaxVariantGroups)
547 {
548 var optionsCount = variantGroup.Options.Count();
549
550 if (variantsCount < showMaxVariants)
551 {
552 string optionWidth = !string.IsNullOrEmpty(variant.Color) ? "w-25" : "";
553
554 <article class="static-variants-option @optionWidth @(productVariantTheme)" title="@product.Name @variant.Name" style="pointer-events: initial;">
555 @if (!string.IsNullOrEmpty(variant.Color))
556 {
557 string defaultProductImage = Dynamicweb.Context.Current.Server.UrlEncode(product.DefaultImage.Value);
558 string variantImage = Dynamicweb.Context.Current.Server.UrlEncode(variant.Image.Value);
559 string defaultPrice = !hidePrice ? product.Price.PriceFormatted : "0";
560 string variantPrice = !hidePrice ? product.Price.PriceFormatted : "0";
561
562 if (isLazyLoadingForProductInfoEnabled)
563 {
564 <figure class="w-100 d-block m-0" data-price-formatted="" onmouseover="swift.StaticVariants.SwitchProduct(event, '@product.Id', this.getAttribute('data-price-formatted'), '@variantImage')" onmouseout="swift.StaticVariants.SwitchProduct(event, '@product.Id', this.getAttribute('data-price-formatted'), '@defaultProductImage')">
565 <div class="d-flex align-items-center justify-content-center">
566 <img src="/admin/public/GetImage.ashx?image=@variantImage&width=75&height=75&crop=5&FillCanvas=true&format=webp&Quality=70" height="75" width="75" class="p-1 text-small" loading="lazy" decoding="async" alt="@product.Name, @variant.Name">
567 </div>
568 </figure>
569 }
570 else
571 {
572 <figure class="w-100 d-block m-0" onmouseover="swift.StaticVariants.SwitchProduct(event, '@product.Id', '@defaultPrice', '@variantImage')" onmouseout="swift.StaticVariants.SwitchProduct(event, '@product.Id', '@variantPrice', '@defaultProductImage')">
573 <div class="d-flex align-items-center justify-content-center">
574 <img src="/admin/public/GetImage.ashx?image=@variantImage&width=75&height=75&crop=5&FillCanvas=true&format=webp&Quality=70" height="75" width="75" class="p-1 text-small" loading="lazy" decoding="async" alt="@product.Name, @variant.Name">
575 </div>
576 </figure>
577 }
578 }
579 else
580 {
581 <div class="d-flex align-items-center justify-content-center">
582 @variant.Name
583 </div>
584 }
585 <div class="visually-hidden">
586 <h4>@product.Name, @variant.Name</h4>
587 @if (!hidePrice)
588 {
589 if (isLazyLoadingForProductInfoEnabled)
590 {
591 <span class="text-price js-text-price"></span>
592 }
593 else
594 {
595 <span class="text-price">@product.Price.PriceFormatted</span>
596
597 }
598 }
599 </div>
600 </article>
601 }
602
603 variantsCount++;
604
605 if (variantsCount == showMaxVariants && optionsCount != showMaxVariants)
606 {
607 int left = optionsCount - showMaxVariants;
608 <div class="variant-option ms-1 d-flex justify-content-center align-items-center">
609 <span>+@left</span>
610 </div>
611 }
612 }
613 }
614 </div>
615 variantGroupCount++;
616 }
617 </div>
618 </div>
619 }
620 </div>
621 </a>
622 @if (!hidePrice)
623 {
624 string priceMin = "";
625 string priceMax = "";
626 <div itemprop="offers" itemscope itemtype="https://schema.org/Offer">
627 <div class="px-3">
628 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span>
629
630 @if (showPricesWithVat == "false" && !neverShowVat)
631 {
632 if (isLazyLoadingForProductInfoEnabled)
633 {
634 <span itemprop="price" content="" class="d-none"></span>
635 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span> }
636 else
637 {
638 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted;
639 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span>
640 if (product.Price.Price != product.PriceBeforeDiscount.Price)
641 {
642 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span> }
643 }
644
645 }
646 else
647 {
648
649 if (isLazyLoadingForProductInfoEnabled)
650 {
651 <span itemprop="price" content="" class="d-none"></span>
652 <span class="text-decoration-line-through js-text-decoration-line-through opacity-75 me-3 text-price js-text-price d-none" data-show-if="LiveProductInfo.product.Price.Price != LiveProductInfo.product.PriceBeforeDiscount.Price"></span>
653 }
654 else
655 {
656 string beforePrice = product.PriceBeforeDiscount.PriceFormatted;
657 string googlepriceraw = product.Price.PriceWithVat.ToString();
658 string googlepriceformatted = googlepriceraw.Replace(',', '.');
659
660 <span itemprop="price" content="@googlepriceformatted" class="d-none"></span>
661 if (product.Price.Price != product.PriceBeforeDiscount.Price)
662 {
663 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span>
664
665 }
666 }
667 }
668
669 @if (showPricesWithVat == "false" && !neverShowVat)
670 {
671 if (isLazyLoadingForProductInfoEnabled)
672 {
673 <span class="text-price js-text-price"><span class="spinner-border" role="status"></span></span> }
674 else
675 {
676 string price = product.Price.PriceWithoutVatFormatted;
677 if (product?.VariantInfo?.VariantInfo != null)
678 {
679 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : "";
680 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : "";
681 }
682 if (priceMin != priceMax)
683 {
684 price = priceMin + " - " + priceMax;
685 }
686 <span class="text-price">@price</span>
687 }
688 }
689 else
690 {
691 if (isLazyLoadingForProductInfoEnabled)
692 {
693 <span class="text-price js-text-price"><span class="spinner-border" role="status"></span></span>
694 }
695 else
696 {
697 string price = product.Price.PriceFormatted;
698 var priceStandardFieldValueConverted = CampaignPrice.GetConvertedPriceWithVat(product);
699 bool campaignPrice = CampaignPrice.CheckCampaign(product, priceStandardFieldValueConverted);
700 var priceBefore = CampaignPrice.PriceBeforeWithVat(product, priceStandardFieldValueConverted);
701
702 if (product?.VariantInfo?.VariantInfo != null)
703 {
704 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : "";
705 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : "";
706 }
707 if (priceMin != priceMax)
708 {
709 price = priceMin + " - " + priceMax;
710 }
711
712 <div class="d-flex flex-wrap">
713 @if (campaignPrice)
714 {
715 <span class="text-decoration-line-through opacity-75 me-3 text-price w-100">@priceBefore</span>
716 }
717 <span class="text-price fw-bold w-100">@price</span>
718 </div>
719
720 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : "";
721 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0;
722 var productToShow = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId);
723 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code;
724 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : "";
725 product.ProductFields.TryGetValue("ProductNextBackorderDate", out FieldValueViewModel backInStockDateValue);
726 DateTime backInstockDate = backInStockDateValue.Value != null ? DateTime.Parse(backInStockDateValue.Value.ToString()) : DateTime.Today.AddDays(-1);
727 bool hasBackInstockDate = backInStockDateValue != null && backInstockDate >= DateTime.Today;
728 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery >= DateTime.Today;
729 string expectedDeliveryDate = hasBackInstockDate ? backInstockDate.ToShortDateString() : product.ExpectedDelivery?.ToShortDateString() ?? "";
730
731 <div class="fs-8 mt-1 mb-2 text-start">
732 @if (!string.IsNullOrWhiteSpace(product.StockStatus))
733 {
734 <p class="mb-1">@product.StockStatus</p>
735 if (!string.IsNullOrWhiteSpace(product.StockDeliveryText))
736 {
737 <p class="mb-1">@product.StockDeliveryText</p>
738 }
739 }
740 @if (hasExpectedDelivery || hasBackInstockDate)
741 {
742 <div class="mb-1">
743 <span>@Translate("Expected in stock"): </span>
744 <span>@expectedDeliveryDate</span>
745 </div>
746 }
747 </div>
748
749 @*if (!string.IsNullOrWhiteSpace(product.StockStatus)) {
750 <div class="">@product.StockStatus</div>
751 <div class="">@product.StockDeliveryText</div>
752 {
753 <div>
754 <span>@Translate("Expected in stock"): </span>
755 <span>@expectedDeliveryDate</span>
756 </div>
757 }
758 } else {
759
760 if (hasExpectedDelivery || hasBackInstockDate)
761 {
762 <div>
763 <span>@Translate("Expected in stock"): </span>
764 <span>@expectedDeliveryDate</span>
765 </div>
766 }
767 }*@
768
769 @*if (!string.IsNullOrEmpty(ecomCountryCode) && !hidePrice && product.Id != null && shipId != null)
770 {
771 var shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency);
772
773
774
775 if (shippingFeeAmount != null)
776 {
777 <div class="fs-8 text-black-50 text-start">
778 <p>@Translate("Levering fra") @currency @shippingFeeAmount</p>
779 </div>
780 }
781 }*@
782 }
783 }
784
785 @if (showPricesWithVat == "false" && !neverShowVat)
786 {
787 if (isLazyLoadingForProductInfoEnabled)
788 {
789 <div class="fs-7 opacity-85 text-price js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></div>
790 }
791 else
792 {
793 string price = product.Price.PriceWithVatFormatted;
794 if (product?.VariantInfo?.VariantInfo != null)
795 {
796 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : "";
797 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : "";
798 }
799 if (priceMin != priceMax)
800 {
801 price = priceMin + " - " + priceMax;
802 }
803 <div class="fs-7 opacity-85 text-price">@price @Translate("Incl. VAT")</div>
804
805 }
806 }
807 </div>
808 </div>
809 }
810 @RenderAddToCart(product, link, clickProductLink)
811 </div>
812 </article>
813 }
814
815 }
816 </div>
817
818 <div class="my-3" id="LoadMoreButton">
819 <div class="text-center d-flex flex-column gap-3">
820 <div class="opacity-85">@loadedProducts @Translate("out of") @productList.TotalProductsCount @Translate("products")</div>
821 @if (productList.PageCount != 1)
822 {
823 string sortBySelection = Dynamicweb.Context.Current.Request?.Form["SortBy"] ?? "";
824 sortBySelection = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SortBy")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SortBy") : sortBySelection;
825
826 string searchQuery = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("q")) ? Dynamicweb.Context.Current.Request.QueryString.Get("q") : "";
827 string searchLayout = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout") : "";
828
829 <form method="get" action="@url" data-response-target-element="content" class="w-100">
830 @foreach (FacetGroupViewModel facetGroup in productList.FacetGroups)
831 {
832 foreach (FacetViewModel facetItem in facetGroup.Facets)
833 {
834 foreach (FacetOptionViewModel facetOption in facetItem.Options)
835 {
836 if (facetOption.Selected)
837 {
838 <input type="hidden" name="@facetItem.QueryParameter" value="[@facetOption.Value]">
839 }
840 }
841 }
842 }
843
844 <input type="hidden" name="PageSize" value="@pageSize">
845 <input type="hidden" name="SortBy" value="@sortBySelection">
846 <input type="hidden" name="RequestType" value="UpdateList">
847
848 @if (!string.IsNullOrEmpty(searchQuery))
849 {
850 <input type="hidden" name="q" value="@searchQuery">
851 <input type="hidden" name="SearchLayout" value="@searchLayout">
852 }
853
854 @{
855 string nextPageLink = "/Default.aspx?ID=" + Pageview.Page.ID + "&PageSize=" + pageSize + "&SortBy=" + sortBySelection;
856
857 foreach (FacetGroupViewModel facetGroup in productList.FacetGroups)
858 {
859 foreach (FacetViewModel facetItem in facetGroup.Facets)
860 {
861 foreach (FacetOptionViewModel facetOption in facetItem.Options)
862 {
863 if (facetOption.Selected)
864 {
865 nextPageLink += "&" + facetItem.QueryParameter + "=[" + facetOption.Value + "]";
866 }
867 }
868 }
869 }
870
871 nextPageLink += !string.IsNullOrEmpty(searchQuery) ? "&q=" + searchQuery : "";
872 }
873
874 <a href="@nextPageLink" class="btn btn-primary" onclick="swift.ProductList.Update(event)" id="LoadMoreButton_@Model.ID">@Translate("Load more products")</a>
875 </form>
876
877 }
878 </div>
879 </div>
880 }
881 }
882 else
883 {
884 <div class="alert alert-dark m-0">
885 @Translate("We did not find anything matching your search result")
886 </div>
887 }
888 }
889
890 @helper RenderBadge(string badge, string plusBadge)
891 {
892 <div class="plus-badge d-flex justify-content-center w-100 efa-mod">
893 <div class="d-flex justify-content-end w-75 h-75">
894 <img class="plus-badge_img efa-mod" srcset="@badge" src="" alt="@plusBadge" />
895 </div>
896 </div>
897 }
898
899
900 @helper RenderViewMore(string link, string clickProductLink)
901 {
902 <a href="@link" class="btn btn-secondary flex-fill" @clickProductLink>@Translate("View more")</a>
903 }
904
905 @helper RenderAddToCart(ProductViewModel product, string link, string clickProductLink)
906 {
907 string iconPath = "/Files/icons/";
908 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
909 if (!url.Contains("LayoutTemplate"))
910 {
911 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml";
912 }
913
914 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
915 bool anonymousUser = Pageview.User == null;
916
917 bool hideAddToCart = !string.IsNullOrEmpty(Model.Item.GetString("HideAddToCart")) ? Model.Item.GetBoolean("HideAddToCart") : false;
918 hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable() ? true : hideAddToCart;
919 bool quantitySelector = !string.IsNullOrEmpty(Model.Item.GetString("QuantitySelector")) ? Model.Item.GetBoolean("QuantitySelector") : false;
920
921 bool isDiscontinued = product.Discontinued;
922 bool IsNeverOutOfStock = product.NeverOutOfstock;
923 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : "";
924 disableAddToCart = isDiscontinued ? "disabled" : disableAddToCart;
925 disableAddToCart = IsNeverOutOfStock ? "" : disableAddToCart;
926 disableAddToCart = isLazyLoadingForProductInfoEnabled ? "disabled" : disableAddToCart;
927
928 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : "";
929 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0;
930 var productToShow = Services.Products.GetProductById(product.Id, product.VariantId, product.LanguageId);
931 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code;
932 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : "";
933 var shippingFeeAmount = string.Empty;
934
935 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null)
936 {
937 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency);
938 }
939
940 var addToCartLabel = Model.Item.GetRawValueString("GridLayoutMobile", "grid-1") == "grid-2" ? "" : Translate("Add to cart");
941 string hideOnMobile = Model.Item.GetRawValueString("GridLayoutMobile") == "grid-2" ? "hide-on-mobile" : "";
942 string viewMoreIcon = Model.Item.GetRawValueString("ViewMoreIcon");
943
944 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount;
945 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, product.LanguageId);
946
947 if (!hideAddToCart)
948 {
949 if (product.VariantInfo.VariantInfo == null)
950 {
951 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\"";
952 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1";
953 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty;
954 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : "";
955 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
956 string imagePathHidden = product?.DefaultImage.Value.ToString() ?? "";
957 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70";
958 <div class="px-3 pb-3">
959 <form method="post" action="@url">
960 <input type="hidden" name="redirect" value="false">
961 <input type="hidden" name="MainProductId" value="None">
962 <input type="hidden" name="ProductId" value="@product.Id">
963 <input type="hidden" name="ProductName" value="@product.Name">
964 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code">
965 <input type="hidden" name="ProductReferer" value="product_list_listview">
966 <input type="hidden" name="ProductPrice" value="@product.Price.PriceFormatted">
967 <input type="hidden" name="cartcmd" value="add">
968 @* #38 EA: sbj *@
969 <input type="hidden" name="ProductPriceFormatted" value="@product.Price.PriceFormatted">
970 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")">
971 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")">
972 <input type="hidden" name="ProductImage" value="@imagePathHidden">
973 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost">
974 <input type="hidden" name="ProductDeliveryName" value="@deliveryName">
975 <input type="hidden" name="cartpage" value="@cartUrl">
976
977 @if (!string.IsNullOrEmpty(product.VariantId))
978 {
979 <input type="hidden" name="VariantId" value="@product.VariantId">}
980
981 @if (quantitySelector)
982 {
983 <div class="input-group input-primary-button-group">
984 <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control" style="max-width: 100px" type="number" onkeydown="swift.Cart.UpdateOnEnterKey(event)" @disableAddToCart>
985 <div class="d-flex gap-2">
986 @RenderViewMore(link, clickProductLink)
987 @*<a href="@link" class="btn btn-secondary flex-fill" @clickProductLink>@Translate("View more")</a>*@
988 @if (disableAddToCart == "disabled")
989 {
990 <button name="notify-cookie" type="button" data-href="@link" onclick="addCookie('ProductNotifier',true,1,'@link');" class="btn btn-primary flex-fill js-add-to-cart-button" title="@Translate("Add to cart")" id="AddCookie@(product.Id)">
991 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "envelope.svg") @Translate("Produkt notifikations label")</span>
992 </button>
993 }
994 else
995 {
996 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)">
997 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "shopping-cart.svg") @Translate("Add to cart")</span>
998 </button>
999 }
1000 </div>
1001 </div>
1002 if (stepQty != "1")
1003 {
1004 <div class="invalid-feedback d-none">
1005 @Translate("Please select a quantity that is dividable by") @stepQty
1006 </div>
1007
1008 }
1009 <label for="Quantity_@(product.Id)_@product.VariantId" class="visually-hidden">@Translate("Quantity")</label>
1010 }
1011 else
1012 {
1013 <input id="Quantity_@(product.Id)_@product.VariantId" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart>
1014 <div class="d-flex gap-2">
1015 <a href="@link" class="btn btn-secondary flex-fill view-more-btn px-0" @clickProductLink><div class="@hideOnMobile">@ReadFile(viewMoreIcon)<span>@Translate("View more")</span></div></a>
1016 @if (disableAddToCart == "disabled")
1017 {
1018 <button name="notify-cookie" type="button" data-href="@link" class="btn btn-primary flex-fill js-add-to-cart-button" title="@Translate("Produkt notifikations label")" id="AddCookie@(product.Id)">
1019 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center @hideOnMobile">@ReadFile(iconPath + "envelope.svg") <span class="add-to-cart-label"> @Translate("Produkt notifikations label")</span></span>
1020 </button>
1021 }
1022 else
1023 {
1024 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button px-0" title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)">
1025 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center @hideOnMobile">@ReadFile(iconPath + "shopping-cart.svg") <span class="add-to-cart-label">@Translate("Add to cart")</span></span>
1026 @*<span class="icon-2 d-flex gap-2 align-items-center justify-content-center d-md-none">@ReadFile(iconPath + "shopping-cart.svg")</span>*@
1027 </button>
1028 }
1029 </div>
1030
1031 }
1032 </form>
1033 </div>
1034 }
1035 else
1036 {
1037 string buttonWidth = quantitySelector ? "calc(var(--swift-button-primary-padding-x) * 2 + 1rem + 7rem)" : "calc(var(--swift-button-primary-padding-x) * 2 + 1rem)"; @* Set the width of the container to: button-primary-padding-x × 2 + 1rem for the icon + 7rem for the quantity input *@ string buttonText = quantitySelector ? Translate("Select") : "<span class=\"icon-2\">" + @ReadFile(iconPath + "shopping-cart.svg") + "</span>";
1038
1039 string variantSelectorServicePageId = !string.IsNullOrEmpty(Model.Item.GetString("VariantSelectorServicePageId")) ? Model.Item.GetLink("VariantSelectorServicePageId").PageId.ToString() : "";
1040 variantSelectorServicePageId = variantSelectorServicePageId != "" ? variantSelectorServicePageId : GetPageIdByNavigationTag("VariantSelectorService").ToString();
1041
1042 string disableVariantSelector = isLazyLoadingForProductInfoEnabled ? "disabled" : "";
1043 <div class="px-3 pb-3">
1044 <form action="/Default.aspx?ID=@variantSelectorServicePageId" data-response-target-element="DynamicModalContent" data-preloader="inline" class="d-inline-block">
1045 <input type="hidden" name="ProductID" value="@product.Id">
1046 <input type="hidden" name="QuantitySelector" value="@quantitySelector.ToString()">
1047 <input type="hidden" name="HideInventory" value="@Model.Item.GetBoolean("HideInventory").ToString()">
1048 <input type="hidden" name="HideStockState" value="@Model.Item.GetBoolean("HideStockState").ToString()">
1049 <input type="hidden" name="VariantSelectorServicePage" value="@variantSelectorServicePageId">
1050 <input type="hidden" name="ViewType" value="ModalContent">
1051 <button type="button" onclick="swift.PageUpdater.Update(event)" class="btn btn-primary" style="width: @buttonWidth" @disableVariantSelector @disableVariantSelector title="@Translate("Select")" data-bs-toggle="modal" data-bs-target="#DynamicModal" id="OpenVariantSelectorModal@(product.Id)_@Pageview.CurrentParagraph.ID">@buttonText</button>
1052 </form>
1053 </div>
1054
1055 }
1056 }
1057 @RenderModal(product)
1058
1059 }
1060
1061 @helper RenderProduct(ProductInfoViewModel relatedProduct, string productId)
1062 {
1063 var relProduct = relatedProduct.GetProduct();
1064 var mainProductId = productId.IsNotNullOrEmpty() ? productId : "None";
1065 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
1066 bool anonymousUser = Pageview.User == null;
1067 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable();
1068 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) : false;
1069
1070 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : "";
1071 string ratioCssClass = ratio != "" ? "ratio" : "";
1072 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
1073
1074 string theme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("Theme")) ? Dynamicweb.Context.Current.Request.Form.Get("Theme") : "";
1075 string themePadding = theme != string.Empty ? "p-3" : string.Empty;
1076 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : "";
1077 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty;
1078 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
1079 string ContentPadding = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ContentPadding")) ? Dynamicweb.Context.Current.Request.Form.Get("ContentPadding") : "";
1080
1081 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower();
1082 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat);
1083
1084 string variantIdForLink = !string.IsNullOrEmpty(relProduct.VariantId) ? $"&VariantID={relProduct.VariantId}" : "";
1085 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(relProduct.DefaultVariantId) ? $"&VariantID={relProduct.DefaultVariantId}" : variantIdForLink;
1086
1087 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop");
1088 link += $"&GroupID={relProduct.PrimaryOrDefaultGroup.Id}";
1089 link += $"&ProductID={relProduct.Id}";
1090 link += variantIdForLink;
1091 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link);
1092
1093 string imagePath = relProduct?.DefaultImage.Value.ToString() ?? "";
1094 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70";
1095
1096 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : "";
1097 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : "";
1098 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : "";
1099 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0;
1100 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : "";
1101
1102 var badgeParms = new Dictionary<string, object>();
1103 badgeParms.Add("saleBadgeType", saleBadgeType);
1104 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName);
1105 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName);
1106 badgeParms.Add("campaignBadgesValues", campaignBadgesValues);
1107 badgeParms.Add("newPublicationDays", newPublicationDays);
1108
1109 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false;
1110 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false;
1111 DateTime createdDate = relProduct.Created.Value;
1112 bool showBadges = saleBadgeEnabled && relProduct.Discount.Price != 0 ? true : false;
1113 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges;
1114 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges;
1115
1116 string disableAddToCart = (relProduct.StockLevel <= 0) ? "disabled" : "";
1117 bool isNeverOutOfStock = relProduct.NeverOutOfstock;
1118 disableAddToCart = isNeverOutOfStock ? "" : disableAddToCart;
1119
1120 string iconPath = "/Files/icons/";
1121 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg");
1122 string addToCartLabel = !addToCartIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(addToCartIcon) + "</span>" : "";
1123 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : "";
1124 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? Translate("Add to cart") : "";
1125 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : "";
1126 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular");
1127 string inputSize = string.Empty;
1128
1129 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
1130 if (!url.Contains("LayoutTemplate"))
1131 {
1132 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml";
1133 }
1134 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
1135 string imagePathHidden = relProduct?.DefaultImage.Value.ToString() ?? "";
1136 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70";
1137
1138 switch (buttonSize)
1139 {
1140 case "small":
1141 inputSize = " input-group-sm";
1142 buttonSize = " btn-sm";
1143 break;
1144 case "regular":
1145 buttonSize = string.Empty;
1146 break;
1147 case "large":
1148 inputSize = " input-group-lg";
1149 buttonSize = " btn-lg";
1150 break;
1151 }
1152
1153 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : "";
1154 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0;
1155 var productToShow = Services.Products.GetProductById(relProduct.Id, relProduct.VariantId, relProduct.LanguageId);
1156 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code;
1157 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : "";
1158 var shippingFeeAmount = string.Empty;
1159
1160 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null)
1161 {
1162 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency);
1163 }
1164
1165 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount;
1166 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, relProduct.LanguageId);
1167
1168 <div class="text-decoration-none d-block h-100">
1169 <div class="h-100 d-flex flex-column justify-content-between@(theme)">
1170 @{
1171 FieldValueViewModel plusDesignerModalIndikator;
1172 relProduct.ProductFields.TryGetValue("PlusDesignerLinkEA", out plusDesignerModalIndikator);
1173 string target = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "target=\"_blank\"" : string.Empty;
1174 string rel = Pageview.AreaSettings.GetBoolean("OpenLinksInNewTab") ? "rel=\"noopener\"" : string.Empty;
1175
1176 if (!string.IsNullOrEmpty(plusDesignerModalIndikator.Value.ToString()))
1177 {
1178 <div class="plus-designer__container" style="position: absolute; left: 0; bottom: 10px; background: #fff; border: 1px solid #228B64; border-left: none; z-index: 100; width: 75%; max-width: 200px; ">
1179 <object>
1180 <a href="@plusDesignerModalIndikator.Value" @target @rel class="text-decoration-none">
1181 <div class="plus-designer__inner p-1 ps-3">
1182 <p class="m-0 opacity-75 fs-7">Try me in</p>
1183 <div class="plus-designer__image-container d-flex w-100">
1184 <img class="" src="/Files/Templates/Designs/Swift/Assets/Images/plusdesignerlogo.svg" alt="Alternate Text" style="width:90%" />
1185 <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right" style="width:10%"><polyline points="9 18 15 12 9 6"></polyline></svg>
1186 </div>
1187 </div>
1188 </a>
1189 </object>
1190 </div>
1191
1192 }
1193
1194 }
1195 <div class="@(imageTheme)" style="@imageOutlineStyle">
1196 <div class="@(ratioCssClass) position-relative m-auto mw200 efa-mod" style="@ratioVariable">
1197 @if (showBadges)
1198 {
1199 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2">
1200 @{
1201 @RenderPartial("Components/EcommerceBadge.cshtml", relProduct, badgeParms)
1202 }
1203 </div>
1204 }
1205 <img loading="lazy" decoding="async" src="@imagePath" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="@relProduct.Name">
1206 </div>
1207 </div>
1208 <div class="flex-fill p-3 pb-0 d-flex flex-column justify-content-between @themePadding">
1209 <h3 class="h6 opacity-85">@relProduct.Name @relProduct.VariantName</h3>
1210
1211 @if (!hidePrice)
1212 {
1213 <div>
1214 <p class="h6 m-0">
1215 @if (showPricesWithVat == "false" && !neverShowVat)
1216 {
1217 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price)
1218 {
1219 <span class="text-decoration-line-through opacity-75 me-1">
1220 @relProduct.PriceBeforeDiscount.PriceWithoutVatFormatted
1221 </span>
1222 }
1223 }
1224 else
1225 {
1226 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price)
1227 {
1228 <span class="text-decoration-line-through opacity-75 me-1">
1229 @relProduct.PriceBeforeDiscount.PriceFormatted
1230 </span>
1231 }
1232 }
1233
1234 @if (showPricesWithVat == "false" && !neverShowVat)
1235 {
1236 <span class="text-price fw-bold">@relProduct.Price.PriceWithoutVatFormatted</span>
1237 }
1238 else
1239 {
1240 <span class="text-price fw-bold">@relProduct.Price.PriceFormatted</span>
1241 }
1242 </p>
1243 @if (shippingFeeAmount != null)
1244 {
1245 <div class="fs-8 text-black-50 text-start">
1246 <p>@deliveryCost</p>
1247 </div>
1248 }
1249
1250
1251 @if (showPricesWithVat == "false" && !neverShowVat)
1252 {
1253 <small class="opacity-85 fst-normal">@relProduct.Price.PriceWithVatFormatted @Translate("Incl. VAT")</small>
1254 }
1255
1256 </div>
1257 }
1258 </div>
1259
1260 <div class="d-flex gap-4 p-3 pt-0">
1261 @{
1262 string stepQty = relProduct.PurchaseQuantityStep > 1 ? relProduct.PurchaseQuantityStep.ToString() : "1";
1263 string valueQty = relProduct.PurchaseMinimumQuantity > relProduct.PurchaseQuantityStep ? relProduct.PurchaseMinimumQuantity.ToString() : stepQty;
1264
1265 }
1266 <form method="post" action="@url">
1267 <input type="hidden" name="redirect" value="false">
1268 <input type="hidden" name="MainProductId" value="@mainProductId">
1269 <input type="hidden" name="ProductId" value="@relProduct.Id">
1270 <input type="hidden" name="ProductName" value="@relProduct.Name">
1271 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code">
1272 <input type="hidden" name="ProductReferer" value="product_list_listview">
1273 <input type="hidden" name="ProductPrice" value="@relProduct.Price.PriceFormatted">
1274 <input type="hidden" name="cartcmd" value="add">
1275 @* #38 EA: sbj *@
1276 <input type="hidden" name="ProductPriceFormatted" value="@relProduct.Price.PriceFormatted">
1277 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")">
1278 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")">
1279 <input type="hidden" name="ProductImage" value="@imagePathHidden">
1280 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost">
1281 <input type="hidden" name="ProductDeliveryName" value="@deliveryName">
1282 <input type="hidden" name="cartpage" value="@cartUrl">
1283
1284 <input id="Quantity_@(relProduct.Id)_@relProduct.VariantId" name="Quantity" value="@valueQty" type="hidden" @disableAddToCart>
1285 <div class="d-flex gap-3 p-3 pt-0">
1286 <a href="@link" class="btn btn-secondary flex-fill">@Translate("View more")</a>
1287 <a onclick="swift.Cart.Update(event)" class="btn btn-primary @(buttonSize) @flexFill flex-fill js-add-to-cart-button" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(relProduct.Id)_@Pageview.CurrentParagraph.ID">
1288 @if (!Model.Item.GetBoolean("HideButtonText"))
1289 {
1290 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2">
1291 @addToCartLabel
1292 </span>
1293 }
1294 else
1295 {
1296 @addToCartLabel
1297 }
1298 </a>
1299 </div>
1300 </form>
1301 </div>
1302
1303 </div>
1304 </div>
1305 @*@RenderModalRelatedProducts(relProduct)*@
1306 }
1307
1308 @helper RenderModal(ProductViewModel product)
1309 {
1310 <div id="cartNotificationModal_@(product.Id)" class="modal" tabindex="-1" aria-labelledby="cartNotificationModalTitel" aria-hidden="true">
1311 @{
1312 string modelId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ModelID")) ? Dynamicweb.Context.Current.Request.Form.Get("ModelID") : product.Id;
1313
1314 string scrollBarForceMobile = Dynamicweb.Context.Current.Request.Form.Get("NavigationShowScrollbar") != string.Empty ? "--swiffy-slider-track-height:0.5rem !important;" : string.Empty;
1315 bool hideSliderNavigation = false;
1316
1317 int itemsShown = 3;
1318
1319 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : "";
1320 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty;
1321 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
1322
1323 string imagePath = product?.DefaultImage.Value.ToString() ?? "";
1324 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70";
1325
1326 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : "";
1327 string ratioCssClass = ratio != "" ? "ratio" : "";
1328 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
1329
1330 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : "";
1331 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : "";
1332 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : "";
1333 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0;
1334 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : "";
1335
1336 var badgeParms = new Dictionary<string, object>();
1337 badgeParms.Add("saleBadgeType", saleBadgeType);
1338 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName);
1339 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName);
1340 badgeParms.Add("campaignBadgesValues", campaignBadgesValues);
1341 badgeParms.Add("newPublicationDays", newPublicationDays);
1342
1343 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false;
1344 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false;
1345 DateTime createdDate = product.Created.Value;
1346
1347 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false;
1348 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges;
1349 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges;
1350 var productCounter = 1;
1351
1352 var shopPageId = GetPageIdByNavigationTag("Shop");
1353
1354 bool relatedExist = false;
1355 string modalProductCon = "";
1356 string modalProductSize = "";
1357 foreach (var relatedGroup in product.RelatedGroups)
1358 {
1359 if (relatedGroup.Id == "Addon")
1360 {
1361 if (relatedGroup.Products.Count > 0)
1362 {
1363 relatedExist = true;
1364 modalProductCon = "col-xl-7";
1365 }
1366 }
1367 }
1368 if (relatedExist) {
1369 modalProductSize = "xl";
1370 } else {
1371 modalProductSize = "lg";
1372 }
1373
1374 }
1375 @*<script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script>
1376 <script type="module">
1377 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css');
1378 </script>*@
1379 <div class="modal-dialog theme plus-primary modal-@modalProductSize">
1380 <div class="modal-content rounded-0 container">
1381 <div class="row">
1382 <div class="col-12 @modalProductCon p-0 border-bottom border-lg-none">
1383 <div class="modal-header w-100 text-center">
1384 <h3 class="modal-title w-100" id="cartNotificationModalTitel">@Translate("Følgende er tilføjet til indkøbslisten", "Følgende er tilføjet til indkøbslisten")</h3>
1385 <button type="button" class="btn-close position-absolute m-0 end-0 me-3 d-block d-xl-none" data-bs-dismiss="modal" aria-label="Close"></button>
1386 </div>
1387 <div id="cartNotificationModalBody" class="modal-body">
1388 <div class="container-fluid">
1389 <div class="row">
1390 <div class="@(imageTheme) col-12" style="@imageOutlineStyle">
1391 <div class="@(ratioCssClass) col-4 col-xl-6 m-auto position-relative" style="@ratioVariable">
1392 @if (showBadges)
1393 {
1394 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2">
1395 @{
1396 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)
1397 }
1398 </div>
1399 }
1400 <img loading="lazy" decoding="async" src="@imagePath" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="@product.Name" id="cartNotificationModal_Image">
1401 </div>
1402 </div>
1403 <div class="d-flex flex-wrap py-3 col-12 gap-md-3">
1404 <div class="col-12 col-md-auto info">
1405 <div class="fc-dark efa-mod"><strong id="cartNotificationModal_Name"></strong></div>
1406 <div class="fs-5 fw-bold fc-dark efa-mod" id="cartNotificationModal_Price"></div>
1407 <div class="fs-8 fc-grey efa-mod" id="cartNotificationModal_DeliveryCost"></div>
1408 <div class="fs-8 fc-grey efa-mod" id="cartNotificationModal_DeliveryName"></div>
1409 <div id="cartNotificationModal_Quantity"></div>
1410 </div>
1411 <div class="col-12 col-lg action align-self-end">
1412 <div class="col-12 d-flex mt-3">
1413 <a id="cartNotificationModal_Button" class="btn btn-primary flex-fill" href="">@Translate("Gå til indkøbslisten", "Gå til indkøbslisten")</a>
1414 </div>
1415 </div>
1416 </div>
1417 </div>
1418 </div>
1419 </div>
1420 </div>
1421 @if (relatedExist)
1422 {
1423 <div class="col-12 col-xl-5 p-0">
1424 <div class="modal-header w-100 text-center">
1425 <h3 class="modal-title text-center w-100">@Translate("Add-on products", "Tilbehør")</h3>
1426 <button type="button" class="btn-close position-absolute m-0 end-0 me-3 d-none d-xl-block" data-bs-dismiss="modal" aria-label="Close"></button>
1427 </div>
1428 <div class="modal-body">
1429 <div id="addonsList_@product.Id" class="list row gy-3 overflow-auto">
1430 @{
1431 foreach (RelatedGroupViewModel relatedGroup in product.RelatedGroups.Where(x => x.Id == "Addon"))
1432 {
1433 foreach (ProductInfoViewModel relatedProduct in relatedGroup.Products)
1434 {
1435 <div class="elements col-6 col-xl-12 align-self-end">
1436 @RenderAddons(relatedProduct, productCounter, product.Id)
1437 </div>
1438 productCounter += 1;
1439 }
1440 }
1441
1442 }
1443 </div>
1444 </div>
1445 </div>
1446 }
1447 </div>
1448 </div>
1449 </div>
1450 </div>
1451 }
1452
1453 @helper RenderAddons(ProductInfoViewModel relatedProduct, int productCounter, string productID)
1454 {
1455 var relProduct = relatedProduct.GetProduct();
1456 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
1457 bool anonymousUser = Pageview.User == null;
1458 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable();
1459 bool showFavoritesSelectorMasterProduct = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.Form.Get("ShowFavoritesSelectorMasterProduct")) : false;
1460
1461 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : "";
1462 string ratioCssClass = ratio != "" ? "ratio" : "";
1463 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
1464
1465 string theme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("Theme")) ? Dynamicweb.Context.Current.Request.Form.Get("Theme") : "";
1466 string themePadding = theme != string.Empty ? "p-3" : string.Empty;
1467 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : "";
1468 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty;
1469 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
1470 string ContentPadding = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ContentPadding")) ? Dynamicweb.Context.Current.Request.Form.Get("ContentPadding") : "";
1471
1472 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower();
1473 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat);
1474
1475 string variantIdForLink = !string.IsNullOrEmpty(relProduct.VariantId) ? $"&VariantID={relProduct.VariantId}" : "";
1476 variantIdForLink = string.IsNullOrEmpty(variantIdForLink) && !string.IsNullOrEmpty(relProduct.DefaultVariantId) ? $"&VariantID={relProduct.DefaultVariantId}" : variantIdForLink;
1477
1478 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop");
1479 link += $"&GroupID={relProduct.PrimaryOrDefaultGroup.Id}";
1480 link += $"&ProductID={relProduct.Id}";
1481 link += variantIdForLink;
1482 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link);
1483
1484 string imagePath = relProduct?.DefaultImage.Value.ToString() ?? "";
1485 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70";
1486
1487 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : "";
1488 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : "";
1489 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : "";
1490 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0;
1491 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : "";
1492
1493 var badgeParms = new Dictionary<string, object>();
1494 badgeParms.Add("saleBadgeType", saleBadgeType);
1495 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName);
1496 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName);
1497 badgeParms.Add("campaignBadgesValues", campaignBadgesValues);
1498 badgeParms.Add("newPublicationDays", newPublicationDays);
1499
1500 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false;
1501 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false;
1502 DateTime createdDate = relProduct.Created.Value;
1503 bool showBadges = saleBadgeEnabled && relProduct.Discount.Price != 0 ? true : false;
1504 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges;
1505 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges;
1506
1507 string disableAddToCart = (relProduct.StockLevel <= 0) ? "disabled" : "";
1508 bool isNeverOutOfStock = relProduct.NeverOutOfstock;
1509 disableAddToCart = isNeverOutOfStock ? "" : disableAddToCart;
1510
1511 string iconPath = "/Files/icons/";
1512 string addToCartIcon = Model.Item.GetRawValueString("Icon", iconPath + "shopping-cart.svg");
1513 string addToCartLabel = !addToCartIcon.Contains("_none") ? "<span class=\"icon-2\">" + ReadFile(addToCartIcon) + "</span>" : "";
1514 addToCartLabel += !addToCartIcon.Contains("_none") && !Model.Item.GetBoolean("HideButtonText") ? " " : "";
1515 addToCartLabel += !Model.Item.GetBoolean("HideButtonText") ? Translate("Add to cart") : "";
1516 string flexFill = Model.Item.GetRawValueString("HorizontalAlignment", "") == "full" ? "flex-fill" : "";
1517 string buttonSize = Model.Item.GetRawValueString("ButtonSize", "regular");
1518 string inputSize = string.Empty;
1519 string viewMoreIcon = Model.Item.GetRawValueString("ViewMoreIcon");
1520
1521 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
1522 if (!url.Contains("LayoutTemplate"))
1523 {
1524 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml";
1525 }
1526 string cartUrl = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("EasyFlowCart"));
1527 string imagePathHidden = relProduct?.DefaultImage.Value.ToString() ?? "";
1528 imagePathHidden = "/Admin/Public/GetImage.ashx?image=" + imagePathHidden + "&width=" + 350 + "&Format=WebP&Quality=70";
1529
1530 switch (buttonSize)
1531 {
1532 case "small":
1533 inputSize = " input-group-sm";
1534 buttonSize = " btn-sm";
1535 break;
1536 case "regular":
1537 buttonSize = string.Empty;
1538 break;
1539 case "large":
1540 inputSize = " input-group-lg";
1541 buttonSize = " btn-lg";
1542 break;
1543 }
1544
1545 string ecomCountryCode = !string.IsNullOrEmpty(Pageview.Area.EcomCountryCode) ? Pageview.Area.EcomCountryCode : "";
1546 var countryVat = Services.Countries.GetCountry(ecomCountryCode).Vat > 0 ? Services.Countries.GetCountry(ecomCountryCode).Vat : 0;
1547 var productToShow = Services.Products.GetProductById(relProduct.Id, relProduct.VariantId, relProduct.LanguageId);
1548 var currency = Dynamicweb.Ecommerce.Common.Context.Currency.Code;
1549 string shipId = ecomCountryCode.IsNotNullOrEmpty() ? ShippingBasedOnProductSetting.GetShippingId(ecomCountryCode, productToShow) : "";
1550 var shippingFeeAmount = string.Empty;
1551
1552 if (!string.IsNullOrEmpty(ecomCountryCode) && shipId != null)
1553 {
1554 shippingFeeAmount = ShippingBasedOnProductSetting.GetShippingAmount(shipId, countryVat, productToShow, currency);
1555 }
1556
1557 var deliveryCost = Translate("Levering fra") + " " + currency + " " + shippingFeeAmount;
1558 var deliveryName = ShippingBasedOnProductSetting.GetShippingName(shipId, relProduct.LanguageId);
1559 string stepQty = relProduct.PurchaseQuantityStep > 1 ? relProduct.PurchaseQuantityStep.ToString() : "1";
1560 string valueQty = relProduct.PurchaseMinimumQuantity > relProduct.PurchaseQuantityStep ? relProduct.PurchaseMinimumQuantity.ToString() : stepQty;
1561
1562 relProduct.ProductFields.TryGetValue("ProductNextBackorderDate", out FieldValueViewModel backInStockDateValue);
1563 DateTime backInstockDate = backInStockDateValue.Value != null ? DateTime.Parse(backInStockDateValue.Value.ToString()) : DateTime.Today.AddDays(-1);
1564 bool hasBackInstockDate = backInStockDateValue != null && backInstockDate >= DateTime.Today;
1565 bool hasExpectedDelivery = relProduct.ExpectedDelivery != null && relProduct.ExpectedDelivery >= DateTime.Today;
1566 string expectedDeliveryDate = hasBackInstockDate ? backInstockDate.ToShortDateString() : relProduct.ExpectedDelivery?.ToShortDateString() ?? "";
1567
1568 <div class="element w-100 d-flex">
1569
1570 <div class="w-100 row m-0 align-items-end">
1571 <div class="product-info col ps-lg-0">
1572 <div class="col-6 col-xl-3 m-auto m-xl-0 image-container pe-2">
1573 <img class="h-100 w-100" src="@imagePath" alt="" style="object-fit: cover;" />
1574 </div>
1575 <div>
1576 <div><strong>@relProduct.Name</strong></div>
1577 @if (!hidePrice)
1578 {
1579 <div>
1580 <p class="h6 m-0">
1581 @if (showPricesWithVat == "false" && !neverShowVat)
1582 {
1583 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price)
1584 {
1585 <span class="text-decoration-line-through opacity-75 me-1">
1586 @relProduct.PriceBeforeDiscount.PriceWithoutVatFormatted
1587 </span>
1588 }
1589 }
1590 else
1591 {
1592 if (relProduct.Price.Price != relProduct.PriceBeforeDiscount.Price)
1593 {
1594 <span class="text-decoration-line-through opacity-75 me-1">
1595 @relProduct.PriceBeforeDiscount.PriceFormatted
1596 </span>
1597 }
1598 }
1599
1600 @if (showPricesWithVat == "false" && !neverShowVat)
1601 {
1602 <span class="text-price fw-bold">@relProduct.Price.PriceWithoutVatFormatted</span>
1603 }
1604 else
1605 {
1606 <span class="text-price fw-bold">@relProduct.Price.PriceFormatted</span>
1607 }
1608 </p>
1609
1610 <div class="fs-8 mt-1 mb-2 text-start">
1611 @if (!string.IsNullOrWhiteSpace(relProduct.StockStatus))
1612 {
1613 <p class="mb-1">@relProduct.StockStatus</p>
1614 if (!string.IsNullOrWhiteSpace(relProduct.StockDeliveryText))
1615 {
1616 <p class="mb-1">@relProduct.StockDeliveryText</p>
1617 }
1618 }
1619 @if (hasExpectedDelivery || hasBackInstockDate)
1620 {
1621 <div class="mb-1">
1622 <span>@Translate("Expected in stock"): </span>
1623 <span>@expectedDeliveryDate</span>
1624 </div>
1625 }
1626 </div>
1627
1628 @*<div class="fs-8 text-black-50 text-start">
1629 <p class="m-0">@Translate("Levering fra") @currency @shippingFeeAmount</p>
1630 </div>*@
1631 @if (showPricesWithVat == "false" && !neverShowVat)
1632 {
1633 <small class="opacity-85 fst-normal">@relProduct.Price.PriceWithVatFormatted @Translate("Incl. VAT")</small>
1634 }
1635
1636 </div>
1637 }
1638 </div>
1639 </div>
1640 <div class="action col-auto px-3 ps-lg-0 w-100 w-lg-auto">
1641 <form method="post" action="@url">
1642 <input type="hidden" name="redirect" value="false">
1643 <input type="hidden" name="ProductId" value="@relProduct.Id">
1644 <input type="hidden" name="ProductName" value="@relProduct.Name">
1645 <input type="hidden" name="ProductCurrency" value="@Dynamicweb.Ecommerce.Common.Context.Currency.Code">
1646 <input type="hidden" name="ProductReferer" value="product_list_listview">
1647 <input type="hidden" name="ProductPrice" value="@relProduct.Price.PriceFormatted">
1648 <input type="hidden" name="cartcmd" value="add">
1649 @* #38 EA: sbj *@
1650 <input type="hidden" name="ProductPriceFormatted" value="@relProduct.Price.PriceFormatted">
1651 <input type="hidden" name="ProductUnit" value="@Translate("stk.", "stk.")">
1652 <input type="hidden" name="ProductTotalText" value="@Translate("I alt", "I alt")">
1653 <input type="hidden" name="ProductDeliveryCost" value="@deliveryCost">
1654 <input type="hidden" name="ProductDeliveryName" value="@deliveryName">
1655 <input type="hidden" name="ProductImage" value="@imagePathHidden">
1656 <input type="hidden" name="cartpage" value="@cartUrl">
1657 @if (relProduct.StockLevel > 0)
1658 {
1659 <div class="d-flex flex-nowrap w-100 mb-2 bg-light efa-mod" style="height:40px;">
1660
1661 <div class="d-flex align-items-center justify-content-center btn border-0" id="minus-btn" onclick="quantityBtn('Quantity_@(productID)_@(relProduct.Id)_@relProduct.VariantId', 'minus')" style="width:calc(100%/3);">
1662 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-dash-lg" viewBox="0 0 16 16">
1663 <path fill-rule="evenodd" d="M2 8a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11A.5.5 0 0 1 2 8Z" />
1664 </svg>
1665 </div>
1666 <div class="d-flex align-items-center justify-content-center" style="width:calc(100%/3);">
1667 <input id="Quantity_@(productID)_@(relProduct.Id)_@relProduct.VariantId" name="Quantity" value="1" step="1" min="1" class="form-control p-0 bg-light border-0 fc-green quantity_selector efa-mod" style="max-width:40px;" type="number" onkeydown="swift.Cart.UpdateOnEnterKey(event)" @disableAddToCart>
1668 </div>
1669 <div class="d-flex align-items-center justify-content-center btn border-0" id="plus-btn" onclick="quantityBtn('Quantity_@(productID)_@(relProduct.Id)_@relProduct.VariantId', 'plus')" style="width:calc(100%/3);">
1670 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-lg" viewBox="0 0 16 16">
1671 <path fill-rule="evenodd" d="M8 2a.5.5 0 0 1 .5.5v5h5a.5.5 0 0 1 0 1h-5v5a.5.5 0 0 1-1 0v-5h-5a.5.5 0 0 1 0-1h5v-5A.5.5 0 0 1 8 2Z" />
1672 </svg>
1673 </div>
1674 <script type="text/javascript">
1675 function quantityBtn(id, symbol) {
1676 console.log(document.getElementById(id));
1677 var idValue = document.getElementById(id).value;
1678 var value = parseInt(idValue, 10);
1679 var step = 1;
1680 value = isNaN(value) ? 0 : value;
1681 switch (symbol) {
1682 case "minus":
1683 value = value - step;
1684 break;
1685 case "plus":
1686 value = value + step;
1687 break;
1688 }
1689 console.log(value);
1690 if (value > 0) {
1691 document.getElementById(id).value = value;
1692 }
1693 //UpdateQuantity();
1694 }
1695 </script>
1696
1697 </div>
1698 <a onclick="swift.Cart.Update(event)" data-container="productRel" class="btn btn-primary @(buttonSize) @flexFill w-100 w-lg-auto flex-fill js-add-to-cart-button addToCart" style="white-space: nowrap" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(relProduct.Id)_@Pageview.CurrentParagraph.ID">
1699 @if (!Model.Item.GetBoolean("HideButtonText"))
1700 {
1701 <span class="text-nowrap d-flex align-items-center justify-content-center gap-2">
1702 @addToCartLabel
1703 </span>
1704 }
1705 else
1706 {
1707 @addToCartLabel
1708 }
1709 </a>
1710 }
1711 else
1712 {
1713 <div class="w-100 efa-mod" style="height:40px;">
1714 @*<button name="notify-cookie" data-href="@link" type="button" class="btn btn-primary flex-fill h-100 js-add-to-cart-button" title="@Translate("Produkt notifikations label")" id="AddCookie@(relProduct.Id)">
1715 <span class="icon-2 d-flex gap-2 align-items-center justify-content-center">@ReadFile(iconPath + "envelope.svg")</span>
1716 </button>*@
1717 <a href="@link" class="btn btn-secondary flex-fill view-more-btn w-100"><div class="">@ReadFile(viewMoreIcon)<span>@Translate("View more")</span></div></a>
1718 </div>
1719 }
1720 </form>
1721 </div>
1722 </div>
1723 </div>
1724 }
1725
1726 @*@helper RenderModalRelatedProducts(ProductViewModel product)
1727 {
1728 <div id="cartNotificationModal_@(product.Id)" class="modal" tabindex="-1" aria-labelledby="cartNotificationModalTitel" aria-hidden="true">
1729 @{
1730 string modelId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ModelID")) ? Dynamicweb.Context.Current.Request.Form.Get("ModelID") : product.Id;
1731
1732 string scrollBarForceMobile = Dynamicweb.Context.Current.Request.Form.Get("NavigationShowScrollbar") != string.Empty ? "--swiffy-slider-track-height:0.5rem !important;" : string.Empty;
1733 bool hideSliderNavigation = false;
1734
1735 int itemsShown = 3;
1736
1737 string imageTheme = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageTheme")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageTheme") : "";
1738 string imageOutlineStyle = imageTheme == string.Empty ? "border: 1px solid transparent;" : string.Empty;
1739 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
1740
1741 string imagePath = product?.DefaultImage.Value.ToString() ?? "";
1742 imagePath = "/Admin/Public/GetImage.ashx?image=" + imagePath + "&width=" + 350 + "&Format=WebP&Quality=70";
1743
1744 string ratio = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio")) ? Dynamicweb.Context.Current.Request.Form.Get("ImageAspectRatio") : "";
1745 string ratioCssClass = ratio != "" ? "ratio" : "";
1746 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
1747
1748 string saleBadgeType = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeType") : "";
1749 string saleBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("SaleBadgeCssClassName") : "";
1750 string newBadgeCssClassName = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName")) ? Dynamicweb.Context.Current.Request.Form.Get("NewBadgeCssClassName") : "";
1751 int newPublicationDays = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) ? Convert.ToInt32(Dynamicweb.Context.Current.Request.Form.Get("NewPublicationDays")) : 0;
1752 string campaignBadgesValues = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues")) ? Dynamicweb.Context.Current.Request.Form.Get("CampaignBadgesValues") : "";
1753
1754 var badgeParms = new Dictionary<string, object>();
1755 badgeParms.Add("saleBadgeType", saleBadgeType);
1756 badgeParms.Add("saleBadgeCssClassName", saleBadgeCssClassName);
1757 badgeParms.Add("newBadgeCssClassName", newBadgeCssClassName);
1758 badgeParms.Add("campaignBadgesValues", campaignBadgesValues);
1759 badgeParms.Add("newPublicationDays", newPublicationDays);
1760
1761 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(saleBadgeCssClassName) && saleBadgeCssClassName != "none" ? true : false;
1762 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(newBadgeCssClassName) && newBadgeCssClassName != "none" ? true : false;
1763 DateTime createdDate = product.Created.Value;
1764
1765 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false;
1766 showBadges = (newBadgeEnabled && newPublicationDays == 0) || (newBadgeEnabled && (createdDate.AddDays(newPublicationDays) > DateTime.Now)) ? true : showBadges;
1767 showBadges = !string.IsNullOrEmpty(campaignBadgesValues) ? true : showBadges;
1768
1769 var shopPageId = GetPageIdByNavigationTag("Shop");
1770
1771 bool relatedExist = false;
1772 foreach (var relatedGroup in product.RelatedGroups)
1773 {
1774 if (relatedGroup.Id == "Addon")
1775 {
1776 if (relatedGroup.Products.Count > 0)
1777 {
1778 relatedExist = true;
1779 }
1780 }
1781 }
1782
1783 }
1784 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script>
1785 <script type="module">
1786 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css');
1787 </script>
1788 <div class="modal-dialog theme plus-primary modal-xl">
1789 <div class="modal-content rounded-0">
1790 <div class="modal-header w-100 text-center">
1791 <h3 class="modal-title w-100" id="cartNotificationModalTitel">@Translate("Følgende er tilføjet til indkøbslisten", "Følgende er tilføjet til indkøbslisten")</h3>
1792 <button type="button" class="btn-close position-absolute m-0 end-0 me-3" data-bs-dismiss="modal" aria-label="Close"></button>
1793 </div>
1794 <div id="cartNotificationModalBody" class="modal-body">
1795 <div class="container-fluid">
1796 <div class="row">
1797 <div class="col-12 col-md-6 col-lg-3 m-auto me-lg-0 @(imageTheme)" style="@imageOutlineStyle">
1798 <div class="@(ratioCssClass) position-relative" style="@ratioVariable">
1799 @if (showBadges)
1800 {
1801 <div class="position-absolute top-0 left-0 p-1 p-lg-2" style="z-index: 2">
1802 @{
1803 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)
1804 }
1805 </div>
1806 }
1807 <img loading="lazy" decoding="async" src="" class="h-100 w-100 @(imageThemePadding)" style="object-fit: contain;" alt="" id="cartNotificationModal_Image">
1808 </div>
1809 </div>
1810 <div class="col-12 col-md-6 col-lg-4 m-auto ms-lg-0 d-flex align-content-between flex-wrap">
1811 <div class="col-12 ms-auto fc-dark efa-mod"><strong id="cartNotificationModal_Name"></strong></div>
1812 <div class="col-12 ms-auto">
1813 <div class="col-12 ms-auto fs-5 fw-bold fc-dark efa-mod" id="cartNotificationModal_Price"></div>
1814 <div class="fs-8 fc-grey efa-mod text-start">
1815 <p class="" id="cartNotificationModal_DeliveryCost"></p>
1816 </div>
1817
1818 <div class="col-12 d-flex flex-wrap mb-3 ms-auto" id="cartNotificationModal_Quantity"></div>
1819 <div class="col-12 d-flex mb-2">
1820 <a id="cartNotificationModal_Button" class="btn btn-primary flex-fill" href="">@Translate("Gå til indkøbslisten", "Gå til indkøbslisten")</a>
1821 </div>
1822 <div class="col-12 d-flex">
1823 <div class="btn btn-secondary col-12" data-bs-dismiss="modal">@Translate("Continue shopping")</div>
1824 </div>
1825 </div>
1826 </div>
1827 </div>
1828 </div>
1829 </div>
1830
1831 @if (relatedExist)
1832 {
1833 <div class="modal-footer d-none d-md-block d-lg-block d-xl-block" id="cartNotificationModal_Related">
1834 <div class="modal-title w-100 text-center">
1835 <h3>@Translate("Add-on products", "Tilbehør")</h3>
1836 </div>
1837 <div class="container-fluid">
1838 <div class="row">
1839 <div class="col-12 m-auto">
1840 <div id="slider_@(modelId)" class="swiffy-slider slider-item-show@(itemsShown) slider-nav-visible" style="--swiffy-slider-nav-light:var(--swift-foreground-color); --swiffy-slider-nav-dark:var(--swift-background-color); @(scrollBarForceMobile)">
1841 <ul class="slider-container">
1842 @{
1843 foreach (RelatedGroupViewModel relatedGroup in product.RelatedGroups.Where(x => x.Id == "Addon"))
1844 {
1845 foreach (ProductInfoViewModel relatedProduct in relatedGroup.Products)
1846 {
1847 <li>
1848 @RenderProduct(relatedProduct, product.Id)
1849 </li>
1850 }
1851 }
1852 }
1853
1854 </ul>
1855 <button type="button" title="@Translate("Previous slide")" class="slider-nav" style="z-index: 2;">
1856 <span class="visually-hidden">@Translate("Previous slide")</span>
1857 </button>
1858 <button type="button" title="@Translate("Next slide")" class="slider-nav slider-nav-next" style="z-index: 2;">
1859 <span class="visually-hidden">@Translate("Next slide")</span>
1860 </button>
1861
1862 </div>
1863 <script type="module">
1864 swiffyslider.initSlider(document.querySelector('#slider_@(modelId)'));
1865 </script>
1866 </div>
1867 </div>
1868 </div>
1869 </div>
1870 }
1871 </div>
1872 </div>
1873 </div>
1874 }*@
1875
1876 <script type="text/javascript">
1877
1878 var notifyCookieBtn = document.getElementsByName("notify-cookie");
1879 if (notifyCookieBtn) {
1880 for (let i = 0; i < notifyCookieBtn.length; i++) {
1881 notifyCookieBtn[i].addEventListener("click", function (event) {
1882 let link = notifyCookieBtn[i].getAttribute("data-href"),
1883 cName = "ProductNotifier",
1884 cValue = true,
1885 date = new Date(),
1886 expDays = 1;
1887 date.setTime(date.getTime() + (expDays * 24 * 60 * 60 * 1000));
1888 const expires = "expires=" + date.toUTCString();
1889 document.cookie = cName + "=" + cValue + "; " + expires + "; path=/";
1890
1891 window.location.href = link;
1892 });
1893 }
1894 }
1895
1896 document.addEventListener("update.swift.cart", function (event) {
1897 var data = Object.fromEntries(event.detail.formData.entries());
1898 var mainProductId = data.MainProductId;
1899
1900 if (mainProductId != "None") {
1901 var openqs = '#cartNotificationModal_' + mainProductId;
1902
1903 var prodId = data.ProductId;
1904
1905 var price = data.ProductPrice;
1906 if (price.includes(",")) {
1907 price = price.replace(/,/g, ".");
1908 price = parseFloat(price).toFixed(2);
1909 }
1910
1911 var quantity = data.Quantity;
1912 var detailsPrice = data.ProductPrice;
1913 var totalprice = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");;
1914 if (quantity > 1) {
1915 var total = price * quantity;
1916 var totalpriceCalc = parseFloat(total).toFixed(2);
1917 totalpriceCalc = totalpriceCalc.toString();
1918 if (totalpriceCalc.includes(".")) {
1919 totalprice = totalpriceCalc.replace(".", ",");
1920 }
1921 else {
1922 totalprice = totalpriceCalc.concat("", ",00");
1923 }
1924 }
1925
1926 var details = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");
1927
1928 document.querySelector(openqs).querySelector("#cartNotificationModal_Button").href = data.cartpage;
1929 document.querySelector(openqs).querySelector("#cartNotificationModal_Quantity").innerHTML = data.Quantity + " " + data.ProductUnit;
1930 document.querySelector(openqs).querySelector("#cartNotificationModal_Name").innerHTML = data.ProductName;
1931 document.querySelector(openqs).querySelector("#cartNotificationModal_Price").innerHTML = details;
1932 document.querySelector(openqs).querySelector("#cartNotificationModal_Image").src = data.ProductImage;
1933 document.querySelector(openqs).querySelector("#cartNotificationModal_Image").alt = data.ProductName;
1934 document.querySelector(openqs).querySelector("#cartNotificationModal_DeliveryCost").innerHTML = data.ProductDeliveryCost;
1935 document.querySelector(openqs).querySelector("#cartNotificationModal_DeliveryName").innerHTML = data.ProductDeliveryName;
1936 document.querySelector(openqs).querySelector("#cartNotificationModal_Related").classList.remove('d-md-block', 'd-lg-block', 'd-xl-block');
1937 }
1938 else {
1939 var prodId = data.ProductId;
1940 mainProductId = prodId;
1941
1942 var price = data.ProductPrice;
1943 if (price.includes(",")) {
1944 price = price.replace(/,/g, ".");
1945 price = parseFloat(price).toFixed(2);
1946 }
1947
1948 var quantity = data.Quantity;
1949 var detailsPrice = data.ProductPrice;
1950 var totalprice = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");;
1951 if (quantity > 1) {
1952 var total = price * quantity;
1953 var totalpriceCalc = parseFloat(total).toFixed(2);
1954 totalpriceCalc = totalpriceCalc.toString();
1955 if (totalpriceCalc.includes(".")) {
1956 totalprice = totalpriceCalc.replace(".", ",");
1957 }
1958 else {
1959 totalprice = totalpriceCalc.concat("", ",00");
1960 }
1961 }
1962
1963 var qs = "#cartNotificationModal_" + prodId;
1964 const myModalAlternative = new bootstrap.Modal(qs)
1965
1966 var details = data.ProductPrice.includes(",") ? data.ProductPrice : detailsPrice.concat("", ",00");
1967
1968 document.querySelector(qs).querySelector("#cartNotificationModal_Button").href = data.cartpage;
1969 document.querySelector(qs).querySelector("#cartNotificationModal_Quantity").innerHTML = data.Quantity + " " + data.ProductUnit;
1970 document.querySelector(qs).querySelector("#cartNotificationModal_Name").innerHTML = data.ProductName;
1971 document.querySelector(qs).querySelector("#cartNotificationModal_Price").innerHTML = details;
1972 document.querySelector(qs).querySelector("#cartNotificationModal_Image").src = data.ProductImage;
1973 document.querySelector(qs).querySelector("#cartNotificationModal_Image").alt = data.ProductName;
1974 document.querySelector(qs).querySelector("#cartNotificationModal_DeliveryCost").innerHTML = data.ProductDeliveryCost;
1975 document.querySelector(qs).querySelector("#cartNotificationModal_DeliveryName").innerHTML = data.ProductDeliveryName;
1976 if (document.querySelector(qs).querySelector("#cartNotificationModal_Related")) {
1977 if (!document.querySelector(qs).querySelector("#cartNotificationModal_Related").classList.contains('d-md-block', 'd-lg-block', 'd-xl-block')) {
1978 document.querySelector(qs).querySelector("#cartNotificationModal_Related").classList.add('d-md-block', 'd-lg-block', 'd-xl-block');
1979 }
1980 }
1981
1982 myModalAlternative.show();
1983 }
1984 let addonsListHeight = 0;
1985 console.log(mainProductId);
1986 let addonList = document.querySelector("#addonsList_" + mainProductId);
1987 console.log(addonList);
1988 let addonListElems = addonList.children;
1989 console.log(addonListElems);
1990 if (addonListElems.length > 3) {
1991 for (var i = 0; 3 > i; i++) {
1992 console.log(addonListElems[i].clientHeight);
1993 addonsListHeight = addonsListHeight + addonListElems[i].clientHeight + 16;
1994 }
1995 addonList.style.maxHeight = addonsListHeight + "px";
1996 }
1997
1998 });
1999
2000 </script>
2001 @* #38 EA: end *@
2002
2003
2004 <script>
2005 dataLayer = window.dataLayer || [];
2006 dataLayer.push({ ecommerce: null });
2007 dataLayer.push({
2008 event: 'view_item_list',
2009 ecommerce: {
2010 currency: '@Dynamicweb.Ecommerce.Common.Context.Currency.Code',
2011 ecomm_pagetype: 'category',
2012 items: [
2013 @foreach (ProductViewModel product in productList.Products)
2014 {
2015 <text>{
2016 item_name: '@product.Name',
2017 item_id: '@product.Number',
2018 price: @product.Price.Price,
2019 quantity: 1,
2020 item_category: '@product.PrimaryOrDefaultGroup.Name',
2021 item_brand: 'PLUS',
2022 item_variant: ''
2023 },</text>
2024 }
2025 ]
2026 },
2027 user_data: {
2028 first_name: '@(string.IsNullOrEmpty(CartId) ? "" : order?.DeliveryName)',
2029 last_name: '',
2030 email_address: '@(string.IsNullOrEmpty(CartId) ? "" : order?.CustomerEmail)',
2031 phone_number: '@(string.IsNullOrEmpty(CartId) ? "" : order?.CustomerPhone)',
2032 }
2033 });
2034 </script>
2035