Salade de betteraves,

fromage de chèvre et réduction de vinaigre balsamique

Salade de betteraves,

Pour 4 personnes | 25 minutes

Cette salade accompagnera aussi bien du poisson qu'une viande, ou trouvera sa place dans un buffet.

Ingrédients

Réduction de vinaigre balsamique :

  • 100 ml de vinaigre balsamique
  • 50 ml de miel liquide

Salade :

  • 400 g de betteraves, si possible de différentes couleurs
  • 100 g de noix
  • 100 g de roquette
  • 100 g de fromage de chèvre frais
  • un peu d'huile d'olive
  • sel et poivre

Préparation

Versez le vinaigre balsamique et le miel dans une casserole et faites réduire de moitié environ. Réfrigérez la réduction.

Pelez les betteraves et émincez-les en tranches fines à l'aie d'une mandoline. Placez-les dans de l'eau froide.

Hachez les noix.

Égouttez les betteraves et dressez-les dans un plat avec la roquette, les noix et les éclats de fromage de chèvre. Ajoutez un filet d'huile d'olive, salez et poivrez, puis arrosez de réduction de balsamique à votre convenance.

Error executing template "Designs/Swift/Paragraph/Swift_RelatedProducts_Custom.cshtml"
System.Data.SqlClient.SqlException (0x80131904): Execution Timeout Expired.  The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception (0x80004005): The wait operation timed out
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
   at System.Data.SqlClient.SqlDataReader.get_MetaData()
   at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString, Boolean isInternal, Boolean forDescribeParameterEncryption, Boolean shouldCacheForAlwaysEncrypted)
   at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
   at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
   at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
   at Dynamicweb.Data.Database.CreateDataReader(IDbCommand command, CommandBehavior behavior)
   at Dynamicweb.Data.Database.CreateDataReader(CommandBuilder commandBuilder, IDbConnection connection, IDbTransaction transaction, CommandBehavior behavior, Int32 commandTimeout)
   at Dynamicweb.Data.Database.CreateDataReader(CommandBuilder commandBuilder, IDbConnection connection, IDbTransaction transaction, Int32 commandTimeout)
   at Dynamicweb.Ecommerce.Products.ProductRepository.Dynamicweb.Ecommerce.Products.IProductRepository.GetProductKeysByGroupId(String groupId, Boolean useOrderBy, Boolean includeVariants, String productLanguageId, Boolean doRefactoring, Boolean useAssortments)
   at Dynamicweb.Ecommerce.Orders.Discounts.Discount.ProcessProductSelections(HashSet`1& productKeys, HashSet`1& includedQueries, HashSet`1& excludedQueries)
   at Dynamicweb.Ecommerce.Orders.Discounts.Discount.GetRelevantProductKeys()
   at Dynamicweb.Ecommerce.Orders.Discounts.DiscountService.StatelessSetLookup(Discount discount, ConcurrentDictionary`2 lookup)
   at Dynamicweb.Ecommerce.Orders.Discounts.DiscountService.InitializeLookup()
   at System.Lazy`1.CreateValue()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Lazy`1.get_Value()
   at Dynamicweb.Ecommerce.Orders.Discounts.DiscountService.GetDiscounts(DiscountApplyType[] discountTypes, String[] productKeys, User user)
   at Dynamicweb.Ecommerce.Orders.Discounts.DiscountInfoCollection.LoadDiscounts()
   at Smartpage.EvaSolo.CampaignPrices.Helpers.DiscountHelper.GetDiscountInfo(Product product, Currency currency, Country country, Shop shop, User user, String format) in D:\a\1\s\Smartpage.EvaSolo.CampaignPrices\Helpers\DiscountHelper.cs:line 48
   at Custom.EvaSolo.CampaignPrices.FieldTypeProviders.OnSaleProvider.GetProductValue(Product product, Object fieldValue, String languageId, Dictionary`2 settings) in D:\a\1\s\Smartpage.EvaSolo.CampaignPrices\FieldTypeProviders\OnSaleProvider.cs:line 27
   at Dynamicweb.Ecommerce.Products.ProductFieldTypeProvider.GetProductValue(Product product, FieldType fieldType, Object fieldValue)
   at Dynamicweb.Ecommerce.Products.ProductFieldValueCollection.RefreshProviderFields(Product product)
   at Dynamicweb.Ecommerce.Products.ProductRepository.ExtractProduct(IDataReader dataReader, Nullable`1& groupProductRelationSortingExists)
   at Dynamicweb.Ecommerce.Products.ProductRepository.GetProductById(String productId, String productVariantId, String productLanguageId)
   at Dynamicweb.Ecommerce.Products.ProductService.FetchMissingProductsInternal(IProductRepository repo, IEnumerable`1 keys)
   at Dynamicweb.Caching.ServiceCache`2.GetCache(IEnumerable`1 keys)
   at Dynamicweb.Caching.ServiceCache`2.GetCache(TKey key)
   at Dynamicweb.Ecommerce.Products.ProductService.GetProductById(String productId, String productVariantId, String productLanguageId, User user, Boolean showUntranslated)
   at Dynamicweb.Ecommerce.Products.ProductService.GetProductById(String productId, String productVariantId, String productLanguageId, Boolean useAssortments)
   at Dynamicweb.Ecommerce.Content.Items.Editors.ProductEditor.ParseValue(IEnumerable`1 value)
   at Dynamicweb.Ecommerce.Content.Items.Editors.ProductCatalogEditor.GetViewModelValue(Object value)
   at System.Lazy`1.CreateValue()
   at System.Lazy`1.LazyInitValue()
   at Dynamicweb.Frontend.ItemFieldViewModel.GetValue[T]()
   at Dynamicweb.Frontend.ItemViewModel.GetValue[T](String systemName)
   at CompiledRazorTemplates.Dynamic.RazorEngine_66f8b4a1ddb743b0a2aba6f698ce0e70.Execute() in D:\dynamicweb.net\Solutions\twodayco3\evasolo.cloud.dynamicweb-cms.com\Files\Templates\Designs\Swift\Paragraph\Swift_RelatedProducts_Custom.cshtml:line 128
   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()
ClientConnectionId:077a6f4a-d841-4c19-b87e-820b384f100a
Error Number:-2,State:0,Class:11

1 @inherits Dynamicweb.Rendering.ViewModelTemplate<Dynamicweb.Frontend.ParagraphViewModel> 2 @using System 3 @using System.Collections.Generic 4 @using System.Linq 5 @using Dynamicweb 6 @using Dynamicweb.Core 7 @using Dynamicweb.Core.Encoders 8 @using Dynamicweb.Environment 9 @using Dynamicweb.Ecommerce.ProductCatalog 10 @using Dynamicweb.Ecommerce.Products 11 @using Dynamicweb.Frontend 12 @using Smartpage.EvaSolo.Clerk.Helpers 13 14 @* CUSTOMIZED STANDARD SWIFT (v1.25.0) TEMPLATE *@ 15 16 @{ 17 ProductViewModel product = null; 18 if (Dynamicweb.Context.Current.Items.Contains("ProductDetails")) 19 { 20 product = (ProductViewModel)Dynamicweb.Context.Current.Items["ProductDetails"]; 21 } 22 else if (Pageview.Page.Item["DummyProduct"] != null && Pageview.IsVisualEditorMode) 23 { 24 var pageViewModel = Dynamicweb.Frontend.ContentViewModelFactory.CreatePageInfoViewModel(Pageview.Page); 25 ProductListViewModel productList = pageViewModel.Item.GetValue("DummyProduct") != null ? pageViewModel.Item.GetValue("DummyProduct") as ProductListViewModel : new ProductListViewModel(); 26 27 if (productList?.Products is object) 28 { 29 product = productList.Products[0]; 30 } 31 } 32 33 string title = Model?.Item?.GetRawValueString("Title", Translate("Products")); 34 string campaignValues = Model.Item.GetRawValueString("CampaignBadges", string.Empty); 35 36 //Styling 37 string titleFontSize = Model.Item.GetRawValueString("TitleFontSize", "h3"); 38 string subtitleFontSize = Model.Item.GetRawValueString("SubtitleFontSize", "fs-5"); 39 string buttonStyle = Model.Item.GetRawValueString("ButtonStyle", ""); 40 buttonStyle = buttonStyle == "primary" ? " btn-primary" : buttonStyle; 41 buttonStyle = buttonStyle == "secondary" ? " btn-secondary" : buttonStyle; 42 buttonStyle = buttonStyle == "link" ? " btn-link" : buttonStyle; 43 string maxWidth = Model.Item.GetRawValueString("TextReadability", ""); 44 maxWidth = maxWidth == "max-width-on" ? " mw-75ch" : maxWidth; 45 maxWidth = maxWidth == "max-width-off" ? "" : maxWidth; 46 47 string generalTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("GeneralTheme")) ? " theme " + Model.Item.GetRawValueString("GeneralTheme").Replace(" ", "").Trim().ToLower() : ""; 48 string theme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("Theme")) ? " theme " + Model.Item.GetRawValueString("Theme").Replace(" ", "").Trim().ToLower() : ""; 49 string imageTheme = !string.IsNullOrWhiteSpace(Model.Item.GetRawValueString("ImageTheme")) ? " theme " + Model.Item.GetRawValueString("ImageTheme").Replace(" ", "").Trim().ToLower() : ""; 50 51 //Link generation 52 string pageId = !string.IsNullOrEmpty(Model.Item.GetRawValueString("ProductSliderServicePage")) ? Model.Item.GetLink("ProductSliderServicePage").PageId.ToString() : ""; 53 if (string.IsNullOrEmpty(pageId)) 54 { 55 pageId = GetPageIdByNavigationTag("ProductSliderService").ToString(); 56 } 57 58 string url = "/Default.aspx?ID=" + pageId; 59 if (!url.Contains("LayoutTemplate", StringComparison.OrdinalIgnoreCase)) 60 { 61 url += url.Contains("?") ? "&LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml" : "?LayoutTemplate=Designs/Swift/Swift_PageClean.cshtml"; 62 } 63 64 if (Pageview.IsVisualEditorMode) 65 { 66 url += "&VisualEdit=True"; 67 } 68 69 bool isLazyLoadingForProductInfoEnabled = Dynamicweb.Core.Converter.ToBoolean(Dynamicweb.Context.Current.Items["IsLazyLoadingForProductInfoEnabled"]); 70 if (isLazyLoadingForProductInfoEnabled) 71 { 72 url += "&getproductinfo=true"; 73 } 74 75 //Source type 76 string sourceType = Model.Item.GetRawValueString("RelationType", "trending"); 77 IList<string> relateFromGroupIds = new List<string> { }; 78 IList<string> relateFromProductVariantIds = new List<string> { }; 79 IList<string> relateFromProductIds = new List<string> { }; 80 bool hasVariants = false; 81 82 //--- VARIANTS --- 83 if (sourceType == "variants" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateToVariants") is ProductListViewModel productsToRelateToVariants) 84 { 85 foreach (var productSelection in productsToRelateToVariants.Products) 86 { 87 relateFromProductIds.Add(productSelection.Id); 88 } 89 } 90 91 //--- MOST SOLD --- 92 if (sourceType == "most-sold" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToMostSold") is IList<ProductGroupViewModel> groupsToRelateToMostSold) 93 { 94 foreach (var fromGroup in groupsToRelateToMostSold) 95 { 96 relateFromGroupIds.Add(fromGroup.Id); 97 } 98 } 99 100 //--- TRENDING --- 101 if (sourceType == "trending" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToTrending") is IList<ProductGroupViewModel> groupsToRelateToTrending) 102 { 103 foreach (var fromGroup in groupsToRelateToTrending) 104 { 105 relateFromGroupIds.Add(fromGroup.Id); 106 } 107 } 108 109 //--- LATEST --- 110 if (sourceType == "latest" && Model.Item.GetValue<IList<ProductGroupViewModel>>("GroupsToRelateToLatest") is IList<ProductGroupViewModel> groupsToRelateToLatest) 111 { 112 foreach (var fromGroup in groupsToRelateToLatest) 113 { 114 relateFromGroupIds.Add(fromGroup.Id); 115 } 116 } 117 118 //--- FREQUENTLY BOUGHT --- 119 if (sourceType == "frequently" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo") is ProductListViewModel productsToRelateTo) 120 { 121 foreach (var fromProduct in productsToRelateTo.Products) 122 { 123 relateFromProductIds.Add(fromProduct.Id); 124 } 125 } 126 127 //--- SELECTED PRODUCTS --- 128 if ((sourceType == "selected" || sourceType == "frequently") && Model.Item.GetValue<ProductListViewModel>("Products") is ProductListViewModel products) 129 { 130 hasVariants = products.Products.Any(p => !string.IsNullOrEmpty(p.VariantId)); 131 foreach (var productSelection in products.Products) 132 { 133 if (hasVariants) 134 { 135 if (!string.IsNullOrEmpty(productSelection.VariantId)) 136 { 137 relateFromProductVariantIds.Add($"{productSelection.Id} {productSelection.VariantId}"); 138 } 139 else 140 { 141 relateFromProductVariantIds.Add($"{productSelection.Id}"); 142 } 143 } 144 145 relateFromProductIds.Add($"{productSelection.Id}"); 146 } 147 } 148 149 //--- UPSELL PRODUCTS --- 150 if (sourceType == "upsell-products") 151 { 152 string upSell = product.ProductFields.ContainsKey("Custom_UpSell") ? Converter.ToString(product.ProductFields["Custom_UpSell"].Value) : string.Empty; 153 List<Product> upSellProducts = GetActiveUpSellProducts(upSell); 154 relateFromProductIds = upSellProducts != null ? upSellProducts.Select(p => p.Id).ToList() : new List<string>(); 155 } 156 157 //--- RELATED PRODUCTS --- 158 if (sourceType == "related-products" && Model.Item.GetValue<ProductListViewModel>("ProductsToRelateTo2") is ProductListViewModel selectedRelationProduct) 159 { 160 if (selectedRelationProduct.Products.Any()) 161 { 162 product = selectedRelationProduct.Products.FirstOrDefault(); 163 } 164 165 if (product?.RelatedGroups != null) 166 { 167 foreach (var group in product.RelatedGroups) 168 { 169 foreach (var relatedProduct in group.Products) 170 { 171 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 172 { 173 relateFromProductVariantIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 174 } 175 else 176 { 177 relateFromProductVariantIds.Add($"{relatedProduct.ProductId}"); 178 } 179 } 180 } 181 } 182 } 183 184 //Create group id collection and products id collection strings 185 string groupIds = product is object ? product.PrimaryOrDefaultGroup.Id : string.Join(",", relateFromGroupIds); 186 string productVariantIds = relateFromProductVariantIds.Count > 0 ? string.Join(",", relateFromProductVariantIds) : ""; 187 string productIds = product is object && relateFromProductIds.Count == 0 ? product.Id : string.Join(",", relateFromProductIds); 188 //Set the parameters to the url 189 string linkParameters = ""; 190 linkParameters += sourceType != "related-products" && sourceType != "frequently" && sourceType != "selected" ? "&GroupId=" + groupIds : ""; 191 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType != "most-sold" && sourceType != "trending" && sourceType != "latest" && sourceType != "frequently" && sourceType != "related-products" ? "&MainProductId=" + productIds : ""; 192 linkParameters += !string.IsNullOrEmpty(productVariantIds) && sourceType == "related-products" ? "&ProductVariantId=" + productVariantIds : ""; 193 linkParameters += sourceType == "variants" ? "&IsVariant=true" : ""; 194 linkParameters += sourceType == "latest" ? "&SortBy=Created" : ""; 195 linkParameters += sourceType == "most-sold" ? "&SortBy=OrderCount" : ""; 196 linkParameters += sourceType == "trending" ? "&SortBy=OrderCountGrowth" : ""; 197 linkParameters += !string.IsNullOrEmpty(productIds) && sourceType == "frequently" ? $"&BoughtWithProductIds=[{productIds}]" : ""; 198 var productListPageId = GetPageIdByNavigationTag("Shop"); 199 string link = "/Default.aspx?ID=" + productListPageId + linkParameters; 200 201 // Slider settings (documentation: swiffyslider.com/configuration) 202 string navigationStyle = $"{Model.Item.GetRawValueString("NavigationStyle", "slider-nav-round")}"; 203 string navigationPlacement = $"{Model.Item.GetRawValueString("NavigationPlacement", "slider-nav-on-slides")}"; 204 string indicatorStyle = $"{Model.Item.GetRawValueString("IndicatorStyle", "slider-indicators-hidden")}"; 205 string revealSlides = Model.Item.GetRawValueString("RevealSlides", "no-reveal") == "reveal" ? "slider-item-reveal" : string.Empty; 206 string navigationAlwaysVisible = (Model.Item.GetBoolean("NavigationAlwaysVisible")) ? "slider-nav-visible" : string.Empty; 207 string navigationVisibleOnTouch = (Model.Item.GetBoolean("NavigationVisibleOnTouch")) ? "slider-nav-touch" : string.Empty; 208 string navigationShowScrollbar = (Model.Item.GetBoolean("NavigationShowScrollbar")) ? "slider-nav-scrollbar" : string.Empty; 209 string navigationSmall = (Model.Item.GetBoolean("NavigationSmall")) ? "slider-nav-sm" : string.Empty; 210 string navigationInvertColors = (Model.Item.GetBoolean("NavigationInvertColors")) ? "slider-nav-dark" : string.Empty; 211 string navigationSlideEntirePage = (Model.Item.GetBoolean("NavigationSlideEntirePage")) ? "slider-nav-page" : string.Empty; 212 string navigationNoLoop = (Model.Item.GetBoolean("NavigationNoLoop")) ? "slider-nav-noloop" : string.Empty; 213 string indicatorsOutsideSlider = (Model.Item.GetBoolean("IndicatorsOutsideSlider") && indicatorStyle != string.Empty) ? "slider-indicators-outside" : string.Empty; 214 string indicatorsHighlightActive = (Model.Item.GetBoolean("IndicatorsHighlightActive")) ? "slider-indicators-highlight" : string.Empty; 215 string indicatorsInvertColors = (Model.Item.GetBoolean("IndicatorsInvertedColors")) ? "slider-indicators-dark" : string.Empty; 216 string indicatorsVisibleOnSmallDevices = (Model.Item.GetBoolean("IndicatorsVisibleOnSmallDevices")) ? "slider-indicators-sm" : string.Empty; 217 218 bool productsFound = true; 219 if (sourceType != "clerk" && string.IsNullOrEmpty(groupIds) && string.IsNullOrEmpty(productIds) && string.IsNullOrEmpty(productVariantIds)) //CUSTOM 220 { 221 if (Pageview.IsVisualEditorMode && product != null) //CUSTOM 222 { 223 productIds = product.Id; 224 sourceType = "selected"; 225 } 226 else 227 { 228 productsFound = false; 229 } 230 } 231 else if (sourceType == "upsell-products") 232 { 233 productsFound = relateFromProductIds.Count > 0; 234 } 235 } 236 237 @*//CUSTOM*@ 238 @if (Pageview.IsVisualEditorMode == true && sourceType == "clerk" && PageView.Current().AreaSettings.GetItem("CustomSettings").GetRawValueString("ClerkPublicKey_Custom").IsNullOrEmpty()) 239 { 240 <div class="alert alert-danger" role="alert"> 241 <span>@Translate("Product slider: The Clerk integration requires a public key in the website settings")</span> 242 </div> 243 } 244 @*//--CUSTOM*@ 245 246 @*Container element for the request*@ 247 @if (productsFound) 248 { 249 <form method="post" action="@url" id="RelatedProductsForm_@Model.ID" data-response-target-element="RelatedProducts_@Model.ID" data-preloader="inline" data-update-url="false" class="item_@Model.Item.SystemName.ToLower()"> 250 <input type="hidden" name="ModelID" value="@Model.ID"> 251 <input type="hidden" name="SourceType" value="@sourceType"> 252 253 @*--- SLIDER SETTINGS ---*@ 254 <input type="hidden" name="NavigationStyle" value="@navigationStyle"> 255 <input type="hidden" name="NavigationPlacement" value="@navigationPlacement"> 256 <input type="hidden" name="IndicatorStyle" value="@indicatorStyle"> 257 <input type="hidden" name="RevealSlides" value="@revealSlides"> 258 <input type="hidden" name="NavigationAlwaysVisible" value="@(navigationAlwaysVisible)"> 259 <input type="hidden" name="NavigationVisibleOnTouch" value="@(navigationVisibleOnTouch)"> 260 <input type="hidden" name="NavigationShowScrollbar" value="@(navigationShowScrollbar)"> 261 <input type="hidden" name="NavigationSmall" value="@(navigationSmall)"> 262 <input type="hidden" name="NavigationInvertColors" value="@(navigationInvertColors)"> 263 <input type="hidden" name="NavigationNoLoop" value="@(navigationNoLoop)"> 264 <input type="hidden" name="NavigationSlideEntirePage" value="@(navigationSlideEntirePage)"> 265 <input type="hidden" name="IndicatorsOutsideSlider" value="@(indicatorsOutsideSlider)"> 266 <input type="hidden" name="IndicatorsHighlightActive" value="@(indicatorsHighlightActive)"> 267 <input type="hidden" name="IndicatorsInvertColors" value="@(indicatorsInvertColors)"> 268 <input type="hidden" name="IndicatorsVisibleOnSmallDevices" value="@(indicatorsVisibleOnSmallDevices)"> 269 270 @*--- VARIANTS ---*@ 271 @if (sourceType == "variants") 272 { 273 <input type="hidden" name="isVariant" value="true"> 274 <input type="hidden" name="MainProductID" id="MainProductID_@Model.ID" value="@productIds"> 275 } 276 277 @*--- MOST SOLD ---*@ 278 @if (sourceType == "most-sold") 279 { 280 <input type="hidden" name="SortBy" value="OrderCount"> 281 if (groupIds != "") 282 { 283 <input type="hidden" name="GroupId" value="@groupIds"> 284 } 285 } 286 287 @*--- TRENDING ---*@ 288 @if (sourceType == "trending") 289 { 290 <input type="hidden" name="SortBy" value="OrderCountGrowth"> 291 if (groupIds != "") 292 { 293 <input type="hidden" name="GroupId" value="@groupIds"> 294 } 295 } 296 297 @*--- FREQUENTLY BOUGHT ---*@ 298 @if (sourceType == "frequently" && !string.IsNullOrEmpty(productIds)) 299 { 300 <input type="hidden" name="BoughtWithProductIds" value="[@productIds]"> 301 } 302 @if (sourceType != "frequently" && hasVariants) 303 { 304 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 305 } 306 307 @*--- LATEST ---*@ 308 @if (sourceType == "latest") 309 { 310 <input type="hidden" name="SortBy" value="Created"> 311 <input type="hidden" name="GroupId" value="@groupIds"> 312 } 313 314 @*--- SELECTED PRODUCTS ---*@ 315 @if (sourceType == "selected" && !string.IsNullOrEmpty(productIds) && !hasVariants) 316 { 317 <input type="hidden" name="MainProductId" id="MainProductID_@Model.ID" value="@productIds"> 318 } 319 @if (sourceType == "selected" && hasVariants) 320 { 321 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 322 } 323 324 @*--- UPSELL PRODUCTS ---*@ 325 @if (sourceType == "upsell-products" && !string.IsNullOrEmpty(productIds) && !hasVariants) 326 { 327 <input type="hidden" name="MainProductId" id="MainProductID_@Model.ID" value="@productIds"> 328 } 329 @if (sourceType == "upsell-products" && hasVariants) 330 { 331 <input type="hidden" name="ProductVariantId" value="@productVariantIds"> 332 } 333 334 @*--- RELATED PRODUCTS ---*@ 335 @if (sourceType == "related-products") 336 { 337 <input type="hidden" name="ProductVariantId" id="MainProductID_@Model.ID" value="@productVariantIds"> 338 } 339 340 @*//CUSTOM*@ 341 @*--- CLERK ---*@ 342 @if (sourceType == "clerk") 343 { 344 // Doc.: https://docs.clerk.io/reference/recommendations-popular 345 346 var clerkEndPoint = Model.Item.GetString("ClerkEndpoint"); 347 var clerkKeywords = Model.Item.GetString("ClerkKeywords"); 348 var clerkQuery = new List<string>(); 349 var clerkFilter = new List<string>(); 350 351 if (!string.IsNullOrEmpty(clerkKeywords)) 352 { 353 switch (clerkEndPoint) 354 { 355 case "recommendations/keywords": 356 clerkQuery.Add("keywords=['" + clerkKeywords + "']"); 357 break; 358 } 359 } 360 361 if (product != null && !string.IsNullOrEmpty(product.Id)) 362 { 363 switch (clerkEndPoint) 364 { 365 case "recommendations/bundle": 366 clerkQuery.Add("product=" + product.Id); 367 break; 368 369 case "recommendations/complementary": 370 case "recommendations/substituting": 371 case "recommendations/most_sold_with": 372 clerkQuery.Add("products=[" + product.Id + "]"); // NOTE: Clerk format id as integer (Data Sync product feed is string) 373 break; 374 } 375 } 376 377 IList<ProductGroupViewModel> clerkGroupsToRelateTo = Model.Item.GetValue<IList<ProductGroupViewModel>>("ClerkGroupsToRelateTo"); 378 switch (clerkEndPoint) 379 { 380 case "recommendations/category/popular": 381 case "recommendations/category/trending": 382 case "recommendations/category/new": 383 if (clerkGroupsToRelateTo != null && clerkGroupsToRelateTo.Any()) 384 { 385 clerkQuery.Add("category=" + clerkGroupsToRelateTo.First().Id); 386 } 387 else if (Context.Current.Request.HasRequest("GroupID")) 388 { 389 clerkQuery.Add("category=" + Context.Current.Request.GetString("GroupID")); 390 } 391 392 break; 393 394 default: 395 if (clerkGroupsToRelateTo != null && clerkGroupsToRelateTo.Any()) 396 { 397 clerkFilter.Add("categories in ['" + string.Join("','", clerkGroupsToRelateTo.Select(i => i.Id)) + "']"); 398 } 399 400 break; 401 } 402 403 switch (clerkEndPoint) 404 { 405 case "recommendations/page/substituting": 406 case "recommendations/page/related_products": 407 case "recommendations/page/related_categories": 408 clerkQuery.Add("page=" + Model.PageID); 409 break; 410 } 411 412 IList<ItemViewModel> clerkFilters = Model.Item.GetItems("ClerkFilters"); 413 if (clerkFilters != null) 414 { 415 foreach (var i in clerkFilters) 416 { 417 switch (i.GetString("OperatorType")) 418 { 419 case "=": 420 case "!=": 421 case "contains": 422 case "contains not": 423 clerkFilter.Add($"{i.GetString("Field")} {i.GetString("OperatorType")} '{i.GetString("Value")}'"); 424 break; 425 426 case "in": 427 case "not in": 428 clerkFilter.Add($"{i.GetString("Field")} {i.GetString("OperatorType")} ['{i.GetString("Value").Replace(",", "','")}']"); 429 break; 430 431 case "= true": 432 case "= false": 433 clerkFilter.Add($"{i.GetString("Field")} {i.GetString("OperatorType")}"); 434 break; 435 436 default: 437 clerkFilter.Add($"{i.GetString("Field")} {i.GetString("OperatorType")} {i.GetString("Value")}"); 438 break; 439 } 440 } 441 } 442 443 <input type="hidden" name="ClerkEndpoint" value="@HtmlEncoder.HtmlAttributeEncode(clerkEndPoint)" /> 444 <input type="hidden" name="ClerkQuery" value="@HtmlEncoder.HtmlAttributeEncode(string.Join("&", clerkQuery))" /> 445 <input type="hidden" name="ClerkFilter" value="@HtmlEncoder.HtmlAttributeEncode(string.Join(" and ", clerkFilter))" /> 446 <input type="hidden" name="ClerkLabels" value="@HtmlEncoder.HtmlAttributeEncode(Model.Item.GetString("ClerkLabels"))" /> 447 <input type="hidden" name="ClerkLogArea" value="@HtmlEncoder.HtmlAttributeEncode($"{Pageview.Area.Name} (ID: {Pageview.Area.ID})")" /> 448 <input type="hidden" name="ClerkLogPage" value="@HtmlEncoder.HtmlAttributeEncode($"{Pageview.Page.MenuText} (ID: {Pageview.Page.ID})")" /> 449 <input type="hidden" name="ClerkLogParagraph" value="@HtmlEncoder.HtmlAttributeEncode($"{Pageview.CurrentParagraph.Header} (ID: {Pageview.CurrentParagraph.ID})")" /> 450 } 451 @*//--CUSTOM*@ 452 453 @* General parameters *@ 454 <input type="hidden" name="Link" value="@link"> 455 <input type="hidden" name="HideTitle" value="@Model.Item.GetString("HideTitle")"> 456 <input type="hidden" name="HidePrices" value="@Model.Item.GetString("CustomHidePrices")" /> @*//CUSTOM*@ 457 458 @if (Model.Item.GetInt32("ProductsCount") != 0) 459 { 460 <input type="hidden" name="PageSize" value="@Model.Item.GetInt32("ProductsCount")"> 461 } 462 <input type="hidden" name="HeadingTitle" id="RelatedProductsTitle_@Model.ID" value="@title"> 463 @if (!string.IsNullOrEmpty(Model.Item.GetString("Subtitle"))) 464 { 465 <input type="hidden" name="Subtitle" value="@Model.Item.GetString("Subtitle")"> 466 } 467 @if (!string.IsNullOrEmpty(Model.Item.GetString("LinkText"))) 468 { 469 <input type="hidden" name="LinkText" value="@Model.Item.GetString("LinkText")"> 470 } 471 @if (!string.IsNullOrEmpty(Model.Item.GetString("ImageAspectRatio"))) 472 { 473 string ratio = Model.Item.GetRawValueString("ImageAspectRatio", ""); 474 ratio = ratio != "0" ? ratio : ""; 475 <input type="hidden" name="ImageAspectRatio" value="@ratio"> 476 } 477 @if (!string.IsNullOrEmpty(Model.Item.GetString("Layout"))) 478 { 479 <input type="hidden" name="Layout" value="@Model.Item.GetRawValueString("Layout")"> 480 } 481 @if (titleFontSize != "") 482 { 483 <input type="hidden" name="TitleFontSize" value="@titleFontSize"> 484 } 485 @if (subtitleFontSize != "") 486 { 487 <input type="hidden" name="SubtitleFontSize" value="@subtitleFontSize"> 488 } 489 @if (buttonStyle != "") 490 { 491 <input type="hidden" name="ButtonStyle" value="@buttonStyle"> 492 } 493 @if (generalTheme != "") 494 { 495 <input type="hidden" name="GeneralTheme" value="@generalTheme"> 496 } 497 @if (theme != "") 498 { 499 <input type="hidden" name="Theme" value="@theme"> 500 } 501 @if (imageTheme != "") 502 { 503 <input type="hidden" name="ImageTheme" value="@imageTheme"> 504 } 505 @if (!string.IsNullOrEmpty(Model.Item.GetString("ContentPadding"))) 506 { 507 string contentPadding = Model.Item.GetRawValueString("ContentPadding"); 508 <input type="hidden" name="ContentPadding" value="@contentPadding"> 509 } 510 <input type="hidden" name="TextReadability" value="@maxWidth"> 511 <input type="hidden" name="ParentColumnSize" id="ParentColumnSize_@Model.ID" value="12"> 512 513 <input type="hidden" name="SaleBadgeType" value="@Model.Item.GetRawValue("SaleBadgeType")"> 514 <input type="hidden" name="SaleBadgeCssClassName" value="@Model.Item.GetRawValue("SaleBadgeDesign")"> 515 <input type="hidden" name="NewBadgeCssClassName" value="@Model.Item.GetRawValue("NewBadgeDesign")"> 516 <input type="hidden" name="NewPublicationDays" value="@Model.Item.GetInt32("NewPublicationDays")"> 517 518 @if (campaignValues != "") 519 { 520 <input type="hidden" name="CampaignBadgesValues" value="@campaignValues"> 521 } 522 </form> 523 524 <script type="module" src="/Files/Templates/Designs/Swift/Assets/js/swiffy-slider.js"></script> 525 <script> 526 window.addEventListener("load", () => { 527 swift.AssetLoader.Load('/Files/Templates/Designs/Swift/Assets/css/swiffy-slider.min.css', 'css'); 528 }); 529 </script> 530 531 if (Pageview.IsVisualEditorMode) 532 { 533 <div class="alert alert-info" role="alert"> 534 <span>@Translate("Product slider: Edit this column to configure")</span> 535 </div> 536 } 537 538 if (sourceType == "upsell-products") 539 { 540 <div class="w-100 h-100"> 541 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 542 <div id="RelatedProducts_@Model.ID"></div> 543 </div> 544 } 545 else if (sourceType != "related-products") 546 { 547 <div class="w-100 h-100"> 548 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 549 <div id="RelatedProducts_@Model.ID" class="h-100 swift_product_slider_container" data-product-count="@Model.Item.GetInt32("ProductsCount")"></div> @*//CUSTOM*@ 550 </div> 551 } 552 else if (product?.RelatedGroups != null) 553 { 554 @* Create multiple slider containers, if type is Product relation *@ 555 <div class="grid w-100 h-100@(generalTheme)" style="grid-row-gap: 4rem"> 556 <div id="@Model.ID" class="user-select-none" style="scroll-margin-top:var(--header-height,150px)"></div> 557 @foreach (var group in product.RelatedGroups) 558 { 559 <div id="RelatedProducts_@(Model.ID)_@group.Id" class="g-col-12 h-100 swift_product_slider_container"></div> 560 } 561 </div> 562 } 563 564 @* Initialize *@ 565 if (sourceType != "related-products") 566 { 567 <script type="module"> 568 if (document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]")) { 569 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@Model.ID").closest("[data-col-size]").getAttribute("data-col-size"); 570 } 571 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")).then(function () { 572 setTimeout(function () { 573 const isVisualEditor = @(Converter.ToString(Pageview.IsVisualEditorMode).ToLowerInvariant()); 574 const productSliderContainer = document.querySelector(".swift_product_slider_container"); 575 576 if (productSliderContainer && productSliderContainer.innerHTML !== "") { 577 productSliderContainer.classList.remove("d-none"); 578 } else if (!isVisualEditor && productSliderContainer) { 579 productSliderContainer.closest("[class*=column]").classList.add("d-none"); 580 } 581 custom.UpsellProducts.bindEvents(); 582 }, 150); 583 }); 584 </script> 585 } 586 else if (product?.RelatedGroups != null) 587 { 588 @* Create multiple sliders, if type is Product relation *@ 589 foreach (var group in product.RelatedGroups) 590 { 591 IList<string> fromProductIds = new List<string> { }; 592 593 foreach (var relatedProduct in group.Products) 594 { 595 if (!string.IsNullOrEmpty(relatedProduct.VariantId)) 596 { 597 fromProductIds.Add($"{relatedProduct.ProductId} {relatedProduct.VariantId}"); 598 } 599 else 600 { 601 fromProductIds.Add($"{relatedProduct.ProductId}"); 602 } 603 } 604 605 <script type="module"> 606 document.querySelector("#ParentColumnSize_@Model.ID").value = document.querySelector("#RelatedProducts_@(Model.ID)_@group.Id").closest("[data-col-size]").getAttribute("data-col-size"); 607 document.querySelector("#MainProductID_@Model.ID").value = "@string.Join(",", fromProductIds)"; 608 document.querySelector("#RelatedProductsTitle_@Model.ID").value = "@group.Name"; 609 document.querySelector("#RelatedProductsForm_@Model.ID").setAttribute("data-response-target-element", "RelatedProducts_@(Model.ID)_@group.Id"); 610 611 swift.PageUpdater.Update(document.querySelector("#RelatedProductsForm_@Model.ID")); 612 </script> 613 } 614 } 615 } 616 617 @functions { 618 List<Product> GetActiveUpSellProducts(string productNumbers) 619 { 620 if (!string.IsNullOrEmpty(productNumbers)) 621 { 622 List<Product> upSellProducts = Product.GetProductsByProductIDs(productNumbers.Split(',').Select(x => x.Trim()).ToArray(), true).ToList(); 623 624 if (upSellProducts != null && upSellProducts.Any()) 625 { 626 List<Product> activeUpSellProducts = new List<Product>(); 627 628 foreach (Product upSellProduct in upSellProducts) 629 { 630 if (upSellProduct.Active && GetStock(upSellProduct) > 0 && !upSellProduct.IsProductDisabledInClerkSync()) 631 { 632 activeUpSellProducts.Add(upSellProduct); 633 } 634 } 635 636 return activeUpSellProducts; 637 } 638 } 639 640 return null; 641 } 642 643 double GetStock(Product product) 644 { 645 double productStock = product.Stock; 646 647 if (Converter.ToBoolean(Dynamicweb.Ecommerce.Services.Products.GetProductFieldValue(product, "Custom_FurnitureWarehouse"))) 648 { 649 productStock = Converter.ToDouble(Dynamicweb.Ecommerce.Services.Products.GetProductFieldValue(product, "Custom_AlternativeStock")); 650 } 651 652 return productStock; 653 } 654 } 655

Voir aussi