Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsGallery.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
at CompiledRazorTemplates.Dynamic.RazorEngine_0f736e6a0c354ea8a4d0a26fccca7b4b.Execute() in D:\dynamicweb.net\Solutions\scanoffice.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsGallery.cshtml:line 18
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.Frontend
4
5 @{
6 ProductViewModel product = new ProductViewModel();
7
8 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails"))
9 {
10 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"];
11 }
12
13 @* Collect the images *@
14 var selectedImageCategories = Model.Item.GetList("ImageAssets").SelectedValues;
15 bool includeImagePatternImages = Model.Item.GetBoolean("ImagePatternImages");
16
17 @* Needed image data collection to support both DefaultImage, ImagePatterns and Image Assets *@
18 IEnumerable<MediaViewModel> assetsImages = product.AssetCategories.Where(x => selectedImageCategories.Contains(x.SystemName)).SelectMany(x => x.Assets);
19 IEnumerable<MediaViewModel> images = new MediaViewModel[]{};
20 images = includeImagePatternImages && assetsImages.Count() == 0 ? images.Append(product.DefaultImage) : images;
21 images = images.Union(assetsImages);
22 images = includeImagePatternImages ? images.Union(product.ImagePatternImages) : images;
23
24 bool defaultImageFallback = Model.Item.GetBoolean("DefaultImageFallback");
25
26 int totalImages = 0;
27 foreach (MediaViewModel asset in images) {
28 var assetName = asset.Value.ToLower();
29 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) {
30 totalImages++;
31 }
32 }
33
34 if (totalImages == 0)
35 {
36 if (defaultImageFallback) {
37 images = new List<MediaViewModel>(){ product.DefaultImage };
38 totalImages = 1;
39 } else {
40 images = new List<MediaViewModel>(){ };
41 totalImages = 0;
42 }
43 }
44
45 @* Layout settings *@
46 string spacing = Model.Item.GetRawValueString("Spacing", "4");
47 spacing = spacing == "none" ? "gap-0" : spacing;
48 spacing = spacing == "small" ? "gap-3" : spacing;
49 spacing = spacing == "large" ? "gap-4" : spacing;
50
51 string layout = Model.Item.GetRawValueString("Layout", "grid");
52 }
53
54 @* Get images from selected categories or get all images *@
55 @if (totalImages != 0 && images.Count() != 0)
56 {
57 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
58
59 if (layout != "custom-slider")
60 {
61
62 @* Show the gallery on large screens *@
63 <div class="d-none d-lg-block h-100 @theme">
64 <div class="grid @spacing">
65 @{
66 int imageNumber = 0;
67
68 foreach (MediaViewModel asset in images)
69 {
70 var assetName = asset.Value.ToLower();
71
72 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif"))
73 {
74 string colClass = totalImages > 1 ? "g-col-lg-6" : "g-col-12";
75 colClass = layout == "full-first" && imageNumber == 0 ? "g-col-12" : colClass;
76 colClass = layout == "full-last" && imageNumber == (totalImages - 1) ? "g-col-12" : colClass;
77 colClass = layout == "advanced-grid" && imageNumber > 1 ? "g-col-4" : colClass;
78
79 colClass = layout == "advanced-grid" && totalImages == 1 ? "g-col-12" : colClass;
80 colClass = layout == "advanced-grid" && totalImages == 3 && imageNumber == 2 ? "g-col-12" : colClass;
81 colClass = layout == "advanced-grid" && totalImages == 4 && imageNumber == 2 ? "g-col-6" : colClass;
82 colClass = layout == "advanced-grid" && totalImages == 4 && imageNumber == 3 ? "g-col-6" : colClass;
83 colClass = layout == "advanced-grid" && totalImages == 6 && imageNumber == 5 ? "g-col-12" : colClass;
84 colClass = layout == "advanced-grid" && totalImages == 7 && imageNumber == 5 ? "g-col-6" : colClass;
85 colClass = layout == "advanced-grid" && totalImages == 7 && imageNumber == 6 ? "g-col-6" : colClass;
86 colClass = layout == "advanced-grid" && totalImages == 9 && imageNumber == 8 ? "g-col-12" : colClass;
87
88 <div class="@colClass">
89 @RenderImage(asset, product, imageNumber)
90 </div>
91
92 imageNumber++;
93 }
94 }
95 }
96 </div>
97 </div>
98
99 }
100
101 @* Modal *@
102 <div class="modal fade" id="modal_@Model.ID" tabindex="-1" aria-labelledby="productDetailsGalleryModalTitle" aria-hidden="true">
103 <div class="modal-dialog modal-dialog-centered modal-xl">
104 <div class="modal-content">
105 <div class="modal-header visually-hidden">
106 <h5 class="modal-title" id="productDetailsGalleryModalTitle">@Translate("Full image")</h5>
107 </div>
108 <div class="modal-body">
109 <div id="FullImages_@Model.ID" class="d-flex align-items-center justify-content-center">
110 @foreach (MediaViewModel asset in images)
111 {
112 var assetName = asset.Value.ToLower();
113 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif"))
114 {
115 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value;
116 var parms = new Dictionary<string, object>();
117 parms.Add("columns", Model.GridRowColumnCount);
118 parms.Add("cssClass", "mw-100 mh-100");
119 <span class="d-flex align-items-center justify-content-center overflow-hidden h-100">
120 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms)
121 </span>
122 }
123 }
124 </div>
125 <script type="module">
126 var sliderFull_@(Model.ID) = tns({
127 container: '#FullImages_@Model.ID',
128 items: 1,
129 gutter: 16,
130 mouseDrag: true,
131 touch: true,
132 arrowKeys: true,
133 nav: false,
134 loop: false,
135 speed: 10,
136 controls: false,
137 swipeAngle: 30,
138 preventScrollOnTouch: 'auto'
139 });
140
141 if (document.getElementById('modal_@Model.ID')) {
142 document.getElementById('modal_@Model.ID').addEventListener('show.bs.modal', function (event) {
143 var slideNumber = event.relatedTarget.getAttribute("data-image-number");
144 sliderFull_@(Model.ID).goTo(slideNumber);
145 });
146 }
147 </script>
148 </div>
149 </div>
150 </div>
151 </div>
152
153
154
155 @* Show the thumbs on small screens *@
156 string defaultSliderClass = "d-block d-lg-none mini-product-image-slider mx-lg-0" + theme;
157 string customSliderClass = "d-block d-lg-flex flex-lg-row mini-product-image-slider mx-lg-0" + theme;
158 string sliderClass = layout == "custom-slider" ? @customSliderClass : @defaultSliderClass;
159 <div class="@sliderClass">
160 <div class="custom-slider d-lg-flex flex-lg-column flex-lg-fill border me-lg-2">
161 <div id="SmallScreenImages_@Model.ID">
162 @{
163 var smallImageNumber = 0;
164
165 foreach (MediaViewModel asset in images)
166 {
167 var assetName = asset.Value.ToLower();
168
169 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif"))
170 {
171 @RenderImage(asset, product, smallImageNumber, "small")
172 smallImageNumber++;
173 }
174 }
175 }
176 </div>
177 </div>
178
179
180 @{
181 if (images.Count() > 1)
182 {
183 <div id="SmallScreenImagesThumbnails_@Model.ID" class="custom-thumbnails tns-nav-thumbnails grid gap-2 my-3 m-lg-0 d-lg-flex flex-lg-column h-100">
184 @{
185 foreach (MediaViewModel asset in images) {
186 var assetName = asset.Value.ToLower();
187 if (assetName.Contains(".jpg") || assetName.Contains(".webp") || assetName.Contains(".png") || assetName.Contains(".gif")) {
188
189 string imagePath = Dynamicweb.Context.Current.Server.UrlEncode(assetName);
190 string imagePathThumb = $"/Admin/Public/GetImage.ashx?image={imagePath}&width=160&format=webp";
191
192 <div class="tns-nav-thumbnail-item ratio ratio-4x3 border outline-none" style="width:80px;"><img src="@imagePathThumb" class="p-2" style="object-fit: cover;"></div>
193 }
194 }
195 }
196 </div>
197 }
198 }
199
200 @* Tiny slider *@
201 <script type="module">
202 var slider_@Model.ID = tns({
203 container: '#SmallScreenImages_@Model.ID',
204 items: 1,
205 gutter: 16,
206 mouseDrag: true,
207 touch: true,
208 arrowKeys: true,
209 nav: true,
210 navContainer: "#SmallScreenImagesThumbnails_@Model.ID",
211 navAsThumbnails: true,
212 loop: false,
213 controls: false,
214 swipeAngle: 30,
215 preventScrollOnTouch: 'auto',
216 autoHeight: false,
217 });
218 </script>
219 </div>
220 }
221
222 @helper RenderImage(MediaViewModel asset, ProductViewModel product, int number, string screenSize = "large") {
223 string layout = Model.Item.GetRawValueString("Layout", "grid");
224 string padding = "";
225 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : "";
226
227 if (theme != "")
228 {
229 padding = "p-3";
230 }
231
232 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", "");
233 ratio = ratio != "0" ? ratio : "";
234 string ratioCssClass = ratio != "" && ratio != "fill" ? " ratio" : "";
235 string ratioVariable = ratio != "" && ratio != "fill" ? "--bs-aspect-ratio: " + ratio : "";
236 ratioCssClass = ratio != "" && ratio == "fill" && screenSize == "small" ? " ratio" : ratioCssClass;
237 ratioVariable = ratio != "" && ratio == "fill" && screenSize == "small" ? "--bs-aspect-ratio: 66%" : ratioVariable;
238 string fillClass = ratio == "fill" ? " h-100" : "";
239
240 string imagePath = !string.IsNullOrEmpty(asset.Value) ? asset.Value : product.DefaultImage.Value;
241 string imageLinkPath = Dynamicweb.Context.Current.Server.UrlEncode(imagePath);
242 string productName = product.Name;
243
244 var parms = new Dictionary<string, object>();
245 parms.Add("alt", product.Name);
246 parms.Add("itemprop", "image");
247 parms.Add("columns", Model.GridRowColumnCount);
248
249 if (ratio == "fill" && layout != "grid") {
250 parms.Add("cssClass", "w-100 h-100 image-zoom-lg");
251 }
252 else
253 {
254 parms.Add("cssClass", "mw-100 mh-100");
255 }
256
257
258 <div class="h-100 @(padding)@(theme)">
259 <a href="@imageLinkPath" class="d-block h-100 @(ratioCssClass)@(fillClass)" data-bs-toggle="modal" data-bs-target="#modal_@Model.ID" data-image-number="@number">
260 <span class="d-flex align-items-center justify-content-center overflow-hidden h-100">
261 @RenderPartial("Components/Image.cshtml", new FileViewModel { Path = imagePath }, parms)
262 </span>
263 </a>
264 </div>
265 }
266
Error executing template "Designs/Swift/Paragraph/Swift_ProductDetailsInfo.cshtml"
System.ArgumentNullException: Value cannot be null.
Parameter name: source
at System.Linq.Enumerable.Where[TSource](IEnumerable`1 source, Func`2 predicate)
at CompiledRazorTemplates.Dynamic.RazorEngine_1ecbe748485d47ea8fb5ec3fdd273fb6.Execute() in D:\dynamicweb.net\Solutions\scanoffice.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_ProductDetailsInfo.cshtml:line 18
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.Products.FieldDisplayGroups
4 @using Dynamicweb.Frontend
5 @using Dynamicweb.Ecommerce.Products
6 @using ScanOffice.CustomModules
7 @{
8 bool isVisualEditor = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) : false;
9 bool userIsLoggedIn = Pageview.User != null && Pageview.User.ID > 0 ? true : false;
10 ProductViewModel product = new ProductViewModel();
11
12 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails"))
13 {
14 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"];
15 }
16 string ItemAvailabilityIdentifier = string.Join(".", product.Id, Dynamicweb.Ecommerce.Common.Context.LanguageID, "itemAvailableDate");
17
18 var _customerServiceETA = product.ProductFields.Where(x => x.Key == "CustomerServiceETA").FirstOrDefault().Value.Value;
19 bool customerServiceETA = _customerServiceETA == null ? false : Convert.ToBoolean(_customerServiceETA);
20
21 //SQSCOFECOM-250 - disable add to cart button with custom text message
22 string DisableAddToCartButton = product.ProductFields.Where(x => x.Key == "DisableAddToCartButton").FirstOrDefault().Value.Value.ToString();
23
24 var _expectedDeliveryDate = product.ProductFields.Where(x => x.Key == "ExpectedDeliveryDate").FirstOrDefault().Value.Value;
25 DateTime expectedDeliveryDate = Convert.ToDateTime(_expectedDeliveryDate);
26
27 var bundleIncludedProducts = product.ProductFields.Single(x => x.Key == "BundleIncludedProducts");
28 string bipValue = bundleIncludedProducts.Value.Value.ToString();
29 bool bipViewProduct = string.IsNullOrWhiteSpace(bipValue) || bipValue == " " ? false : true;
30
31 var hideproductfieldValue = product.ProductFields.Single(x => x.Key == "HideProduct").Value.Value.ToString();
32 bool productNotAvailable = Convert.ToBoolean(hideproductfieldValue);
33
34 string[] variantId = product.VariantId.Split('.');
35 string disableAddToCart = string.IsNullOrEmpty(product.VariantId) && product.VariantInfo.VariantInfo != null ? "disabled" : "";
36 string hideStockState = string.IsNullOrEmpty(product.VariantId) && product.VariantInfo.VariantInfo != null ? "d-none" : "";
37
38 string url = "/Default.aspx?ID=" + (GetPageIdByNavigationTag("CartService"));
39 if (!url.Contains("LayoutTemplate"))
40 {
41 url += url.Contains("?") ? "&LayoutTemplate=Swift_MiniCart.cshtml" : "?LayoutTemplate=Swift_MiniCart.cshtml";
42 }
43
44 IEnumerable<string> selectedDisplayGroups = Model.Item.GetList("MainFeatures").SelectedValues;
45 List<CategoryFieldViewModel> mainFeatures = new List<CategoryFieldViewModel>();
46
47 foreach (var selection in selectedDisplayGroups)
48 {
49
50 foreach (CategoryFieldViewModel group in product.FieldDisplayGroups.Values)
51 {
52
53 if (selection == group.Id)
54 {
55 mainFeatures.Add(group);
56 }
57 }
58 }
59
60 if (DisableAddToCartButton == "True")
61 {
62 disableAddToCart = "disabled";
63 }
64
65
66 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
67
68 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "display-6");
69
70 string contentPadding = Model.Item.GetRawValueString("ContentPadding", "");
71 contentPadding = contentPadding == "small" ? "p-2 p-md-3" : contentPadding;
72 contentPadding = contentPadding == "large" ? "p-4 p-md-5" : contentPadding;
73
74 string NoutovarastoTRE = product.ProductFields.Where(x => x.Key == "NoutovarastoTRE").FirstOrDefault().Value.Value.ToString();
75 }
76
77 <div class="h-100 @(contentPadding) @(theme)">
78 <div class="d-flex flex-column js-product">
79 <div>
80 <h4 class="lato-h4" itemprop="name">@product.Name</h4>
81 @if (!Model.Item.GetBoolean("HideProductNumber"))
82 {
83 <div class="open-sans-caption text-color-medium">@product.Number</div>
84 }
85 </div>
86
87 @if (!string.IsNullOrEmpty(product.ShortDescription))
88 {
89 <div class="mt-3 open-sans-body-one" itemprop="disambiguatingDescription">
90 @product.ShortDescription
91 </div>
92 }
93
94 @{
95 bool showWarehouseLocation = false;
96
97 if (Pageview.User != null)
98 {
99 if (Pageview.User.Groups != null)
100 {
101 showWarehouseLocation = Pageview.User.Groups.Any(g =>
102 g?.CustomFieldValues?.Any(cfv =>
103 cfv?.CustomField?.SystemName == "AccessUser_ShowWarehouseLocation" && cfv?.Value?.ToString().ToLower() == "true"
104 ) == true
105 );
106 }
107
108 if (!showWarehouseLocation && Pageview.User.CustomFieldValues != null)
109 {
110 showWarehouseLocation = Pageview.User.CustomFieldValues.Any(cfv =>
111 cfv?.CustomField?.SystemName == "AccessUser_ShowWarehouseLocation" && cfv?.Value?.ToString().ToLower() == "true"
112 );
113 }
114 }
115
116 if (showWarehouseLocation)
117 {
118 <div class="lato-h5" itemprop="warehouseLocation">
119 @NoutovarastoTRE
120 </div>
121 }
122 }
123
124 @if (userIsLoggedIn && !bipViewProduct && !Helpers.IsHiddedPrice())
125 {
126 <div>
127 <span class="open-sans-caption text-color-medium">@Translate("Price")</span>
128 <div class="h4 mb-0" itemprop="offers" itemscope itemtype="https://schema.org/Offer">
129 <span itemprop="priceCurrency" content="@product.Price.CurrencyCode" class="d-none"></span>
130 <span itemprop="price" content="@product.Price.Price" class="d-none"></span>
131 <span class="text-price lato-h5 text-color-primary">@product.Price.PriceFormatted</span>
132 </div>
133 @if (product.Price.Price != product.PriceBeforeDiscount.Price)
134 {
135 <div class="text-decoration-line-through opacity-85">
136 @product.PriceBeforeDiscount.PriceFormatted
137 </div>
138 }
139 </div>}
140
141 @if (mainFeatures.Count > 0)
142 {
143 <div class="grid gap-0">
144 @foreach (CategoryFieldViewModel mainFeatureGroup in mainFeatures)
145 {
146 foreach (var field in mainFeatureGroup.Fields)
147 {
148 @RenderField(field.Value)
149 }
150 }
151 </div>
152 }
153
154 @if (product.VariantInfo.VariantInfo != null)
155 {
156 int groupNumber = 1;
157
158 <div class="mb-3 js-variant-selector" data-combinations="@string.Join(",", product.VariantCombinations())">
159 @foreach (var variantGroup in product.VariantGroups())
160 {
161 VariantGroupViewModel group = variantGroup;
162
163 <h3 class="h6">@group.Name</h3>
164 <div class="mb-3 js-variant-group" data-group-id="@groupNumber">
165 @foreach (var option in group.Options)
166 {
167 string active = variantId.Contains(option.Id) ? "active" : "";
168
169 if (!string.IsNullOrEmpty(option.Color))
170 {
171 <button type="button" class="btn colorbox rounded-circle me-1 mb-2 d-inline-block variant-option js-variant-option @active" style="background-color: @option.Color" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id"></button>
172 }
173 else if (!string.IsNullOrEmpty(option.Color) && !string.IsNullOrEmpty(option.Image.Value))
174 {
175 <button type="button" class="btn p-0 d-inline-block mb-2 variant-option js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id">
176 <img src="/Admin/Public/GetImage.ashx?image=@(option.Image.Value)&width=42&Format=WebP&Quality=70" />
177 </button>
178 }
179 else
180 {
181 <button type="button" class="btn btn-secondary d-inline-block mb-2 variant-option js-variant-option @active" onclick="swift.VariantSelector.OptionClick(event)" data-variant-id="@option.Id">
182 @option.Name
183 </button>
184 }
185 }
186 </div>
187
188 groupNumber++;
189 }
190 </div>
191 }
192 @if (DisableAddToCartButton == "True" && !bipViewProduct)
193 {
194 string DisableAddToCartText = Helpers.GetAreaItemValue("DisableAddToCart");
195 <div class="d-flex align-items-center">
196 <small class="text-danger"><span class="open-sans-body-one"><i class="fas fa-circle instock"></i> @DisableAddToCartText</span></small>
197 </div>
198 }
199 else if (!Model.Item.GetBoolean("HideStockState") && userIsLoggedIn && !bipViewProduct)
200 {
201 var ItemStatus = Helpers.ItemAvailabilityStatus(ItemAvailabilityIdentifier, customerServiceETA, expectedDeliveryDate, DisableAddToCartButton);
202 <div class="mt-2 js-stock-state @hideStockState">
203 <small class="@ItemStatus.Status"><span class="open-sans-body-one me-2"><i class="fas fa-circle instock"></i> @Translate(ItemStatus.statusText)</span></small>
204 </div>
205 }
206 @if (userIsLoggedIn && !bipViewProduct)
207 {
208 if (!productNotAvailable)
209 {
210 <form method="post" action="@url" class="mt-3">
211 <div class="d-flex flex-column">
212 <input type="hidden" name="redirect" value="false" />
213 <input type="hidden" name="ProductId" value="@product.Id" />
214 <input type="hidden" name="cartcmd" value="add" />
215
216 @if (!string.IsNullOrEmpty(product.VariantId))
217 {
218 <input type="hidden" name="VariantId" value="@product.VariantId" />
219 }
220
221 @if (!Model.Item.GetBoolean("QuantitySelector"))
222 {
223 <div class="flex-fill">
224 <input id="Quantity_@product.Id" name="Quantity" value="1" type="hidden">
225 <button type="button" onclick="swift.Cart.Update(event)" class="btn btn-primary js-add-to-cart-button w-100 @disableAddToCart" title="@Translate("Add to cart")" id="AddToCartButton@(product.Id)">@Translate("Add to cart")</button>
226 </div>
227 }
228 else
229 {
230 <div class="flex-fill input-primary-button-group d-flex flex-row mb-2">
231 <span class="btn btn-primary me-1 @disableAddToCart" onclick="swift.ProductList.UpdateProductQuantity('minus', 'Quantity_@product.Id.Replace("/",@"\\/")', '@product.PurchaseQuantityStep')"><i class="fas fa-minus fa-sm"></i></span>
232 @if (product.PurchaseMinimumQuantity == 0)
233 {
234 <input id="Quantity_@product.Id" name="Quantity" value="1" class="form-control no-arrows mx-2 text-center" type="number" min="1">
235 }
236 else
237 {
238 <input id="Quantity_@product.Id" name="Quantity" value="@product.PurchaseMinimumQuantity" class="form-control no-arrows mx-2 text-center" type="number" min="@product.PurchaseMinimumQuantity">
239
240 }
241 <span class="btn btn-primary ms-1 @disableAddToCart" onclick="swift.ProductList.UpdateProductQuantity('plus', 'Quantity_@product.Id.Replace("/",@"\\/")', '@product.PurchaseQuantityStep')"><i class="fas fa-plus fa-sm"></i></span>
242 </div>
243
244 <div class="flex-fill input-group input-primary-button-group d-flex flex-row">
245 <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)">@Translate("Add to cart")</button>
246 </div>
247 }
248 </div>
249 </form>
250 }
251 }
252 </div>
253 </div>
254
255 @helper RenderField(FieldValueViewModel field)
256 {
257 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
258 bool noValues = false;
259
260 if (!string.IsNullOrEmpty(fieldValue))
261 {
262 if (field.Value.GetType() == typeof(System.Collections.Generic.List<FieldOptionValueViewModel>))
263 {
264 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
265 noValues = values.Count > 0 ? false : true;
266 }
267 }
268
269 if (!string.IsNullOrEmpty(fieldValue) && noValues == false)
270 {
271 <dt class="g-col-12 g-col-sm-4 g-col-lg-12 fw-bold m-0">@field.Name</dt>
272 <dd class="g-col-12 g-col-sm-8 g-col-lg-12 mb-3">
273 @RenderFieldValue(field)
274 </dd>
275 }
276 }
277
278 @helper RenderFieldValue(FieldValueViewModel field)
279 {
280 string fieldValue = field?.Value != null ? field.Value.ToString() : "";
281
282 fieldValue = fieldValue == "False" ? Translate("No") : fieldValue;
283 fieldValue = fieldValue == "True" ? Translate("Yes") : fieldValue;
284
285 bool isColor = false;
286
287 if (field.Value.GetType() == typeof(System.Collections.Generic.List<Dynamicweb.Ecommerce.ProductCatalog.FieldOptionValueViewModel>))
288 {
289 int valueCount = 0;
290 System.Collections.Generic.List<FieldOptionValueViewModel> values = field.Value as System.Collections.Generic.List<FieldOptionValueViewModel>;
291 int totalValues = values.Count;
292
293 foreach (FieldOptionValueViewModel option in values)
294 {
295 if (option.Value.Substring(0, 1) == "#")
296 {
297 isColor = true;
298 }
299
300 if (!isColor)
301 {
302 @option.Name
303 }
304 else
305 {
306 <span class="colorbox-sm" style="background-color: @option.Value" title="@option.Value"></span>
307 }
308
309 if (valueCount != totalValues && valueCount < (totalValues - 1))
310 {
311 if (isColor)
312 {
313 <text> </text>
314 }
315 else
316 {
317 <text>, </text>
318 }
319 }
320 valueCount++;
321 }
322 }
323 else
324 {
325 if (fieldValue.Substring(0, 1) == "#")
326 {
327 isColor = true;
328 }
329
330 if (!isColor)
331 {
332 @fieldValue
333 }
334 else
335 {
336 <span class="colorbox-sm" style="background-color: @fieldValue" title="@fieldValue"></span>
337 }
338 }
339 }
340
341 @if (product.VariantInfo.VariantInfo != null)
342 {
343 <script>
344 //Initialize the variant selector
345 document.addEventListener('load', function (event) {
346 swift.VariantSelector.init();
347 });
348 </script>
349 }
350
351 <script>
352 window.addEventListener('keydown', function (e) {
353 if (e.keyIdentifier == 'U+000A' || e.keyIdentifier == 'Enter' || e.keyCode == 13) {
354 if (e.target.nodeName == 'INPUT' && e.target.getAttribute("type") != "search") {
355 e.preventDefault();
356 return false;
357 }
358 }
359 }, true);
360 </script>
361