Error executing template "Designs/Swift/Paragraph/Swift_ProductListGridView_Custom.cshtml"
System.ArgumentException: An item with the same key has already been added.
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at Dynamicweb.Ecommerce.FieldTypeProviders.ConversionUnitFieldTypeProviderBase.GetLanguageUnits(Dictionary`2 settings)
at Dynamicweb.Ecommerce.FieldTypeProviders.ConversionUnitFieldTypeProviderBase.GetValue(Object value, String languageId, Dictionary`2 settings)
at Dynamicweb.Ecommerce.Products.ProductFieldTypeProvider.GetProductValue(Product product, FieldType fieldType, Object fieldValue)
at Dynamicweb.Ecommerce.Products.Categories.ProductCategoryFieldValueService.GetCategoryValue(Product product, String defaultLanguageId, IEnumerable`1 orderedGroups, Field catField, Boolean includeInheritance)
at Dynamicweb.Ecommerce.Products.Categories.ProductCategoryFieldValueService.GetCategoryValue(Product product, String categoryId, String fieldId, Boolean includeInheritance)
at Dynamicweb.Ecommerce.Products.Categories.ProductCategoryFieldValueService.GetProductCategoryFieldValue(Product product, String categoryId, Field field)
at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.CreateView(ProductViewModelSettings settings, Product product, Field field)
at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.GetFieldDisplayGroupValues(ProductViewModelSettings settings, Product product, String languageID, Lazy`1 productIds)
at Dynamicweb.Ecommerce.ProductCatalog.ViewEngine.<>c__DisplayClass3_1.<BulkCreateView>b__54()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at CompiledRazorTemplates.Dynamic.RazorEngine_295d8218648b4f2585c7ef4b0a26036a.<RenderProductList>b__5_0(TextWriter __razor_helper_writer)
at CompiledRazorTemplates.Dynamic.RazorEngine_295d8218648b4f2585c7ef4b0a26036a.Execute()
at RazorEngine.Templating.TemplateBase.RazorEngine.Templating.ITemplate.Run(ExecuteContext context, TextWriter reader)
at RazorEngine.Templating.RazorEngineService.RunCompile(ITemplateKey key, TextWriter writer, Type modelType, Object model, DynamicViewBag viewBag)
at RazorEngine.Templating.RazorEngineServiceExtensions.<>c__DisplayClass16_0.<RunCompile>b__0(TextWriter writer)
at RazorEngine.Templating.RazorEngineServiceExtensions.WithWriter(Action`1 withWriter)
at Dynamicweb.Rendering.RazorTemplateRenderingProvider.Render(Template template)
at Dynamicweb.Rendering.TemplateRenderingService.Render(Template template)
at Dynamicweb.Rendering.Template.RenderRazorTemplate()
1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel>
2 @using Dynamicweb.Ecommerce.ProductCatalog
3 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites
4 @using System.Web;
5 @using Dynamicweb.Core;
6 @using Dynamicweb.Environment
7 @using Mennt.Tysse.Custom.Helpers
8
9 @functions {
10 Dictionary<string, object> favoriteParameters { get; set; }
11
12 public static string TruncateWithEllipsis(string s, int length)
13 {
14 //there may be a more appropiate unicode character for this
15 const string Ellipsis = "..";
16
17 if (Ellipsis.Length > length)
18 throw new ArgumentOutOfRangeException("length", length, "length must be at least as long as ellipsis.");
19
20 if (s.Length > length)
21 return s.Substring(0, length - Ellipsis.Length) + Ellipsis;
22 else
23 return s;
24 }
25 }
26
27 @{
28 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
29 string themePadding = theme != string.Empty ? "p-0" : string.Empty;
30
31 }
32
33 @if (!string.IsNullOrEmpty(theme))
34 {
35 <div class="h-100@(theme) @themePadding">
36 @RenderProductList()
37 </div>
38 }
39 else
40 {
41 <div class="pt-3">
42 @RenderProductList()
43 </div>
44 }
45
46 @helper RenderProductList () {
47 ProductListViewModel productList = new ProductListViewModel();
48
49 if (Dynamicweb.Context.Current.Items.Contains("ProductList"))
50 {
51 productList = (ProductListViewModel)Dynamicweb.Context.Current.Items["ProductList"];
52 }
53
54 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
55 bool anonymousUser = Pageview.User == null;
56 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser;
57
58 string detailsPageLink = Dynamicweb.Context.Current.Items["DetailsPageLink"] != null ? Dynamicweb.Context.Current.Items["DetailsPageLink"].ToString() : "";
59 string productTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ProductTheme")) ? " theme " + Model.Item.GetRawValueString("ProductTheme").Replace(" ", "").Trim().ToLower() : "";
60 string productThemePadding = productTheme != string.Empty ? "px-3 px-0" : string.Empty;
61
62 string url = Dynamicweb.Context.Current.Request.RawUrl;
63 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false;
64 string staticVariantsLayout = Model.Item.GetRawValueString("StaticVariantsLayout", "hide");
65
66 string groupId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("GroupID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("GroupID") : "";
67
68 var badgeParms = new Dictionary<string, object>();
69 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType"));
70 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign"));
71 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign"));
72 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays"));
73 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges"));
74
75 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false;
76 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false;
77
78 string googleAnalyticsTrackingID = Pageview.AreaSettings.GetString("GoogleAnalyticsTrackingID");
79 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID");
80 var cookieOptInLevel = CookieManager.GetCookieOptInLevel();
81 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical"));
82 Mennt.Tysse.Custom.Models.Dealer selectedDealer = DealerHelper.SelectedDealer();
83
84
85
86
87 var favoriteParameters = new Dictionary<string, object>();
88 if (!anonymousUser && !hideFavoritesSelector)
89 {
90 int defaultFavoriteListId = 0;
91
92 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists();
93 if (favoreiteLists.Count() == 1) {
94 foreach (FavoriteList list in favoreiteLists) {
95 defaultFavoriteListId = list.ListId;
96 }
97 }
98
99 favoriteParameters.Add("ListId", defaultFavoriteListId);
100 }
101
102 if (productList.TotalProductsCount > 0) {
103 int pageSizeSetting = 30;
104 int pageSize = productList.PageSize;
105 pageSize += pageSizeSetting;
106
107 int loadedProducts = productList.PageSize > productList.TotalProductsCount ? productList.TotalProductsCount : productList.PageSize;
108
109 var i = 0;
110
111 <div class="grid grid-1 grid-md-2 grid-xxl-3">
112
113 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking)
114 {
115 <script>
116 gtag("event", "view_item_list", {
117 item_list_id: "product_list_gridview",
118 item_list_name: "Product list (Gridview)",
119 items: [
120 @foreach (ProductViewModel product in productList.Products)
121 {
122 <text>{
123 item_id: "@product.Number",
124 item_name: "@product.Name",
125 currency: "@product.Price.CurrencyCode",
126 price: @product.Price.Price
127 },</text>
128 }
129 ]
130 });
131 </script>
132 }
133
134 @foreach (ProductViewModel product in productList.Products)
135 {
136 var defaultGroupId = product.PrimaryOrDefaultGroup.Id;
137 var selectedDetailPage = Dynamicweb.Ecommerce.Services.ProductGroups.GetGroup(defaultGroupId).Meta.PrimaryPage;
138 var productName = TruncateWithEllipsis($"{product.Name} {product.VariantName}", 36);
139
140 string link = string.IsNullOrEmpty(selectedDetailPage) ? $"{detailsPageLink}&groupid={defaultGroupId}" : selectedDetailPage;
141 link += "&productid=" + product.Id;
142 link += !string.IsNullOrEmpty(product.VariantId) ? "&variantid=" + product.VariantId : "";
143
144 string imagePath = product?.DefaultImage?.Value ?? "";
145 //imagePath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath);
146
147 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", "");
148 ratio = ratio != "0" ? ratio : "";
149 string ratioCssClass = ratio != "" ? " ratio" : "";
150 string ratioVariable = ratio != "" ? "--bs-aspect-ratio: " + ratio : "";
151
152 string imagePathXs = "/Admin/Public/GetImage.ashx?width=" + 480 + "&image=" + imagePath + "&format=webp";
153 string imagePathS = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp";
154 string imagePathFallBack = "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + imagePath + "&format=webp";
155
156 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : "";
157 string imageThemePadding = imageTheme != string.Empty ? "p-3" : string.Empty;
158 string imageOutlineStyle = imageTheme == string.Empty ? "style=\"border: 1px solid transparent\"" : string.Empty;
159
160 string imageId = "ProductImage_" + product.Id + product.VariantId;
161 string priceId = "ProductPrice_" + product.Id + product.VariantId;
162
163 string iconPath = "/Files/Icons/";
164
165 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower();
166 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat);
167
168 @* Alternative image *@
169 var supportedImageFormats = new string[] { ".jpg", ".webp", ".png", ".gif" };
170 string defaultImage = product.DefaultImage != null ? product.DefaultImage.Value : "";
171 var selectedAssetCategories = Model.Item.GetRawValueString("AlternativeImageAssets");
172 IEnumerable<MediaViewModel> alternativeImagesList = product.AssetCategories.Where(x => selectedAssetCategories.Contains(x.SystemName)).SelectMany(x => x.Assets);
173
174 if (alternativeImagesList.FirstOrDefault() != null) {
175 alternativeImagesList = alternativeImagesList.OrderByDescending(x => x.Value.Equals(defaultImage));
176
177 if (alternativeImagesList.First().Value == defaultImage) {
178 alternativeImagesList = alternativeImagesList.Skip(1);
179 }
180 }
181
182 string alternativeImage = alternativeImagesList.FirstOrDefault() != null ? alternativeImagesList.FirstOrDefault().Value : "";
183 alternativeImage = !string.IsNullOrEmpty(alternativeImage) ? "/Admin/Public/GetImage.ashx?width=" + 640 + "&image=" + alternativeImage + "&format=webp" : "";
184
185 @* Badges *@
186 DateTime createdDate = product.Created.Value;
187 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false;
188 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges;
189 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges;
190
191 @* Main features *@
192 IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList();
193 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>();
194
195 foreach (var selection in selectedDisplayGroups)
196 {
197 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values)
198 {
199 if (selection == group.Id) {
200 mainFeatures.Add(group);
201 }
202 }
203 }
204
205 var hideBuyInformation = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "hideBuyOption");
206 var hideBuyInformationValue = hideBuyInformation.Value.ToString();
207
208 var clickShop = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "ClickShop");
209 var clickShopValue = clickShop.Value.ToString();
210
211 var alternativProduct = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "UseAlternativeProduct");
212 var alternativProductValue = alternativProduct.Value.ToString();
213 var alternativProductNumber = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "AlternativeProduct");
214 var alternativProductNumberValue = alternativProductNumber.Value.ToString();
215 var currentUrl = Dynamicweb.Frontend.PageView.Current().SearchFriendlyUrl;
216
217 @* Custom fields
218 var totalWeightModel = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "TotalWeight");
219 var payloadModel = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "Payload");
220 var insideDimensionModel = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "InsideDimension");
221 var outsideModel = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "OutsideDimension");
222 var amountAxlesModel = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "AmountAxles");
223 var brakeModel = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "Brake");
224 var outsideWheelModel = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "OutsideWheel");
225 var tippModel = product.ProductFields.Values.FirstOrDefault(x => x.SystemName == "Tipp");
226
227 var totalWeightValue = totalWeightModel.Value.ToString();
228 var payloadValue = payloadModel.Value.ToString();
229 var insideDimensionValue = insideDimensionModel.Value.ToString();
230 var outsideModelValue = outsideModel.Value.ToString();
231 var amountAxlesValue = amountAxlesModel.Value.ToString();
232 var brakeValue = brakeModel.Value.ToString();
233 var outsideWheelValue = outsideWheelModel.Value.ToString();
234 var tippValue = tippModel.Value.ToString();*@
235
236 int dealerStock = DealerHelper.GetDealerStock(product.Id);
237
238 <!--Lagerstatus start-->
239 var test = Dynamicweb.Ecommerce.Products.Product.GetProductById(product.Number);
240 var groupID = test.GetDefaultGroupByShopId("SHOP1");
241 var parent = groupID.PrimaryParentGroupId;
242
243 var groups = "";
244 var spareParts = "false";
245 foreach (var x in test.Groups)
246 {
247 groups = x.Name;
248 }
249
250 if (groups.Contains("Reservedeler"))
251 {
252 spareParts = "true";
253 }
254 <!--Lagerstatus end-->
255
256
257
258 <article class="position-relative@(productTheme) product-list-item box p-0 d-flex flex-column" style="height: fit-content">
259 @if (!anonymousUser && !hideFavoritesSelector && product.VariantInfo.VariantInfo == null) {
260 <div class="position-absolute top-0 end-0 my-3" style="z-index: 2">
261 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters)
262 </div>
263 }
264
265 @if (showBadges) {
266 <div class="position-absolute top-0 left-0 p-1 p-lg-2 ps-0 ps-lg-0" style="z-index: 2">
267 @RenderPartial("Components/EcommerceBadge_custom.cshtml", product, badgeParms)
268 </div>
269 }
270
271 <a href="@link">
272 <div class="overflow-hidden@(imageTheme)" @imageOutlineStyle>
273 <div class="ratio" style="@(ratioVariable)">
274 <div class="d-flex justify-content-center align-items-center">
275 @*if (string.IsNullOrEmpty(alternativeImage)) {
276 <img
277 id="@imageId"
278
279 src="@imagePath"
280 loading="lazy"
281 decoding="async"
282 class="mw-100 mh-100 @imageThemePadding"
283 alt="@product.Name">
284 } else {
285 <img
286 id="@imageId"
287 src="@imagePathFallBack"
288 loading="lazy"
289 decoding="async"
290 class="mw-100 mh-100 @imageThemePadding"
291 alt="@product.Name"
292 onmouseover="this.src='@alternativeImage'"
293 onmouseout="this.src='@imagePathFallBack'">
294 }*@
295 <img class="productlist-image" src="@imagePath" alt="@product.Name"/>
296 </div>
297 </div>
298
299
300 <div class="position-relative">
301 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "images") {
302 int variantGroupCount = 0;
303 int showMaxVariantGroups = 2;
304 int showMaxVariants = 3;
305 var productVariantTheme = productTheme != "" ? productTheme : "bg-white";
306
307 <div
308 class="static-variants w-100 d-none d-lg-block position-absolute left-0 bottom-0 @productTheme"
309 id="StaticVariants_@product.Id"
310 style="pointer-events: none;">
311
312 @foreach (var variantGroup in product.VariantGroups())
313 {
314 int variantsCount = 0;
315
316 <div class="d-flex gap-2 mb-2">
317 @foreach (var variant in variantGroup.Options)
318 {
319 if (variantGroupCount < showMaxVariantGroups)
320 {
321 var optionsCount = variantGroup.Options.Count();
322
323 if (variantsCount < showMaxVariants)
324 {
325 string optionWidth = !string.IsNullOrEmpty(variant.Color) ? "w-25" : "";
326
327 <article class="static-variants-option @optionWidth @(productVariantTheme)" title="@product.Name @variant.Name" style="pointer-events: initial;">
328 @if (!string.IsNullOrEmpty(variant.Color))
329 {
330 string defaultProductImage = Dynamicweb.Context.Current.Server.UrlEncode(product.DefaultImage.Value);
331 string variantImage = Dynamicweb.Context.Current.Server.UrlEncode(variant.Image.Value);
332 string defaultPrice = !hidePrice ? product.Price.PriceFormatted : "0";
333 string variantPrice = !hidePrice ? product.Price.PriceFormatted : "0";
334
335 <figure class="figure w-100 d-block m-0" onmouseover="switchVariantProduct('@product.Id', '@defaultPrice', '@variantImage')" onmouseout="switchVariantProduct('@product.Id', '@variantPrice', '@defaultProductImage')">
336 <div class="d-flex align-items-center justify-content-center">
337 <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">
338 </div>
339 </figure>
340 }
341 else
342 {
343 <div class="d-flex align-items-center justify-content-center">
344 @variant.Name
345 </div>
346 }
347 <div class="visually-hidden">
348 <h4>@Translate("Variant Name")</h4>
349 <p>@product.Name, @variant.Name</p>
350 @if (!hidePrice) {
351 <h4>@Translate("Variant Price")</h4>
352 <p><span class="text-price">@product.Price.PriceFormatted</span></p>
353 }
354 </div>
355 </article>
356 }
357
358 variantsCount++;
359
360 if (variantsCount == showMaxVariants && optionsCount != showMaxVariants)
361 {
362 int left = optionsCount - showMaxVariants;
363 <div class="variant-option ms-1 d-flex justify-content-center align-items-center">
364 <span>+@left</span>
365 </div>
366 }
367 }
368 }
369
370 </div>
371
372 variantGroupCount++;
373 }
374 </div>
375 }
376 </div>
377 </div>
378 </a>
379 <div class="@productThemePadding">
380
381 <div>
382 <a href="@link" class="text-decoration-none text-productname-productlist"><h3 class="Product-name-size heading-size-box mb-1" title="@product.Name @product.VariantName">@product.Name</h3></a>
383 @if (!Model.Item.GetBoolean("HideProductNumber")) {
384
385 <p class="fs-7 pb-0 opacity-85 mb-2 productNumber">@product.Number</p>
386
387 }
388
389 <div class="text-shortdecription-productlist mb-3"> @Dynamicweb.Core.Helpers.StringHelper.StripHtml(product.ShortDescription)</div>
390
391 @if (alternativProductValue == "1")
392 {
393 <div>@Translate("UTGÅTT VARE - Erstattet av:") <a href="reservedeler-og-ekstrautstyr?productid=@alternativProductNumberValue">@alternativProductNumberValue</a></div>
394 }
395 @if (mainFeatures.Count > 0)
396 {
397
398 foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures)
399 {
400
401 @RenderFieldsFromList(mainFeatureGroup.Fields, i)
402
403 }
404
405 }
406
407 @*<ul class="p-0 lh-sm mt-3" style="list-style-type: none">
408 @if (!string.IsNullOrEmpty(totalWeightValue)) {
409 <li class="d-flex justify-content-between mb-1">@Translate("Totalvekt") <span>@totalWeightValue</span></li>
410 <li class="d-flex justify-content-between mb-1">@Translate("Kassemål (LxBxH)") <span>@insideDimensionValue</span></li>
411 <li class="justify-content-between mb-1 hidden-custom-field-@i" style="display:none">@Translate("Nyttelast") <span>@payloadValue</span></li>
412 <li class="justify-content-between mb-1 hidden-custom-field-@i" style="display:none">@Translate("Utvendig mål (LxBxH)") <span>@outsideModelValue</span></li>
413 <li class="justify-content-between mb-1 hidden-custom-field-@i" style="display:none">@Translate("Antall aksler") <span>@amountAxlesValue</span></li>
414 <li class="justify-content-between mb-1 hidden-custom-field-@i" style="display:none">@Translate("Brems") <span>@Translate(brakeValue)</span></li>
415 <li class="justify-content-between mb-1 hidden-custom-field-@i" style="display:none">@Translate("Utenpåliggende hjul") <span>@Translate(outsideWheelValue)</span></li>
416 <li class="justify-content-between mb-1 hidden-custom-field-@i" style="display:none">@Translate("Tipp") <span>@Translate(tippValue)</span></li>
417 }
418 </ul>*@
419
420 <div class="d-none">
421 @if (product.ProductCategories != null) {
422
423 if (product.ProductCategories.Count > 0) {
424 foreach (var group in product.ProductCategories) {
425 CategoryFieldViewModel category = group.Value;
426 bool hideHeader = Model.Item.GetBoolean("HideGroupHeaders");
427
428 @*if (!hideHeader) {
429 <h4 class="h4 mb-4">@group.Key</h4>
430 }*@
431
432 if (product.ProductCategories.Count == 2)
433 {
434 if (group.Key == "Varehenger")
435 {
436 @RenderFieldsFromList(category.Fields, i);
437 }
438 }
439 else
440 {
441 @RenderFieldsFromList(category.Fields, i);
442 }
443
444 }
445 }
446 }
447 </div>
448 @if (!currentUrl.Contains("ekstrautstyr")) {
449 <div class="mb-2" onclick="toggleCustomFields(@i)" style="cursor:pointer; font-size:13px; font-weight:500;">
450 <span id="toggleFields_@i">Flere detaljer</span>
451 <span class="icon-2">
452 <img id="toggleFieldsIcon_@i" src="/Files/Templates/Designs/Swift/Assets/icons/chevron-down.svg" />
453 </span>
454 </div>
455 }
456 </div>
457 </div>
458
459 <div class="d-flex flex-column justify-content-end mt-auto px-3 pb-2 pt-0">
460 @if ( hideBuyInformationValue == "True" )
461 {
462 <div class="regular-bread-text">@Translate("IKKE SALGSVARE - Klikk på produkt for å få frem stykkliste")</div>
463 }
464 else
465 {
466 if (!hidePrice) {
467 string priceMin = "";
468 string priceMax = "";
469
470 <div>
471 <div class="d-flex align-items-baseline">
472 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span>
473
474 @if (showPricesWithVat == "false" && !neverShowVat) {
475 string beforePrice = product.PriceBeforeDiscount.PriceWithVatFormatted;
476
477 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span>
478 if (product.Price.Price != product.PriceBeforeDiscount.Price) {
479 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span>
480 }
481 } else {
482 string beforePrice = product.PriceBeforeDiscount.PriceWithVatFormatted;
483
484 <span itemprop="price" content="@product.Price.Price" class="d-none"></span>
485 if (product.Price.Price != product.PriceBeforeDiscount.Price) {
486 <span class="text-decoration-line-through opacity-75 me-3 text-price">@beforePrice</span>
487 }
488 }
489
490 @if (showPricesWithVat == "false" && !neverShowVat) {
491 string price = product.Price.PriceWithoutVatFormatted;
492 if (product?.VariantInfo?.VariantInfo != null) {
493 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : "";
494 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : "";
495 }
496 if (priceMin != priceMax) {
497 price = priceMin + " - " + priceMax;
498 }
499 <span class="text-price fw-bold">@price</span>
500 } else {
501 string price = product.Price.PriceWithVatFormatted;
502 string priceWithoutVat = product.Price.PriceWithoutVatFormatted;
503 if (product?.VariantInfo?.VariantInfo != null) {
504 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : "";
505 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : "";
506 }
507 if (priceMin != priceMax) {
508 price = priceMin + " - " + priceMax;
509 }
510 if (alternativProductValue != "1")
511 {
512 if (product.Price.Price != product.PriceBeforeDiscount.Price) {
513 <span class="text-price price-size netPrice tysse-red">@price</span>
514 <span class="text-price price-size priceWithoutVat tysse-red" style="display: none;">@priceWithoutVat <span style="font-size: 14px;">@Translate("eks.MVA")</span></span>
515 } else {
516 <span class="text-price price-size netPrice">@price</span>
517 <span class="text-price price-size priceWithoutVat" style="display: none;">@priceWithoutVat <span style="font-size: 14px;">@Translate("eks.MVA")</span></span>
518 }
519 }
520 }
521 </div>
522 @*if (showPricesWithVat == "false" && !neverShowVat) {
523 string price = product.Price.PriceWithVatFormatted;
524 if (product?.VariantInfo?.VariantInfo != null) {
525 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : "";
526 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : "";
527 }
528 if (priceMin != priceMax) {
529 price = priceMin + " - " + priceMax;
530 }
531 <div class="fs-7 opacity-85 text-price">@price @Translate("Incl. VAT")</div>
532 }*@
533 </div>
534 }
535 }
536 @if (product.VariantInfo.VariantInfo != null && staticVariantsLayout == "swatches") {
537 var optionCount = product.VariantInfo.VariantInfo.Count();
538 var showMaxVariants = 5;
539
540 <div class="d-flex flex-row gap-1 align-items-center">
541 @foreach (VariantInfoViewModel variant in product.VariantInfo.VariantInfo.Take(showMaxVariants))
542 {
543 <span class="colorbox colorbox-sm rounded-circle me-1" style="background-color: @variant.OptionColor"></span>
544 }
545 @if (optionCount > showMaxVariants)
546 {
547 int left = optionCount - showMaxVariants;
548 <span class="ms-2">+@left</span>
549 }
550 </div>
551 }
552 <!-- <div class="mt-2">
553 <a href="@link" class="btn btn-primary w-100">@Translate("Les mer")</a>
554 </div> -->
555
556 </div>
557 <div class="px-3 pb-3 gap-2" style="display: grid; grid-template-columns: 1fr 1fr;">
558
559 <!-- Lagerstatus start -->
560 <div>
561
562
563 @if(selectedDealer != null){
564
565 <div data-productid="@product.Id">
566 @if (spareParts == "true") {
567 <div class="">
568 <span class="icon-2" style="color: #D79C04;"> @ReadFile(iconPath + "alert-circle.svg")</span>
569 <span class="x-small-text-bold">@Translate("Usikker lagerstatus")</span>
570 </div>
571 }
572
573 else{
574
575
576 if(dealerStock <= 0){
577 <div data-productid="@product.Id" class="notInStock">
578 <span class="icon-2" style="color: grey;"> @ReadFile(iconPath + "alert-circle.svg")</span>
579 <span class="x-small-text-bold">@Translate("Ikke på lager")</span>
580 </div>
581 <div class="x-small-text">@Translate("Kontakt oss for leveringstid")</div>
582 <div class="x-small-text">@Translate("Tlf:") @selectedDealer.Phone</div>
583 }
584 else
585 {
586 <div data-productid="@product.Id" class="inStock" style="min-height: 57px; display: flex;">
587 <span class="icon-2" style="color: green; align-self: end;"> @ReadFile(iconPath + "check-circle.svg")</span>
588 <span data-productid="@product.Id" class="x-small-text-bold stockText" style="align-self: end; margin-left: 5px;">@Translate("På lager")</span>
589 </div>
590 }
591
592 }
593
594 <hr class="my-1" style="margin: 0" />
595 <!-- VELG BUTIKK-->
596 @if (spareParts == "true") {
597 <div class="mt-1">
598 <div class="small-text">@Translate("")</div>
599 </div>
600 }
601
602 <div>
603 <div class="dealerNameSlider x-small-text">@selectedDealer.Name</div>
604 <div data-bs-toggle="modal" data-bs-target="#FindDealer" data-productid="@product.Id" data-group="@spareParts" class="x-small-text text-decoration-underline dealer-modal" style=" cursor: pointer;">Endre butikk</div>
605 </div>
606
607 </div>
608 }
609 else
610 {
611 <!-- Ingen butikk valgt start-->
612 <div data-productid="@product.Id" class="noDealer d-block">
613 <div class="">
614 <span class="icon-2" style="color: #A9A9A9;"> @ReadFile(iconPath + "home.svg")</span>
615 <span class="x-small-text-bold">@Translate("Ingen butikk valgt")</span>
616 </div>
617 <hr class="my-1" style="margin: 0" />
618 <div data-bs-toggle="modal" data-bs-target="#FindDealer" data-productid="@product.Id" data-group="@spareParts" class="x-small-text text-decoration-underline dealer-modal" style=" cursor: pointer;">Velg butikk for lagerstatus</div>
619 </div>
620 <!-- Ingen butikk valgt end-->
621 }
622 </div>
623 <!-- Lagerstatus end -->
624
625
626 <!-- LAGERSTATUS SENDES I POST START -->
627 @if (clickShopValue == "2")
628 {
629 <div>
630 @if (product.StockLevel > 100)
631 {
632 <span class="icon-2" style="color: green;"> @ReadFile(iconPath + "check-circle.svg")</span>
633 <span class="x-small-text-bold">På nettlager </span>
634 <hr class="my-1" style="margin: 0">
635 <div class="x-small-text">(100+)</div>
636 }
637 else if (product.StockLevel > 50)
638 {
639 <span class="icon-2"style="color: green;"> @ReadFile(iconPath + "check-circle.svg")</span>
640 <span class="x-small-text-bold">På nettlager </span>
641 <hr class="my-1" style="margin: 0">
642 <div class="x-small-text">(50+)</div>
643 }
644 else if (product.StockLevel > 10)
645 {
646 <span class="icon-2" style="color: green;"> @ReadFile(iconPath + "check-circle.svg")</span>
647 <span class="x-small-text-bold">På nettlager </span>
648 <hr class="my-1" style="margin: 0">
649 <div class="x-small-text">(10+)</div>
650 }
651 else if (product.StockLevel > 0)
652 {
653 <span class="icon-2"style="color: green;"> @ReadFile(iconPath + "check-circle.svg")</span>
654 <span class="x-small-text-bold">På nettlager</span>
655 <hr class="my-1" style="margin: 0">
656 <div class="x-small-text">@product.StockLevel</div>
657 }
658 else
659 {
660 <span class="icon-2"> @ReadFile(iconPath + "alert-circle.svg")</span>
661 <span class="x-small-text-bold">@Translate("Ikke på lager")</span>
662 <hr class="my-1" style="margin: 0">
663 <div class="x-small-text"style="color: white !important;">.</div>
664 }
665 </div>
666
667
668 }
669 else
670 {
671 }
672
673
674 </div>
675 <!-- LAGERSTATUS SENDES I POST SLUTT -->
676
677 </article>
678 i++;
679 }
680 </div>
681
682 <div class="my-3">
683 <div class="text-center d-flex flex-column gap-3">
684 <div class="opacity-85">@loadedProducts @Translate("out of") @productList.TotalProductsCount @Translate("products")</div>
685 @if (productList.PageCount != 1) {
686 string sortBySelection = Dynamicweb.Context.Current.Request?.Form["SortBy"] ?? "NameForSort";
687 sortBySelection = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SortBy")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SortBy") : sortBySelection;
688
689 var SearchParameter = HttpContext.Current.Request.QueryString.Get("q");
690
691 <form method="get" action="@url" data-response-target-element="content" class="w-100">
692 @foreach (FacetGroupViewModel facetGroup in productList.FacetGroups)
693 {
694 foreach (FacetViewModel facetItem in facetGroup.Facets)
695 {
696 foreach (FacetOptionViewModel facetOption in facetItem.Options)
697 {
698 if (facetOption.Selected)
699 {
700 <input type="hidden" name="@facetItem.QueryParameter" value="[@facetOption.Value]" />
701 }
702 }
703 }
704 }
705
706 @if (productList?.Group?.Id != null) {
707 <input type="hidden" name="GroupId" value="@productList.Group.Id" />
708
709 }
710
711 <input type="hidden" name="PageSize" value="@pageSize" />
712 <input type="hidden" name="SortBy" value="@sortBySelection" />
713 <input type="hidden" name="RequestType" value="UpdateList" />
714 <input type="hidden" name="q" value="@SearchParameter" />
715
716
717 <button class="btn btn-primary" type="button" onclick="swift.ProductList.Update(event)">@Translate("Load more products")</button>
718 </form>
719 }
720 </div>
721 </div>
722
723 <script>
724 function switchVariantProduct(id, price, imagesrc) {
725 var productImageElement = document.querySelector("#ProductImage_" + id);
726 var productPriceElement = document.querySelector("#ProductPrice_" + id + " .text-price");
727
728 if (productPriceElement) {
729 productPriceElement.innerText = price;
730 }
731
732 if (productImageElement) {
733 productImageElement.src = imagesrc;
734
735 var imageSrcset = productImageElement.srcset;
736 imageSrcset = imageSrcset.replace(/image=.*?&/g, 'image=' + imagesrc + "&");
737
738 productImageElement.srcset = imageSrcset;
739 }
740 }
741
742 function toggleCustomFields (i) {
743 var hiddenFields = document.getElementsByClassName("hidden-custom-field-" + i);
744 var button = document.getElementById("toggleFields_" + i);
745 var icon = document.getElementById("toggleFieldsIcon_" + i);
746
747 for (var i = 0; i < hiddenFields.length; i++) {
748 if (hiddenFields[i].style.display == 'none') {
749 hiddenFields[i].style.display = 'flex';
750 button.innerHTML = "Mindre detaljer";
751 icon.src = "/Files/Templates/Designs/Swift/Assets/icons/chevron-up.svg";
752 }
753 else
754 {
755 hiddenFields[i].style.display = 'none';
756 button.innerHTML = "Flere detaljer";
757 icon.src = "/Files/Templates/Designs/Swift/Assets/icons/chevron-down.svg";
758 }
759 }
760 }
761
762
763 </script>
764 } else {
765 if (!Pageview.IsVisualEditorMode) {
766 <div class="alert alert-dark m-0">
767 @Translate("We did not find anything matching your search result")
768 </div>
769 } else {
770 <div class="alert alert-dark m-0" role="alert">
771 <span>@Translate("Product list: The list will be shown here, if any")</span>
772 </div>
773 }
774 }
775 }
776
777 @helper RenderFieldsFromList(Dictionary<string, FieldValueViewModel> fields, int i) {
778
779 var count = 0;
780
781 <div class="mt-0">
782 <div class="mb-0">
783 @foreach (var field in fields) {
784 string fieldValue = field.Value?.Value != null ? field.Value.ToString() : "";
785
786 if (count >= 2)
787 {
788 @RenderHiddenCategoriesFields(field.Value, i)
789 }
790 else
791 {
792 @RenderCategoriesFields(field.Value, i)
793 }
794
795 if (!string.IsNullOrEmpty(fieldValue)) {
796 count++;
797 }
798 }
799
800 </div>
801 </div>
802 }
803
804 @helper RenderCategoriesFields(FieldValueViewModel field, int i)
805 {
806 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
807 bool noValues = false;
808
809 if (!string.IsNullOrEmpty(fieldValue))
810 {
811 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
812 {
813 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
814 noValues = values.Count > 0 ? false : true;
815 }
816 }
817
818 if (!string.IsNullOrEmpty(fieldValue) && noValues == false && fieldValue != "0" )
819 {
820 <div class="d-flex">
821 <span class="font-size-productlist-product" style="font-size: 13px; font-weight: 400;">@field.Name</span>
822 <span class="ms-auto font-size-productlist-product"style="font-size: 13px; font-weight: 400;">
823 @RenderCategoriesFieldsValue(field)
824 </span>
825 </div>
826 }
827 }
828
829 @helper RenderHiddenCategoriesFields(FieldValueViewModel field, int i)
830 {
831 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
832 bool noValues = false;
833
834 if (!string.IsNullOrEmpty(fieldValue))
835 {
836 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
837 {
838 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
839 noValues = values.Count > 0 ? false : true;
840 }
841 }
842
843 if (!string.IsNullOrEmpty(fieldValue) && noValues == false && fieldValue != "0")
844 {
845 <div class="hidden-custom-field-@i" style="display: none">
846 <span class="font-size-productlist-product" style="font-size: 13px">@field.Name</span>
847 <span class="ms-auto font-size-productlist-product"style="font-size: 13px">
848 @RenderCategoriesFieldsValue(field)
849 </span>
850 </div>
851 }
852 }
853
854 @helper RenderCategoriesFieldsValue(FieldValueViewModel field)
855 {
856 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
857
858 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue;
859 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue;
860
861 bool isColor = false;
862
863 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>))
864 {
865 int valueCount = 0;
866 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
867 int totalValues = values.Count;
868
869 foreach (FieldOptionValueViewModel option in values)
870 {
871 if (option.Value.Substring(0, 1) == "#")
872 {
873 isColor = true;
874 }
875
876 if (!isColor)
877 {
878 @option.Name
879 }
880 else
881 {
882 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span>
883 }
884
885 if (valueCount != totalValues && valueCount < (totalValues - 1))
886 {
887 if (isColor)
888 {
889 <text> </text>
890 }
891 else
892 {
893 <text>, </text>
894 }
895 }
896 valueCount++;
897 }
898 }
899 else
900 {
901 if (fieldValue.Substring(0, 1) == "#")
902 {
903 isColor = true;
904 }
905
906 if (!isColor)
907 {
908 @fieldValue
909 }
910 else
911 {
912 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span>
913 }
914 }
915 }
916
917 @helper RenderField(FieldValueViewModel field) {
918 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
919
920 if (fieldValue != "") {
921 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue;
922 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue;
923
924 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>)) {
925 fieldValue = "";
926
927 foreach (FieldOptionValueViewModel option in field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>) {
928 fieldValue = option.Value;
929 }
930 }
931
932 bool isColor = false;
933 if (fieldValue.Contains("#") && (Translate(field.Name) == Translate("Color") || Translate(field.Name) == Translate("Colour"))) {
934 isColor = true;
935 }
936
937 if (!string.IsNullOrEmpty(fieldValue)) {
938 if (!isColor) {
939 <li>@fieldValue</li>
940 } else {
941 <li class="position-relative">
942 <span class="colorbox-sm" style="background-color: @fieldValue"></span>
943 </li>
944 }
945 }
946 }
947 }
948
949