1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel>
2 @using Dynamicweb.Ecommerce.ProductCatalog
3 @using Dynamicweb.Ecommerce.CustomerExperienceCenter.Favorites
4 @using Dynamicweb.Ecommerce.Products.FieldDisplayGroups
5 @using Dynamicweb.Frontend
6 @using Dynamicweb.Core
7 @using System.Drawing
8 @using Dynamicweb.Environment
9
10 @functions {
11 //Find contrast color (white, black)
12 public static string GetContrastColor(string hexString)
13 {
14 System.Drawing.Color bg = System.Drawing.ColorTranslator.FromHtml(hexString);
15
16 int nThreshold = 105;
17 int bgDelta = Convert.ToInt32((bg.R * 0.299) + (bg.G * 0.587) +
18 (bg.B * 0.114));
19
20 string foreColor = (255 - bgDelta < nThreshold) ? "#333" : "#fff";
21 return foreColor;
22 }
23 }
24
25 @{
26 string googleAnalyticsTrackingID = Pageview.AreaSettings.GetString("GoogleAnalyticsTrackingID");
27 string googleAnalyticsMeasurementID = Pageview.AreaSettings.GetString("GoogleAnalyticsMeasurementID");
28 var cookieOptInLevel = CookieManager.GetCookieOptInLevel();
29 bool allowTracking = cookieOptInLevel == CookieOptInLevel.All || (cookieOptInLevel == CookieOptInLevel.Functional && CookieManager.GetCookieOptInCategories().Contains("Statistical"));
30
31 ProductViewModel product = new ProductViewModel();
32
33 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails"))
34 {
35 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"];
36 }
37
38 string anonymousUsersLimitations = Pageview.AreaSettings.GetRawValueString("AnonymousUsers", "");
39 bool anonymousUser = Pageview.User == null;
40 bool isErpConnectionDown = !Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsWebServiceConnectionAvailable();
41 bool hideAddToCart = anonymousUsersLimitations.Contains("cart") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHideAddToCart") && isErpConnectionDown;
42 hideAddToCart = product?.VariantInfo?.VariantInfo != null && Model.Item.GetBoolean("HideVariantSelector") ? true : hideAddToCart;
43 bool hideStock = Model.Item.GetBoolean("HideStockState") || (Pageview.AreaSettings.GetBoolean("ErpDownHideStock") && isErpConnectionDown);
44 bool hidePrice = anonymousUsersLimitations.Contains("price") && anonymousUser || Pageview.AreaSettings.GetBoolean("ErpDownHidePrices") && isErpConnectionDown;
45 bool hideFavoritesSelector = !string.IsNullOrEmpty(Model.Item.GetString("HideFavoritesSelector")) ? Model.Item.GetBoolean("HideFavoritesSelector") : false;
46
47 bool isDiscontinued = product.Discontinued;
48 bool IsNeverOutOfStock = product.NeverOutOfstock;
49 string[] variantId = { };
50
51 if (product?.VariantId != null) {
52 variantId = product.VariantId.Split('.');
53 }
54
55 string disableAddToCart = (product.StockLevel <= 0) ? "disabled" : "";
56 disableAddToCart = isDiscontinued ? "disabled" : disableAddToCart;
57 disableAddToCart = IsNeverOutOfStock ? "" : disableAddToCart;
58
59 // Does product has a expected delivery data
60 bool hasExpectedDelivery = product.ExpectedDelivery != null && product.ExpectedDelivery > DateTime.Now;
61 string expectedDeliveryDate = product.ExpectedDelivery?.ToShortDateString() ?? "";
62
63 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService"));
64 if (!url.Contains("LayoutTemplate"))
65 {
66 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml";
67 }
68
69 IEnumerable<string> selectedDisplayGroups = Model.Item.GetRawValueString("MainFeatures").Split(',').ToList();
70 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>();
71
72 foreach (var selection in selectedDisplayGroups)
73 {
74 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values)
75 {
76 if (selection == group.Id)
77 {
78 mainFeatures.Add(group);
79 }
80 }
81 }
82
83 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
84
85 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-6");
86
87 string contentPadding = Model.Item.GetRawValueString("ContentPadding", "");
88 contentPadding = contentPadding == "small" ? "p-2 p-md-3" : contentPadding;
89 contentPadding = contentPadding == "large" ? "p-4 p-md-5" : contentPadding;
90
91 string quantityPricesLayout = Model.Item.GetRawValueString("QuantityPricesLayout", "list");
92
93 string minQty = product.PurchaseMinimumQuantity != 1 ? "min=\"" + product.PurchaseMinimumQuantity.ToString() + "\"" : "min=\"1\"";
94 string stepQty = product.PurchaseQuantityStep > 1 ? product.PurchaseQuantityStep.ToString() : "1";
95 string valueQty = product.PurchaseMinimumQuantity > product.PurchaseQuantityStep ? product.PurchaseMinimumQuantity.ToString() : stepQty;
96 string qtyValidCheck = stepQty != "1" ? "onkeyup=\"swift.Cart.QuantityValidate(event)\"" : "";
97
98 string showPricesWithVat = Pageview.Area.EcomPricesWithVat.ToLower();
99 bool neverShowVat = string.IsNullOrEmpty(showPricesWithVat);
100
101 string priceMin = "";
102 string priceMax = "";
103
104 var favoriteParameters = new Dictionary<string, object>();
105 if (!anonymousUser && !hideFavoritesSelector)
106 {
107 IEnumerable<FavoriteList> favoreiteLists = Pageview.User.GetFavoriteLists();
108 int defaultFavoriteListId = 0;
109
110 if (favoreiteLists.Count() == 1) {
111 foreach (FavoriteList list in favoreiteLists) {
112 defaultFavoriteListId = list.ListId;
113 }
114 }
115
116 favoriteParameters.Add("ListId", defaultFavoriteListId);
117 }
118
119 var priceParms = new Dictionary<string, object>();
120 priceParms.Add("theme", "");
121
122 var badgeParms = new Dictionary<string, object>();
123 badgeParms.Add("size", "h7");
124 badgeParms.Add("saleBadgeType", Model.Item.GetRawValue("SaleBadgeType"));
125 badgeParms.Add("saleBadgeCssClassName", Model.Item.GetRawValue("SaleBadgeDesign"));
126 badgeParms.Add("newBadgeCssClassName", Model.Item.GetRawValue("NewBadgeDesign"));
127 badgeParms.Add("newPublicationDays", Model.Item.GetInt32("NewPublicationDays"));
128 badgeParms.Add("campaignBadgesValues", Model.Item.GetRawValueString("CampaignBadges"));
129
130 bool saleBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("SaleBadgeDesign")) && Model.Item.GetRawValueString("SaleBadgeDesign") != "none" ? true : false;
131 bool newBadgeEnabled = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("NewBadgeDesign")) && Model.Item.GetRawValueString("NewBadgeDesign") != "none" ? true : false;
132 DateTime createdDate = product.Created.Value;
133 bool showBadges = saleBadgeEnabled && product.Discount.Price != 0 ? true : false;
134 showBadges = (newBadgeEnabled && Model.Item.GetInt32("NewPublicationDays") == 0) || (newBadgeEnabled && (createdDate.AddDays(Model.Item.GetInt32("NewPublicationDays")) > DateTime.Now)) ? true : showBadges;
135 showBadges = !string.IsNullOrEmpty(Model.Item.GetRawValueString("CampaignBadges")) ? true : showBadges;
136
137 string liveInfoClass = "";
138 string productInfoFeed = "";
139 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Ecommerce.DynamicwebLiveIntegration.TemplatesHelper.IsLazyLoadingForProductInfoEnabled;
140 if (isLazyLoadingForProductInfoEnabled)
141 {
142 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed"))
143 {
144 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString();
145 if (!string.IsNullOrEmpty(productInfoFeed))
146 {
147 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\"";
148 }
149 }
150 liveInfoClass = "js-live-info";
151 }
152 @* Product stock state *@
153 double? currentStockLevel = product.StockLevel;
154
155
156 string stockStateLabel = !string.IsNullOrEmpty(product.StockStatus) ? product.StockStatus : "";
157
158 string stockStateCss = currentStockLevel > 0 ? "text-success" : "text-danger";
159 string stockStateIconCss = currentStockLevel > 0 ? "bg-success" : "bg-danger";
160 }
161
162 @if (!string.IsNullOrWhiteSpace(googleAnalyticsMeasurementID) && allowTracking)
163 {
164 <script>
165 gtag("event", "view_item", {
166 currency: "@product.Price.CurrencyCode",
167 value: @product.Price.Price,
168 items: [
169 {
170 item_id: "@product.Number",
171 item_name: "@product.Name",
172 currency: "@product.Price.CurrencyCode",
173 price: @product.Price.Price
174 }
175 ]
176 });
177 </script>
178 }
179
180 <div class="h-100 @(contentPadding) @(theme)
[email protected]()" @productInfoFeed>
181 <div class="d-flex flex-column gap-4 js-product" data-product-id="@product.Id">
182 @if (showBadges) {
183 <div class="swift_badge-collection">
184 @RenderPartial("Components/EcommerceBadge.cshtml", product, badgeParms)
185 </div>
186 }
187
188 <div class="d-flex flex-column gap-2">
189 <h1 class="@titleFontSize m-0" itemprop="name">@product.Name</h1>
190 @if (!Model.Item.GetBoolean("HideProductNumber"))
191 {
192 @RenderPartial("Paragraph/Swift_ProductNumber.cshtml", Model)
193 }
194 </div>
195
196 @if (!hidePrice && !isDiscontinued)
197 {
198 @RenderPartial("Paragraph/Swift_ProductPrice.cshtml", Model, priceParms)
199
200 if (isLazyLoadingForProductInfoEnabled)
201 {
202 <div class="product-prices-container @liveInfoClass d-none" data-show-if="LiveProductInfo.product.Prices.length > 0">
203 @if (quantityPricesLayout == "list")
204 {
205 <div class="mt-3 product-prices">
206 <small class="d-block opacity-75 product-prices-template"><span><span class="js-text-price-quantity"></span> @Translate("PCS")</span> - <span class="fw-bold"><span class="js-text-price-price"></span> <span class="d-none" data-show-if="LiveProductInfo.productPrice.Quantity > 1">@Translate("pr. PCS")</span></span></small>
207 </div>
208 }
209 else if (quantityPricesLayout == "table")
210 {
211 <div class="grid">
212 <table class="table table-sm mt-3 g-col-12 g-col-lg-6">
213 <thead>
214 <tr>
215 <td>@Translate("QTY")</td>
216 <td>@Translate("pr. PCS")</td>
217 </tr>
218 </thead>
219 <tbody class="product-prices">
220 <tr class="product-prices-template">
221 <td class="js-text-price-quantity"></td>
222 <td class="js-text-price-price"></td>
223 </tr>
224 </tbody>
225 </table>
226 </div>
227 }
228 </div>
229 }
230 else
231 {
232 if (product.Prices.Count > 0)
233 {
234 <div>
235 @if (quantityPricesLayout == "list")
236 {
237 <div class="mt-3">
238 @foreach (PriceListViewModel quantityPrice in product.Prices)
239 {
240 string quantityLabel = Translate("PCS");
241 string quantityPriceSuffix = quantityPrice.Quantity > 1 ? Translate("pr. PCS") : "";
242
243 <small class="d-block opacity-75"><span>@quantityPrice.Quantity @quantityLabel</span> - <span class="fw-bold">@quantityPrice.Price.PriceFormatted @quantityPriceSuffix</span></small>
244 }
245 </div>
246 }
247 else if (quantityPricesLayout == "table")
248 {
249 <div class="grid">
250 <table class="table table-sm mt-3 g-col-12 g-col-lg-6">
251 <thead>
252 <tr>
253 <td>@Translate("QTY")</td>
254 <td>@Translate("pr. PCS")</td>
255 </tr>
256 </thead>
257 <tbody>
258 @foreach (PriceListViewModel quantityPrice in product.Prices)
259 {
260 <tr>
261 <td>@quantityPrice.Quantity</td>
262 <td>@quantityPrice.Price.PriceFormatted</td>
263 </tr>
264 }
265 </tbody>
266 </table>
267 </div>
268 }
269 </div>
270 }
271 }
272 }
273
274 @RenderPartial("Paragraph/Swift_ProductShortDescription.cshtml", Model)
275
276 @if (mainFeatures.Count > 0)
277 {
278 foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures)
279 {
280 <dl class="grid gap-0">
281 @foreach (var field in mainFeatureGroup.Fields)
282 {
283 @RenderField(field.Value)
284 }
285 </dl>
286 }
287 }
288
289 @if (product.VariantInfo.VariantInfo != null && !Model.Item.GetBoolean("HideVariantSelector"))
290 {
291 int groupNumber = 1;
292
293 string baseUrl = $"Default.aspx?ID={GetPageIdByNavigationTag("Shop")}&GroupID={product.PrimaryOrDefaultGroup.Id}&ProductID={product.Id}";
294 string variantUrl = "";
295 if (!string.IsNullOrEmpty(product.VariantId))
296 {
297 variantUrl = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl($"Default.aspx?ID={GetPageIdByNavigationTag("Shop")}&GroupID={product.PrimaryOrDefaultGroup.Id}&ProductID={product.Id}&VariantID={product.VariantId}");
298 }
299
300 <form class="js-variant-selector" data-combinations="@string.Join(",", product.VariantCombinations())" data-base-url="@baseUrl" data-friendly-url="@variantUrl">
301 <input type="hidden" name="variantid" />
302
303 @foreach (var variantGroup in product.VariantGroups())
304 {
305 VariantGroupViewModel group = variantGroup;
306
307 <h3 class="h6">@group.Name</h3>
308 <div class="d-flex gap-2 flex-wrap js-variant-group" data-group-id="@groupNumber">
309 @foreach (var option in group.Options)
310 {
311 string active = variantId.Contains(option.Id) ? "active" : "";
312
313 if (!string.IsNullOrEmpty(option.Color))
314 {
315 string contrastColor = GetContrastColor(option.Color);
316 <button type="button" class="btn colorbox rounded-circle d-inline-block variant-option border js-variant-option @active" style="background-color: @option.Color; --variantoption-check-color: @contrastColor" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id" id="@(product.Id)_@(option.Id)
[email protected]"></button>
317 }
318 else if (!string.IsNullOrEmpty(option.Color) && !string.IsNullOrEmpty(option.Image.Value))
319 {
320 <button type="button" class="btn p-0 d-inline-block variant-option border js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id">
321 <img src="/Admin/Public/GetImage.ashx?image=@(option.Image.Value)&width=42&Format=WebP&Quality=70" />
322 </button>
323 }
324 else
325 {
326 <button type="button" class="btn btn-secondary d-inline-block variant-option js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id">
327 @option.Name
328 </button>
329 }
330 }
331 </div>
332
333 groupNumber++;
334 }
335 </form>
336 }
337
338 <div class="d-flex flex-row flex-nowrap gap-2">
339 @if (!hideAddToCart)
340 {
341 <form method="post" action="@url" class="flex-fill">
342 <input type="hidden" name="redirect" value="false" />
343 <input type="hidden" name="ProductId" value="@product.Id" />
344 <input type="hidden" name="ProductName" value="@product.Name" />
345 <input type="hidden" name="ProductPrice" value="@product.Price.Price" />
346 <input type="hidden" name="ProductCurrency" value="@product.Price.CurrencyCode" />
347 <input type="hidden" name="ProductReferer" value="product_details_info">
348 <input type="hidden" name="cartcmd" value="add" />
349
350 @if (!string.IsNullOrEmpty(product.VariantId))
351 {
352 <input type="hidden" name="VariantId" value="@product.VariantId" />
353 }
354 @if (!Model.Item.GetBoolean("QuantitySelector"))
355 {
356 <input id="
[email protected]" name="Quantity" value="@valueQty" type="hidden">
357 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary w-100 js-add-to-cart-button @disableAddToCart" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)
[email protected]">@Translate("Add to cart")</button>
358 } else {
359 <div class="input-group input-primary-button-group js-input-group d-flex flex-row flex-nowrap">
360 <label for="Quantity_@(product.Id)" class="visually-hidden">@Translate("Quantity")</label>
361 <input id="
[email protected]" name="Quantity" value="@valueQty" step="@stepQty" @minQty class="form-control" style="max-width: 96px; min-width:64px;" type="number" onkeydown="swift.Cart.UpdateOnEnterKey(event)">
362 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary flex-fill js-add-to-cart-button @disableAddToCart" @disableAddToCart title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)
[email protected]">@Translate("Add to cart")</button>
363 </div>
364
365 if (stepQty != "1")
366 {
367 <div class="invalid-feedback d-none">
368 @Translate("Please select a quantity that is dividable by") @stepQty
369 </div>
370 }
371 }
372 </form>
373 if (!anonymousUser && !hideFavoritesSelector)
374 {
375 @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters)
376 }
377 }
378 else if (!anonymousUser && !hideFavoritesSelector && !isDiscontinued)
379 {
380 <div class="flex-fill" id="
[email protected]">
381 @Translate("Add to favorites") @RenderPartial("Components/ToggleFavorite.cshtml", product, favoriteParameters)
382 </div>
383 }
384
385 @if (isDiscontinued && product.ReplacementProduct != null) {
386 List<ProductInfoViewModel> replacementProductList = new List<ProductInfoViewModel>();
387 replacementProductList.Add(product.ReplacementProduct);
388 var replacementProduct = replacementProductList.GetProducts().FirstOrDefault();
389
390 if ((product.DiscontinuedAction == 0 || product.DiscontinuedAction == 1) && product?.ReplacementProduct.ProductId != null) {
391 var parms = new Dictionary<string, object>();
392 parms.Add("cssClass", "d-block mw-100 mh-100 m-auto");
393 parms.Add("fullwidth", true);
394 parms.Add("columns", Model.GridRowColumnCount);
395
396 string imagePath = replacementProduct?.DefaultImage?.Value != null ? replacementProduct.DefaultImage.Value : "";
397
398 string link = "Default.aspx?ID=" + GetPageIdByNavigationTag("Shop");
399 link += $"&GroupID={replacementProduct.PrimaryOrDefaultGroup.Id}";
400 link += $"&ProductID={replacementProduct.Id}";
401 link += !string.IsNullOrEmpty(replacementProduct.VariantId) ? $"&VariantID={replacementProduct.VariantId}" : "";
402 link = Dynamicweb.Frontend.SearchEngineFriendlyURLs.GetFriendlyUrl(link);
403
404 <div class="w-100">
405 <div class="fw-bold w-100">@Translate("Sorry, this product is no longer available").</div>
406 <div>@Translate("We recommend this replacement product instead"):</div>
407
408 <a href="@link">
409 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms)
410 </a>
411
412 <div>@replacementProduct.Name</div>
413
414 @if (!hidePrice)
415 {
416 <div class="mb-3">
417 <div class="h4" itemprop="offers" itemscope itemtype="https://schema.org/Offer">
418 @if (showPricesWithVat == "false" && !neverShowVat)
419 {
420 if (isLazyLoadingForProductInfoEnabled)
421 {
422 <span itemprop="price" content="" class="d-none"></span>
423 <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>
424 }
425 else
426 {
427 string beforePrice = product.PriceBeforeDiscount.PriceWithoutVatFormatted;
428
429 <span itemprop="price" content="@product.Price.PriceWithoutVat" class="d-none"></span>
430 if (product.Price.Price != product.PriceBeforeDiscount.Price)
431 {
432 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span>
433 }
434 }
435 }
436 else
437 {
438 if (isLazyLoadingForProductInfoEnabled)
439 {
440 <span itemprop="price" content="" class="d-none"></span>
441 <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>
442 }
443 else
444 {
445 string beforePrice = product.PriceBeforeDiscount.PriceFormatted;
446
447 <span itemprop="price" content="@product.Price.Price" class="d-none"></span>
448 if (product.Price.Price != product.PriceBeforeDiscount.Price)
449 {
450 <span class="text-decoration-line-through opacity-75 me-3">@beforePrice</span>
451 }
452 }
453 }
454
455 @if (showPricesWithVat == "false" && !neverShowVat)
456 {
457 if (isLazyLoadingForProductInfoEnabled)
458 {
459 <span class="text-price js-text-price"><div class="spinner-border" role="status"></div></span>
460 }
461 else
462 {
463 string price = product.Price.PriceWithoutVatFormatted;
464 if (product?.VariantInfo?.VariantInfo != null)
465 {
466 priceMin = product?.VariantInfo?.PriceMin?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithoutVatFormatted : "";
467 priceMax = product?.VariantInfo?.PriceMax?.PriceWithoutVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithoutVatFormatted : "";
468 }
469 if (priceMin != priceMax)
470 {
471 price = priceMin + " - " + priceMax;
472 }
473 <span class="text-price">@price</span>
474 }
475 }
476 else
477 {
478 if (isLazyLoadingForProductInfoEnabled)
479 {
480 <span class="text-price js-text-price"><div class="spinner-border" role="status"></div></span>
481 }
482 else
483 {
484 string price = product.Price.PriceFormatted;
485 if (product?.VariantInfo?.VariantInfo != null)
486 {
487 priceMin = product?.VariantInfo?.PriceMin?.PriceFormatted != null ? product.VariantInfo.PriceMin.PriceFormatted : "";
488 priceMax = product?.VariantInfo?.PriceMax?.PriceFormatted != null ? product.VariantInfo.PriceMax.PriceFormatted : "";
489 }
490 if (priceMin != priceMax)
491 {
492 price = priceMin + " - " + priceMax;
493 }
494 <span class="text-price">@price</span>
495 }
496 }
497 </div>
498
499 @if (showPricesWithVat == "false" && !neverShowVat)
500 {
501 if (isLazyLoadingForProductInfoEnabled)
502 {
503 <small class="opacity-85 fst-normal js-text-price-with-vat d-none" data-suffix="@Translate("Incl. VAT")"></small>
504 }
505 else
506 {
507 string price = product.Price.PriceWithVatFormatted;
508 if (product?.VariantInfo?.VariantInfo != null)
509 {
510 priceMin = product?.VariantInfo?.PriceMin?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMin.PriceWithVatFormatted : "";
511 priceMax = product?.VariantInfo?.PriceMax?.PriceWithVatFormatted != null ? product.VariantInfo.PriceMax.PriceWithVatFormatted : "";
512 }
513 if (priceMin != priceMax)
514 {
515 price = priceMin + " - " + priceMax;
516 }
517 <small class="opacity-85 fst-normal">@price @Translate("Incl. VAT")</small>
518 }
519 }
520 </div>
521 }
522
523 <a href="@link" class="btn btn-primary w-100">@Translate("Go to the replacement")</a>
524 </div>
525 }
526 }
527 </div>
528 </div>
529 @if (!hideStock)
530 {
531 <div class="js-stock-state
[email protected]()">
532 <div class="small">
533 <span class="@stockStateCss">@stockStateLabel </span>
534 </div>
535 </div>
536 }
537 </div>
538
539 @helper RenderField(FieldValueViewModel field)
540 {
541 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
542 bool noValues = false;
543
544 if (!string.IsNullOrEmpty(fieldValue))
545 {
546 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
547 {
548 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
549 noValues = values.Count > 0 ? false : true;
550 }
551 }
552
553 if (!string.IsNullOrEmpty(fieldValue) && noValues == false)
554 {
555 <dt class="g-col-12 g-col-sm-4 g-col-lg-12 fw-bold m-0">@field.Name</dt>
556 <dd class="g-col-12 g-col-sm-8 g-col-lg-12 mb-3">
557 @RenderFieldValue(field)
558 </dd>
559 }
560 }
561
562 @helper RenderFieldValue(FieldValueViewModel field)
563 {
564 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
565
566 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue;
567 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue;
568
569 bool isColor = false;
570
571 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>))
572 {
573 int valueCount = 0;
574 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
575 int totalValues = values.Count;
576
577 foreach (FieldOptionValueViewModel option in values)
578 {
579 if (option.Value.Substring(0, 1) == "#")
580 {
581 isColor = true;
582 }
583
584 if (!isColor)
585 {
586 @option.Name
587 }
588 else
589 {
590 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span>
591 }
592
593 if (valueCount != totalValues && valueCount < (totalValues - 1))
594 {
595 if (isColor)
596 {
597 <text> </text>
598 }
599 else
600 {
601 <text>, </text>
602 }
603 }
604 valueCount++;
605 }
606 }
607 else
608 {
609 if (fieldValue.Substring(0, 1) == "#")
610 {
611 isColor = true;
612 }
613
614 if (!isColor)
615 {
616 @fieldValue
617 }
618 else
619 {
620 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span>
621 }
622 }
623 }
624