Error executing template "Designs/Mobler2018/eCom/Product/Product.cshtml"
System.NullReferenceException: Object reference not set to an instance of an object.
at CompiledRazorTemplates.Dynamic.RazorEngine_cd03d32dbe654b1e9aea5fc97fbb3cf7.Execute() in D:\dynamicweb.net\Solutions\mobler.LIVE\Files\Templates\Designs\Mobler2018\eCom\Product\Product.cshtml:line 492
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 @using System.Web
2 @using Mobler.Website.CustomModules.MoblerHelpers
3 @using System.Text.RegularExpressions
4 @using CampaignModule.Models
5 @using Dynamicweb.Core
6 @using Dynamicweb.Ecommerce.Products
7 @using Group = Dynamicweb.Ecommerce.Products.Group
8 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>>
9
10 @{
11 var request = HttpContext.Current.Request;
12 string shopname = MoblerHelpers.GetShopName();
13 string SelectPlaceholder = Translate("ShopSelectPlaceholder", "Indtast by, postnummer eller adresse");
14 var shopInfo = MoblerHelpers.ShopInfo();
15 int ShopPageId = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetInt("ShopPageId");
16 var AllShops = Firstweb.Custom.CustomCode.Frontend.Helpers.Shops.GetAllShops(ShopPageId);
17 int AjaxCartPageId = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetInt("AjaxCartPageId");
18 int CustomersAlsoSawPageId = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetInt("CustomersAlsoSawPageId");
19 string relewiseRecommandationsEndpoint = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("RelewiseRecommandationsPageId");
20 bool userIsAnonymous= Mobler.Website.CustomCode.RelewiseCustom.RelewiseConnectorSetting.IsAnonymous;
21 bool showRecommendations = Mobler.Website.CustomCode.RelewiseCustom.RelewiseConnectorSetting.ShowRecommendations;
22 string CartPage = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("CartPage");
23 string VariantsEndpoint = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("VariantsUrl");
24 string VariantDetailsEndpoint = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("VariantDetailsUrl");
25 string BlackFridayTheme = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("BlackFridayTheme");
26 string ProductID = GetString("Ecom:Product.ID");
27 string GroupID = Firstweb.Custom.CustomCode.Frontend.Helpers.EcomGroups.GetProductDefaultShopGroupID(ProductID); //GetString("Ecom:Product.PrimaryOrFirstGroupID");
28 string DefaultVariantID = GetString("Ecom:Product.DefaultVariantComboID");
29 string ProductImage = MoblerHelpers.GetProductListImage(ProductID, DefaultVariantID);
30 string ProductName = GetString("Ecom:Product.Name");
31 string ProductNumber = GetString("Ecom:Product.Number");
32 string ProductCatalogLink = !String.IsNullOrEmpty(GetString("Ecom:Product:Field.ProductCatelog.Value.Clean")) ? GetString("Ecom:Product:Field.ProductCatelog.Value.Clean") : "";
33 string VariantId = GetString("Ecom:Product.VariantID");
34 string ShortDescription = GetString("Ecom:Product.ShortDescription");
35 string LongDescription = GetString("Ecom:Product.LongDescription").Replace("||", "<br/>");
36 string CurrentVariantName = GetString("Ecom:Product.SelectedVariantComboName");
37 var FaqQuestions = MoblerHelpers.GetProductFAQs(ProductID, GroupID);
38
39 // Group Specific - Exclude Bed Accessories
40 bool excludeBedAccessories = GetBoolean("Ecom:Product.CategoryField.Senge.ExcludeBedAccessories.Value");
41
42
43 Mobler.Website.CustomCode.Frontend.PayeverHelper payeverHelper = new Mobler.Website.CustomCode.Frontend.PayeverHelper();
44 Mobler.Website.CustomCode.Models.PayeverWidget PayeverWidget = payeverHelper.GetPayeverWidgetInformation();
45
46 string MetaDescription = GetString("Ecom:Product.MetaDescription");
47
48 if (string.IsNullOrEmpty(MetaDescription))
49 {
50 MetaDescription = Mobler.Website.CustomCode.Frontend.Helpers.Text.GetFirstLine(LongDescription.Replace("<br/>", " "));
51 }
52 else
53 {
54 MetaDescription = ""; //Meta data description bliver sat af DW, hvis dette felt er udfyldt, så vi skal ikke opsætte noget.
55 }
56
57 var RelatedBlogs = Firstweb.Custom.CustomCode.Frontend.Helpers.Blogs.GetBlogsRelatedToProduct(ProductID);
58 bool NewItem = GetBoolean("Ecom:Product:Field.NewItem");
59 DateTime NewItemExpiryDate = GetDate( "Ecom:Product:Field.NewItemExpiryDate" );
60 bool LowPrice = GetBoolean("Ecom:Product:Field.Splash3");
61
62 Mobler.Website.CustomCode.Frontend.ProductsDisplayVariant productsDisplayVariant = Mobler.Website.CustomCode.Frontend.ProductsDisplayVariant.Instance();
63
64 bool ForSale = !GetBoolean("Ecom:Product:Field.NotForSaleOnline");
65 string FirstImage = "";
66 var Images = MoblerHelpers.GetProductImages(GetString("Ecom:Product.ID"), VariantId);
67 bool First = true;
68 bool FirstIndicator = true;
69 int IndicatorIncrementer = 0;
70 string ProductLink = GetString("Ecom:Product.Link.Clean");
71
72 double ActualPrice = GetDouble("Ecom:Product.Price.Price");
73 double spar = 0;
74 double price = 0;
75 double normalPrice = 0;
76
77 string priceFormatted = "";
78 string sparFormatted = "";
79 string normalPriceFormatted = "";
80
81 string campaignColor = "";
82 string campaignText = "";
83 string campaignTextClean = "";
84 string campaignDate = "";
85
86 string campaignDateStart = "";
87 string campaignDateEnd = "";
88
89 bool CampaignActiveOnProduct = GetBoolean("CampaignModule:Product.CampaignActiveOnProduct");
90
91 bool IsLocalWebshopProduct = (Mobler.Website.CustomCode.Frontend.Helper.IsLocalWebshop() && !GetBoolean("Ecom:Product:Field.SystemIsMasterShopProduct"));
92
93
94 if (!CampaignActiveOnProduct && !IsLocalWebshopProduct)
95 {
96 price = ActualPrice;
97 priceFormatted = MoblerHelpers.formatPrice(price);
98 }
99 else if (IsLocalWebshopProduct)
100 {
101 price = ActualPrice;
102 priceFormatted = MoblerHelpers.formatPrice(price);
103
104 normalPrice = GetDouble("Ecom:Product:Field.ForPris");
105 normalPriceFormatted = MoblerHelpers.formatPrice(normalPrice);
106
107 spar = GetDouble("Ecom:Product:Field.Spar");
108 sparFormatted = MoblerHelpers.formatPrice(spar);
109
110 bool SplashLwCombiPrice = GetBoolean("Ecom:Product:Field.SplashLwCombiPrice");
111 DateTime SplashLwCombiPriceExpiryDate = GetDate("Ecom:Product:Field.SplashLwCombiPriceExpiryDate");
112
113 if (SplashLwCombiPrice && (SplashLwCombiPriceExpiryDate == DateTime.MinValue || SplashLwCombiPriceExpiryDate > DateTime.Now))
114 {
115 campaignTextClean = "KOMBI";
116 }
117
118
119 }
120 else
121 {
122 normalPrice = GetDouble("CampaignModule:Product.CampaignProduct.NormalPrice");
123 normalPriceFormatted = GetString("CampaignModule:Product.CampaignProduct.NormalPrice.FormattedRetail");
124
125 price = GetDouble("CampaignModule:Product.CampaignProduct.CampaignPrice");
126 priceFormatted = GetString("CampaignModule:Product.CampaignProduct.CampaignPrice.FormattedRetail");
127
128 spar = GetDouble("CampaignModule:Product.CampaignProduct.DiscountAmount");
129 sparFormatted = GetString("CampaignModule:Product.CampaignProduct.DiscountAmount.FormattedRetail");
130
131 campaignColor = GetString("CampaignModule:Product.Campaign.Color");
132 campaignTextClean = GetString("CampaignModule:Product.Campaign.Text");
133
134 campaignDateStart = GetString("CampaignModule:Product.CampaignProduct.Campaign.Start");
135 campaignDateEnd = GetString("CampaignModule:Product.CampaignProduct.Campaign.End");
136 campaignText = $"{Translate("Campaign.BeforeDate.Text", "Gældende fra:")} {campaignDateStart} {Translate("Campaign.BeforeDate.Text2", "t.o.m.")} {campaignDateEnd}";
137 }
138
139 string DataLayerPrice = ActualPrice.ToString().Replace(".", "").Replace(",", ".");
140 System.Web.HttpContext.Current.Items["OverrideOgTags"] = true;
141 string OgImage = "https://mobler.dk" + Images.FirstOrDefault();
142
143 string TrimmedTeaser = Firstweb.Custom.CustomCode.Frontend.Helpers.JSFormatting.TrimLinebreaks(ShortDescription);
144 string TrimmedName = Firstweb.Custom.CustomCode.Frontend.Helpers.JSFormatting.TrimLinebreaks(ProductName);
145
146 var ParentGroups = Firstweb.Custom.CustomCode.Frontend.Helpers.EcomGroups.getBreadCrumbGroupList(GroupID, true);
147 var DataLayerParentGroup = ParentGroups.Last().Name;
148 List<string> DataLayerParentGroupStrings = ParentGroups.Select(group => group.Name).ToList();
149 string DataLayerParentGroups = string.Join(" > ", DataLayerParentGroupStrings);
150
151 // Handle USPs on the product page
152 int uspPageId = 0;
153 string uspProductdetailLinkProductLevel = GetString("Ecom:Product:Field.uspproductdetaillink.Value.Clean");
154 string uspProductdetailLinkGroupLevel = ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("uspproductdetaillink").Value.ToString();
155 bool hasUspProductdetailLinkProductLevel = !string.IsNullOrEmpty(uspProductdetailLinkProductLevel);
156 bool hasUspProductdetailLinkGroupLevel = !string.IsNullOrEmpty(uspProductdetailLinkGroupLevel);
157
158 // Check if there's a product detail link on the product level
159 if (hasUspProductdetailLinkProductLevel)
160 {
161 Int32.TryParse(uspProductdetailLinkProductLevel.Substring(uspProductdetailLinkProductLevel.LastIndexOf('=') + 1), out uspPageId);
162 }
163 // Check if there's a product detail link on the group level
164 else if (hasUspProductdetailLinkGroupLevel)
165 {
166 Int32.TryParse(uspProductdetailLinkGroupLevel.Substring(uspProductdetailLinkGroupLevel.LastIndexOf('=') + 1), out uspPageId);
167 }
168
169 // Handle display of product catalogue image, if it exists on the group level
170 string productCatalogueImage = ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("ProductCatalogueImage").Value.ToString();
171
172 // Handle USP Icons and Text
173 string usp1Icon = ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("usp_1_icon").Value.ToString();
174 if (usp1Icon == "")
175 {
176 usp1Icon = "piggy-bank";
177 }
178 string usp2Icon = ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("usp_2_icon").Value.ToString();
179 if (usp2Icon == "")
180 {
181 usp2Icon = "truck";
182 }
183 string usp3Icon = ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("usp_3_icon").Value.ToString();
184 if (usp3Icon == "")
185 {
186 usp3Icon = "calendar";
187 }
188
189 string usp1IconText = Translate("Product.USPIcon.One.Title", "Prismatch");
190 string usp2IconText = Translate("Product.USPIcon.Two.Title", "Fri kantstenslevering*");
191 string usp3IconText = Translate("Product.USPIcon.Three.Title", "Rentefri finansiering");
192 string usp1IconTextFromField = ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("uspicontext1").Value.ToString();
193 string usp2IconTextFromField = ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("uspicontext2").Value.ToString();
194 string usp3IconTextFromField = ParentGroups.Last().ProductGroupFieldValues.GetProductGroupFieldValue("uspicontext3").Value.ToString();
195
196 if (!string.IsNullOrEmpty(usp1IconTextFromField))
197 {
198 usp1IconText = usp1IconTextFromField;
199 }
200 if (!string.IsNullOrEmpty(usp2IconTextFromField))
201 {
202 usp2IconText = usp2IconTextFromField;
203 }
204 if (!string.IsNullOrEmpty(usp3IconTextFromField))
205 {
206 usp3IconText = usp3IconTextFromField;
207 }
208
209 string usp1IconClass = "fa-solid fa-" + usp1Icon + " fs3";
210 string usp2IconClass = "fa-solid fa-" + usp2Icon + " fs3";
211 string usp3IconClass = "fa-solid fa-" + usp3Icon + " fs3";
212
213 string seeProductInStore = "";
214 if (!String.IsNullOrEmpty(GetString("Ecom:Product:Field.seeproductinstore.Value.Clean")))
215 {
216 seeProductInStore = GetString("Ecom:Product:Field.seeproductinstore.Value.Clean");
217 }
218
219 string ShowOnPageUrl = "";
220 string GetCartEndpoint = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("GetCartJson");
221
222 string QuickDeliveryColor = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("QuickDeliveryColor");
223 string QuickDeliveryDescription = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("QuickDeliveryDescription");
224 string NormalDeliveryDescription = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("NormalDeliveryDescription");
225 string BrandName = GetString("Ecom:Product:Field.Maerkevarer.Value.Clean");
226 string DeliveryTime = Translate("DeliveryTime." + BrandName, "Gns. leveringstid 3-12 dage");
227 bool HideDelivery = DeliveryTime == "Skjul";
228 string DeliveryColor = "#00AB5D";
229 bool QuickDelivery = GetBoolean("Ecom:Product:Field.QuickDelivery.Value.Clean");
230 string CustomDelivery = GetString("Ecom:Product:Field.CustomDeliveryTime.Value.Clean");
231 if (QuickDelivery)
232 {
233 DeliveryColor = QuickDeliveryColor;
234 DeliveryTime = Translate("DeliveryTime.QuickDelivery", "Ekstra hurtig levering");
235 }
236 else if (!String.IsNullOrEmpty(CustomDelivery))
237 {
238 DeliveryTime = CustomDelivery;
239 }
240 string DeliveryFontWeightModifier = QuickDelivery ? "font-weight-bold" : "";
241 bool DisplayAverageDeliveryTime = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetBoolean("DisplayAverageDeliveryTime");
242
243 double CostPrice = GetDouble("Ecom:Product:Field.FirstwebCostPrice.Value.Raw");
244 string DailyPriceBackgroundColor = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("AktuelDagsprisBackgroundColor");
245 string DailyPriceTextColor = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("AktuelDagsprisTextColor");
246 string EAN = GetString("Ecom:Product:Field.FirstEan.Value");
247 string productionDescription = GetString("Ecom:Product:Field.FirstwebProductionDescription");
248
249 Mobler.Website.CustomCode.RelewiseCustom.RelewiseCaller relewiseCaller = new Mobler.Website.CustomCode.RelewiseCustom.RelewiseCaller();
250 relewiseCaller.TrackProductView(ProductID, VariantId);
251 bool hasVariants = false;
252
253 string purchasedWithStyle = Mobler.Website.CustomCode.Frontend.Helper.IsLocalWebshop() ? "display:none" : "";
254 }
255
256 @SnippetStart("PricesContainer")
257 <div class="row">
258 <div class="col-12 col-sm-12 offset-sm-0 d-flex justify-content-center justify-content-lg-start flex-column">
259 @if (GetLoop("Co3VariantCombinations").Count > 0) {
260 hasVariants = true;
261 // Only show the extra price at top, if we have variants
262 <div class="d-flex flex-wrap flex-column justify-content-end border-grey border-bottom-0 p-4">
263 <p class="price font-weight-bold m-0">
264 <span class="unimportant-hidden" data-bind="css: { 'd-inline': SplashType() == 1 }">@Translate("ProductNow", "NU") </span>
265 <span class="total-price" data-bind="text: Price"></span>
266 </p>
267 @if (campaignTextClean == "KOMBI")
268 {
269 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: 5 }"></div>
270 <div class="d-none" data-bind="setInitValue: { observable: BeforePriceDouble, value: @normalPrice }"></div>
271 <div class="d-none" data-bind="setInitValue: { observable: BeforePrice, value: '@normalPriceFormatted' }"></div>
272 <div class="d-none" data-bind="setInitValue: { observable: SavingDouble, value: @spar }"></div>
273 <div class="d-none" data-bind="setInitValue: { observable: Saving, value: '@sparFormatted' }"></div>
274 }
275 else if (spar > 0)
276 {
277 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: 1 }"></div>
278 <div class="d-none" data-bind="setInitValue: { observable: BeforePriceDouble, value: @normalPrice }"></div>
279 <div class="d-none" data-bind="setInitValue: { observable: BeforePrice, value: '@normalPriceFormatted' }"></div>
280 <div class="d-none" data-bind="setInitValue: { observable: SavingDouble, value: @spar }"></div>
281 <div class="d-none" data-bind="setInitValue: { observable: Saving, value: '@sparFormatted' }"></div>
282 }
283 else if (NewItem)
284 {
285 if (NewItemExpiryDate > DateTime.Now)
286 {
287 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: 2 }"></div>
288 }
289 }
290 else if (LowPrice)
291 {
292 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: 3 }"></div>
293 }
294 else if (CostPrice > 0)
295 {
296 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: 4 }"></div>
297 }
298
299 <div class="w-100 unimportant-hidden" data-bind="css: { 'd-flex': SplashType() == 1 || SplashType() == 5}">
300 <p class="color-subtle fs0 m-0 text-uppercase mr-3">@Translate("PriceBefore", "Før") <span class="before-price" data-bind="text: BeforePrice"></span></p>
301 <p class="splash fs0 text-uppercase m-0 px-2">@Translate("PriceSaving", "Spar") <span class="total-saving" data-bind="text: Saving"></span></p>
302 </div>
303
304 <div class="price-info fs-s mt-4">
305 @if (excludeBedAccessories)
306 {
307 <p class="price-info-text color-subtle mb-1 js-update-price-info-text">@Translate("Ecom:Product.Pricing.Excludes.Beds", "Prisen er ekskl. ben og gavl.")</p>
308 }
309 @if (hasVariants)
310 {
311 <div class="variant-price-disclaimer color-subtle mb-1">@Translate("VariantPriceDisclaimer", "Prisen kan variere efter materialevalg")</div>
312 }
313 </div>
314
315 @if (!string.IsNullOrEmpty(campaignDateStart) && !string.IsNullOrEmpty(campaignDateEnd) && !string.IsNullOrEmpty(campaignText))
316 {
317 <div class="d-none" data-bind="setInitValue: { observable: CampaignText, value: '@campaignText' }"></div>
318 <div class="price-info fs-xs">
319 <p class="mb-1"><span class="dot dot-yellow"></span> <span data-bind="text: CampaignText"></span></p>
320 </div>
321 }
322 else
323 {
324 <div class="d-none" data-bind="setInitValue: { observable: CampaignText, value: '' }"></div>
325 }
326 @* <p data-bind="text: CampaignText"></p> *@
327 </div>
328 }
329
330 @if (GetLoop("Co3VariantCombinations").Count > 0)
331 {
332 <div class="variant-selectors">
333 @{
334 hasVariants = true;
335 var VariantGroupsWithMultipleOptions = GetLoop("VariantGroups").Where(x => x.GetLoop("VariantAvailableOptions").Count > 1);
336 int VariantGroupCount = VariantGroupsWithMultipleOptions.Count();
337 int VariantCombinationsCount = GetLoop("Co3VariantCombinations").Count;
338 int PossibleVariantCombinations = 1;
339 }
340 @foreach (var VG in GetLoop("VariantGroups"))
341 {
342 PossibleVariantCombinations *= VG.GetLoop("VariantAvailableOptions").Count;
343 }
344 @if (VariantGroupCount == 1)
345 {
346 var RelevantVariantGroup = VariantGroupsWithMultipleOptions.FirstOrDefault();
347 var SelectedVariant = RelevantVariantGroup.GetLoop("VariantAvailableOptions").Where(x => x.GetBoolean("Ecom:VariantOption.Selected")).FirstOrDefault();
348 int variantCount = RelevantVariantGroup.GetLoop("VariantAvailableOptions").Count;
349 var variantSelectorClass = "variant-options bg-white";
350 if (RelevantVariantGroup.GetString("Ecom:VariantGroup.Label").ToLower().Contains("farve") && variantCount <= 5)
351 {
352 variantSelectorClass = "variant-selector__color-picker d-flex flex-wrap";
353 }
354 string variantInfoText = Translate("VariantInfo-" + RelevantVariantGroup.GetString("Ecom:VariantGroup.Label"), "Variant Info Text");
355 string variantInfoTextEscapedLineBreaks = variantInfoText.Replace(System.Environment.NewLine, "<br />");
356
357 <div class="position-relative border-grey p-4">
358 <div class="variant-selector__header">
359 <div class="variant-selector__header-inner">
360 <span class="font-weight-bold fs0">@RelevantVariantGroup.GetString("Ecom:VariantGroup.Label")</span>
361 <div class="variant-selector__header-choices">
362 <span class="variantoptionname fs0" data-bind="text: SelectedVariantOption0">@SelectedVariant.GetString("Ecom:VariantOption.Name")</span>
363 </div>
364 </div>
365 <span class="variant-selector__toggle" data-bind="click: ToggleVariantInfo.bind($data, '@variantInfoTextEscapedLineBreaks')">
366 <span class="fas fa-info-circle"></span>
367 </span>
368 </div>
369
370 @if (!RelevantVariantGroup.GetString("Ecom:VariantGroup.Label").ToLower().Contains("farve") || variantCount > 5)
371 {
372 <div class="variant-dropdown border-grey p-2 mt-3 d-flex justify-content-between align-items-center pointer js-variant-dropdown">
373 <p class="m-0"><span data-bind="text: SelectedVariantOption0">@SelectedVariant.GetString("Ecom:VariantOption.Name")</span></p>
374 <i class="fas fa-chevron-down"></i>
375 </div>
376 }
377
378 <div class="@variantSelectorClass">
379 @foreach (LoopItem Variant in RelevantVariantGroup.GetLoop("VariantAvailableOptions"))
380 {
381 LoopItem VariantCombination = GetLoop("Co3VariantCombinations").FirstOrDefault(x => x.GetString("Ecom:VariantCombination.VariantID").ToString().Contains(Variant.GetString("Ecom:VariantOption.ID")));
382 string VariantName = Variant.GetString("Ecom:VariantOption.Name");
383 string VariantIdentifier = Variant.GetString("Ecom:VariantOption.ID");
384 string VariantLink = VariantCombination.GetString("Ecom:VariantCombination.Link.Clean");
385 string VariantPreview = String.Empty;
386 var ObservableToSet = "SelectedVariantOption0";
387 var ObservableToSetId = "SelectedVariantOptionId0";
388 bool ShowPreview = RelevantVariantGroup.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") || RelevantVariantGroup.GetString("Ecom:VariantGroup.Name").ToLower().Contains("materiale");
389
390 List<string> variantCombinationVariantIds = GetLoop("Co3VariantCombinations").Select(p => p.GetString("Ecom:VariantCombination.VariantID")).ToList();
391 string variantOptionColor = Variant.GetString("Ecom:VariantOption.ColorHex");
392 string variantIdWithColorOptionId = variantCombinationVariantIds.FirstOrDefault(vid => vid.Contains(VariantIdentifier));
393
394 if (!String.IsNullOrEmpty(Variant.GetString("Ecom:VariantOption.ImgSmall.Clean")) && ShowPreview)
395 {
396 if (Variant.GetString("Ecom:VariantOption.ImgSmall.Clean").StartsWith("#"))
397 {
398 VariantPreview = "style= \"background-color: " + Variant.GetString("Ecom:VariantOption.ImgSmall.Clean") + ";\"";
399 }
400 else
401 {
402 VariantPreview = "style=\"background-image: url('" + Variant.GetString("Ecom:VariantOption.ImgSmall.Clean") + "');\"";
403 }
404 }
405
406 if (!RelevantVariantGroup.GetString("Ecom:VariantGroup.Label").ToLower().Contains("farve") || variantCount > 5)
407 {
408 <div class="option pointer color-primary d-flex w-100 p-2 bg-grey" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VariantIdentifier', @ObservableToSet, '@VariantName', @ObservableToSetId)">
409 @if (!String.IsNullOrEmpty(VariantPreview))
410 {
411 <span class="option-preview mr-2" @VariantPreview></span>
412 }
413 <span>@VariantName</span>
414 </div>
415 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSet, value: '@SelectedVariant.GetString("Ecom:VariantOption.Name")' }"></div>
416 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSetId, value: '@SelectedVariant.GetString("Ecom:VariantOption.ID")' }"></div>
417 }
418 else
419 {
420 if (variantIdWithColorOptionId.IsNullOrEmpty() == false)
421 {
422 bool selectedVariant = Variant.GetBoolean("Ecom:VariantOption.Selected");
423 string selectedVariantClass = "";
424 if (selectedVariant)
425 {
426 selectedVariantClass = "selected";
427 }
428
429 <div id="@VariantIdentifier" class="product-item__variant large @selectedVariantClass" style="background-color: @variantOptionColor;" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VariantIdentifier', @ObservableToSet, '@VariantName', @ObservableToSetId)"></div>
430 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSet, value: '@SelectedVariant.GetString("Ecom:VariantOption.Name")' }"></div>
431 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSetId, value: '@SelectedVariant.GetString("Ecom:VariantOption.ID")' }"></div>
432 }
433 }
434 }
435 </div>
436 </div>
437 }
438 else if (PossibleVariantCombinations == VariantCombinationsCount)
439 {
440 Dictionary<string, string> CurrentlySelectedVariantOptions = new Dictionary<string, string>();
441 foreach (var VG in GetLoop("VariantGroups"))
442 {
443 foreach (var VO in VG.GetLoop("VariantAvailableOptions").Where(x => x.GetBoolean("Ecom:VariantOption.Selected")))
444 {
445 CurrentlySelectedVariantOptions.Add(VG.GetString("Ecom:VariantGroup.Name"), VO.GetString("Ecom:VariantOption.ID"));
446 }
447 }
448 int VGLoopCounter = 0;
449 foreach (var VG in GetLoop("VariantGroups"))
450 {
451 List<LoopItem> RelevantVariantCombinations = new List<LoopItem>();
452 var VariantCombinationLink = GetLoop("Co3VariantCombinations");
453 var SelectedVariant = VG.GetLoop("VariantAvailableOptions").FirstOrDefault(x => x.GetBoolean("Ecom:VariantOption.Selected"));
454 var ObservableToSet = "SelectedVariantOption" + VGLoopCounter.ToString();
455 var ObservableToSetId = "SelectedVariantOptionId" + VGLoopCounter.ToString();
456 int variantCount = VG.GetLoop("VariantAvailableOptions").Count;
457
458 VGLoopCounter++;
459
460 var variantSelectorClass = "variant-options bg-white";
461 if (!VG.GetString("Ecom:VariantGroup.ID").ToLower().Contains("fasthed"))
462 {
463 if (VG.GetString("Ecom:VariantGroup.Label").ToLower().Contains("farve") && variantCount <= 5)
464 {
465 variantSelectorClass = "variant-selector__color-picker d-flex flex-wrap";
466 }
467 else if (VG.GetString("Ecom:VariantGroup.Label").ToLower().Contains("farve") && variantCount > 5)
468 {
469 variantSelectorClass = "variant-options bg-white";
470 }
471 else
472 {
473 variantSelectorClass = "radio-button d-flex flex-column";
474 }
475 }
476
477 foreach (var VC in VariantCombinationLink)
478 {
479 var Add = true;
480 foreach (var KVP in CurrentlySelectedVariantOptions.Where(s => s.Key != VG.GetString("Ecom:VariantGroup.Name")))
481 {
482 if (!VC.GetString("Ecom:VariantCombination.VariantID").ToString().Contains(KVP.Value))
483 {
484 Add = false;
485 }
486 }
487 if (Add)
488 {
489 RelevantVariantCombinations.Add(VC);
490 }
491 }
492 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSet, value: '@SelectedVariant.GetString("Ecom:VariantOption.Name")' }"></div>
493 <div class="d-none" data-bind="setInitValue: { observable: @ObservableToSetId, value: '@SelectedVariant.GetString("Ecom:VariantOption.ID")' }"></div>
494
495 string variantInfoText = Translate("VariantInfo-" + VG.GetString("Ecom:VariantGroup.Label"), "Variant Info Text");
496 string variantInfoTextEscapedLineBreaks = variantInfoText.Replace(System.Environment.NewLine, "<br />");
497
498 <div class="variant-selector position-relative border-grey border-bottom-0 p-4" data-variantgroupid="@VG.GetString("Ecom:VariantGroup.ID")">
499 <div class="variant-selector__header">
500 <div class="variant-selector__header-inner">
501 <span class="font-weight-bold fs0">@VG.GetString("Ecom:VariantGroup.Label")</span>
502 <div class="variant-selector__header-choices">
503 <span class="variantoptionname fs0" data-bind="text: @ObservableToSet">@SelectedVariant.GetString("Ecom:VariantOption.Name")</span>
504 </div>
505 </div>
506 <span class="variant-selector__toggle" data-bind="click: ToggleVariantInfo.bind($data, '@variantInfoTextEscapedLineBreaks')">
507 <span class="fas fa-info-circle"></span>
508 </span>
509 </div>
510 @if (VG.GetString("Ecom:VariantGroup.Label").ToLower().Contains("fasthed") || VG.GetString("Ecom:VariantGroup.Label").ToLower().Contains("farve") && variantCount > 5)
511 {
512 <div class="variant-dropdown border-grey p-2 mt-3 d-flex justify-content-between align-items-center pointer js-variant-dropdown">
513 <p class="m-0"><span data-bind="text: @ObservableToSet">@SelectedVariant.GetString("Ecom:VariantOption.Name")</span></p>
514 <i class="fas fa-chevron-down"></i>
515 </div>
516 }
517
518 <div class="@variantSelectorClass">
519 @foreach (var VO in VG.GetLoop("VariantAvailableOptions"))
520 {
521 var VariantCombination = RelevantVariantCombinations.Where(vc => vc.GetString("Ecom:VariantCombination.VariantID").ToString().Contains(VO.GetString("Ecom:VariantOption.ID"))).FirstOrDefault();
522 string VariantLink = VariantCombination.GetString("Ecom:VariantCombination.Link.Clean");
523 string VariandOptionId = VariantCombination.GetString("Ecom:VariantCombination.VariantID");
524 string VariantPreview = String.Empty;
525 string VOName = VO.GetString("Ecom:VariantOption.Name");
526 string VOId = VO.GetString("Ecom:VariantOption.ID");
527 bool ShowPreview = VG.GetString("Ecom:VariantGroup.Name").ToString().ToLower().Contains("farve") || VG.GetString("Ecom:VariantGroup.Name").ToString().ToLower().Contains("materiale") ? true : false;
528
529 List<string> variantCombinationVariantIds = GetLoop("Co3VariantCombinations").Select(p => p.GetString("Ecom:VariantCombination.VariantID")).ToList();
530 string variantOptionColor = VO.GetString("Ecom:VariantOption.ColorHex");
531 string variantIdWithColorOptionId = variantCombinationVariantIds.FirstOrDefault(vid => vid.Contains(VariandOptionId));
532
533 if (!String.IsNullOrEmpty(VO.GetString("Ecom:VariantOption.ImgSmall.Clean")) && ShowPreview)
534 {
535 if (VO.GetString("Ecom:VariantOption.ImgSmall.Clean").StartsWith("#"))
536 {
537 VariantPreview = "style= \"background-color: " + VO.GetString("Ecom:VariantOption.ImgSmall.Clean") + ";\"";
538 }
539 else
540 {
541 VariantPreview = "style=\"background-image: url('" + VO.GetString("Ecom:VariantOption.ImgSmall.Clean") + "');\"";
542 }
543 }
544
545 if (VG.GetString("Ecom:VariantGroup.ID").ToLower().Contains("fasthed") || VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") && variantCount > 5)
546 {
547 <div class="option d-flex w-100 p-2 bg-grey color-primary pointer" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VOId', @ObservableToSet, '@VOName', @ObservableToSetId)">
548 @if (!String.IsNullOrEmpty(VariantPreview))
549 {
550 <span class="option-preview mr-2" @VariantPreview></span>
551 }
552 @VOName
553 </div>
554 }
555 else if (VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve") && variantCount <= 5)
556 {
557 bool selectedVariant = VO.GetBoolean("Ecom:VariantOption.Selected");
558 string selectedVariantClass = "";
559 if (selectedVariant)
560 {
561 selectedVariantClass = "selected";
562 }
563
564 if (variantIdWithColorOptionId.IsNullOrEmpty() == false)
565 {
566 <div id="@VOId" class="variantToggle product-item__variant large @selectedVariantClass" style="background-color: @variantOptionColor;" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VOId', @ObservableToSet, '@VOName', @ObservableToSetId, $element)"><i class="fa-solid fa-check"></i></div>
567 }
568 }
569 else if (!VG.GetString("Ecom:VariantGroup.ID").ToLower().Contains("fasthed") || VG.GetString("Ecom:VariantGroup.Name").ToLower().Contains("farve"))
570 {
571 bool selectedVariant = VO.GetBoolean("Ecom:VariantOption.Selected");
572 string selectedVariantClass = "";
573 if (selectedVariant)
574 {
575 selectedVariantClass = "selected";
576 }
577
578 <div id="@VOId" class="variantToggle product-item__variant--radiobutton @selectedVariantClass mb-2" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VOId', @ObservableToSet, '@VOName', @ObservableToSetId, $element)">
579 <span class="radiobutton"></span>@VOName
580 </div>
581 }
582 }
583 </div>
584 </div>
585 }
586 }
587 else if (GetLoop("VariantGroups").Any(x => x.GetString("Ecom:VariantGroup.Name").ToString().ToLower().Contains("farve")) && GetLoop("VariantGroups").Any(x => x.GetString("Ecom:VariantGroup.Name").ToString().ToLower().Contains("materiale")))
588 {
589 <div class="mt-3">
590 <p class="mb-1">@Translate("CurrentDesignName", "Valgte design: ")</p>
591 <p class="font-weight-bold">@CurrentVariantName</p>
592 </div>
593 <div class="d-flex">
594 <div class="btn btn-primary" data-bind="click: ToggleVariantPicker">@Translate("ChooseNewDesign", "Vælg design")</div>
595 </div>
596 }
597 else
598 {
599 var SelectedVariantId = GetString("Ecom:Product.VariantID");
600 var SelectedVariant = GetLoop("Co3VariantCombinations").Where(x => x.GetString("Ecom:VariantCombination.VariantID") == SelectedVariantId).FirstOrDefault();
601 var SelectedVariantName = "";
602 if (SelectedVariant != null)
603 {
604 SelectedVariantName = SelectedVariant.GetString("Ecom:VariantCombination.VariantText");
605 }
606 <div class="d-none" data-bind="setInitValue: { observable: SelectedVariantOption0, value: '@SelectedVariantName' }"></div>
607 <div class="d-none" data-bind="setInitValue: { observable: SelectedVariantOptionId0, value: '@SelectedVariantId' }"></div>
608
609 <div class="position-relative">
610 <div class="variant-dropdown p-2 box-shadow mt-3 d-flex justify-content-between align-items-center pointer js-variant-dropdown">
611 <p class="m-0"><span class="font-weight-bold">@Translate("FallbackVariantsText", "VARIANTER")</span> - <span data-bind="text: SelectedVariantOption0">@SelectedVariantName</span></p>
612 <i class="fas fa-chevron-down"></i>
613 </div>
614 <div class="variant-options bg-white">
615 @foreach (var Variant in GetLoop("Co3VariantCombinations"))
616 {
617 string VariantName = Variant.GetString("Ecom:VariantCombination.VariantText");
618 string VCId = Variant.GetString("Ecom:VariantCombination.VariantID");
619 <div class="option color-primary pointer d-block p-2 bg-grey" data-bind="click: UpdateVariantAndSetObservable.bind($data, '@VCId', SelectedVariantOption0, '@VariantName', SelectedVariantOptionId0)">@VariantName</div>
620 }
621 </div>
622 </div>
623 }
624 </div>
625 }
626
627 @if (GetInteger("Ecom:Product.RelatedCount") > 0)
628 {
629 <div class="optionalselectors position-relative border-grey border-bottom-0 p-4">
630 <div class="optional-selectors__header">
631 <span class="font-weight-bold fs0">@Translate("Tilvalg", "Tilvalg")</span>
632 <span class="variant-selector__toggle" data-bind="click: ToggleOptionalInfo">
633 <span class="fas fa-info-circle"></span>
634 </span>
635 </div>
636 <div class="optional-selectors__body">
637 @foreach (LoopItem relatedGroup in GetLoop("ProductRelatedGroups").Where(g => g.GetLoop("Products").Count > 0))
638 {
639 string groupName = relatedGroup.GetString("Ecom:Product:RelatedGroup.Name");
640 string groupNameInLowerCase = groupName.ToLower().Replace(" ", "-");
641
642 <div class="optionalselectors--selector d-flex align-items-center mb-4 position-relative js-optional-selector" data-group-name="@groupNameInLowerCase">
643 <div class="optionalselectors--selector__label u-inline-block d-flex">
644 @groupName
645 <span class="optionalselector-remove js-optional-selector-remove optionalselector-remove__@groupNameInLowerCase d-none" data-bind="click: RemoveOptionalProduct"><i class="fa-solid fa-circle-xmark"></i></span>
646 </div>
647 <div class="variant-dropdown border-grey p-2 d-flex justify-content-between align-items-center pointer js-variant-dropdown js-optional-dropdown">
648 <p class="m-0"><span>@Translate("Vælg", "Vælg")</span></p>
649 <svg class="svg-inline--fa fa-chevron-down fa-w-14" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-down" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" data-fa-i2svg=""><path fill="currentColor" d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"></path></svg><!-- <i class="fas fa-chevron-down"></i> Font Awesome fontawesome.com -->
650 </div>
651 <div class="variant-options bg-white">
652 @foreach (LoopItem product in relatedGroup.GetLoop("Products"))
653 {
654 string optionalProductId = product.GetString("Ecom:Product.ID");
655 string optionalProductName = product.GetString("Ecom:Product.Name");
656 string optionalProductPrice = MoblerHelpers.formatPrice(product.GetString("Ecom:Product.Price.PriceWithVAT.Value"));
657 string optionalProductPriceUnformatted = product.GetString("Ecom:Product.Price.Price.Value");
658 string optionalProductPriceBefore = product.GetString("CampaignModule:Product.CampaignProduct.NormalPrice");
659 string optionalProductPriceDiscount = product.GetString("CampaignModule:Product.CampaignProduct.DiscountAmount.FormattedRetail");
660 string optionalProductDiscountText = "";
661 if (!String.IsNullOrEmpty(optionalProductPriceDiscount))
662 {
663 optionalProductDiscountText = "(SPAR: " + optionalProductPriceDiscount + ")";
664 }
665 string optionalProductVariantId = product.GetString("Ecom:Product.VariantID");
666 string optionalProductVariantText = product.GetString("Ecom:Product.VariantText");
667
668 <div class="option d-flex w-100 p-2 bg-grey color-primary pointer" id="@optionalProductId@optionalProductVariantId" data-bind="click: UpdateOptionalProduct.bind($data, '@optionalProductId', '@optionalProductVariantId', '@optionalProductName', '@optionalProductVariantText', '@optionalProductPrice', @optionalProductPriceUnformatted, '@optionalProductPriceBefore', '@optionalProductPriceDiscount', '@groupNameInLowerCase')">
669 @optionalProductName @optionalProductVariantText - @optionalProductPrice @optionalProductDiscountText
670 </div>
671 }
672 </div>
673 </div>
674 }
675 </div>
676 </div>
677 }
678
679 <div class="d-flex flex-wrap flex-column justify-content-end border-grey p-4">
680 @if (ForSale)
681 {
682 <div class="addtocart-price">
683 <div class="d-flex align-items-center">
684 <div class="d-flex justify-content-between w-100">
685 <div class="quantity-controls d-flex align-items-center">
686 <div class="control minus" data-bind="click: DecreaseQuantity">
687 -
688 </div>
689 <input type="text" name="Quantity" value="1" data-bind="value: Quantity"/>
690 <div class="control plus" data-bind="click: IncrementQuantity">
691 +
692 </div>
693 </div>
694 @{
695 string addToCartButtonClass = BlackFridayTheme == "True" ? "btn-black-friday bf-bg-black" : "brand-primary-outline font-weight-bold";
696 }
697 <p class="btn @addToCartButtonClass add-to-cart m-0 d-flex align-items-center px-4" data-bind="click: AddToCart.bind($data, '@DataLayerPrice')">
698 @Translate("ProductAddToCart", "Læg i kurv")
699 </p>
700 </div>
701 </div>
702 <div class="d-flex flex-wrap flex-column justify-content-between mt-4 unimportant-hidden">
703 <p class="price font-weight-bold m-0">
704 <span class="unimportant-hidden" data-bind="css: { 'd-inline': SplashType() == 1 }">@Translate("ProductNow", "NU") </span>
705 <span class="total-price" data-bind="text: Price"></span>
706 <span id="priceWithoutAddons" class="unimportant-hidden" data-bind="text: PriceDouble"></span>
707 </p>
708 @if (campaignTextClean == "KOMBI")
709 {
710 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: 5 }"></div>
711 <div class="d-none" data-bind="setInitValue: { observable: BeforePriceDouble, value: @normalPrice }"></div>
712 <div class="d-none" data-bind="setInitValue: { observable: BeforePrice, value: '@normalPriceFormatted' }"></div>
713 <div class="d-none" data-bind="setInitValue: { observable: SavingDouble, value: @spar }"></div>
714 <div class="d-none" data-bind="setInitValue: { observable: Saving, value: '@sparFormatted' }"></div>
715 }
716 else if (spar > 0)
717 {
718 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: 1 }"></div>
719 <div class="d-none" data-bind="setInitValue: { observable: BeforePriceDouble, value: @normalPrice }"></div>
720 <div class="d-none" data-bind="setInitValue: { observable: BeforePrice, value: '@normalPriceFormatted' }"></div>
721 <div class="d-none" data-bind="setInitValue: { observable: SavingDouble, value: @spar }"></div>
722 <div class="d-none" data-bind="setInitValue: { observable: Saving, value: '@sparFormatted' }"></div>
723 }
724 else if (NewItem)
725 {
726 if (NewItemExpiryDate > DateTime.Now)
727 {
728 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: 2 }"></div>
729 }
730 }
731 else if (LowPrice)
732 {
733 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: 3 }"></div>
734 }
735 else if (CostPrice > 0)
736 {
737 <div class="d-none" data-bind="setInitValue: { observable: SplashType, value: 4 }"></div>
738 }
739
740 <div class="w-100 unimportant-hidden" data-bind="css: { 'd-flex': SplashType() == 1 || SplashType() == 5}">
741 <p class="color-subtle fs0 m-0 text-uppercase mr-3">@Translate("PriceBefore", "Før") <span class="before-price" data-bind="text: BeforePrice"></span></p>
742 <p class="splash fs0 text-uppercase m-0 px-2">@Translate("PriceSaving", "Spar") <span class="total-saving" data-bind="text: Saving"></span></p>
743 </div>
744
745 <div class="price-info fs-s mt-4">
746 @if (excludeBedAccessories)
747 {
748 <p class="mb-1 color-subtle price-info-text js-update-price-info-text">@Translate("Ecom:Product.Pricing.Excludes.Beds", "Prisen er ekskl. ben og gavl.")</p>
749 }
750 @if (hasVariants)
751 {
752 <div class="variant-price-disclaimer color-subtle fs-s mb-1">@Translate("VariantPriceDisclaimer", "Prisen kan variere efter materialevalg")</div>
753 }
754 </div>
755
756 @if (!string.IsNullOrEmpty(campaignDateStart) && !string.IsNullOrEmpty(campaignDateEnd) && !string.IsNullOrEmpty(campaignText))
757 {
758 <div class="d-none" data-bind="setInitValue: { observable: CampaignText, value: '@campaignText' }"></div>
759 <div class="price-info fs-xs">
760 <p class="mb-1"><span class="dot dot-yellow"></span> <span data-bind="text: CampaignText"></span></p>
761 </div>
762 }
763 else
764 {
765 <div class="d-none" data-bind="setInitValue: { observable: CampaignText, value: '' }"></div>
766 }
767 @* <p data-bind="text: CampaignText"></p> *@
768 </div>
769
770 <div class="optionalproducts d-none">
771 <hr />
772 <h5>@Translate("Tilvalg", "Tilvalg")</h5>
773 <div class="optionalproduct"></div>
774 <p><strong>Sum af tilvalg: </strong><span id="optionalPrice"></span></p>
775 </div>
776
777 @if (!string.IsNullOrEmpty(PayeverWidget.WidgetId) && !string.IsNullOrEmpty(PayeverWidget.BusinessId) && !string.IsNullOrEmpty(PayeverWidget.CheckoutId))
778 {
779 <div>
780 <div id="payever" class="payever-widget-finexp"
781 data-widgetid="@PayeverWidget.WidgetId"
782 data-checkoutid="@PayeverWidget.CheckoutId"
783 data-business="@PayeverWidget.BusinessId"
784 data-type="text"
785 data-reference="order-id"
786 data-amount="@GetString("Ecom:Product.Price.Price").Replace(".", string.Empty).Replace(",", ".")">
787 </div>
788 <script>
789 var script = document.createElement('script');
790 script.src = 'https://widgets.payever.org/finance-express/widget.min.js';
791 script.onload = function() {
792 PayeverPaymentWidgetLoader.init(
793 '.payever-widget-finexp'
794 );
795 };
796 document.head.appendChild(script);
797 </script>
798 </div>
799 }
800
801 @if ((QuickDelivery || DisplayAverageDeliveryTime) && !HideDelivery)
802 {
803 <div class="w-100 mt-3">
804 <div class="delivery-information">
805 <svg class="delivery-icon mr-2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" fill="@DeliveryColor" x="0px" y="0px" width="30px" height="30px" viewBox="0 0 612 612" style="enable-background:new 0 0 612 612;" xml:space="preserve">
806 <g><g>
807 <path d="M226.764,375.35c-28.249,0-51.078,22.91-51.078,51.16c0,28.166,22.829,51.078,51.078,51.078s51.078-22.912,51.078-51.078 C277.841,398.26,255.013,375.35,226.764,375.35z M226.764,452.049c-14.125,0-25.54-11.498-25.54-25.541 c0-14.123,11.415-25.539,25.54-25.539c14.124,0,25.539,11.416,25.539,25.539C252.302,440.551,240.888,452.049,226.764,452.049z M612,337.561v54.541c0,13.605-11.029,24.635-24.636,24.635h-26.36c-4.763-32.684-32.929-57.812-66.927-57.812 c-33.914,0-62.082,25.129-66.845,57.812H293.625c-4.763-32.684-32.93-57.812-66.845-57.812c-33.915,0-62.082,25.129-66.844,57.812 h-33.012c-13.606,0-24.635-11.029-24.635-24.635v-54.541H612L612,337.561z M494.143,375.35c-28.249,0-51.16,22.91-51.16,51.16 c0,28.166,22.912,51.078,51.16,51.078c28.166,0,51.077-22.912,51.077-51.078C545.22,398.26,522.309,375.35,494.143,375.35z M494.143,452.049c-14.125,0-25.539-11.498-25.539-25.541c0-14.123,11.414-25.539,25.539-25.539 c14.042,0,25.539,11.416,25.539,25.539C519.682,440.551,508.185,452.049,494.143,452.049z M602.293,282.637l-96.817-95.751 c-6.159-6.077-14.453-9.526-23.076-9.526h-48.86v-18.313c0-13.631-11.004-24.635-24.635-24.635H126.907 c-13.55,0-24.635,11.005-24.635,24.635v3.86L2.3,174.429l177.146,23.068L0,215.323l178.814,25.423L0,256.25l102.278,19.29 l-0.007,48.403h509.712v-17.985C611.983,297.171,608.452,288.796,602.293,282.637z M560.084,285.839h-93.697 c-2.135,0-3.86-1.724-3.86-3.859v-72.347c0-2.135,1.725-3.86,3.86-3.86h17.82c0.985,0,1.971,0.411,2.71,1.068l75.796,72.347 C565.257,281.569,563.532,285.839,560.084,285.839z" />
808 </g></g>
809 </svg>
810 <p class="delivery-text @DeliveryFontWeightModifier">@DeliveryTime - <a href="#" class="text-underline" data-bind="click: ToggleDeliveryInfo" style="color: @DeliveryColor">@Translate("Product.DeliveryInfo.ReadmoreText", "Læs mere")</a></p>
811 </div>
812 </div>
813 }
814 </div>
815 }
816 else
817 {
818 <p>@Translate("Ecom:Product.NotForSaleMessage", "Produktet kan ikke købes.")</p>
819 }
820 </div>
821
822 @if (seeProductInStore != "")
823 {
824 <div class="d-flex flex-wrap flex-column justify-content-end border-grey p-4 mt-2">
825 <div class="align-self-center w-100">
826 <a href="@seeProductInStore" class="btn brand-primary-outline see-product-btn font-weight-bold add-to-cart m-0 d-flex align-items-center justify-content-center w-100 px-4">@Translate("Se produktet i en af vores butikker", "Se produktet i en af vores butikker")</a>
827 </div>
828 </div>
829 }
830 </div>
831 </div>
832
833 <div class="delivery-information-popup" data-bind="css: { 'd-flex': DeliveryInfoOpen }, click: ToggleDeliveryInfo">
834 <div class="description-content bg-white px-3 py-4 px-lg-4 position-relative" data-bind="click: OpenDeliveryInfo, clickBubble: false">
835 <div class="modal-closer-custom" data-bind="click: ToggleDeliveryInfo, clickBubble: false">
836 <img src="/Files/Templates/Designs/Mobler2018/assets/img/icons/close.svg" />
837 </div>
838 @if (QuickDelivery)
839 {
840 <div>
841 @QuickDeliveryDescription
842 </div>
843 }
844 else if (DisplayAverageDeliveryTime)
845 {
846 <div>
847 @NormalDeliveryDescription
848 </div>
849 }
850 </div>
851 </div>
852
853 <div class="variant-information-popup" data-bind="css: { 'd-flex': VariantInfoOpen }, click: ToggleVariantInfo">
854 <div class="description-content bg-white px-3 py-4 px-lg-4 position-relative" data-bind="click: OpenVariantInfo, clickBubble: false">
855 <div class="modal-closer-custom" data-bind="click: ToggleVariantInfo, clickBubble: false">
856 <img src="/Files/Templates/Designs/Mobler2018/assets/img/icons/close.svg" />
857 </div>
858 <p></p>
859 </div>
860 </div>
861
862 <div class="delivery-information-popup" data-bind="css: { 'd-flex': OptionalInfoOpen }, click: ToggleOptionalInfo">
863 <div class="description-content bg-white px-3 py-4 px-lg-4 position-relative" data-bind="click: OpenOptionalInfo, clickBubble: false">
864 <div class="modal-closer-custom" data-bind="click: ToggleOptionalInfo, clickBubble: false">
865 <img src="/Files/Templates/Designs/Mobler2018/assets/img/icons/close.svg" />
866 </div>
867 <div>
868 @Translate("TilvalgInfoboks", "Tilvalg tekst")
869 </div>
870 </div>
871 </div>
872
873 @if (CostPrice > 0)
874 {
875 <div class="w-100 mt-3">
876
877 <div class="delivery-information">
878 <div class="live-price-animation"></div>
879 <p class="delivery-text">@Translate("Product.PriceInfo.RecommendedPriceInfo", "Aktuel dagspris - ")<a href="#" class="text-underline" data-bind="click: TogglePriceInfo">@Translate("Product.PriceInfo.RecommendedPriceLink", "Læs mere")</a></p>
880 </div>
881
882 </div>
883 }
884
885 <div class="w-100">
886 @if (GetBoolean("Ecom:Product:Field.Farvevarianter1.Value"))
887 {
888 <img class="img-fluid mb-3 mt-5" src="/Files/Images/Farver/farver1.png" />
889 <p class="mb-0">@Translate("ProductColor1Text", "Fås i flere forskellige varianter og moduler, besøg dit lokale Møblér bolighus og få vejledning til design af din sofa.")</p>
890 }
891 else if (GetBoolean("Ecom:Product:Field.Farvevarianter2.Value"))
892 {
893 <img class="img-fluid mb-3 mt-5" src="/Files/Images/Farver/farver2.png" />
894 <p class="mb-0">@Translate("ProductColor2Text", "Farve 2 hjælpetekst")</p>
895 }
896 else if (GetBoolean("Ecom:Product:Field.Farvevarianter3.Value"))
897 {
898 <img class="img-fluid mb-3 mt-5" src="/Files/Images/Farver/farver3.png" />
899 <p class="mb-0">@Translate("ProductColor3Text", "Farve 3 hjælpetekst")</p>
900 }
901 else if (GetBoolean("Ecom:Product:Field.Farvevarianter4.Value"))
902 {
903 <img class="img-fluid mb-3 mt-5" src="/Files/Images/Farver/farver4.png" />
904 <p class="mb-0">@Translate("ProductColor4Text", "Farve 4 hjælpetekst")</p>
905 }
906 else if (GetBoolean("Ecom:Product:Field.Farvevarianter5.Value"))
907 {
908 <img class="img-fluid mb-3 mt-5" src="/Files/Images/Farver/farver5.png" />
909 <p class="mb-0">@Translate("ProductColor5Text", "Farve 5 hjælpetekst")</p>
910 }
911 </div>
912 @SnippetEnd("PricesContainer")
913
914 @SnippetStart("ProductDetailsMeta")
915 @if (!string.IsNullOrEmpty(MetaDescription))
916 {
917 <meta name="description" content="@MetaDescription" />
918 }
919 @SnippetEnd("ProductDetailsMeta")
920
921 @SnippetStart("OgTags")
922 <meta property="og:type" content="product" />
923 <meta property="og:description" content="@Regex.Replace(ShortDescription, "<.*?>", String.Empty)" />
924 <meta property="og:image" content="@OgImage" />
925 @SnippetEnd("OgTags")
926
927 @using System.Collections
928 @using Dynamicweb.Core
929 @using Dynamicweb.Ecommerce
930 @using Mobler.Website.CustomModules.MoblerHelpers
931 @using Dynamicweb.Ecommerce.Products
932 @using Dynamicweb.Ecommerce.Variants
933 @using Humanizer
934 @using Mobler.Website.CustomCode
935 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>>
936
937 @using Mobler.Website.CustomModules.MoblerHelpers
938 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>>
939
940 @helper RenderPricing(LoopItem product, bool hasVariants)
941 {
942 // Theming
943 string blackFridayTheme = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("BlackFridayTheme");
944
945 // Pricing
946 DateTime newItemExpiryDate = product.GetDate( "Ecom:Product:Field.NewItemExpiryDate" );
947 bool hasNewItemExpiryDate = newItemExpiryDate.Date > DateTime.Now.Date;
948 bool newItem = product.GetBoolean("Ecom:Product:Field.NewItem") && hasNewItemExpiryDate;
949 bool LowPrice = product.GetBoolean("Ecom:Product:Field.Splash3");
950 double CostPrice = product.GetDouble("Ecom:Product:Field.FirstwebCostPrice.Value.Raw");
951 string PriceSaving = product.GetString("Ecom:Product:Field.Spar.Value");
952 double ActualPrice = product.GetDouble("Ecom:Product.Price.Price");
953
954 // Initialize variables
955 double spar = 0;
956 double price = 0;
957 double normalPrice = 0;
958
959 string priceFormatted = "";
960 string sparFormatted = "";
961 string normalPriceFormatted = "";
962
963 string campaignColor = "";
964 string campaignText = "";
965 string campaignDate = "";
966
967 bool CampaignActiveOnProduct = product.GetBoolean("CampaignModule:Product.CampaignActiveOnProduct");
968 bool IsLocalWebshopProduct = (Mobler.Website.CustomCode.Frontend.Helper.IsLocalWebshop() && !product.GetBoolean("Ecom:Product:Field.SystemIsMasterShopProduct"));
969
970 if (!CampaignActiveOnProduct && !IsLocalWebshopProduct)
971 {
972 price = ActualPrice;
973 priceFormatted = MoblerHelpers.formatPrice(price);
974 }
975 else if (IsLocalWebshopProduct)
976 {
977 price = ActualPrice;
978 priceFormatted = MoblerHelpers.formatPrice(price);
979
980 normalPrice = product.GetDouble("Ecom:Product:Field.ForPris");
981 normalPriceFormatted = MoblerHelpers.formatPrice(normalPrice);
982
983 spar = product.GetDouble("Ecom:Product:Field.Spar");
984 sparFormatted = MoblerHelpers.formatPrice(spar);
985
986 bool SplashLwCombiPrice = product.GetBoolean("Ecom:Product:Field.SplashLwCombiPrice");
987 DateTime SplashLwCombiPriceExpiryDate = product.GetDate("Ecom:Product:Field.SplashLwCombiPriceExpiryDate");
988
989 if (SplashLwCombiPrice && (SplashLwCombiPriceExpiryDate == DateTime.MinValue || SplashLwCombiPriceExpiryDate > DateTime.Now))
990 {
991 campaignText = "KOMBI";
992 }
993 }
994 else
995 {
996 normalPrice = product.GetDouble("CampaignModule:Product.CampaignProduct.NormalPrice");
997 normalPriceFormatted = product.GetString("CampaignModule:Product.CampaignProduct.NormalPrice.FormattedRetail");
998
999 price = product.GetDouble("CampaignModule:Product.CampaignProduct.CampaignPrice");
1000 priceFormatted = product.GetString("CampaignModule:Product.CampaignProduct.CampaignPrice.FormattedRetail");
1001
1002 spar = product.GetDouble("CampaignModule:Product.CampaignProduct.DiscountAmount");
1003 sparFormatted = product.GetString("CampaignModule:Product.CampaignProduct.DiscountAmount.FormattedRetail");
1004
1005 campaignColor = product.GetString("CampaignModule:Product.Campaign.Color");
1006 campaignText = product.GetString("CampaignModule:Product.Campaign.Text");
1007 }
1008
1009 // Set Splash Types
1010 string splashType = "";
1011 if (campaignText == "KOMBI")
1012 {
1013 splashType = "combo";
1014 }
1015 else if (CostPrice > 0)
1016 {
1017 splashType = "priceshape";
1018 }
1019 else if (spar > 0)
1020 {
1021 splashType = "offer";
1022 }
1023 else if (newItem)
1024 {
1025 splashType = "new";
1026 }
1027 else if (LowPrice)
1028 {
1029 splashType = "low";
1030 }
1031
1032 <div>
1033 <p class="product-item__price fs3 font-weight-bold">
1034 @if (hasVariants)
1035 {
1036 <span class="font-weight-semibold fs0 mr-2">@Translate("ProductPrice.From", "Fra")</span>
1037 }
1038 @priceFormatted
1039 </p>
1040 @if (splashType == "combo")
1041 {
1042 <div class="splash splash--on-top">
1043 <p class="m-0">@Translate("ProductCombinationOffer", "Sætpris")</p>
1044 </div>
1045 <div class="d-flex justify-content-between mb-2">
1046 <p class="color-subtle fs0 m-0 text-uppercase">@Translate("PriceBeforeCombo", "Før") @normalPriceFormatted</p>
1047 <p class="splash fs0 text-uppercase m-0 px-2">@Translate("PriceSaving", "Spar") @sparFormatted</p>
1048 </div>
1049 }
1050 else if (splashType == "priceshape")
1051 {
1052 string DailyPriceBackgroundColor = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("AktuelDagsprisBackgroundColor");
1053 string DailyPriceTextColor = Firstweb.Custom.CustomCode.Frontend.Helpers.AreaItems.GetString("AktuelDagsprisTextColor");
1054
1055 <div class="splash splash--on-top price-shape" style="background-color: @DailyPriceBackgroundColor">
1056 <p class="m-0" style="color: @DailyPriceTextColor;">@Translate("ProductDailyPriceSplash", "Dagspris")</p>
1057 </div>
1058 }
1059 else if (splashType == "offer")
1060 {
1061 if (blackFridayTheme == "True")
1062 {
1063 <div class="splash splash--on-top bf-bg-black bf-color-white">
1064 <p class="m-0">@Translate("ProductOffer", "Tilbud")</p>
1065 </div>
1066 }
1067 else
1068 {
1069 <div class="splash splash--on-top p-1">
1070 <p class="m-0">@Translate("ProductOffer", "Tilbud")</p>
1071 </div>
1072 }
1073 <div class="d-flex justify-content-between mb-2">
1074 <p class="color-subtle fs0 m-0 text-uppercase">@Translate("PriceBefore", "Før") @normalPriceFormatted</p>
1075 <p class="splash fs0 m-0 text-uppercase px-2">@Translate("PriceSaving", "Spar") @sparFormatted</p>
1076 </div>
1077 }
1078 else if (splashType == "new")
1079 {
1080 <div class="splash splash--on-top p-1 new-item">
1081 <p class="m-0">@Translate("ProductNew", "Nyhed")</p>
1082 </div>
1083 }
1084 else if (splashType == "low")
1085 {
1086 <div class="splash splash--on-top p-1 low-price">
1087 <p class="m-0">@Translate("ProductLowPrice", "Fast lavpris")</p>
1088 </div>
1089 }
1090 </div>
1091 }
1092
1093
1094 @helper RenderProduct(LoopItem product, Boolean powerStep = false)
1095 {
1096 // Group Specific - Exclude Bed Accessories
1097 bool excludeBedAccessories = product.GetBoolean("Ecom:Product.CategoryField.Senge.ExcludeBedAccessories.Value");
1098
1099 // Product General Info
1100 string productId = product.GetString("Ecom:Product.ID");
1101 string productName = product.GetString("Ecom:Product.Name");
1102 string productLink = product.GetString("Ecom:Product.Link.Clean");
1103 string shortDescription = product.GetString("Ecom:Product.ShortDescription");
1104 string languageId = product.GetString("Ecom:Product.LanguageID");
1105
1106 // Variants
1107 string defaultVariantId = product.GetString("Ecom:Product.DefaultVariantComboID");
1108 string variantId = product.GetString("Ecom:Product.VariantID");
1109 if (String.IsNullOrEmpty(variantId)) {
1110 variantId = defaultVariantId;
1111 }
1112 if (!String.IsNullOrEmpty(variantId)) {
1113 productLink = productLink + "&variantid=" + variantId;
1114 }
1115
1116 // Product Images
1117 List<string> images = MoblerHelpers.GetProductImages(productId, variantId);
1118 string productImage = string.Format("/Admin/Public/GetImage.ashx?Image={0}&Width=280&height=220&Format=webP&Quality=90&Crop=5&resolution=50", images.FirstOrDefault()); //MoblerHelpers.GetProductListImageWithMainImage(ProductID, ProductNumber);
1119
1120 // Product Dimensions
1121 string productDepth = product.GetString("Ecom:Product:Field.dybdeint.Value.Clean");
1122 string productHeight = product.GetString("Ecom:Product:Field.hoejdeint.Value.Clean");
1123 string productWidth = product.GetString("Ecom:Product:Field.breddeint.Value.Clean");
1124 bool hasProductDepth = !string.IsNullOrWhiteSpace(productDepth) && productDepth != "0";
1125 bool hasProductHeight = !string.IsNullOrWhiteSpace(productHeight) && productHeight != "0";
1126 bool hasProductWidth = !string.IsNullOrWhiteSpace(productWidth) && productWidth != "0";
1127
1128 // Campaign
1129 string campaignDateStart = "";
1130 string campaignDateEnd = "";
1131
1132 bool campaignActiveOnProduct = product.GetBoolean("CampaignModule:Product.CampaignActiveOnProduct");
1133 if (campaignActiveOnProduct)
1134 {
1135 campaignDateStart = product.GetString("CampaignModule:Product.CampaignProduct.Campaign.Start");
1136 campaignDateEnd = product.GetString("CampaignModule:Product.CampaignProduct.Campaign.End");
1137 }
1138
1139 bool hasCampaignDateStart = !string.IsNullOrWhiteSpace(campaignDateStart);
1140
1141 bool hasCampaignDateEnd = !string.IsNullOrWhiteSpace(campaignDateEnd);
1142 string campaignText = "";
1143 if (hasCampaignDateStart && hasCampaignDateStart)
1144 {
1145 campaignText = $"{Translate("Campaign.BeforeDate.Text", "Gældende fra:")} {campaignDateStart} {Translate("Campaign.BeforeDate.Text2", "t.o.m.")} {campaignDateEnd}";
1146 }
1147
1148 <div class="col-12 col-sm-6 col-lg-3 product-item js-product-list-item">
1149 <div class="product-item__header">
1150 <a href="@productLink" class="product-item__image">
1151 <img class="js-product-image-@productId" src="@productImage" alt="@productName" loading="lazy"/>
1152 </a>
1153 @{
1154 int colorOptionsToShow = 4;
1155 List<string> variantCombinationVariantIds = product.GetLoop("Co3VariantCombinations").Select(p => p.GetString("Ecom:VariantCombination.VariantID")).ToList();
1156 bool hasVariants = false;
1157 }
1158 @if (variantCombinationVariantIds.Any() && !powerStep)
1159 {
1160 hasVariants = true;
1161
1162 <div class="product-item__variants d-flex align-items-center">
1163 @foreach (LoopItem vg in product.GetLoop("VariantGroups"))
1164 {
1165 int variantOptionWithColorCount = 0;
1166
1167 @* Only show variants with hex colors *@
1168 foreach (LoopItem variantOption in vg.GetLoop("VariantAvailableOptions"))
1169 {
1170 <p class="unimportant-hidden">@variantOptionWithColorCount</p>
1171 string variantOptionColor = variantOption.GetString("Ecom:VariantOption.ColorHex");
1172
1173 // Get count of variants with color
1174 if (variantOptionColor.Any())
1175 {
1176 variantOptionWithColorCount++;
1177 }
1178 // Only show this specific amount of variants
1179 if (variantOptionColor.Any() && variantOptionWithColorCount < colorOptionsToShow)
1180 {
1181 // Get variant option id, then compare with variant combinations, to get the link to the specific variant
1182 string variantOptionId = variantOption.GetString("Ecom:VariantOption.ID");
1183 string variantIdWithColorOptionId = variantCombinationVariantIds.FirstOrDefault(vid => vid.Contains(variantOptionId));
1184
1185 if (variantIdWithColorOptionId.IsNullOrEmpty() == false)
1186 {
1187 string variantLink = product.GetString("Ecom:Product.Link.Clean") + "&variantid=" + variantIdWithColorOptionId;
1188 <a href="@variantLink" class="product-item__variant" style="background-color: @variantOptionColor;"></a>
1189 }
1190 }
1191 else if (variantOptionColor.Any() && variantOptionWithColorCount == colorOptionsToShow)
1192 {
1193 <a href="@productLink" class="fs2 product-item__more-variants">+</a>
1194 }
1195 else
1196 {
1197 break;
1198 }
1199 }
1200 }
1201 @if (hasVariants)
1202 {
1203 <a href="@productLink" class="ml-4 fs-s">
1204 <i class="fas fa-pen mr-1 fs-2"></i>
1205 @Translate("Ecom:Product.Pricing.Designer", "Design selv")
1206 </a>
1207 }
1208 </div>
1209 }
1210 </div>
1211
1212 <div class="product-item__body">
1213 <a href="@productLink" class="product-item__title">
1214 <h3 class="fs0">@productName</h3>
1215 </a>
1216 <div class="product-item__description fs0 color-subtle">@shortDescription</div>
1217 @if (hasProductDepth && !powerStep || hasProductHeight && !powerStep || hasProductWidth && !powerStep)
1218 {
1219 <p class="product-item__dimensions fs0 color-subtle">
1220 @if (hasProductDepth)
1221 {
1222 <span>D/L: @productDepth</span>
1223 }
1224 @if (hasProductHeight)
1225 {
1226 <span>H: @productHeight</span>
1227 }
1228 @if (hasProductWidth)
1229 {
1230 <span>B: @productWidth</span>
1231 }
1232 </p>
1233 }
1234 </div>
1235
1236 <div class="product-item__footer">
1237 @RenderPricing(product, hasVariants)
1238
1239 @if (excludeBedAccessories && !powerStep)
1240 {
1241 <p class="color-subtle fs-s m-0">@Translate("Ecom:Product.Pricing.Excludes.Beds", "Prisen er ekskl. ben og gavl.")</p>
1242 }
1243 @if (hasVariants && !powerStep)
1244 {
1245 <div class="variant-price-disclaimer color-subtle fs-s">@Translate("VariantPriceDisclaimer", "Prisen kan variere efter materialevalg")</div>
1246 }
1247 @if (hasCampaignDateStart && hasCampaignDateEnd && !powerStep)
1248 {
1249 <div class="product-item__campaign">
1250 <p class="color-subtle fs-s">@campaignText</p>
1251 </div>
1252 }
1253 </div>
1254 </div>
1255 }
1256
1257 @using Mobler.Website.CustomModules.MoblerHelpers
1258 @inherits Dynamicweb.Rendering.RazorTemplateBase<Dynamicweb.Rendering.RazorTemplateModel<Dynamicweb.Rendering.Template>>
1259
1260 @helper RenderBlogTeaser(Firstweb.Custom.CustomCode.Frontend.Helpers.Blog Blog)
1261 {
1262 string PageId = GetGlobalValue( "Global:Page.ID" );
1263 string CleanLink = Blog.Link.Replace("default.aspx?id", "Default.aspx?Id");
1264 <div class="col-12 col-md-4 px-0 px-md-3 mb-4">
1265 <a href="@CleanLink" class="blog d-block">
1266 <div class="image" style="background-image:url('@Blog.Image')">
1267 <img class="d-none" src="@Blog.Image" alt="Alternate Text" />
1268 </div>
1269 <div class="text">
1270 <div class="positioning">
1271 <h4 class="col-10 col-md-9 px-2 pt-3 bg-white font-weight-bold text-center mx-auto">@Blog.Header</h4>
1272 @if ( PageId != "7613" )
1273 {
1274 <p class="text-center color-subtle">@Blog.Date</p>
1275 }
1276 <p class="text-center font-weight-bold m-0 color-primary">@Translate("BlogReadMore", "Læs mere her")</p>
1277 </div>
1278 </div>
1279 </a>
1280 </div>
1281 }
1282
1283 @helper RenderLargeBlogTeaser(Firstweb.Custom.CustomCode.Frontend.Helpers.Blog Blog, string ImagePosition)
1284 {
1285 string ImageClass = ImagePosition == "Left" ? "" : "offset-md-4";
1286 string TextClass = ImagePosition == "Left" ? "" : "left";
1287 string Image = "/Admin/Public/GetImage.ashx?Image=" + Blog.Image + "&Format=webP&Quality=90&Height=400&Crop=0";
1288 string CleanLink = Blog.Link.Replace("default.aspx?id", "Default.aspx?Id");
1289 <div class="col-12 blog">
1290 <div class="row">
1291 <div class="col-12 col-md-8 px-0 px-md-3 @ImageClass">
1292 <div class="image-container" style="background-image:url('@Image')">
1293 <img class="img-fluid" src="@Image" alt="@Blog.Header" />
1294 </div>
1295 </div>
1296 <div class="col-12 col-md-6 text-container mb-5 mb-md-0 @TextClass">
1297 <div class="text bg-white p-3">
1298 <div>
1299 <h4>@Blog.Header</h4>
1300 @Blog.Teaser
1301 </div>
1302 <a class="font-weight-bold" href="@CleanLink">@Translate("BlogReadMore", "Læs mere her")</a>
1303 </div>
1304 </div>
1305 </div>
1306 </div>
1307 }
1308
1309 @helper RenderLatestBlogsTeasers()
1310 {
1311 var LatestBlogs = Firstweb.Custom.CustomCode.Frontend.Helpers.Blogs.GetLatestBlogs();
1312 foreach (var Blog in LatestBlogs)
1313 {
1314 @RenderBlogTeaser(Blog)
1315 }
1316 }
1317
1318
1319 <div class="container d-none d-md-block">
1320 <div class="bread">
1321 <p class="color-subtle">@Translate("Breadcrumb.CurrentPage", "Her er du:")</p>
1322 <p class="bread-item color-subtle">
1323 <a href="/">
1324 @Translate("Breadcrumb.Frontpage", "Forside")
1325 </a>
1326 </p>
1327 @foreach (var Group in ParentGroups)
1328 {
1329 <p class="bread-item color-subtle">
1330 @if (Group == ParentGroups.First())
1331 {
1332 @Group.Name
1333 ShowOnPageUrl = Group.ProductGroupFieldValues.GetProductGroupFieldValue("FirstwebGroupPrimaryPage").Value.ToString();
1334 }
1335 else
1336 {
1337 <a href="/@ShowOnPageUrl&GroupId=@Group.Id">
1338 @Group.Name
1339 </a>
1340 }
1341 </p>
1342 }
1343 <p class="active color-subtle">@ProductName</p>
1344 </div>
1345 </div>
1346
1347 <div class="container my-5 product-details position-relative" data-bind="viewModel: 'ProductViewModel'">
1348 <div class="d-none" data-bind="setInitValue: { observable: CartPageId, value: '@AjaxCartPageId' }"></div>
1349 <div class="d-none" data-bind="setInitValue: { observable: MiniCart, value: '.js-mini-cart-lines' }"></div>
1350 <div class="d-none" data-bind="setInitValue: { observable: CartCount, value: '.js-cart-count' }"></div>
1351 <div class="d-none" data-bind="setInitValue: { observable: CustomersAlsoSawLines, value: '.js-customers-also-saw-lines' }"></div>
1352 <div class="d-none" data-bind="setInitValue: { observable: CustomersAlsoSawPage, value: '@CustomersAlsoSawPageId' }"></div>
1353 <div class="d-none" data-bind="setInitValue: { observable: RelewiseRecommandationsEndpoint, value: '@relewiseRecommandationsEndpoint' }"></div>
1354 <div class="d-none" data-bind="setInitValue: { observable: MainProductId, value: '@ProductID' }"></div>
1355 <div class="d-none" data-bind="setInitValue: { observable: VariantsEndpoint, value: '@VariantsEndpoint' }"></div>
1356 <div class="d-none" data-bind="setInitValue: { observable: VariantDetailsEndpoint, value: '@VariantDetailsEndpoint' }"></div>
1357 <div class="d-none" data-bind="setInitValue: { observable: TeaserText, value: '@TrimmedTeaser' }"></div>
1358 <div class="d-none" data-bind="setInitValue: { observable: ProductName, value: '@TrimmedName' }"></div>
1359 <div class="d-none" data-bind="setInitValue: { observable: ProductNumber, value: '@ProductNumber' }"></div>
1360 <div class="d-none" data-bind="setInitValue: { observable: VariantId, value: '@VariantId' }"></div>
1361 <div class="d-none" data-bind="setInitValue: { observable: Price, value: '@priceFormatted' }"></div>
1362 <div class="d-none" data-bind="setInitValue: { observable: PriceDouble, value: '@price' }"></div>
1363
1364 <div class="d-none js-update-cart" data-bind="setInitValue: { observable: CartEndpoint, value: '@GetCartEndpoint' }, click: GetCart"></div>
1365
1366 <h1 class="header fs4 mb-5" data-bind="text: ProductName">@ProductName</h1>
1367 <p class="color-subtle fs0 mt-0 mb-2">@Translate("ProductProductNumber", "Produktnummer:") <span data-bind="text: ProductNumber">@ProductNumber</span></p>
1368 <div class="short-description mb-4 fs2 content-left">
1369 @ShortDescription
1370 <p class="m-0">
1371 @if ( !string.IsNullOrWhiteSpace( GetString( "Ecom:Product:Field.dybdeint.Value.Clean" ) ) )
1372 {
1373 if ( GetString( "Ecom:Product:Field.dybdeint.Value.Clean" ) != "0" )
1374 {
1375 <span>D/L: @GetValue( "Ecom:Product:Field.dybdeint.Value.Clean" )</span>
1376 }
1377 }
1378 @if ( !string.IsNullOrWhiteSpace( GetString( "Ecom:Product:Field.hoejdeint.Value.Clean" ) ) )
1379 {
1380 if ( GetString( "Ecom:Product:Field.hoejdeint.Value.Clean" ) != "0" )
1381 {
1382 <span>H: @GetValue( "Ecom:Product:Field.hoejdeint.Value.Clean" )</span>
1383 }
1384 }
1385 @if ( !string.IsNullOrWhiteSpace( GetString( "Ecom:Product:Field.breddeint.Value.Clean" ) ) )
1386 {
1387 if ( GetString( "Ecom:Product:Field.breddeint.Value.Clean" ) != "0" )
1388 {
1389 <span>B: @GetValue( "Ecom:Product:Field.breddeint.Value.Clean" )</span>
1390 }
1391 }
1392 </p>
1393 </div>
1394
1395 <div class="d-flex flex-wrap product-top">
1396 <div class="image-container position-relative overflow-hidden">
1397 <div class="splash splash--on-top unimportant-hidden" data-bind="css: { 'd-flex': SplashType() == 1 }"><p class="m-0">@Translate("ProductOffer", "Tilbud")</p></div>
1398 <div class="splash splash--on-top unimportant-hidden new-item" data-bind="css: { 'd-flex': SplashType() == 2 }"><p class="m-0">@Translate("ProductNew", "Nyhed")</p></div>
1399 <div class="splash splash--on-top unimportant-hidden low-price" data-bind="css: { 'd-flex': SplashType() == 3 }"><p class="m-0">@Translate("ProductLowPrice", "Fast lavpris")</p></div>
1400 <div class="splash splash--on-top unimportant-hidden price-shape" data-bind="css: { 'd-flex': SplashType() == 4 }" style="background-color: @DailyPriceBackgroundColor;"><p class="m-0" style="color: @DailyPriceTextColor;">@Translate("ProductDailyPriceSplash", "Dagspris")</p></div>
1401 <div class="splash splash--on-top unimportant-hidden splash--small" data-bind="css: { 'd-flex': SplashType() == 5}"><p class="m-0">@Translate("ProductCombinationOffer", "Sætpris")</p></div>
1402
1403 <div id="imageSlider" class="carousel slide" data-ride="carousel" data-interval="5000">
1404 <div class="carousel-inner align-items-center" data-bind="if: Images().length == 0">
1405
1406 @foreach (var Image in Images)
1407 {
1408 if (First)
1409 {
1410 <a href="/Admin/Public/GetImage.ashx?Image=@Image&Format=webP&Quality=90&width=2000" class="mb-3 mb-lg-0 carousel-item active" data-gallery>
1411 <img class="product-image main-image" src="/Admin/Public/GetImage.ashx?Image=@Image&Format=webP&Quality=90&width=800&height=500&crop=5" alt="@ProductName" />
1412 </a>
1413 First = false;
1414 FirstImage = "/Admin/Public/GetImage.ashx?Image=" + Image + "&Format=webP&Quality=90&width=800&height=500&crop=5";
1415 }
1416 else
1417 {
1418 <a href="/Admin/Public/GetImage.ashx?Image=@Image&Format=webP&Quality=90&width=1600" class="mb-3 mb-lg-0 carousel-item" data-gallery>
1419 <img class="product-image main-image" src="/Admin/Public/GetImage.ashx?Image=@Image&Format=webP&Quality=90&width=800&height=500&crop=5" alt="@ProductName" />
1420 </a>
1421 }
1422
1423 }
1424
1425 </div>
1426 <div class="carousel-inner align-items-center d-flex js-ko-carousel" data-bind="foreach: Images()">
1427
1428 <a data-bind="attr: { href: '/Admin/Public/GetImage.ashx?Image=' + $data + '&Format=webP&Quality=90&width=1600' }" class="mb-3 mb-lg-0 carousel-item active" data-gallery>
1429 <img class="product-image main-image" data-bind="attr: { src: '/Admin/Public/GetImage.ashx?Image=' + $data + '&Format=webP&Quality=90&width=800&height=500&crop=5' }" />
1430 </a>
1431
1432 </div>
1433 </div>
1434
1435 @if (Images.Count > 0)
1436 {
1437 <div class="row small-gutter carousel-row mb-2" data-bind="css: { 'd-none': Images().length > 0 }">
1438 @if (Images.Count > 0)
1439 {
1440 foreach (var Image in Images)
1441 {
1442 if (FirstIndicator)
1443 {
1444 <div data-target="#imageSlider" data-slide-to="@IndicatorIncrementer" class="col-4 mt-lg-3 cursor-pointer active">
1445 <div class="secondary-image">
1446 <img class="product-image" src="/Admin/Public/GetImage.ashx?Image=@Image&Format=webP&Quality=90&width=400&height=230&crop=5" alt="@ProductName" />
1447 </div>
1448 </div>
1449 FirstIndicator = false;
1450 }
1451 else
1452 {
1453 <div data-target="#imageSlider" data-slide-to="@IndicatorIncrementer" class="col-4 mt-lg-3 cursor-pointer">
1454 <div class="secondary-image">
1455 <img class="product-image" src="/Admin/Public/GetImage.ashx?Image=@Image&Format=webP&Quality=90&width=160&height=100&crop=5" alt="@ProductName" />
1456 </div>
1457 </div>
1458 }
1459 IndicatorIncrementer++;
1460 }
1461 }
1462
1463 @if (!string.IsNullOrEmpty(GetString("Ecom:Product:Field.VideoKlip.Value")))
1464 {
1465 string BackgroundSize = !String.IsNullOrEmpty(GetString("Ecom:Product:Field.VideoBillede.Value.Clean")) ? "cover" : "contain";
1466 string OverlayImage = !String.IsNullOrEmpty(GetString("Ecom:Product:Field.VideoBillede.Value.Clean")) ? GetString("Ecom:Product:Field.VideoBillede.Value.Clean") : FirstImage;
1467 <div class="youtube-video-container col-4 mt-lg-3" data-bind="youtubeVideo: { videoId: '@GetString("Ecom:Product:Field.VideoKlip.Value")', overlayId: 'js-video-overlay', playerId: 'player' }">
1468 <div id="js-video-overlay" class="video-overlay pointer" style="background-image:url('@OverlayImage');background-size: @BackgroundSize;">
1469 <div class="visual-overlay d-flex flex-column justify-content-center align-items-center h-100 w-100">
1470 <img src="/Files/Templates/Designs/Mobler2018/assets/img/icons/Play.svg" />
1471 </div>
1472 </div>
1473 </div>
1474 }
1475
1476 </div>
1477
1478 <div class="row small-gutter carousel-row mb-2 unimportant-hidden" data-bind="css: { 'd-flex': Images().length > 0 }">
1479 <span class="images" style="display:contents;" data-bind="foreach: Images()">
1480 <!-- ko if: $index() == 0 -->
1481 <div data-target="#imageSlider" class="col-4 mt-lg-3 cursor-pointer active" data-bind="click: $parent.ChangeSlide.bind($data, $index())">
1482 <div class="secondary-image">
1483 <img class="product-image" data-bind="attr: { src: '/Admin/Public/GetImage.ashx?Image=' + $data + '&Format=webP&Quality=90&width=400&height=230&crop=5' }" alt="@ProductName" />
1484 </div>
1485 </div>
1486 <!-- /ko -->
1487 <!-- ko if: $index() != 0 -->
1488 <div data-target="#imageSlider" class="col-4 mt-lg-3 cursor-pointer" data-bind="click: $parent.ChangeSlide.bind($data, $index())">
1489 <div class="secondary-image">
1490 <img class="product-image" data-bind="attr: { src: '/Admin/Public/GetImage.ashx?Image=' + $data + '&Format=webP&Quality=90&width=400&height=230&crop=5' }" alt="@ProductName" />
1491 </div>
1492 </div>
1493 <!-- /ko -->
1494 </span>
1495
1496 @if (!String.IsNullOrEmpty(GetString("Ecom:Product:Field.VideoKlip.Value")))
1497 {
1498 string BackgroundSize = !String.IsNullOrEmpty(GetString("Ecom:Product:Field.VideoBillede.Value.Clean")) ? "cover" : "contain";
1499 string OverlayImage = !String.IsNullOrEmpty(GetString("Ecom:Product:Field.VideoBillede.Value.Clean")) ? GetString("Ecom:Product:Field.VideoBillede.Value.Clean") : FirstImage;
1500 <div class="youtube-video-container mt-lg-3" data-bind="youtubeVideo: { videoId: '@GetString("Ecom:Product:Field.VideoKlip.Value")', overlayId: 'js-videovariant-overlay', playerId: 'player' }">
1501 <div id="js-videovariant-overlay" class="video-overlay pointer" style="background-image:url('@OverlayImage');background-size: @BackgroundSize;">
1502 <div class="visual-overlay d-flex flex-column justify-content-center align-items-center h-100 w-100">
1503 <img src="/Files/Templates/Designs/Mobler2018/assets/img/icons/Play.svg" />
1504 <p class="mt-3 mb-0 color-white">@Translate("Product.WatchVideo", "Se video")</p>
1505 </div>
1506 </div>
1507 </div>
1508 }
1509 </div>
1510 }
1511
1512 <div class="shortcuts d-flex">
1513 <div class="shortcuts__header fs0"><b>@Translate( "ShortCuts", "Genveje:" )</b></div>
1514 <div class="shortcuts__body">
1515 @if (!String.IsNullOrWhiteSpace(GetString("Ecom:Product:Field.ProviderLink.Value.Clean")))
1516 {
1517 <div class="shortcut"><i class="fa-solid fa-pen"></i> <a href="@GetString("Ecom:Product:Field.ProviderLink.Value.Clean")" target="_blank">@Translate("Product.DesignYourself.Title", "Design selv")</a></div>
1518 }
1519 <div class="shortcut" data-bind="click: ScrollToElement.bind($element, specifications)"><i class="fa fa-clipboard"></i> <a href="#">@Translate("Product.Specifications.Title", "Specifikationer" )</a></div>
1520 @if ( !String.IsNullOrEmpty( ProductCatalogLink ) )
1521 {
1522 <div class="shortcut"><i class="fa fa-book-open"></i> <a href="@ProductCatalogLink" target="_blank">@Translate( "Product.InspirationBrochure.Title", "Inspirationsbrochure" )</a></div>
1523 }
1524 </div>
1525 </div>
1526
1527 @* <div class="pl-lg-4 prices-container flex-wrap flex-column mt-3 mt-lg-0 position-relative d-flex d-lg-none"> *@
1528 @* @RenderSnippet("PricesContainer") *@
1529 @* </div> *@
1530
1531 <div class="product-bottom-details">
1532 <div class="d-flex flex-wrap mt-5">
1533 <div id="js-full-description">
1534 <h2 class="fs3 mb-2 font-weight-bold">@Translate("Product.ProductDescription.Title", "Produktbeskrivelse")</h2>
1535 <div class="usps d-flex mt-4 mb-4">
1536 <div class="usps--usp d-flex align-items-center"><i class="@usp1IconClass"></i><strong>@usp1IconText</strong></div>
1537 <div class="usps--usp d-flex align-items-center"><i class="@usp2IconClass"></i><strong>@usp2IconText</strong></div>
1538 <div class="usps--usp d-flex align-items-center"><i class="@usp3IconClass"></i><strong>@usp3IconText</strong></div>
1539 </div>
1540
1541 <div class="product-description">
1542 <div class="product-description--inner read-more-container">
1543 <div class="unimportant-hidden" data-bind="html: FullDescription, css: { 'd-block': FullDescription().length > 0 }"></div>
1544 </div>
1545 <p class="read-more"><a href="#" class="text-underline" data-bind="click: ProductDescriptionReadMore.bind($data, $element)">@Translate("Product.Description.ReadMore.Title", "Læs hele beskrivelsen")</a></p>
1546 </div>
1547
1548 <div class="product-description" data-bind="css: { 'd-none': FullDescription().length > 0 }">
1549 <div class="product-description--inner read-more-container">
1550 @LongDescription
1551 @if ( !string.IsNullOrEmpty( productionDescription ) )
1552 {
1553 <h4 class="mt-4">@Translate( "Product.ProductionDescription.Title", "Produktionsbeskrivelse" )</h4>
1554 @:@productionDescription
1555 }
1556 </div>
1557 <p class="read-more"><a href="#" class="text-underline" data-bind="click: ProductDescriptionReadMore.bind($data, $element)">@Translate( "Product.ProductDescription.ReadMore.Title", "Læs hele beskrivelsen" )</a></p>
1558 </div>
1559 </div>
1560
1561 </div>
1562
1563 <div class="d-flex flex-wrap mt-5">
1564 <div id="specifications" class="specifications-container w-100">
1565 <h2 class="fs3 mb-4 font-weight-bold">@Translate("Product.Specifications.Title", "Specifikationer")</h2>
1566 <div class="product-specifications">
1567 @* Render product specifications from variants *@
1568 <dl class="d-flex flex-wrap w-100 read-more-container unimportant-hidden" data-bind="foreach: Specifications">
1569 <dt class="w-50 border-top" data-bind="text: Name"></dt>
1570 <dd class="w-50 border-top" data-bind="text: Value"></dd>
1571 </dl>
1572
1573 <dl class="d-flex flex-wrap w-100 read-more-container" data-bind="css: { 'd-none': Specifications().length > 0 }">
1574 @foreach (LoopItem displayGroup in GetLoop("FieldDisplayGroups"))
1575 {
1576 foreach (LoopItem field in displayGroup.GetLoop("Fields"))
1577 {
1578 if (!field.GetString("Ecom:FieldDisplayGroup.Field.Value").IsNullOrEmpty() && field.GetString("Ecom:FieldDisplayGroup.Field.Value") != "0" && field.GetString("Ecom:FieldDisplayGroup.Field.Value") != "," && !field.GetString("Ecom:FieldDisplayGroup.Field.Value").Contains(",,"))
1579 {
1580 <dt class="w-50 border-top">@field.GetString("Ecom:FieldDisplayGroup.Field.Name")</dt>
1581 <dd class="w-50 border-top">
1582 @if (field.GetBoolean("Ecom:FieldDisplayGroup.Field.IsList"))
1583 {
1584 @field.GetString("Ecom:FieldDisplayGroup.Field.OptionLabel")
1585 }
1586 else
1587 {
1588 @field.GetString("Ecom:FieldDisplayGroup.Field.Value")
1589 }
1590 </dd>
1591 }
1592 }
1593 }
1594 </dl>
1595 <p class="read-more"><a href="#" class="text-underline" data-bind="click: ProductDescriptionReadMore.bind($data, $element)">@Translate("Product.Specifications.ReadMore.Title", "Vis alle specifikationer")</a></p>
1596 </div>
1597 </div>
1598 </div>
1599 </div>
1600 </div>
1601
1602 <div id="blueimp-gallery" class="blueimp-gallery blueimp-gallery-controls">
1603 <div class="slides"></div>
1604 <h3 class="title"></h3>
1605 <a class="prev">‹</a>
1606 <a class="next">›</a>
1607 <a class="close">x</a>
1608 <a class="play-pause"></a>
1609 <ol class="indicator"></ol>
1610 </div>
1611
1612 <div class="pl-lg-4 prices-container flex-wrap flex-column mt-3 mt-lg-0 position-relative">
1613 @RenderSnippet("PricesContainer")
1614 </div>
1615 </div>
1616 @if (!string.IsNullOrEmpty(productCatalogueImage))
1617 {
1618 <img id="GroupCatalogueImage" class="unimportant-hidden" src="/Admin/Public/GetImage.ashx?Image=/Files/Images/@productCatalogueImage&Width=320&height=200&Format=webP&Quality=90&Crop=5&resolution=50" />
1619 }
1620 @if (!string.IsNullOrEmpty(ProductCatalogLink))
1621 {
1622 <a id="ProductCatalogueLink" class="unimportant-hidden" href="@ProductCatalogLink" target="_blank"></a>
1623 }
1624 @if (uspPageId > 0)
1625 {
1626 <div class="productdetail-usps mt-8 d-flex">
1627 <div class="row">
1628 @RenderPageContent(uspPageId)
1629 </div>
1630 </div>
1631 }
1632
1633 @if (showRecommendations)
1634 {
1635 <div id="customersAlsoViewed" class="related-products unimportant-hidden">
1636 <div class="related-products__header mb-4">
1637 <h4>@Translate("Product:RelewiseCustomersAlsoViewed.Headline", "Andre har også set")</h4>
1638 </div>
1639 <div id="customersAlsoViewedContainer" class="related-products__body">
1640 </div>
1641 </div>
1642 }
1643
1644 @if (showRecommendations && !userIsAnonymous)
1645 {
1646 <div id="recentlyViewedProducts" class="related-products unimportant-hidden">
1647 <div class="related-products__header mb-4">
1648 <h4>@Translate("Product:RelewiseLatestVisitedProducts.Headline", "Dine seneste besøgte produkter")</h4>
1649 </div>
1650 <div id="recentlyViewedProductsContainer" class="related-products__body">
1651
1652 </div>
1653 </div>
1654 }
1655
1656 <div class="after-add-to-cart bg-white box-shadow" data-bind="css: { 'd-block': ProductAddedToCart }">
1657 <div class="closer fs3 pointer" data-bind="click: ProductAddedToCart(false)">
1658 <i class="fas fa-times-circle color-primary"></i>
1659 </div>
1660 <div class="after-add-to-cart__inner">
1661 <div class="p-3">
1662 <p class="font-weight-bold fs text-uppercase m-0">
1663 <i class="fas fa-check-circle color-primary"></i>
1664 @Translate("ProductProductAddedToCart", "Varen er lagt i indkøbskurven")
1665 </p>
1666 </div>
1667 <div class="mini-cart large-cart">
1668
1669 <div class="position-relative">
1670 <div class="cart-command-loader unimportant-hidden justify-content-center align-items-center" data-bind="css: { 'd-flex': Loading }">
1671 <div class="d-flex align-items-center">
1672 <p class="m-0 mr-2">@Translate("Cart.UpdatingCart", "Opdaterer kurv")</p>
1673 <i class="fas fa-circle-notch fa-spin"></i>
1674 </div>
1675 </div>
1676
1677 <div class="lines" data-bind="foreach: Orderlines">
1678 <div class="cartline d-flex justify-content-between align-items-center px-3 py-2 position-relative bg-grey">
1679 <span class="delete-orderline color-subtle cursor-pointer mr-2" data-bind="click: $parent.DeleteOrderline.bind($data, OrderlineId)">
1680 <i class="fas fa-times"></i>
1681 </span>
1682 <div class="product-image w-10">
1683 <img class="img-fluid" data-bind="attr: { src: ImagePath }"/>
1684 </div>
1685 <div class="flex-fill px-3 w-75">
1686 <p class="font-weight-bold fs0 m-0" data-bind="text: ProductName"></p>
1687 <p class="m-0 color-subtle small-quantity-indicator"><span data-bind="text: Quantity"></span> stk. a <span data-bind="text: QuantityPrice"></span></p>
1688 <div class="variant-dimensions" data-bind="foreach: VariantDimensions">
1689 <p class="m-0 color-subtle color-dark-grey font-weight-bold">
1690 <span data-bind="text: Label"></span>
1691 <span class="font-weight-normal" data-bind="text: Value"></span>
1692 </p>
1693 </div>
1694 </div>
1695 <div class="qty-counter mr-1">
1696 <div class="d-flex">
1697 <div class="quantity-controls d-flex align-items-center mr-3">
1698 <div class="control minus" data-bind="click: $parent.QuantityControl.bind($data, -1, OrderlineId)">
1699 -
1700 </div>
1701 <input disabled type="text" name="Quantity" value="1" data-bind="value: Quantity, attr: { 'data-id': 'js-input-' + OrderlineId }"/>
1702 <div class="control plus" data-bind="click: $parent.QuantityControl.bind($data, 1, OrderlineId)">
1703 +
1704 </div>
1705 </div>
1706 </div>
1707 </div>
1708
1709 <p class="fs0 m-0 orderline-price" data-bind="text: OrderlinePrice"></p>
1710 </div>
1711 </div>
1712 @if (BlackFridayTheme == "True")
1713 {
1714 <div class="bf-bg-black p-3 d-flex justify-content-between align-items-center price-summary">
1715 <p class="m-0 color-white fs-12px font-weight-semibold">@Translate("MiniCartTotal", "Din kurv i alt (ex. fragt)")</p>
1716 <p class="m-0 color-white fs1 font-weight-bold" data-bind="text: CartTotalNoFees"></p>
1717 </div>
1718 }
1719 else
1720 {
1721 <div class="bg-brand p-3 d-flex justify-content-between align-items-center price-summary">
1722 <p class="m-0 color-white fs-12px font-weight-semibold">@Translate("MiniCartTotal", "Din kurv i alt (ex. fragt)")</p>
1723 <p class="m-0 color-white fs1 font-extra-bold" data-bind="text: CartTotalNoFees"></p>
1724 </div>
1725 }
1726 </div>
1727
1728 </div>
1729
1730 <div id="purchasedWith" class="power-step unimportant-hidden" style="@purchasedWithStyle">
1731 <div class="power-step__header">
1732 <h6>@Translate("Checkout.PowerStep.Title", "Andre har også købt") </h6>
1733 </div>
1734 <div id="purchasedWithContainer">
1735 </div>
1736 </div>
1737
1738
1739 <div class="p-3 d-flex justify-content-between after-add-to-cart__buttons">
1740 <p class="pointer py-2 px-3 bg-dark-grey color-white rounded m-0" data-bind="click: ProductAddedToCart(false)">@Translate("MiniCartContinueShopping", "Handel videre")</p>
1741
1742 @if (BlackFridayTheme == "True")
1743 {
1744 <a href="@CartPage" class="pointer py-2 px-3 bf-bg-black color-white rounded d-block no-underline">@Translate("MiniCartGoToCheckout", "Gå til kassen")</a>
1745 }
1746 else
1747 {
1748 <a href="@CartPage" class="pointer py-2 px-3 bg-brand color-white rounded d-block no-underline">@Translate("MiniCartGoToCheckout", "Gå til kassen")</a>
1749 }
1750
1751 </div>
1752 <div class="js-customers-also-saw-lines"></div>
1753 </div>
1754 </div>
1755 <div class="variant-picker" data-bind="css: { 'open': VariantPickerOpen }">
1756 <div class="closer color-primary fs5 pointer" data-bind="click: ToggleVariantPicker">
1757 <i class="fas fa-times-circle"></i>
1758 </div>
1759 <div class="container py-5 position-relative">
1760 <h3 class="fs4 text-center">Design din @ProductName</h3>
1761 @{
1762 var ColorDimensions = GetLoop( "VariantGroups" ).Where( g => g.GetString( "Ecom:VariantGroup.Name" ).ToLower().Contains( "farve" ) );
1763 var NonColorDimensions = GetLoop( "VariantGroups" ).Where( g => !g.GetString( "Ecom:VariantGroup.Name" ).ToLower().Contains( "farve" ) );
1764 int FilterCounter = 0;
1765 string RowClass = NonColorDimensions.Any() && ColorDimensions.Any() ? "flex-row-reverse" : "";
1766 }
1767 <div class="row mt-5 @RowClass">
1768 @if ( ColorDimensions.Any() )
1769 {
1770 <div class="col-12 col-md-8">
1771 @foreach ( var VariantDimension in ColorDimensions )
1772 {
1773 string DimensionName = VariantDimension.GetString( "Ecom:VariantGroup.Name" );
1774 string FilterName = "filter-" + FilterCounter;
1775 FilterCounter++;
1776 <div class="mb-5 mb-md-0">
1777 <p class="font-weight-bold fs2 mb-2">@DimensionName</p>
1778 <div class="row custom-row">
1779 @foreach ( var VariantOption in VariantDimension.GetLoop( "VariantAvailableOptions" ).OrderBy( v => v.GetString( "Ecom:VariantOption.Name" ) ) )
1780 {
1781 string VariantOptionName = VariantOption.GetString( "Ecom:VariantOption.Name" );
1782 string VariantOptionId = VariantOption.GetString( "Ecom:VariantOption.ID" );
1783 string Preview = VariantOption.GetString( "Ecom:VariantOption.ImgSmall.Clean" );
1784 string PreviewCss = "";
1785 if ( !String.IsNullOrEmpty( Preview ) )
1786 {
1787 if ( Preview.StartsWith( "#" ) )
1788 {
1789 PreviewCss = "style= \"background-color: " + Preview + ";\"";
1790 }
1791 else
1792 {
1793 PreviewCss = "style=\"background-image: url('" + Preview + "');\"";
1794 }
1795 }
1796 <div class="col-2 col-sm-3 col-md-2 mb-3 d-flex flex-column justify-content-end pointer color-variant" data-bind="click: AddToFilter.bind($data, $element, '@FilterName', '@VariantOptionId')">
1797 <p class="mb-0 color-variant-name d-none d-sm-block">@VariantOptionName</p>
1798 <div class="variant-preview position-relative d-flex" @PreviewCss></div>
1799 </div>
1800 }
1801 </div>
1802 </div>
1803 }
1804 </div>
1805 }
1806 @if ( NonColorDimensions.Any() )
1807 {
1808 <div class="col-12 col-sm-6 col-md-4">
1809 @foreach ( var VariantDimension in NonColorDimensions )
1810 {
1811 string DimensionName = VariantDimension.GetString( "Ecom:VariantGroup.Name" );
1812 string FilterName = "filter-" + FilterCounter;
1813 FilterCounter++;
1814 <div class="mb-5">
1815 <p class="font-weight-bold fs2 mb-2">@DimensionName</p>
1816 <div class="row custom-row">
1817 @foreach ( var VariantOption in VariantDimension.GetLoop( "VariantAvailableOptions" ).OrderBy( v => v.GetString( "Ecom:VariantOption.Name" ) ) )
1818 {
1819 string VariantOptionName = VariantOption.GetString( "Ecom:VariantOption.Name" );
1820 string VariantOptionId = VariantOption.GetString( "Ecom:VariantOption.ID" );
1821 string Preview = VariantOption.GetString( "Ecom:VariantOption.ImgSmall.Clean" );
1822 string PreviewCss = "";
1823 if ( !String.IsNullOrEmpty( Preview ) )
1824 {
1825 if ( Preview.StartsWith( "#" ) )
1826 {
1827 PreviewCss = "style= \"background-color: " + Preview + ";\"";
1828 }
1829 else
1830 {
1831 PreviewCss = "style=\"background-image: url('/Files" + Preview + "');\"";
1832 }
1833 }
1834 if ( DimensionName == "STØRRELSE" )
1835 {
1836 <div class="col-6 col-sm-4 mb-3 d-flex flex-column justify-content-end pointer size-variant" data-bind="click: AddToFilter.bind($data, $element, '@FilterName', '@VariantOptionId')">
1837 <div class="variant-preview position-relative d-flex justify-content-center align-items-center" @PreviewCss>
1838 <p class="mb-0 color-variant-name font-weight-bold">@VariantOptionName</p>
1839 </div>
1840 </div>
1841 }
1842 else
1843 {
1844 <div class="col-6 col-sm-4 mb-3 d-flex flex-column justify-content-end pointer" data-bind="click: AddToFilter.bind($data, $element, '@FilterName', '@VariantOptionId')">
1845 <p class="mb-0 color-variant-name">@VariantOptionName</p>
1846 <div class="variant-preview position-relative d-flex" @PreviewCss></div>
1847 </div>
1848 }
1849 }
1850 </div>
1851 </div>
1852 }
1853 </div>
1854 }
1855
1856 <div class="col-12 d-flex justify-content-end">
1857 <p class="color-primary pointer my-3" data-bind="click: ResetFilters">Nulstil</p>
1858 </div>
1859 </div>
1860 <p class="font-weight-bold fs2 mt-5">
1861 @Translate( "ChooseProduct", "Vælg produkt" )
1862 </p>
1863 <div class="row align-items-end" data-bind="foreach: FilteredVariants">
1864 <div class="col-12 col-sm-6 col-md-3 mb-3">
1865 <a class="color-black no-underline" data-bind="attr: { href: Link }">
1866 <p class="fs0 font-weight-bold m-0" data-bind="text: Name"></p>
1867 <img class="img-fluid box-shadow my-1" data-bind="attr: { src: Image }" />
1868 <div class="d-flex justify-content-center mt-2">
1869 <div class="btn btn-primary">@Translate( "VariantChooseProduct", "Vælg" )</div>
1870 </div>
1871 </a>
1872 </div>
1873 </div>
1874 <div data-bind="visible: FilteredVariants().length == 0">
1875 <p class="font-weight-bold mt-3 fs3">@Translate( "VariantsNoResults", "Filtreringen gav ingen resultater" )</p>
1876 </div>
1877 </div>
1878 </div>
1879
1880 <div class="add-to-cart-overlay" data-bind="css: { 'd-block': ProcessingAjax }">
1881 <p class="fs4 m-0 color-white">@Translate("ProductAjaxAddingToCart", "Tilføjer til kurv") <i class="fa fa-circle-notch fa-spin ml-3 fs5"></i></p>
1882 </div>
1883
1884
1885 <div class="add-to-cart-overlay" data-bind="css: { 'd-block': ProcessingVariantDetailsAjax }">
1886 <p class="fs4 m-0 color-white">@Translate("ProductAjaxGettingVariant", "Henter variant") <i class="fa fa-circle-notch fa-spin ml-3 fs5"></i></p>
1887 </div>
1888 </div>
1889
1890 <div class="modal fade video-modal" id="videoModal" tabindex="-1" role="dialog" aria-labelledby="video-modal" aria-hidden="true">
1891 <div class="modal-dialog modal-dialog-centered" role="document">
1892 <div class="modal-content">
1893 <div class="modal-body">
1894 <div id="player"></div>
1895 </div>
1896 </div>
1897 </div>
1898 </div>
1899
1900 @SnippetStart("DataLayerOverwrites")
1901 <script>
1902 ecomm_pagetype = "Product";
1903 ecomm_totalvalue = @GetString("Ecom:Product.Price.Price").Replace(".","").Replace(",",".");
1904 ecomm_prodid = "@GetString("Ecom:Product.ID")";
1905 </script>
1906 @SnippetEnd("DataLayerOverwrites")
1907
1908 <script>
1909 var dataLayer = window.dataLayer || [];
1910 dataLayer.push({
1911 'event': 'product',
1912 'ecommerce': {
1913 'detail': {
1914 'actionField': {
1915 'list': '@DataLayerParentGroups'
1916 },
1917 'products': [{
1918 'id': '@ProductID',
1919 'name': '@ProductName',
1920 'image': '@OgImage',
1921 'brand': '@BrandName',
1922 'variant': '@CurrentVariantName',
1923 'category': '@DataLayerParentGroup',
1924 'price': '@DataLayerPrice'
1925 }]
1926 }
1927 },
1928 'page': {
1929 'type': 'product',
1930 'environment': 'production'
1931 }
1932 });
1933 </script>
1934