1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel>
2 @using Dynamicweb.Ecommerce.ProductCatalog
3 @using Dynamicweb.Core
4 @functions {
5 public string GetGroupFieldValue(string systemName, IList<FieldValueViewModel>? groupFields)
6 {
7 string groupField = string.Empty;
8 if (groupFields != null && groupFields is object)
9 {
10 var Systemfield = groupFields.FirstOrDefault(field => field.SystemName == systemName);
11 if(Systemfield != null)
12 {
13 groupField = groupFields.FirstOrDefault(field => field.SystemName == systemName)?.Value?.ToString();
14 }
15 }
16 return groupField;
17 }
18
19 void RenderInjectedContent(string Name, ProductListViewModel? productList)
20 {
21 var InjectedContentTop = GetGroupFieldValue(Name, productList?.Group?.GroupFields);
22 int.TryParse(InjectedContentTop.ToLower().Replace("default.aspx?id=", ""), out int InjectedContentTopId);
23 if (InjectedContentTopId > 0)
24 {
25 <div class="d-block g-col-12 mb-4 w-100">
26 @RenderGrid(InjectedContentTopId)
27 </div>
28 }
29 }
30 }
31 @{
32 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : "";
33 int productsCount = 0;
34 int maxProductsCounter = 0;
35
36 string productInfoFeed = "";
37 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]);
38 if (isLazyLoadingForProductInfoEnabled)
39 {
40 if (Dynamicweb.Context.Current.Items.Contains("ProductInfoFeed"))
41 {
42 productInfoFeed = Dynamicweb.Context.Current.Items["ProductInfoFeed"]?.ToString();
43 if (!string.IsNullOrEmpty(productInfoFeed))
44 {
45 productInfoFeed = $"data-product-info-feed=\"{productInfoFeed}\"";
46 }
47 }
48 }
49
50 ProductListViewModel productList = null;
51 if (Dynamicweb.Context.Current.Items.Contains("ProductList"))
52 {
53 productList = (ProductListViewModel)Dynamicweb.Context.Current.Items["ProductList"];
54 }
55 else if (Pageview.Page.Item["DummyProductGroup"] != null && Pageview.IsVisualEditorMode)
56 {
57 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page);
58 ProductListViewModel groupList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel();
59
60 if (groupList?.Products is object)
61 {
62 productList = groupList;
63 }
64 }
65 }
66
67 @if (productList is object)
68 {
69 RenderInjectedContent("InjectedContentTop", productList);
70
71 <div class="h-100@(theme) product-list
[email protected]()" @productInfoFeed>
72 @{
73 bool isVisualEditor = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) ? Convert.ToBoolean(Dynamicweb.Context.Current.Request.QueryString.Get("VisualEdit")) : false;
74
75 string groupId = productList?.Group?.Id != null ? productList.Group.Id : "";
76 string url = Dynamicweb.Context.Current.Request.RawUrl;
77
78 if (productList.TotalProductsCount > 0)
79 {
80 int pageSizeSetting = Converter.ToInt32(Dynamicweb.Context.Current.Request.QueryString.Get("OriginalPageSize")) > 0 ? Converter.ToInt32(Dynamicweb.Context.Current.Request.QueryString.Get("OriginalPageSize")) : productList.PageSize;
81 int pageNumber = Converter.ToInt32(Dynamicweb.Context.Current.Request.QueryString.Get("PageNum")) > 0 ? Converter.ToInt32(Dynamicweb.Context.Current.Request.QueryString.Get("PageNum")) : productList.CurrentPage;
82 int pageSize = Converter.ToInt32(Dynamicweb.Context.Current.Request.QueryString.Get("PageSize")) > 0 ? Converter.ToInt32(Dynamicweb.Context.Current.Request.QueryString.Get("PageSize")) : productList.PageSize;
83 pageNumber = pageSize != pageSizeSetting ? (pageSize / pageSizeSetting) : pageNumber;
84 int loadedProducts = productList.PageSize > productList.TotalProductsCount ? productList.TotalProductsCount : pageSizeSetting * pageNumber;
85 loadedProducts = loadedProducts > productList.TotalProductsCount ? productList.TotalProductsCount : loadedProducts;
86
87 pageNumber += 1;
88
89 string searchQuery = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("q")) ? Dynamicweb.Context.Current.Request.QueryString.Get("q") : string.Empty;
90 searchQuery = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.Form.Get("q")) ? Dynamicweb.Context.Current.Request.Form.Get("q") : searchQuery;
91 string searchLayout = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SearchLayout") : string.Empty;
92
93 int itemSourcePageId = Convert.ToInt32(Model.Item.GetRawValueString("ListComponentSource", "0"));
94
95 var pageService = new Dynamicweb.Content.PageService();
96 itemSourcePageId = itemSourcePageId > 0 && pageService.GetPageOrLanguage(itemSourcePageId, Pageview.AreaID) != null ? pageService.GetPageOrLanguage(itemSourcePageId, Pageview.AreaID).ID : itemSourcePageId;
97
98 var page = Dynamicweb.Content.Services.Pages.GetPage(itemSourcePageId);
99
100 if (page != null)
101 {
102 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(page);
103
104 string gridColumnSize = Model.Item.GetRawValueString("GridLayoutDesktop", "3-columns");
105 gridColumnSize = gridColumnSize == "2-columns" ? "g-col-lg-6" : gridColumnSize;
106 gridColumnSize = gridColumnSize == "3-columns" ? "g-col-lg-4" : gridColumnSize;
107 gridColumnSize = gridColumnSize == "4-columns" ? "g-col-lg-3" : gridColumnSize;
108 gridColumnSize = gridColumnSize == "6-columns" ? "g-col-lg-2" : gridColumnSize;
109 gridColumnSize = gridColumnSize == "list" ? "g-col-lg-12" : gridColumnSize;
110
111 string gridColumnMobileSize = Model.Item.GetRawValueString("GridLayoutMobile", "2-columns");
112 gridColumnMobileSize = gridColumnMobileSize == "list" ? "g-col-12" : gridColumnMobileSize;
113 gridColumnMobileSize = gridColumnMobileSize == "2-columns" ? "g-col-6" : gridColumnMobileSize;
114
115 string listItemTheme = " theme " + pageViewModel.Item.GetRawValueString("Theme", string.Empty).Replace(" ", "").Trim().ToLower();
116 string listItemPadding = pageViewModel.Item.GetRawValueString("ContentPadding", string.Empty);
117 string listItemPaddingClass = string.Empty;
118
119 switch (listItemPadding)
120 {
121 case "small":
122 listItemPaddingClass = " p-2 p-xl-3";
123 break;
124 case "large":
125 listItemPaddingClass = " p-3 p-xl-4";
126 break;
127 case "small-x":
128 listItemPaddingClass = " px-2 px-md-3";
129 break;
130 case "large-x":
131 listItemPaddingClass = " px-3 px-md-4";
132 break;
133 }
134
135 <div class="grid gap-2 gap-lg-4 pb-3">
136 <script>
137 gtag("event", "view_item_list", {
138 item_list_id: "product_list_item_repeater",
139 item_list_name: "Product list (Item Repeater)",
140 items: [
141 @foreach (ProductViewModel product in productList.Products)
142 {
143 <text>{
144 item_id: "@product.Number",
145 item_name: "@Dynamicweb.Core.Encoders.HtmlEncoder.JavaScriptStringEncode(product.Name)",
146 item_brand: "@product.ProductFields.GetValueOrDefault("Collection")?.Value",
147 currency: "@product.Price.CurrencyCode",
148 price: @PriceViewModelExtensions.ToStringInvariant(product.Price)
149 },</text>
150 }
151 ]
152 });
153 </script>
154
155 @foreach (ProductViewModel product in productList.Products)
156 {
157 if (maxProductsCounter == 0 || (productsCount < maxProductsCounter))
158 {
159 string link = product.GetProductLink(GetPageIdByNavigationTag("Shop"), false);
160
161 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails"))
162 {
163 Dynamicweb.Context.Current.Items["ProductDetails"] = product;
164 }
165 else
166 {
167 Dynamicweb.Context.Current.Items.Add("ProductDetails", product);
168 }
169
170 // INJECTED Content
171 if(productsCount == 8)
172 {
173 RenderInjectedContent("InjectedContentMiddle", productList);
174 }
175
176 if (Model.Item.GetString("ListComponentSource") != null)
177 {
178 <article class="@gridColumnMobileSize @gridColumnSize @listItemTheme @listItemPaddingClass d-flex flex-column position-relative product p-0 mb-4 overflow-hidden js-product" data-product-id="@product.Id" data-variant-id="@product.VariantId" itemscope itemtype="https://schema.org/Product">
179 @{
180 string clickProductLink = "onclick=\"return clickProductLink('" + @product.Id + "', '" + @product.Name + "', '" + @product.VariantName + "', '" + @product.Price.CurrencyCode + "', '" + @PriceViewModelExtensions.ToStringInvariant(product.Price) + "')\"";
181 string onMouseOver = "onmouseover=\"swapImages(event)\" onmouseout=\"swapImages(event)\"";
182 if (Pageview.Device == Dynamicweb.Frontend.Devices.DeviceType.Mobile)
183 {
184 onMouseOver = "";
185 }
186 }
187 <a href="@link" class="stretched-link" @onMouseOver @clickProductLink>
188 <span class="visually-hidden">@product.Name</span>
189 </a>
190
191 <script>
192 function clickProductLink(productId, productName, productVariant, productCurrency, productPrice) {
193 if (typeof gtag !== "undefined") {
194 gtag("event", "select_item", {
195 item_list_id: "product_list_item_repeater",
196 item_list_name: "Product list (Item Repeater)",
197 items: [
198 {
199 item_id: productId,
200 item_name: productName,
201 currency: productCurrency,
202 item_list_id: "product_list_item_repeater",
203 item_list_name: "Product list (Item Repeater)",
204 item_variant: productVariant,
205 price: productPrice
206 }
207 ]
208 });
209 }
210 }
211 </script>
212
213 @RenderGrid(itemSourcePageId)
214 </article>
215 }
216
217 productsCount++;
218 }
219 }
220 </div>
221
222 <div class="my-3" id="ProductListLoadMore">
223 <div class="text-center">
224 <div class="opacity-85 mb-3">@loadedProducts @Translate("out of") @productList.TotalProductsCount @Translate("products")</div>
225 @if (productList.PageCount != 1 && maxProductsCounter == 0 && loadedProducts < productList.TotalProductsCount)
226 {
227 string sortBySelection = Dynamicweb.Context.Current.Request?.Form["SortBy"] ?? "";
228 sortBySelection = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("SortBy")) ? Dynamicweb.Context.Current.Request.QueryString.Get("SortBy") : sortBySelection;
229 string mainProductId = !string.IsNullOrEmpty(Dynamicweb.Context.Current.Request.QueryString.Get("MainProductID")) ? Dynamicweb.Context.Current.Request.QueryString.Get("MainProductID") : "";
230
231 <form method="get" action="@url" data-response-target-element=".product-list" data-swap="afterend" class="w-100">
232 @if (productList?.FacetGroups != null)
233 {
234 foreach (FacetGroupViewModel facetGroup in productList.FacetGroups)
235 {
236 foreach (FacetViewModel facetItem in facetGroup.Facets)
237 {
238 foreach (FacetOptionViewModel facetOption in facetItem.Options)
239 {
240 if (facetOption.Selected)
241 {
242 <input type="hidden" name="@facetItem.QueryParameter" value="[@facetOption.Value]">
243 }
244 }
245 }
246 }
247 }
248
249 @if (!string.IsNullOrEmpty(searchQuery))
250 {
251 <input type="hidden" name="q" value="@searchQuery">
252 <input type="hidden" name="SearchLayout" value="@searchLayout">
253 }
254
255 @if (!string.IsNullOrEmpty(mainProductId))
256 {
257 <input type="hidden" name="MainProductID" value="@mainProductId">
258 }
259
260 @if (productList?.Group?.Id != null)
261 {
262 <input type="hidden" name="GroupId" value="@groupId">
263 }
264
265 <input type="hidden" name="OriginalPageSize" value="@pageSizeSetting">
266 <input type="hidden" name="PageSize" value="@(loadedProducts + pageSizeSetting)">
267 <input type="hidden" name="PageNum" value="@pageNumber">
268 <input type="hidden" name="SortBy" value="@sortBySelection">
269 <input type="hidden" name="RequestType" value="UpdateList">
270 <input type="hidden" name="ParagraphID" value="@Model.ID">
271
272 @if (productList?.FacetGroups is object)
273 {
274 string nextPageLink = $"/Default.aspx?ID={Pageview.Page.ID}&PageNum={pageNumber}&SortBy={sortBySelection}";
275
276 foreach (FacetGroupViewModel facetGroup in productList.FacetGroups)
277 {
278 foreach (FacetViewModel facetItem in facetGroup.Facets)
279 {
280 foreach (FacetOptionViewModel facetOption in facetItem.Options)
281 {
282 if (facetOption.Selected)
283 {
284 nextPageLink += "&" + facetItem.QueryParameter + "=[" + facetOption.Value + "]";
285 }
286 }
287 }
288 }
289
290 nextPageLink += !string.IsNullOrEmpty(searchQuery) ? "&q=" + searchQuery : "";
291 string disableLoadMore = isLazyLoadingForProductInfoEnabled ? "d-none" : "";
292
293 <a href="@nextPageLink" class="btn btn-primary swift_load_more_button @disableLoadMore" onclick="swift.ProductList.Update(event)" id="
[email protected]">@Translate("Load more products")</a>
294 }
295 </form>
296 }
297 </div>
298 </div>
299
300 RenderInjectedContent("InjectedContentBottom", productList);
301
302 <script>
303 function switchVariantProduct(id, price, imagesrc) {
304 var productImageElement = document.querySelector("#ProductImage_" + id);
305 var productPriceElement = document.querySelector("#ProductPrice_" + id + " .text-price");
306
307 if (productPriceElement) {
308 productPriceElement.innerText = price;
309 }
310
311 if (productImageElement) {
312 productImageElement.src = imagesrc;
313
314 var imageSrcset = productImageElement.srcset;
315 imageSrcset = imageSrcset.replace(/image=.*?&/g, 'image=' + imagesrc + "&");
316
317 productImageElement.srcset = imageSrcset;
318 }
319 }
320 </script>
321 }
322 else if (Pageview.IsVisualEditorMode)
323 {
324 <div class="alert alert-dark m-0" role="alert">
325 <span>@Translate("The selected component does not exist anymore")</span>
326 </div>
327 }
328 }
329 else
330 {
331 string noProductsFoundMessage = !string.IsNullOrEmpty(Model.Item.GetString("NoProductsFoundMessage")) ? Model.Item.GetString("NoProductsFoundMessage") : Translate("We did not find anything matching your search result");
332 bool hasSubgroups = false;
333
334 if (productList.SubGroups != null)
335 {
336 hasSubgroups = productList.SubGroups.Any();
337 }
338
339 if (!Model.Item.GetBoolean("HideNoProductsFoundMessage"))
340 {
341 if (!isVisualEditor)
342 {
343 string pagegroupId = Dynamicweb.Context.Current.Request["GroupID"] != null ? Dynamicweb.Context.Current.Request["GroupID"].ToString() : "";
344 <div class="alert text-center m-0 p-5 pb-2" role="alert">
345 @noProductsFoundMessage
346 </div>
347 <div class="text-center mb-4">
348 <a href="/
[email protected]&groupid=@pagegroupId">@Translate("Reset filters")</a>
349 </div>
350 }
351 else
352 {
353 <div class="alert alert-dark m-0" role="alert">
354 @Translate("Product list: The list will be shown here, if any")
355 </div>
356 }
357 }
358 else if (!hasSubgroups)
359 {
360 <div class="alert alert-dark m-0" role="alert">
361 @noProductsFoundMessage
362 </div>
363 }
364 }
365 }
366 </div>
367 if (Dynamicweb.Context.Current.Items.Contains("CompareStore") == false)
368 {
369 Dynamicweb.Context.Current?.Items.Add("CompareStore", $"compare-{Pageview.AreaID}");
370 }
371 @RenderPartial("Components/CompareProducts.cshtml")
372 }
373 else if (Pageview.IsVisualEditorMode)
374 {
375 <div class="alert alert-dark m-0" role="alert">
376 <span>@Translate("Product list item repeater: The repeater paragraph will be shown here, if any products are available")</span>
377 </div>
378 }
379