data and UI customization enhancements
This commit is contained in:
		
							parent
							
								
									d6c6582b51
								
							
						
					
					
						commit
						445101b376
					
				| @ -29,6 +29,7 @@ services: | ||||
|       - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word | ||||
|       - ExternalCatalogBaseUrl=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5101    #Local: You need to open your local dev-machine firewall at range 5100-5105.  at range 5100-5105.	   | ||||
|       - EventBusConnection=rabbitmq | ||||
|       - UseCustomizationData=True | ||||
|     ports: | ||||
|       - "5101:80" | ||||
| 
 | ||||
| @ -40,6 +41,7 @@ services: | ||||
|       - XamarinCallback=http://${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}:5105/xamarincallback      #localhost do not work for UWP login, so we have to use "external" IP always | ||||
|       - ConnectionStrings__DefaultConnection=Server=sql.data;Database=Microsoft.eShopOnContainers.Service.IdentityDb;User Id=sa;Password=Pass@word  | ||||
|       - MvcClient=http://${ESHOP_EXTERNAL_DNS_NAME_OR_IP}:5100              #Local: You need to open your local dev-machine firewall at range 5100-5105.   | ||||
|       - UseCustomizationData=True | ||||
|     ports: | ||||
|       - "5105:80" | ||||
| 
 | ||||
| @ -50,6 +52,7 @@ services: | ||||
|       - ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word | ||||
|       - identityUrl=http://identity.api              #Local: You need to open your local dev-machine firewall at range 5100-5105.  at range 5100-5105.  | ||||
|       - EventBusConnection=rabbitmq | ||||
|       - UseCustomizationData=True | ||||
|     ports: | ||||
|       - "5102:80" | ||||
| 
 | ||||
| @ -77,6 +80,7 @@ services: | ||||
|       - OrderingUrlHC=http://ordering.api/hc | ||||
|       - IdentityUrlHC=http://identity.api/hc     #Local: Use ${ESHOP_PROD_EXTERNAL_DNS_NAME_OR_IP}, if using external IP or DNS name from browser.           | ||||
|       - BasketUrlHC=http://basket.api/hc | ||||
|       - UseCustomizationData=True | ||||
|     ports: | ||||
|       - "5104:80" | ||||
| 
 | ||||
| @ -88,7 +92,7 @@ services: | ||||
|       - OrderingUrl=http://ordering.api | ||||
|       - BasketUrl=http://basket.api | ||||
|       - IdentityUrl=http://10.0.75.1:5105        #Local:  Use 10.0.75.1 in a "Docker for Windows" environment, if using "localhost" from browser.  | ||||
|                                                  #Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.  | ||||
|       - UseCustomizationData=True                #Remote: Use ${ESHOP_EXTERNAL_DNS_NAME_OR_IP} if using external IP or DNS name from browser.  | ||||
|     ports: | ||||
|       - "5100:80" | ||||
| 
 | ||||
|  | ||||
| @ -21,6 +21,11 @@ | ||||
|     <Content Include="Pics\**\*;"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|     <Content Include="Setup\**\*;"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|     <Content Remove="Setup\Catalogitems - Copy.zip" /> | ||||
|     <None Remove="Setup\Catalogitems - Copy.zip" /> | ||||
|     <Compile Include="IntegrationEvents\EventHandling\AnyFutureIntegrationEventHandler.cs.txt" /> | ||||
|     <Content Update="web.config;"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
| @ -50,6 +55,7 @@ | ||||
|     <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" /> | ||||
|     <PackageReference Include="Newtonsoft.Json" Version="10.0.2" /> | ||||
|     <PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" /> | ||||
|     <PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
| @ -72,6 +78,9 @@ | ||||
|     <None Update="Pics\*"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </None> | ||||
|     <None Update="Setup\*"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </None> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
|  | ||||
| @ -5,5 +5,7 @@ | ||||
|         public string ExternalCatalogBaseUrl {get;set;} | ||||
| 
 | ||||
|         public string EventBusConnection { get; set; } | ||||
| 
 | ||||
|         public bool UseCustomizationData { get; set; } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -235,7 +235,10 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers | ||||
| 
 | ||||
|             items.ForEach(x => | ||||
|             { | ||||
|                 x.PictureUri = x.PictureUri.Replace("http://externalcatalogbaseurltobereplaced", baseUri); | ||||
|                 if (!x.PictureUri.Contains('/')) | ||||
|                 { | ||||
|                     x.PictureUri = $"{baseUri}/api/v1/pic/{x.PictureUri}"; | ||||
|                 } | ||||
|             }); | ||||
| 
 | ||||
|             return items; | ||||
|  | ||||
| @ -15,16 +15,58 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers | ||||
|             _env = env; | ||||
|         } | ||||
| 
 | ||||
|         [HttpGet("{id}")] | ||||
|         [HttpGet("{filename}")] | ||||
|         // GET: /<controller>/ | ||||
|         public IActionResult GetImage(int id) | ||||
|         public IActionResult GetImage(string filename) | ||||
|         { | ||||
|             var webRoot = _env.WebRootPath; | ||||
|             var path = Path.Combine(webRoot, id + ".png"); | ||||
|             var path = Path.Combine(webRoot, filename); | ||||
| 
 | ||||
|             var buffer = System.IO.File.ReadAllBytes(path);  | ||||
|              | ||||
|             return File(buffer, "image/png"); | ||||
|             string imageFileExtension = Path.GetExtension(filename); | ||||
|             string mimetype = GetImageMimeTypeFromImageFileExtension(imageFileExtension); | ||||
| 
 | ||||
|             var buffer = System.IO.File.ReadAllBytes(path); | ||||
| 
 | ||||
|             return File(buffer, mimetype); | ||||
|         } | ||||
| 
 | ||||
|         private string GetImageMimeTypeFromImageFileExtension(string extension) | ||||
|         { | ||||
|             string mimetype; | ||||
| 
 | ||||
|             switch (extension) | ||||
|             { | ||||
|                 case "png": | ||||
|                     mimetype = "image/png"; | ||||
|                     break; | ||||
|                 case "gif": | ||||
|                     mimetype = "image/gif"; | ||||
|                     break; | ||||
|                 case "jpg": | ||||
|                 case "jpeg": | ||||
|                     mimetype = "image/jpeg"; | ||||
|                     break; | ||||
|                 case "bmp": | ||||
|                     mimetype = "image/bmp"; | ||||
|                     break; | ||||
|                 case "tiff": | ||||
|                     mimetype = "image/tiff"; | ||||
|                     break; | ||||
|                 case "wmf": | ||||
|                     mimetype = "image/wmf"; | ||||
|                     break; | ||||
|                 case "jp2": | ||||
|                     mimetype = "image/jp2"; | ||||
|                     break; | ||||
|                 case "svg": | ||||
|                     mimetype = "image/svg+xml"; | ||||
|                     break; | ||||
|                 default: | ||||
|                     mimetype = "application/octet-stream"; | ||||
|                     break; | ||||
|             } | ||||
| 
 | ||||
|             return mimetype; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,50 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace Catalog.API.Extensions | ||||
| { | ||||
|     public static class LinqSelectExtensions | ||||
|     { | ||||
|         public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector) | ||||
|         { | ||||
|             foreach (TSource element in enumerable) | ||||
|             { | ||||
|                 SelectTryResult<TSource, TResult> returnedValue; | ||||
|                 try | ||||
|                 { | ||||
|                     returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex); | ||||
|                 } | ||||
|                 yield return returnedValue; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler) | ||||
|         { | ||||
|             return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); | ||||
|         } | ||||
| 
 | ||||
|         public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler) | ||||
|         { | ||||
|             return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); | ||||
|         } | ||||
| 
 | ||||
|         public class SelectTryResult<TSource, TResult> | ||||
|         { | ||||
|             internal SelectTryResult(TSource source, TResult result, Exception exception) | ||||
|             { | ||||
|                 Source = source; | ||||
|                 Result = result; | ||||
|                 CaughtException = exception; | ||||
|             } | ||||
| 
 | ||||
|             public TSource Source { get; private set; } | ||||
|             public TResult Result { get; private set; } | ||||
|             public Exception CaughtException { get; private set; } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -2,47 +2,115 @@ | ||||
| { | ||||
|     using EntityFrameworkCore; | ||||
|     using Extensions.Logging; | ||||
|     using global::Catalog.API.Extensions; | ||||
|     using Microsoft.AspNetCore.Builder; | ||||
|     using Microsoft.AspNetCore.Hosting; | ||||
|     using Microsoft.Extensions.DependencyInjection; | ||||
|     using Microsoft.Extensions.Options; | ||||
|     using Model; | ||||
|     using System; | ||||
|     using System.Collections.Generic; | ||||
|     using System.Globalization; | ||||
|     using System.IO; | ||||
|     using System.IO.Compression; | ||||
|     using System.Linq; | ||||
|     using System.Text.RegularExpressions; | ||||
|     using System.Threading.Tasks; | ||||
| 
 | ||||
|     public class CatalogContextSeed | ||||
|     { | ||||
|         public static async Task SeedAsync(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, int? retry = 0) | ||||
|         public static async Task SeedAsync(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory, int? retry = 0) | ||||
|         { | ||||
|             var log = loggerFactory.CreateLogger("catalog seed"); | ||||
| 
 | ||||
|             var context = (CatalogContext)applicationBuilder | ||||
|                 .ApplicationServices.GetService(typeof(CatalogContext)); | ||||
| 
 | ||||
|             context.Database.Migrate(); | ||||
| 
 | ||||
|             var settings = (CatalogSettings)applicationBuilder | ||||
|                 .ApplicationServices.GetRequiredService<IOptions<CatalogSettings>>().Value; | ||||
| 
 | ||||
|             var useCustomizationData = settings.UseCustomizationData; | ||||
|             var contentRootPath = env.ContentRootPath; | ||||
|             var picturePath = env.WebRootPath; | ||||
| 
 | ||||
|             if (!context.CatalogBrands.Any()) | ||||
|             { | ||||
|                 context.CatalogBrands.AddRange( | ||||
|                     GetPreconfiguredCatalogBrands()); | ||||
|                 context.CatalogBrands.AddRange(useCustomizationData | ||||
|                     ? GetCatalogBrandsFromFile(contentRootPath, log) | ||||
|                     : GetPreconfiguredCatalogBrands() | ||||
|                     ); | ||||
| 
 | ||||
|                 await context.SaveChangesAsync(); | ||||
|             } | ||||
| 
 | ||||
|             if (!context.CatalogTypes.Any()) | ||||
|             { | ||||
|                 context.CatalogTypes.AddRange( | ||||
|                     GetPreconfiguredCatalogTypes()); | ||||
|                 context.CatalogTypes.AddRange(useCustomizationData | ||||
|                     ? GetCatalogTypesFromFile(contentRootPath, log) | ||||
|                     : GetPreconfiguredCatalogTypes() | ||||
|                     ); | ||||
| 
 | ||||
|                 await context.SaveChangesAsync(); | ||||
|             } | ||||
| 
 | ||||
|             if (!context.CatalogItems.Any()) | ||||
|             { | ||||
|                 context.CatalogItems.AddRange( | ||||
|                     GetPreconfiguredItems()); | ||||
|                 context.CatalogItems.AddRange(useCustomizationData | ||||
|                     ? GetCatalogItemsFromFile(contentRootPath, context, log) | ||||
|                     : GetPreconfiguredItems() | ||||
|                     ); | ||||
| 
 | ||||
|                 await context.SaveChangesAsync(); | ||||
| 
 | ||||
|                 GetCatalogItemPictures(contentRootPath, picturePath); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerable<CatalogBrand> GetCatalogBrandsFromFile(string contentRootPath, ILogger log) | ||||
|         { | ||||
|             string csvFileCatalogBrands = Path.Combine(contentRootPath, "Setup", "CatalogBrands.csv"); | ||||
| 
 | ||||
|             if (!File.Exists(csvFileCatalogBrands)) | ||||
|             { | ||||
|                 return GetPreconfiguredCatalogBrands(); | ||||
|             } | ||||
| 
 | ||||
|             string[] csvheaders; | ||||
|             try | ||||
|             { | ||||
|                 string[] requiredHeaders = { "catalogbrand" }; | ||||
|                 csvheaders = GetHeaders(requiredHeaders, csvFileCatalogBrands); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 log.LogError(ex.Message); | ||||
|                 return GetPreconfiguredCatalogBrands(); | ||||
|             } | ||||
| 
 | ||||
|             return File.ReadAllLines(csvFileCatalogBrands) | ||||
|                                         .Skip(1) // skip header row | ||||
|                                         .SelectTry(x => CreateCatalogBrand(x)) | ||||
|                                         .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) | ||||
|                                         .Where(x => x != null); | ||||
|         } | ||||
| 
 | ||||
|         static CatalogBrand CreateCatalogBrand(string brand) | ||||
|         { | ||||
|             brand = brand.Trim(); | ||||
| 
 | ||||
|             if (String.IsNullOrEmpty(brand)) | ||||
|             { | ||||
|                 throw new Exception("catalog Brand Name is empty"); | ||||
|             } | ||||
| 
 | ||||
|             return new CatalogBrand | ||||
|             { | ||||
|                 Brand = brand, | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerable<CatalogBrand> GetPreconfiguredCatalogBrands() | ||||
|         { | ||||
|             return new List<CatalogBrand>() | ||||
| @ -50,11 +118,52 @@ | ||||
|                 new CatalogBrand() { Brand = "Azure"}, | ||||
|                 new CatalogBrand() { Brand = ".NET" }, | ||||
|                 new CatalogBrand() { Brand = "Visual Studio" }, | ||||
|                 new CatalogBrand() { Brand = "SQL Server" },  | ||||
|                 new CatalogBrand() { Brand = "SQL Server" }, | ||||
|                 new CatalogBrand() { Brand = "Other" } | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerable<CatalogType> GetCatalogTypesFromFile(string contentRootPath, ILogger log) | ||||
|         { | ||||
|             string csvFileCatalogTypes = Path.Combine(contentRootPath, "Setup", "CatalogTypes.csv"); | ||||
| 
 | ||||
|             if (!File.Exists(csvFileCatalogTypes)) | ||||
|             { | ||||
|                 return GetPreconfiguredCatalogTypes(); | ||||
|             } | ||||
| 
 | ||||
|             string[] csvheaders; | ||||
|             try | ||||
|             { | ||||
|                 string[] requiredHeaders = { "catalogtype" }; | ||||
|                 csvheaders = GetHeaders(requiredHeaders, csvFileCatalogTypes); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 log.LogError(ex.Message); | ||||
|                 return GetPreconfiguredCatalogTypes(); | ||||
|             } | ||||
| 
 | ||||
|             return File.ReadAllLines(csvFileCatalogTypes) | ||||
|                                         .Skip(1) // skip header row | ||||
|                                         .SelectTry(x => CreateCatalogType(x)) | ||||
|                                         .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) | ||||
|                                         .Where(x => x != null); | ||||
|         } | ||||
| 
 | ||||
|         static CatalogType CreateCatalogType(string type) | ||||
|         { | ||||
|             if (String.IsNullOrEmpty(type)) | ||||
|             { | ||||
|                 throw new Exception("catalog Type Name is empty"); | ||||
|             } | ||||
| 
 | ||||
|             return new CatalogType | ||||
|             { | ||||
|                 Type = type, | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerable<CatalogType> GetPreconfiguredCatalogTypes() | ||||
|         { | ||||
|             return new List<CatalogType>() | ||||
| @ -66,23 +175,125 @@ | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerable<CatalogItem> GetCatalogItemsFromFile(string contentRootPath, CatalogContext context, ILogger log) | ||||
|         { | ||||
|             string csvFileCatalogItems = Path.Combine(contentRootPath, "Setup", "CatalogItems.csv"); | ||||
| 
 | ||||
|             if (!File.Exists(csvFileCatalogItems)) | ||||
|             { | ||||
|                 return GetPreconfiguredItems(); | ||||
|             } | ||||
| 
 | ||||
|             string[] csvheaders; | ||||
|             try | ||||
|             { | ||||
|                 string[] requiredHeaders = { "catalogtypename", "catalogbrandname", "description", "name", "price", "pictureuri" }; | ||||
|                 csvheaders = GetHeaders(requiredHeaders, csvFileCatalogItems); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 log.LogError(ex.Message); | ||||
|                 return GetPreconfiguredItems(); | ||||
|             } | ||||
| 
 | ||||
|             var catalogTypeIdLookup = context.CatalogTypes.ToDictionary(ct => ct.Type, ct => ct.Id); | ||||
|             var catalogBrandIdLookup = context.CatalogBrands.ToDictionary(ct => ct.Brand, ct => ct.Id); | ||||
| 
 | ||||
|             return File.ReadAllLines(csvFileCatalogItems) | ||||
|                         .Skip(1) // skip header row | ||||
|                         .Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") ) | ||||
|                         .SelectTry(column => CreateCatalogItem(column, csvheaders, catalogTypeIdLookup, catalogBrandIdLookup)) | ||||
|                         .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) | ||||
|                         .Where(x => x != null); | ||||
|         } | ||||
| 
 | ||||
|         static CatalogItem CreateCatalogItem(string[] column, string[] headers, Dictionary<String, int> catalogTypeIdLookup, Dictionary<String, int> catalogBrandIdLookup) | ||||
|         { | ||||
|             if (column.Count() != headers.Count()) | ||||
|             { | ||||
|                 throw new Exception($"column count '{column.Count()}' not the same as headers count'{headers.Count()}'"); | ||||
|             } | ||||
| 
 | ||||
|             string catalogTypeName = column[Array.IndexOf(headers, "catalogtypename")].Trim(); | ||||
|             if (!catalogTypeIdLookup.ContainsKey(catalogTypeName)) | ||||
|             { | ||||
|                 throw new Exception($"type={catalogTypeName} does not exist in catalogTypes"); | ||||
|             } | ||||
| 
 | ||||
|             string catalogBrandName = column[Array.IndexOf(headers, "catalogbrandname")].Trim(); | ||||
|             if (!catalogBrandIdLookup.ContainsKey(catalogBrandName)) | ||||
|             { | ||||
|                 throw new Exception($"type={catalogTypeName} does not exist in catalogTypes"); | ||||
|             } | ||||
| 
 | ||||
|             string priceString = column[Array.IndexOf(headers, "price")]; | ||||
|             if (!Decimal.TryParse(priceString, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out Decimal price)) // TODO: number styles | ||||
|             { | ||||
|                 throw new Exception($"price={priceString}is not a valid decimal number"); | ||||
|             } | ||||
| 
 | ||||
|             return new CatalogItem() | ||||
|             { | ||||
|                 CatalogTypeId = catalogTypeIdLookup[catalogTypeName], | ||||
|                 CatalogBrandId = catalogBrandIdLookup[catalogBrandName], | ||||
|                 Description = column[Array.IndexOf(headers, "description")], | ||||
|                 Name = column[Array.IndexOf(headers, "name")], | ||||
|                 Price = price, | ||||
|                 PictureUri = column[Array.IndexOf(headers, "pictureuri")] | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerable<CatalogItem> GetPreconfiguredItems() | ||||
|         { | ||||
|             return new List<CatalogItem>() | ||||
|             { | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/1", AvailableStock = 100}, | ||||
|                 new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/2", AvailableStock = 100 }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/3", AvailableStock = 100 }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4", AvailableStock = 100 }, | ||||
|                 new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/5", AvailableStock = 100 }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6", AvailableStock = 100 }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/7", AvailableStock = 100  }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8", AvailableStock = 100 }, | ||||
|                 new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup<T> White Mug", Name = "Cup<T> White Mug", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/9", AvailableStock = 100 }, | ||||
|                 new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/10", AvailableStock = 100 }, | ||||
|                 new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup<T> Sheet", Name = "Cup<T> Sheet", Price = 8.5M, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/11", AvailableStock = 100 }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureUri = "http://externalcatalogbaseurltobereplaced/api/v1/pic/12", AvailableStock = 100 } | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Bot Black Hoodie", Name = ".NET Bot Black Hoodie", Price = 19.5M, PictureUri = "1.png" }, | ||||
|                 new CatalogItem() { CatalogTypeId=1,CatalogBrandId=2, Description = ".NET Black & White Mug", Name = ".NET Black & White Mug", Price= 8.50M, PictureUri = "2.png" }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White T-Shirt", Name = "Prism White T-Shirt", Price = 12, PictureUri = "3.png" }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Foundation T-shirt", Name = ".NET Foundation T-shirt", Price = 12, PictureUri = "4.png" }, | ||||
|                 new CatalogItem() { CatalogTypeId=3,CatalogBrandId=5, Description = "Roslyn Red Sheet", Name = "Roslyn Red Sheet", Price = 8.5M, PictureUri = "5.png" }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=2, Description = ".NET Blue Hoodie", Name = ".NET Blue Hoodie", Price = 12, PictureUri = "6.png" }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Roslyn Red T-Shirt", Name = "Roslyn Red T-Shirt", Price = 12, PictureUri = "7.png"  }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Kudu Purple Hoodie", Name = "Kudu Purple Hoodie", Price = 8.5M, PictureUri = "8.png" }, | ||||
|                 new CatalogItem() { CatalogTypeId=1,CatalogBrandId=5, Description = "Cup<T> White Mug", Name = "Cup<T> White Mug", Price = 12, PictureUri = "9.png" }, | ||||
|                 new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = ".NET Foundation Sheet", Name = ".NET Foundation Sheet", Price = 12, PictureUri = "10.png" }, | ||||
|                 new CatalogItem() { CatalogTypeId=3,CatalogBrandId=2, Description = "Cup<T> Sheet", Name = "Cup<T> Sheet", Price = 8.5M, PictureUri = "11.png" }, | ||||
|                 new CatalogItem() { CatalogTypeId=2,CatalogBrandId=5, Description = "Prism White TShirt", Name = "Prism White TShirt", Price = 12, PictureUri = "12.png" } | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         static string[] GetHeaders(string[] requiredHeaders, string csvfile) | ||||
|         { | ||||
|             string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); | ||||
| 
 | ||||
|             if (csvheaders.Count() != requiredHeaders.Count()) | ||||
|             { | ||||
|                 throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); | ||||
|             } | ||||
| 
 | ||||
|             foreach (var requiredHeader in requiredHeaders) | ||||
|             { | ||||
|                 if (!csvheaders.Contains(requiredHeader)) | ||||
|                 { | ||||
|                     throw new Exception($"does not contain required header '{requiredHeader}'"); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return csvheaders; | ||||
|         } | ||||
| 
 | ||||
|         static void GetCatalogItemPictures(string contentRootPath, string picturePath) | ||||
|         { | ||||
|             DirectoryInfo directory = new DirectoryInfo(picturePath); | ||||
|             foreach (FileInfo file in directory.GetFiles()) | ||||
|             { | ||||
|                 file.Delete(); | ||||
|             } | ||||
| 
 | ||||
|             string zipFileCatalogItemPictures = Path.Combine(contentRootPath, "Setup", "CatalogItems.zip"); | ||||
|             ZipFile.ExtractToDirectory(zipFileCatalogItemPictures, picturePath); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
|  | ||||
							
								
								
									
										8
									
								
								src/Services/Catalog/Catalog.API/Setup/CatalogBrands.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/Services/Catalog/Catalog.API/Setup/CatalogBrands.csv
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| CatalogBrand | ||||
| Azure | ||||
| .NET  | ||||
| Visual Studio  | ||||
| SQL Server | ||||
| Other | ||||
| CatalogBrandTestOne | ||||
| CatalogBrandTestTwo | ||||
| 
 | 
							
								
								
									
										13
									
								
								src/Services/Catalog/Catalog.API/Setup/CatalogItems.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/Services/Catalog/Catalog.API/Setup/CatalogItems.csv
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| CatalogTypeName,CatalogBrandName,Description,Name,Price,PictureUri | ||||
| T-Shirt,.NET,".NET Bot Black Hoodie, and more",.NET Bot Black Hoodie,19.5,1.png | ||||
| Mug,.NET,.NET Black & White Mug,.NET Black & White Mug,8.50,2.png | ||||
| T-Shirt,Other,Prism White T-Shirt,Prism White T-Shirt,12,3.png | ||||
| T-Shirt,.NET,.NET Foundation T-shirt,.NET Foundation T-shirt,12,4.png | ||||
| Sheet,Other,Roslyn Red Sheet,Roslyn Red Sheet,8.5,5.png | ||||
| T-Shirt,.NET,.NET Blue Hoodie,.NET Blue Hoodie,12,6.png | ||||
| T-Shirt,Other,Roslyn Red T-Shirt,Roslyn Red T-Shirt",12,7.png | ||||
| T-Shirt,Other,Kudu Purple Hoodie,Kudu Purple Hoodie,8.5,8.png | ||||
| Mug,Other,Cup<T> White Mug,Cup<T> White Mug,12,9.png | ||||
| Sheet,.NET,.NET Foundation Sheet,.NET Foundation Sheet,12,10.png | ||||
| Sheet,.NET,Cup<T> Sheet,Cup<T> Sheet,8.5,11.png | ||||
| T-Shirt,Other,Prism White TShirt,Prism White TShirt,12,12.png | ||||
| Can't render this file because it contains an unexpected character in line 8 and column 52. | 
							
								
								
									
										7
									
								
								src/Services/Catalog/Catalog.API/Setup/CatalogTypes.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Services/Catalog/Catalog.API/Setup/CatalogTypes.csv
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| CatalogType | ||||
| Mug | ||||
| T-Shirt | ||||
| Sheet | ||||
| USB Memory Stick | ||||
| CatalogTypeTestOne | ||||
| CatalogTypeTestTwo | ||||
| 
 | 
							
								
								
									
										
											BIN
										
									
								
								src/Services/Catalog/Catalog.API/Setup/Catalogitems.zip
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/Services/Catalog/Catalog.API/Setup/Catalogitems.zip
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -153,7 +153,7 @@ | ||||
|             var context = (CatalogContext)app | ||||
|                         .ApplicationServices.GetService(typeof(CatalogContext)); | ||||
| 
 | ||||
|             WaitForSqlAvailabilityAsync(context, loggerFactory, app).Wait(); | ||||
|             WaitForSqlAvailabilityAsync(context, loggerFactory, app, env).Wait(); | ||||
| 
 | ||||
|             ConfigureEventBus(app); | ||||
| 
 | ||||
| @ -165,13 +165,13 @@ | ||||
|             integrationEventLogContext.Database.Migrate(); | ||||
|         } | ||||
| 
 | ||||
|         private async Task WaitForSqlAvailabilityAsync(CatalogContext ctx, ILoggerFactory loggerFactory, IApplicationBuilder app, int retries = 0) | ||||
|         private async Task WaitForSqlAvailabilityAsync(CatalogContext ctx, ILoggerFactory loggerFactory, IApplicationBuilder app, IHostingEnvironment env, int retries = 0) | ||||
|         { | ||||
|             var logger = loggerFactory.CreateLogger(nameof(Startup)); | ||||
|             var policy = CreatePolicy(retries, logger, nameof (WaitForSqlAvailabilityAsync)); | ||||
|             await policy.ExecuteAsync(async () => | ||||
|             { | ||||
|                 await CatalogContextSeed.SeedAsync(app, loggerFactory); | ||||
|                 await CatalogContextSeed.SeedAsync(app, env, loggerFactory); | ||||
|             }); | ||||
| 
 | ||||
|         } | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| { | ||||
|   "ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word", | ||||
|   "ExternalCatalogBaseUrl": "http://localhost:5101", | ||||
|   "UseCustomizationData": true, | ||||
|   "Logging": { | ||||
|     "IncludeScopes": false, | ||||
|     "LogLevel": { | ||||
|  | ||||
| @ -8,5 +8,6 @@ namespace eShopOnContainers.Identity | ||||
|     public class AppSettings | ||||
|     { | ||||
|         public string MvcClient { get; set; } | ||||
|         public bool UseCustomizationData { get; set; } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -3,13 +3,20 @@ | ||||
|     using AspNetCore.Identity; | ||||
|     using EntityFrameworkCore; | ||||
|     using Extensions.Logging; | ||||
|     using global::eShopOnContainers.Identity; | ||||
|     using global::Identity.API.Data; | ||||
|     using global::Identity.API.Models; | ||||
|     using Identity.API.Extensions; | ||||
|     using Microsoft.AspNetCore.Builder; | ||||
|     using Microsoft.AspNetCore.Hosting; | ||||
|     using Microsoft.Extensions.DependencyInjection; | ||||
|     using Microsoft.Extensions.Options; | ||||
|     using System; | ||||
|     using System.Collections.Generic; | ||||
|     using System.IO; | ||||
|     using System.Linq; | ||||
|     using System.Security.Cryptography; | ||||
|     using System.Text.RegularExpressions; | ||||
|     using System.Threading.Tasks; | ||||
| 
 | ||||
|     public class ApplicationContextSeed | ||||
| @ -21,20 +28,29 @@ | ||||
|             _passwordHasher = passwordHasher; | ||||
|         } | ||||
| 
 | ||||
|         public async Task SeedAsync(IApplicationBuilder applicationBuilder, ILoggerFactory loggerFactory, int? retry = 0) | ||||
|         public async Task SeedAsync(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory, int? retry = 0) | ||||
|         { | ||||
|             int retryForAvaiability = retry.Value; | ||||
|             try | ||||
|             { | ||||
|                 var log = loggerFactory.CreateLogger("application seed"); | ||||
| 
 | ||||
|                 var context = (ApplicationDbContext)applicationBuilder | ||||
|                     .ApplicationServices.GetService(typeof(ApplicationDbContext)); | ||||
| 
 | ||||
|                 context.Database.Migrate(); | ||||
| 
 | ||||
|                 var settings = (AppSettings)applicationBuilder | ||||
|                     .ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value; | ||||
| 
 | ||||
|                 var useCustomizationData = settings.UseCustomizationData; | ||||
|                 var contentRootPath = env.ContentRootPath; | ||||
| 
 | ||||
|                 if (!context.Users.Any()) | ||||
|                 { | ||||
|                     context.Users.AddRange( | ||||
|                         GetDefaultUser()); | ||||
|                     context.Users.AddRange(useCustomizationData | ||||
|                         ? GetUsersFromFile(contentRootPath, log) | ||||
|                         : GetDefaultUser()); | ||||
| 
 | ||||
|                     await context.SaveChangesAsync(); | ||||
|                 } | ||||
| @ -46,14 +62,93 @@ | ||||
|                     retryForAvaiability++; | ||||
|                     var log = loggerFactory.CreateLogger("catalog seed"); | ||||
|                     log.LogError(ex.Message); | ||||
|                     await SeedAsync(applicationBuilder, loggerFactory, retryForAvaiability); | ||||
|                     await SeedAsync(applicationBuilder, env, loggerFactory, retryForAvaiability); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         private ApplicationUser GetDefaultUser() | ||||
|         private IEnumerable<ApplicationUser> GetUsersFromFile(string contentRootPath, ILogger log) | ||||
|         { | ||||
|             var user =  | ||||
|             string csvFileUsers = Path.Combine(contentRootPath, "Setup", "Users.csv"); | ||||
| 
 | ||||
|             if (!File.Exists(csvFileUsers)) | ||||
|             { | ||||
|                 return GetDefaultUser(); | ||||
|             } | ||||
| 
 | ||||
|             string[] csvheaders; | ||||
|             try | ||||
|             { | ||||
|                 string[] requiredHeaders = { | ||||
|                     "cardholdername", "cardnumber", "cardtype", "city", "country", | ||||
|                     "email", "expiration", "lastname", "name", "phonenumber", | ||||
|                     "username", "zipcode", "state", "street", "securitynumber", | ||||
|                     "normalizedemail", "normalizedusername", "password" | ||||
|                 }; | ||||
|                 csvheaders = GetHeaders(requiredHeaders, csvFileUsers); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 log.LogError(ex.Message); | ||||
|                 return GetDefaultUser(); | ||||
|             } | ||||
| 
 | ||||
|             List<ApplicationUser> users = File.ReadAllLines(csvFileUsers) | ||||
|                         .Skip(1) // skip header column | ||||
|                         .Select(row => Regex.Split(row, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)") ) | ||||
|                         .SelectTry(column => CreateApplicationUser(column, csvheaders)) | ||||
|                         .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) | ||||
|                         .Where(x => x != null) | ||||
|                         .ToList(); | ||||
| 
 | ||||
|             return users; | ||||
|         } | ||||
| 
 | ||||
|         private ApplicationUser CreateApplicationUser(string[] column, string[] headers) | ||||
|         { | ||||
|             if (column.Count() != headers.Count()) | ||||
|             { | ||||
|                 throw new Exception($"column count '{column.Count()}' not the same as headers count'{headers.Count()}'"); | ||||
|             } | ||||
| 
 | ||||
|             string cardtypeString = column[Array.IndexOf(headers, "cardtype")].Trim(); | ||||
|             if (!int.TryParse(cardtypeString, out int cardtype)) | ||||
|             { | ||||
|                 throw new Exception($"cardtype='{cardtypeString}' is not a number"); | ||||
|             } | ||||
| 
 | ||||
|             var user = new ApplicationUser | ||||
|             { | ||||
|                 CardHolderName = column[Array.IndexOf(headers, "cardholdername")].Trim(), | ||||
|                 CardNumber = column[Array.IndexOf(headers, "cardnumber")].Trim(), | ||||
|                 CardType = cardtype, | ||||
|                 City = column[Array.IndexOf(headers, "city")].Trim(), | ||||
|                 Country = column[Array.IndexOf(headers, "country")].Trim(), | ||||
|                 Email = column[Array.IndexOf(headers, "email")].Trim(), | ||||
|                 Expiration = column[Array.IndexOf(headers, "expiration")].Trim(), | ||||
|                 Id = Guid.NewGuid().ToString(), | ||||
|                 LastName = column[Array.IndexOf(headers, "lastname")].Trim(), | ||||
|                 Name = column[Array.IndexOf(headers, "name")].Trim(), | ||||
|                 PhoneNumber = column[Array.IndexOf(headers, "phonenumber")].Trim(), | ||||
|                 UserName = column[Array.IndexOf(headers, "username")].Trim(), | ||||
|                 ZipCode = column[Array.IndexOf(headers, "zipcode")].Trim(), | ||||
|                 State = column[Array.IndexOf(headers, "state")].Trim(), | ||||
|                 Street = column[Array.IndexOf(headers, "street")].Trim(), | ||||
|                 SecurityNumber = column[Array.IndexOf(headers, "securitynumber")].Trim(), | ||||
|                 NormalizedEmail = column[Array.IndexOf(headers, "normalizedemail")].Trim(), | ||||
|                 NormalizedUserName = column[Array.IndexOf(headers, "normalizedusername")].Trim(), | ||||
|                 SecurityStamp = Guid.NewGuid().ToString("D"), | ||||
|                 PasswordHash = column[Array.IndexOf(headers, "password")].Trim(), // Note: This is the password | ||||
|             }; | ||||
| 
 | ||||
|             user.PasswordHash = _passwordHasher.HashPassword(user, user.PasswordHash); | ||||
| 
 | ||||
|             return user; | ||||
|         } | ||||
| 
 | ||||
|         private IEnumerable<ApplicationUser> GetDefaultUser() | ||||
|         { | ||||
|             var user = | ||||
|             new ApplicationUser() | ||||
|             { | ||||
|                 CardHolderName = "DemoUser", | ||||
| @ -63,23 +158,46 @@ | ||||
|                 Country = "U.S.", | ||||
|                 Email = "demouser@microsoft.com", | ||||
|                 Expiration = "12/20", | ||||
|                 Id = Guid.NewGuid().ToString(),  | ||||
|                 LastName = "DemoLastName",  | ||||
|                 Name = "DemoUser",  | ||||
|                 PhoneNumber = "1234567890",  | ||||
|                 UserName = "demouser@microsoft.com",  | ||||
|                 ZipCode = "98052",  | ||||
|                 State = "WA",  | ||||
|                 Street = "15703 NE 61st Ct",  | ||||
|                 SecurityNumber = "535",  | ||||
|                 NormalizedEmail = "DEMOUSER@MICROSOFT.COM",  | ||||
|                 NormalizedUserName = "DEMOUSER@MICROSOFT.COM",  | ||||
|                 SecurityStamp = Guid.NewGuid().ToString("D") | ||||
|                 Id = Guid.NewGuid().ToString(), | ||||
|                 LastName = "DemoLastName", | ||||
|                 Name = "DemoUser", | ||||
|                 PhoneNumber = "1234567890", | ||||
|                 UserName = "demouser@microsoft.com", | ||||
|                 ZipCode = "98052", | ||||
|                 State = "WA", | ||||
|                 Street = "15703 NE 61st Ct", | ||||
|                 SecurityNumber = "535", | ||||
|                 NormalizedEmail = "DEMOUSER@MICROSOFT.COM", | ||||
|                 NormalizedUserName = "DEMOUSER@MICROSOFT.COM", | ||||
|                 SecurityStamp = Guid.NewGuid().ToString("D"), | ||||
|             }; | ||||
| 
 | ||||
|             user.PasswordHash = _passwordHasher.HashPassword(user, "Pass@word1"); | ||||
| 
 | ||||
|             return user; | ||||
|             return new List<ApplicationUser>() | ||||
|             { | ||||
|                 user | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         static string[] GetHeaders(string[] requiredHeaders, string csvfile) | ||||
|         { | ||||
|             string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); | ||||
| 
 | ||||
|             if (csvheaders.Count() != requiredHeaders.Count()) | ||||
|             { | ||||
|                 throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); | ||||
|             } | ||||
| 
 | ||||
|             foreach (var requiredHeader in requiredHeaders) | ||||
|             { | ||||
|                 if (!csvheaders.Contains(requiredHeader)) | ||||
|                 { | ||||
|                     throw new Exception($"does not contain required header '{requiredHeader}'"); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return csvheaders; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,50 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace Identity.API.Extensions | ||||
| { | ||||
|     public static class LinqSelectExtensions | ||||
|     { | ||||
|         public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector) | ||||
|         { | ||||
|             foreach (TSource element in enumerable) | ||||
|             { | ||||
|                 SelectTryResult<TSource, TResult> returnedValue; | ||||
|                 try | ||||
|                 { | ||||
|                     returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex); | ||||
|                 } | ||||
|                 yield return returnedValue; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler) | ||||
|         { | ||||
|             return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); | ||||
|         } | ||||
| 
 | ||||
|         public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler) | ||||
|         { | ||||
|             return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); | ||||
|         } | ||||
| 
 | ||||
|         public class SelectTryResult<TSource, TResult> | ||||
|         { | ||||
|             internal SelectTryResult(TSource source, TResult result, Exception exception) | ||||
|             { | ||||
|                 Source = source; | ||||
|                 Result = result; | ||||
|                 CaughtException = exception; | ||||
|             } | ||||
| 
 | ||||
|             public TSource Source { get; private set; } | ||||
|             public TResult Result { get; private set; } | ||||
|             public Exception CaughtException { get; private set; } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -8,7 +8,13 @@ | ||||
|     <PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback> | ||||
|     <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | ||||
|   </PropertyGroup> | ||||
|    | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <Content Include="Setup\**\*;"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" /> | ||||
|     <PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.2" /> | ||||
| @ -70,6 +76,9 @@ | ||||
|     <None Update="Dockerfile"> | ||||
|       <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
|     </None> | ||||
|     <None Update="Setup\*"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </None> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|  | ||||
							
								
								
									
										2
									
								
								src/Services/Identity/Identity.API/Setup/Users.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/Services/Identity/Identity.API/Setup/Users.csv
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| CardHolderName,CardNumber,CardType,City,Country,Email,Expiration,LastName,Name,PhoneNumber,UserName,ZipCode,State,Street,SecurityNumber,NormalizedEmail,NormalizedUserName,Password | ||||
| DemoUser,4012888888881881,1,Redmond,U.S.,demouser@microsoft.com,12/20,DemoLastName,DemoUser,1234567890,demouser@microsoft.com,98052,WA,15703 NE 61st Ct,535,DEMOUSER@MICROSOFT.COM,DEMOUSER@MICROSOFT.COM,Pass@word1 | ||||
| 
 | 
| @ -153,7 +153,7 @@ namespace eShopOnContainers.Identity | ||||
| 
 | ||||
|             //Seed Data | ||||
|             var hasher = new PasswordHasher<ApplicationUser>(); | ||||
|             new ApplicationContextSeed(hasher).SeedAsync(app, loggerFactory).Wait(); | ||||
|             new ApplicationContextSeed(hasher).SeedAsync(app, env, loggerFactory).Wait(); | ||||
|         } | ||||
| 
 | ||||
|         private async Task InitializeGrantStoreAndConfiguration(IApplicationBuilder app) | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
|   "MvcClient": "http://localhost:5100", | ||||
|   "SpaClient": "http://localhost:5104", | ||||
|   "XamarinCallback": "http://localhost:5105/xamarincallback", | ||||
|   "UseCustomizationData": true, | ||||
|   "Logging": { | ||||
|     "IncludeScopes": false, | ||||
|     "LogLevel": { | ||||
|  | ||||
| @ -0,0 +1,50 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace Ordering.API.Extensions | ||||
| { | ||||
|     public static class LinqSelectExtensions | ||||
|     { | ||||
|         public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector) | ||||
|         { | ||||
|             foreach (TSource element in enumerable) | ||||
|             { | ||||
|                 SelectTryResult<TSource, TResult> returnedValue; | ||||
|                 try | ||||
|                 { | ||||
|                     returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null); | ||||
|                 } | ||||
|                 catch (Exception ex) | ||||
|                 { | ||||
|                     returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex); | ||||
|                 } | ||||
|                 yield return returnedValue; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler) | ||||
|         { | ||||
|             return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException)); | ||||
|         } | ||||
| 
 | ||||
|         public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler) | ||||
|         { | ||||
|             return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException)); | ||||
|         } | ||||
| 
 | ||||
|         public class SelectTryResult<TSource, TResult> | ||||
|         { | ||||
|             internal SelectTryResult(TSource source, TResult result, Exception exception) | ||||
|             { | ||||
|                 Source = source; | ||||
|                 Result = result; | ||||
|                 CaughtException = exception; | ||||
|             } | ||||
| 
 | ||||
|             public TSource Source { get; private set; } | ||||
|             public TResult Result { get; private set; } | ||||
|             public Exception CaughtException { get; private set; } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -8,40 +8,175 @@ | ||||
|     using System.Threading.Tasks; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; | ||||
|     using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; | ||||
|     using System.Collections.Generic; | ||||
|     using Microsoft.AspNetCore.Hosting; | ||||
|     using System.IO; | ||||
|     using Microsoft.Extensions.Options; | ||||
|     using Microsoft.Extensions.DependencyInjection; | ||||
|     using Microsoft.Extensions.Logging; | ||||
|     using System; | ||||
|     using global::Ordering.API.Extensions; | ||||
| 
 | ||||
|     public class OrderingContextSeed | ||||
|     { | ||||
|         public static async Task SeedAsync(IApplicationBuilder applicationBuilder) | ||||
|         public static async Task SeedAsync(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory) | ||||
|         { | ||||
|             var log = loggerFactory.CreateLogger("ordering seed"); | ||||
| 
 | ||||
|             var context = (OrderingContext)applicationBuilder | ||||
|                 .ApplicationServices.GetService(typeof(OrderingContext)); | ||||
| 
 | ||||
|             var settings = applicationBuilder | ||||
|                 .ApplicationServices.GetRequiredService<IOptions<OrderingSettings>>().Value; | ||||
| 
 | ||||
|             var useCustomizationData = settings.UseCustomizationData; | ||||
|             var contentRootPath = env.ContentRootPath; | ||||
| 
 | ||||
| 
 | ||||
|             using (context) | ||||
|             { | ||||
|                 context.Database.Migrate(); | ||||
| 
 | ||||
|                 if (!context.CardTypes.Any()) | ||||
|                 { | ||||
|                     context.CardTypes.Add(CardType.Amex); | ||||
|                     context.CardTypes.Add(CardType.Visa); | ||||
|                     context.CardTypes.Add(CardType.MasterCard); | ||||
|                     context.CardTypes.AddRange(useCustomizationData | ||||
|                                             ? GetCardTypesFromFile(contentRootPath, log) | ||||
|                                             : GetPredefinedCardTypes()); | ||||
| 
 | ||||
|                     await context.SaveChangesAsync(); | ||||
|                 } | ||||
| 
 | ||||
|                 if (!context.OrderStatus.Any()) | ||||
|                 { | ||||
|                     context.OrderStatus.Add(OrderStatus.Submitted); | ||||
|                     context.OrderStatus.Add(OrderStatus.AwaitingValidation); | ||||
|                     context.OrderStatus.Add(OrderStatus.StockConfirmed); | ||||
|                     context.OrderStatus.Add(OrderStatus.Paid); | ||||
|                     context.OrderStatus.Add(OrderStatus.Shipped); | ||||
|                     context.OrderStatus.Add(OrderStatus.Cancelled); | ||||
|                     context.OrderStatus.AddRange(useCustomizationData | ||||
|                                             ? GetOrderStatusFromFile(contentRootPath, log) | ||||
|                                             : GetPredefinedOrderStatus()); | ||||
|                 } | ||||
| 
 | ||||
|                 await context.SaveChangesAsync(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerable<CardType> GetCardTypesFromFile(string contentRootPath, ILogger log) | ||||
|         { | ||||
|             string csvFileCardTypes = Path.Combine(contentRootPath, "Setup", "CardTypes.csv"); | ||||
| 
 | ||||
|             if (!File.Exists(csvFileCardTypes)) | ||||
|             { | ||||
|                 return GetPredefinedCardTypes(); | ||||
|             } | ||||
| 
 | ||||
|             string[] csvheaders; | ||||
|             try | ||||
|             { | ||||
|                 string[] requiredHeaders = { "CardType" }; | ||||
|                 csvheaders = GetHeaders(requiredHeaders, csvFileCardTypes); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 log.LogError(ex.Message); | ||||
|                 return GetPredefinedCardTypes(); | ||||
|             } | ||||
| 
 | ||||
|             int id = 1; | ||||
|             return File.ReadAllLines(csvFileCardTypes) | ||||
|                                         .Skip(1) // skip header column | ||||
|                                         .SelectTry(x => CreateCardType(x, ref id)) | ||||
|                                         .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) | ||||
|                                         .Where(x => x != null); | ||||
|         } | ||||
| 
 | ||||
|         static CardType CreateCardType(string value, ref int id) | ||||
|         { | ||||
|             if (String.IsNullOrEmpty(value)) | ||||
|             { | ||||
|                 throw new Exception("Orderstatus is null or empty"); | ||||
|             } | ||||
| 
 | ||||
|             return new CardType(id++, value.Trim()); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         private static IEnumerable<CardType> GetPredefinedCardTypes() | ||||
|         { | ||||
|             return new List<CardType>() | ||||
|             { | ||||
|                 CardType.Amex, | ||||
|                 CardType.Visa, | ||||
|                 CardType.MasterCard | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerable<OrderStatus> GetOrderStatusFromFile(string contentRootPath, ILogger log) | ||||
|         { | ||||
|             string csvFileOrderStatus = Path.Combine(contentRootPath, "Setup", "OrderStatus.csv"); | ||||
| 
 | ||||
|             if (!File.Exists(csvFileOrderStatus)) | ||||
|             { | ||||
|                 return GetPredefinedOrderStatus(); | ||||
|             } | ||||
| 
 | ||||
|             string[] csvheaders; | ||||
|             try | ||||
|             { | ||||
|                 string[] requiredHeaders = { "OrderStatus" }; | ||||
|                 csvheaders = GetHeaders(requiredHeaders, csvFileOrderStatus); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 log.LogError(ex.Message); | ||||
|                 return GetPredefinedOrderStatus(); | ||||
|             } | ||||
| 
 | ||||
|             int id = 1; | ||||
|             return File.ReadAllLines(csvFileOrderStatus) | ||||
|                                         .Skip(1) // skip header row | ||||
|                                         .SelectTry(x => CreateOrderStatus(x, ref id)) | ||||
|                                         .OnCaughtException(ex => { log.LogError(ex.Message); return null; }) | ||||
|                                         .Where(x => x != null); | ||||
|         } | ||||
| 
 | ||||
|         static OrderStatus CreateOrderStatus(string value, ref int id) | ||||
|         { | ||||
|             if (String.IsNullOrEmpty(value)) | ||||
|             { | ||||
|                 throw new Exception("Orderstatus is null or empty"); | ||||
|             } | ||||
| 
 | ||||
|             return new OrderStatus(id++, value.Trim().ToLowerInvariant()); | ||||
|         } | ||||
| 
 | ||||
|         static IEnumerable<OrderStatus> GetPredefinedOrderStatus() | ||||
|         { | ||||
|             return new List<OrderStatus>() | ||||
|             { | ||||
|                 OrderStatus.Submitted, | ||||
|                 OrderStatus.AwaitingValidation, | ||||
|                 OrderStatus.StockConfirmed, | ||||
|                 OrderStatus.Paid, | ||||
|                 OrderStatus.Shipped, | ||||
|                 OrderStatus.Cancelled | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         static string[] GetHeaders(string[] requiredHeaders, string csvfile) | ||||
|         { | ||||
|             string[] csvheaders = File.ReadLines(csvfile).First().ToLowerInvariant().Split(','); | ||||
| 
 | ||||
|             if (csvheaders.Count() != requiredHeaders.Count()) | ||||
|             { | ||||
|                 throw new Exception($"requiredHeader count '{ requiredHeaders.Count()}' is different then read header '{csvheaders.Count()}'"); | ||||
|             } | ||||
| 
 | ||||
|             foreach (var requiredHeader in requiredHeaders) | ||||
|             { | ||||
|                 if (!csvheaders.Contains(requiredHeader)) | ||||
|                 { | ||||
|                     throw new Exception($"does not contain required header '{requiredHeader}'"); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             return csvheaders; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -16,6 +16,9 @@ | ||||
|     <Content Include=".dockerignore;"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|     <Content Include="Setup\**\*;"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
| @ -73,6 +76,9 @@ | ||||
|     <None Update="Dockerfile"> | ||||
|       <CopyToOutputDirectory>Always</CopyToOutputDirectory> | ||||
|     </None> | ||||
|     <None Update="Setup\*"> | ||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||
|     </None> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|  | ||||
							
								
								
									
										7
									
								
								src/Services/Ordering/Ordering.API/OrderingSettings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Services/Ordering/Ordering.API/OrderingSettings.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| namespace Microsoft.eShopOnContainers.Services.Ordering.API | ||||
| { | ||||
|     public class OrderingSettings | ||||
|     { | ||||
|         public bool UseCustomizationData { get; set; } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										5
									
								
								src/Services/Ordering/Ordering.API/Setup/CardTypes.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/Services/Ordering/Ordering.API/Setup/CardTypes.csv
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| CardType | ||||
| Amex | ||||
| Visa | ||||
| MasterCard | ||||
| Capital One | ||||
| 
 | 
							
								
								
									
										7
									
								
								src/Services/Ordering/Ordering.API/Setup/OrderStatus.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/Services/Ordering/Ordering.API/Setup/OrderStatus.csv
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| OrderStatus | ||||
| Submitted | ||||
| AwaitingValidation | ||||
| StockConfirmed | ||||
| Paid | ||||
| Shipped | ||||
| Cancelled | ||||
| 
 | 
| @ -87,6 +87,8 @@ | ||||
|                         ServiceLifetime.Scoped  //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request) | ||||
|                     ); | ||||
| 
 | ||||
|             services.Configure<OrderingSettings>(Configuration); | ||||
| 
 | ||||
|             services.AddSwaggerGen(options => | ||||
|             { | ||||
|                 options.DescribeAllEnumsAsStrings(); | ||||
| @ -159,7 +161,7 @@ | ||||
|                    c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); | ||||
|                }); | ||||
| 
 | ||||
|             WaitForSqlAvailabilityAsync(loggerFactory, app).Wait(); | ||||
|             WaitForSqlAvailabilityAsync(loggerFactory, app, env).Wait(); | ||||
|             ConfigureEventBus(app); | ||||
| 
 | ||||
|             var integrationEventLogContext = new IntegrationEventLogContext( | ||||
| @ -200,13 +202,13 @@ | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         private async Task WaitForSqlAvailabilityAsync(ILoggerFactory loggerFactory, IApplicationBuilder app, int retries = 0) | ||||
|         private async Task WaitForSqlAvailabilityAsync(ILoggerFactory loggerFactory, IApplicationBuilder app, IHostingEnvironment env, int retries = 0) | ||||
|         { | ||||
|             var logger = loggerFactory.CreateLogger(nameof(Startup)); | ||||
|             var policy = CreatePolicy(retries, logger, nameof(WaitForSqlAvailabilityAsync)); | ||||
|             await policy.ExecuteAsync(async () => | ||||
|             { | ||||
|                 await OrderingContextSeed.SeedAsync(app); | ||||
|                 await OrderingContextSeed.SeedAsync(app, env, loggerFactory); | ||||
|             }); | ||||
| 
 | ||||
|         } | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| { | ||||
|   "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", | ||||
|   "IdentityUrl": "http://localhost:5105", | ||||
|   "UseCustomizationData": true, | ||||
|   "Logging": { | ||||
|     "IncludeScopes": false, | ||||
|     "LogLevel": { | ||||
|  | ||||
| @ -12,6 +12,7 @@ namespace Microsoft.eShopOnContainers.WebMVC | ||||
|         public string OrderingUrl { get; set; } | ||||
|         public string BasketUrl { get; set; } | ||||
|         public Logging Logging { get; set; } | ||||
|         public bool UseCustomizationData { get; set; } | ||||
|     } | ||||
| 
 | ||||
|     public class Connectionstrings | ||||
|  | ||||
							
								
								
									
										97
									
								
								src/Web/WebMVC/Infrastructure/WebContextSeed.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/Web/WebMVC/Infrastructure/WebContextSeed.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| using Microsoft.AspNetCore.Builder; | ||||
| using Microsoft.AspNetCore.Hosting; | ||||
| using Microsoft.eShopOnContainers.WebMVC; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Microsoft.Extensions.Options; | ||||
| using System; | ||||
| using System.IO; | ||||
| using System.IO.Compression; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace WebMVC.Infrastructure | ||||
| { | ||||
|     public class WebContextSeed | ||||
|     { | ||||
|         public static void Seed(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory) | ||||
|         { | ||||
|             var log = loggerFactory.CreateLogger("WebMVC seed"); | ||||
| 
 | ||||
|             var settings = (AppSettings)applicationBuilder | ||||
|                 .ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value; | ||||
| 
 | ||||
|             var useCustomizationData = settings.UseCustomizationData; | ||||
|             var contentRootPath = env.ContentRootPath; | ||||
|             var webroot = env.WebRootPath; | ||||
| 
 | ||||
|             if (useCustomizationData) | ||||
|             { | ||||
|                 GetPreconfiguredImages(contentRootPath, webroot, log); | ||||
| 
 | ||||
|                 GetPreconfiguredCSS(contentRootPath, webroot, log); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         static void GetPreconfiguredCSS(string contentRootPath, string webroot, ILogger log) | ||||
|         { | ||||
|             try | ||||
|             {  | ||||
|                 string overrideCssFile = Path.Combine(contentRootPath, "Setup", "override.css"); | ||||
|                 if (!File.Exists(overrideCssFile)) | ||||
|                 { | ||||
|                     log.LogError($" override css file '{overrideCssFile}' does not exists."); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 string destinationFilename = Path.Combine(webroot, "css", "override.css"); | ||||
|                 File.Copy(overrideCssFile, destinationFilename, true ); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 log.LogError($"Exception in method GetPreconfiguredCSS WebMVC. Exception Message={ex.Message}"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger log) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); | ||||
|                 if (!File.Exists(imagesZipFile)) | ||||
|                 { | ||||
|                     log.LogError($" zip file '{imagesZipFile}' does not exists."); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 string imagePath = Path.Combine(webroot, "images"); | ||||
|                 string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray(); | ||||
| 
 | ||||
|                 using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read)) | ||||
|                 { | ||||
|                     foreach (ZipArchiveEntry entry in zip.Entries) | ||||
|                     { | ||||
|                         if (imageFiles.Contains(entry.Name)) | ||||
|                         { | ||||
|                             string destinationFilename = Path.Combine(imagePath, entry.Name); | ||||
|                             if (File.Exists(destinationFilename)) | ||||
|                             { | ||||
|                                 File.Delete(destinationFilename); | ||||
|                             } | ||||
|                             entry.ExtractToFile(destinationFilename); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             log.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'"); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch ( Exception ex ) | ||||
|             { | ||||
|                 log.LogError($"Exception in method GetPreconfiguredImages WebMVC. Exception Message={ex.Message}"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								src/Web/WebMVC/Setup/images.zip
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/Web/WebMVC/Setup/images.zip
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										6
									
								
								src/Web/WebMVC/Setup/override.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/Web/WebMVC/Setup/override.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| .esh-catalog-button { | ||||
|     background-color: #FF001b; | ||||
|     border: 2px; | ||||
|     color: #fff; | ||||
|     cursor: pointer; | ||||
| } | ||||
| @ -12,6 +12,7 @@ using Microsoft.Extensions.HealthChecks; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using System; | ||||
| using System.IdentityModel.Tokens.Jwt; | ||||
| using WebMVC.Infrastructure; | ||||
| 
 | ||||
| namespace Microsoft.eShopOnContainers.WebMVC | ||||
| { | ||||
| @ -128,6 +129,9 @@ namespace Microsoft.eShopOnContainers.WebMVC | ||||
|                 Scope = { "openid", "profile", "orders", "basket" } | ||||
|             }; | ||||
| 
 | ||||
|             //Seed Data | ||||
|             WebContextSeed.Seed(app, env, loggerFactory); | ||||
| 
 | ||||
|             //Wait untill identity service is ready on compose.  | ||||
|             app.UseOpenIdConnectAuthentication(oidcOptions); | ||||
| 
 | ||||
|  | ||||
| @ -18,12 +18,14 @@ | ||||
|         <link rel="stylesheet" href="~/css/orders/orders.component.css" /> | ||||
|         <link rel="stylesheet" href="~/css/orders/orders-detail/orders-detail.component.css" /> | ||||
|         <link rel="stylesheet" href="~/css/orders/orders-new/orders-new.component.css" /> | ||||
|         <link rel="stylesheet" href="~/css/override.css" type="text/css" /> | ||||
|     </environment> | ||||
|     <environment names="Staging,Production"> | ||||
|         <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.5/css/bootstrap.min.css" | ||||
|               asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" | ||||
|               asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> | ||||
|         <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> | ||||
|         <link rel="stylesheet" href="~/css/override.css" type="text/css" /> | ||||
|     </environment> | ||||
| </head> | ||||
| <body> | ||||
| @ -55,7 +57,7 @@ | ||||
|                 </section> | ||||
| 
 | ||||
|                 <section class="col-sm-6"> | ||||
|                     <div class="esh-app-footer-text hidden-xs"> e-ShoponContainers. By Microsoft Corp. </div> | ||||
|                     <img class="esh-app-footer-text hidden-xs" src="~/images/main_footer_text.png" width="335" height="26" alt="footer text image" /> | ||||
|                 </section> | ||||
| 
 | ||||
|             </article> | ||||
|  | ||||
| @ -9,6 +9,25 @@ | ||||
|     <DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> | ||||
|   </PropertyGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <Compile Remove="Extensions\LinqSelectExtensions.cs" /> | ||||
|     <Compile Remove="Services\CustomUIService.cs" /> | ||||
|     <Compile Remove="Services\ICustomUIService.cs" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <Content Remove="Views\Shared\_Footer.cshtml" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <Content Include="Setup\images.zip"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|     <Content Include="Setup\override.css"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <!--<ItemGroup> | ||||
|     <Compile Remove="wwwroot\lib\bootstrap\**" /> | ||||
|     <Content Remove="wwwroot\lib\bootstrap\**" /> | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
|   "CallBackUrl": "http://localhost:5100/", | ||||
|   "IsClusterEnv": "False", | ||||
|   "UseResilientHttp": "True", | ||||
|   "UseCustomizationData": true, | ||||
|   "Logging": { | ||||
|     "IncludeScopes": false, | ||||
|     "LogLevel": { | ||||
|  | ||||
							
								
								
									
										42
									
								
								src/Web/WebMVC/compilerconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/Web/WebMVC/compilerconfig.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | ||||
| [ | ||||
|   { | ||||
|     "outputFile": "wwwroot/css/orders/orders.component.css", | ||||
|     "inputFile": "wwwroot/css/orders/orders.component.scss" | ||||
|   }, | ||||
|   { | ||||
|     "outputFile": "wwwroot/css/orders/orders-new/orders-new.component.css", | ||||
|     "inputFile": "wwwroot/css/orders/orders-new/orders-new.component.scss" | ||||
|   }, | ||||
|   { | ||||
|     "outputFile": "wwwroot/css/orders/orders-detail/orders-detail.component.css", | ||||
|     "inputFile": "wwwroot/css/orders/orders-detail/orders-detail.component.scss" | ||||
|   }, | ||||
|   { | ||||
|     "outputFile": "wwwroot/css/catalog/catalog.component.css", | ||||
|     "inputFile": "wwwroot/css/catalog/catalog.component.scss" | ||||
|   }, | ||||
|   { | ||||
|     "outputFile": "wwwroot/css/basket/basket.component.css", | ||||
|     "inputFile": "wwwroot/css/basket/basket.component.scss" | ||||
|   }, | ||||
|   { | ||||
|     "outputFile": "wwwroot/css/basket/basket-status/basket-status.component.css", | ||||
|     "inputFile": "wwwroot/css/basket/basket-status/basket-status.component.scss" | ||||
|   }, | ||||
|   { | ||||
|     "outputFile": "wwwroot/css/shared/components/header/header.css", | ||||
|     "inputFile": "wwwroot/css/shared/components/header/header.scss" | ||||
|   }, | ||||
|   { | ||||
|     "outputFile": "wwwroot/css/shared/components/identity/identity.css", | ||||
|     "inputFile": "wwwroot/css/shared/components/identity/identity.scss" | ||||
|   }, | ||||
|   { | ||||
|     "outputFile": "wwwroot/css/shared/components/pager/pager.css", | ||||
|     "inputFile": "wwwroot/css/shared/components/pager/pager.scss" | ||||
|   }, | ||||
|   { | ||||
|     "outputFile": "wwwroot/css/app.component.css", | ||||
|     "inputFile": "wwwroot/css/app.component.scss" | ||||
|   } | ||||
| ] | ||||
							
								
								
									
										49
									
								
								src/Web/WebMVC/compilerconfig.json.defaults
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/Web/WebMVC/compilerconfig.json.defaults
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| { | ||||
|   "compilers": { | ||||
|     "less": { | ||||
|       "autoPrefix": "", | ||||
|       "cssComb": "none", | ||||
|       "ieCompat": true, | ||||
|       "strictMath": false, | ||||
|       "strictUnits": false, | ||||
|       "relativeUrls": true, | ||||
|       "rootPath": "", | ||||
|       "sourceMapRoot": "", | ||||
|       "sourceMapBasePath": "", | ||||
|       "sourceMap": false | ||||
|     }, | ||||
|     "sass": { | ||||
|       "includePath": "", | ||||
|       "indentType": "space", | ||||
|       "indentWidth": 2, | ||||
|       "outputStyle": "expanded", | ||||
|       "Precision": 5, | ||||
|       "relativeUrls": true, | ||||
|       "sourceMapRoot": "", | ||||
|       "sourceMap": false | ||||
|     }, | ||||
|     "stylus": { | ||||
|       "sourceMap": false | ||||
|     }, | ||||
|     "babel": { | ||||
|       "sourceMap": false | ||||
|     }, | ||||
|     "coffeescript": { | ||||
|       "bare": false, | ||||
|       "runtimeMode": "node", | ||||
|       "sourceMap": false | ||||
|     } | ||||
|   }, | ||||
|   "minifiers": { | ||||
|     "css": { | ||||
|       "enabled": true, | ||||
|       "termSemicolons": true, | ||||
|       "gzip": false | ||||
|     }, | ||||
|     "javascript": { | ||||
|       "enabled": true, | ||||
|       "termSemicolons": true, | ||||
|       "gzip": false | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										58
									
								
								src/Web/WebMVC/wwwroot/css/_variables.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/Web/WebMVC/wwwroot/css/_variables.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | ||||
| // Colors | ||||
| $color-brand: #00A69C; | ||||
| $color-brand-dark: darken($color-brand, 10%); | ||||
| $color-brand-darker: darken($color-brand, 20%); | ||||
| $color-brand-bright: lighten($color-brand, 10%); | ||||
| $color-brand-brighter: lighten($color-brand, 20%); | ||||
| 
 | ||||
| $color-secondary: #83D01B; | ||||
| $color-secondary-dark: darken($color-secondary, 5%); | ||||
| $color-secondary-darker: darken($color-secondary, 20%); | ||||
| $color-secondary-bright: lighten($color-secondary, 10%); | ||||
| $color-secondary-brighter: lighten($color-secondary, 20%); | ||||
| 
 | ||||
| $color-background-dark: #333333; | ||||
| $color-background-darker: #000000; | ||||
| $color-background-bright: #EEEEFF; | ||||
| $color-background-brighter: #FFFFFF; | ||||
| 
 | ||||
| $color-foreground-dark: #333333; | ||||
| $color-foreground-darker: #000000; | ||||
| $color-foreground-bright: #EEEEEE; | ||||
| $color-foreground-brighter: #FFFFFF; | ||||
| 
 | ||||
| // Animations | ||||
| $animation-speed-default: .35s; | ||||
| $animation-speed-slow: .5s; | ||||
| $animation-speed-fast: .15s; | ||||
| 
 | ||||
| // Fonts | ||||
| $font-weight-light: 200; | ||||
| $font-weight-semilight: 300; | ||||
| $font-weight-normal: 400; | ||||
| $font-weight-semibold: 600; | ||||
| $font-weight-bold: 700; | ||||
| 
 | ||||
| $font-size-xs: .65rem;    // 10.4px | ||||
| $font-size-s: .85rem;    // 13.6px | ||||
| $font-size-m: 1rem;      // 16px | ||||
| $font-size-l: 1.25rem;   // 20px | ||||
| $font-size-xl: 1.5rem;   // 24px | ||||
| 
 | ||||
| // Medias | ||||
| $media-screen-xxs: 360px; | ||||
| $media-screen-xs: 640px; | ||||
| $media-screen-s: 768px; | ||||
| $media-screen-m: 1024px; | ||||
| $media-screen-l: 1280px; | ||||
| $media-screen-xl: 1440px; | ||||
| $media-screen-xxl: 1680px; | ||||
| $media-screen-xxxl: 1920px; | ||||
| 
 | ||||
| // Borders | ||||
| $border-light: 1px; | ||||
| 
 | ||||
| // Images | ||||
| $image_path: '../../images/'; | ||||
| $image-main_banner: '#{$image_path}main_banner.png'; | ||||
| $image-arrow_down: '#{$image_path}arrow-down.png'; | ||||
| @ -1,20 +1,14 @@ | ||||
| .esh-app-footer { | ||||
|     background-color: #000000; | ||||
|     border-top: 1px solid #EEEEEE; | ||||
|     margin-top: 2.5rem; | ||||
|     padding-bottom: 2.5rem; | ||||
|     padding-top: 2.5rem; | ||||
|     width: 100%; | ||||
| .esh-app-footer { | ||||
|   background-color: #000000; | ||||
|   border-top: 1px solid #EEEEEE; | ||||
|   margin-top: 2.5rem; | ||||
|   padding-bottom: 2.5rem; | ||||
|   padding-top: 2.5rem; | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| .esh-app-footer-brand { | ||||
|     height: 50px; | ||||
|     width: 230px; | ||||
|   height: 50px; | ||||
|   width: 230px; | ||||
| } | ||||
| 
 | ||||
| .esh-app-footer-text { | ||||
|     color: #83D01B; | ||||
|     line-height: 50px; | ||||
|     text-align: right; | ||||
|     width: 100%; | ||||
| } | ||||
|  | ||||
							
								
								
									
										23
									
								
								src/Web/WebMVC/wwwroot/css/app.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/Web/WebMVC/wwwroot/css/app.component.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | ||||
| @import './variables'; | ||||
| 
 | ||||
| .esh-app { | ||||
|     &-footer { | ||||
|         $margin: 2.5rem; | ||||
|         $padding: 2.5rem; | ||||
| 
 | ||||
|         background-color: $color-background-darker; | ||||
|         border-top: $border-light solid $color-foreground-bright; | ||||
|         margin-top: $margin; | ||||
|         padding-bottom: $padding; | ||||
|         padding-top: $padding; | ||||
|         width: 100%; | ||||
| 
 | ||||
|         $height: 50px; | ||||
| 
 | ||||
|         &-brand { | ||||
|             height: $height; | ||||
|             width: 230px; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| @ -1,38 +1,39 @@ | ||||
| .esh-basketstatus { | ||||
|     cursor: pointer; | ||||
|     display: inline-block; | ||||
|     float: right; | ||||
|     position: relative; | ||||
|     transition: all 0.35s; | ||||
| .esh-basketstatus { | ||||
|   cursor: pointer; | ||||
|   display: inline-block; | ||||
|   float: right; | ||||
|   position: relative; | ||||
|   transition: all 0.35s; | ||||
| } | ||||
| 
 | ||||
|     .esh-basketstatus.is-disabled { | ||||
|         opacity: .5; | ||||
|         pointer-events: none; | ||||
|     } | ||||
| .esh-basketstatus.is-disabled { | ||||
|   opacity: .5; | ||||
|   pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .esh-basketstatus-image { | ||||
|     height: 36px; | ||||
|     margin-top: .5rem; | ||||
|   height: 36px; | ||||
|   margin-top: .5rem; | ||||
| } | ||||
| 
 | ||||
| .esh-basketstatus-badge { | ||||
|     background-color: #83D01B; | ||||
|     border-radius: 50%; | ||||
|     color: #FFFFFF; | ||||
|     display: block; | ||||
|     height: 1.5rem; | ||||
|     left: 50%; | ||||
|     position: absolute; | ||||
|     text-align: center; | ||||
|     top: 0; | ||||
|     transform: translateX(-38%); | ||||
|     transition: all 0.35s; | ||||
|     width: 1.5rem; | ||||
|   background-color: #83D01B; | ||||
|   border-radius: 50%; | ||||
|   color: #FFFFFF; | ||||
|   display: block; | ||||
|   height: 1.5rem; | ||||
|   left: 50%; | ||||
|   position: absolute; | ||||
|   text-align: center; | ||||
|   top: 0; | ||||
|   transform: translateX(-38%); | ||||
|   transition: all 0.35s; | ||||
|   width: 1.5rem; | ||||
| } | ||||
| 
 | ||||
| .esh-basketstatus:hover .esh-basketstatus-badge { | ||||
|     background-color: transparent; | ||||
|     color: #75b918; | ||||
|     transition: all 0.35s; | ||||
|   background-color: transparent; | ||||
|   color: #75b918; | ||||
|   transition: all 0.35s; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,41 @@ | ||||
| @import '../../variables'; | ||||
| 
 | ||||
| .esh-basketstatus { | ||||
|     cursor: pointer; | ||||
|     display: inline-block; | ||||
|     float: right; | ||||
|     position: relative; | ||||
|     transition: all $animation-speed-default; | ||||
| 
 | ||||
|     &.is-disabled { | ||||
|         opacity: .5; | ||||
|         pointer-events: none; | ||||
|     } | ||||
| 
 | ||||
|     &-image { | ||||
|         height: 36px; | ||||
|         margin-top: .5rem; | ||||
|     } | ||||
| 
 | ||||
|     &-badge { | ||||
|         $size: 1.5rem; | ||||
|         background-color: $color-secondary; | ||||
|         border-radius: 50%; | ||||
|         color: $color-foreground-brighter; | ||||
|         display: block; | ||||
|         height: $size; | ||||
|         left: 50%; | ||||
|         position: absolute; | ||||
|         text-align: center; | ||||
|         top: 0; | ||||
|         transform: translateX(-38%); | ||||
|         transition: all $animation-speed-default; | ||||
|         width: $size; | ||||
|     } | ||||
| 
 | ||||
|     &:hover &-badge { | ||||
|         background-color: transparent; | ||||
|         color: $color-secondary-dark; | ||||
|         transition: all $animation-speed-default; | ||||
|     } | ||||
| } | ||||
| @ -1,78 +1,79 @@ | ||||
| .esh-basket { | ||||
|     min-height: 80vh; | ||||
| .esh-basket { | ||||
|   min-height: 80vh; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-titles { | ||||
|     padding-bottom: 1rem; | ||||
|     padding-top: 2rem; | ||||
|   padding-bottom: 1rem; | ||||
|   padding-top: 2rem; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-titles--clean { | ||||
|     padding-bottom: 0; | ||||
|     padding-top: 0; | ||||
|   padding-bottom: 0; | ||||
|   padding-top: 0; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-title { | ||||
|     text-transform: uppercase; | ||||
|   text-transform: uppercase; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-items--border { | ||||
|     border-bottom: 1px solid #EEEEEE; | ||||
|     padding: .5rem 0; | ||||
|   border-bottom: 1px solid #EEEEEE; | ||||
|   padding: .5rem 0; | ||||
| } | ||||
| 
 | ||||
|     .esh-basket-items--border:last-of-type { | ||||
|         border-color: transparent; | ||||
|     } | ||||
| .esh-basket-items--border:last-of-type { | ||||
|   border-color: transparent; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-items-margin-left1 { | ||||
|   margin-left: 1px; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-item { | ||||
|     font-size: 1rem; | ||||
|     font-weight: 300; | ||||
|   font-size: 1rem; | ||||
|   font-weight: 300; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-item--middle { | ||||
|     line-height: 8rem; | ||||
|   line-height: 8rem; | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 1024px) { | ||||
|     .esh-basket-item--middle { | ||||
|         line-height: 1rem; | ||||
|     } | ||||
|   .esh-basket-item--middle { | ||||
|     line-height: 1rem; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .esh-basket-item--mark { | ||||
|     color: #00A69C; | ||||
|   color: #00A69C; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-image { | ||||
|     height: 8rem; | ||||
|   height: 8rem; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-input { | ||||
|     line-height: 1rem; | ||||
|     width: 100%; | ||||
|   line-height: 1rem; | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-checkout { | ||||
|     border: none; | ||||
|     border-radius: 0; | ||||
|     background-color: #83D01B; | ||||
|     color: #FFFFFF; | ||||
|     display: inline-block; | ||||
|     font-size: 1rem; | ||||
|     font-weight: 400; | ||||
|     margin-top: 1rem; | ||||
|     padding: 1rem 1.5rem; | ||||
|     text-align: center; | ||||
|     text-transform: uppercase; | ||||
|     transition: all 0.35s; | ||||
|   background-color: #83D01B; | ||||
|   border: 0; | ||||
|   border-radius: 0; | ||||
|   color: #FFFFFF; | ||||
|   display: inline-block; | ||||
|   font-size: 1rem; | ||||
|   font-weight: 400; | ||||
|   margin-top: 1rem; | ||||
|   padding: 1rem 1.5rem; | ||||
|   text-align: center; | ||||
|   text-transform: uppercase; | ||||
|   transition: all 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-checkout:hover { | ||||
|     background-color: #4a760f; | ||||
|     transition: all 0.35s; | ||||
|   background-color: #4a760f; | ||||
|   transition: all 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-basket-margin12{ | ||||
|     margin-left: 12px; | ||||
| } | ||||
|  | ||||
							
								
								
									
										89
									
								
								src/Web/WebMVC/wwwroot/css/basket/basket.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Web/WebMVC/wwwroot/css/basket/basket.component.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | ||||
| @import '../variables'; | ||||
| 
 | ||||
| @mixin margin-left($distance) { | ||||
|     margin-left: $distance; | ||||
| } | ||||
| 
 | ||||
| .esh-basket { | ||||
|     min-height: 80vh; | ||||
| 
 | ||||
|     &-titles { | ||||
|         padding-bottom: 1rem; | ||||
|         padding-top: 2rem; | ||||
| 
 | ||||
|         &--clean { | ||||
|             padding-bottom: 0; | ||||
|             padding-top: 0; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-title { | ||||
|         text-transform: uppercase; | ||||
|     } | ||||
| 
 | ||||
|     &-items { | ||||
|         &--border { | ||||
|             border-bottom: $border-light solid $color-foreground-bright; | ||||
|             padding: .5rem 0; | ||||
| 
 | ||||
|             &:last-of-type { | ||||
|                 border-color: transparent; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         &-margin-left1 { | ||||
|             @include margin-left(1px); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     $item-height: 8rem; | ||||
| 
 | ||||
|     &-item { | ||||
|         font-size: $font-size-m; | ||||
|         font-weight: $font-weight-semilight; | ||||
| 
 | ||||
|         &--middle { | ||||
|             line-height: $item-height; | ||||
| 
 | ||||
|             @media screen and (max-width: $media-screen-m) { | ||||
|                 line-height: $font-size-m; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         &--mark { | ||||
|             color: $color-brand; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-image { | ||||
|         height: $item-height; | ||||
|     } | ||||
| 
 | ||||
|     &-input { | ||||
|         line-height: 1rem; | ||||
|         width: 100%; | ||||
|     } | ||||
| 
 | ||||
|     &-checkout { | ||||
|         background-color: $color-secondary; | ||||
|         border: 0; | ||||
|         border-radius: 0; | ||||
|         color: $color-foreground-brighter; | ||||
|         display: inline-block; | ||||
|         font-size: 1rem; | ||||
|         font-weight: $font-weight-normal; | ||||
|         margin-top: 1rem; | ||||
|         padding: 1rem 1.5rem; | ||||
|         text-align: center; | ||||
|         text-transform: uppercase; | ||||
|         transition: all $animation-speed-default; | ||||
| 
 | ||||
|         &:hover { | ||||
|             background-color: $color-secondary-darker; | ||||
|             transition: all $animation-speed-default; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| @ -1,147 +1,149 @@ | ||||
| .esh-catalog-hero { | ||||
|     background-image: url("../../images/main_banner.png"); | ||||
|     background-size: cover; | ||||
|     height: 260px; | ||||
|     width: 100%; | ||||
| .esh-catalog-hero { | ||||
|   background-image: url("../../images/main_banner.png"); | ||||
|   background-size: cover; | ||||
|   height: 260px; | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-title { | ||||
|     position: relative; | ||||
|     top: 74.28571px; | ||||
|   position: relative; | ||||
|   top: 74.28571px; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-filters { | ||||
|     background-color: #00A69C; | ||||
|     height: 65px; | ||||
|   background-color: #00A69C; | ||||
|   height: 65px; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-filter { | ||||
|     background-color: transparent; | ||||
|     border-color: #00d9cc; | ||||
|     color: #FFFFFF; | ||||
|     cursor: pointer; | ||||
|     margin-right: 1rem; | ||||
|     margin-top: .5rem; | ||||
|     outline-color: #83D01B; | ||||
|     padding-bottom: 0; | ||||
|     padding-left: 0.5rem; | ||||
|     padding-right: 0.5rem; | ||||
|     padding-top: 1.5rem; | ||||
|     min-width: 140px; | ||||
|     -webkit-appearance: none; | ||||
|   -webkit-appearance: none; | ||||
|   background-color: transparent; | ||||
|   border-color: #00d9cc; | ||||
|   color: #FFFFFF; | ||||
|   cursor: pointer; | ||||
|   margin-right: 1rem; | ||||
|   margin-top: .5rem; | ||||
|   min-width: 140px; | ||||
|   outline-color: #83D01B; | ||||
|   padding-bottom: 0; | ||||
|   padding-left: 0.5rem; | ||||
|   padding-right: 0.5rem; | ||||
|   padding-top: 1.5rem; | ||||
| } | ||||
| 
 | ||||
|     .esh-catalog-filter option { | ||||
|         background-color: #00A69C; | ||||
|     } | ||||
| .esh-catalog-filter option { | ||||
|   background-color: #00A69C; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-label { | ||||
|     display: inline-block; | ||||
|     position: relative; | ||||
|     z-index: 0; | ||||
|   display: inline-block; | ||||
|   position: relative; | ||||
|   z-index: 0; | ||||
| } | ||||
| 
 | ||||
|     .esh-catalog-label::before { | ||||
|         color: rgba(255, 255, 255, 0.5); | ||||
|         content: attr(data-title); | ||||
|         font-size: 0.65rem; | ||||
|         margin-top: 0.65rem; | ||||
|         margin-left: 0.5rem; | ||||
|         position: absolute; | ||||
|         text-transform: uppercase; | ||||
|         z-index: 1; | ||||
|     } | ||||
| .esh-catalog-label::before { | ||||
|   color: rgba(255, 255, 255, 0.5); | ||||
|   content: attr(data-title); | ||||
|   font-size: 0.65rem; | ||||
|   margin-left: 0.5rem; | ||||
|   margin-top: 0.65rem; | ||||
|   position: absolute; | ||||
|   text-transform: uppercase; | ||||
|   z-index: 1; | ||||
| } | ||||
| 
 | ||||
|     .esh-catalog-label::after { | ||||
|         background-image: url("../../images/arrow-down.png"); | ||||
|         height: 7px; | ||||
|         content: ''; | ||||
|         position: absolute; | ||||
|         right: 1.5rem; | ||||
|         top: 2.5rem; | ||||
|         width: 10px; | ||||
|         z-index: 1; | ||||
|     } | ||||
| .esh-catalog-label::after { | ||||
|   background-image: url("../../images/arrow-down.png"); | ||||
|   content: ''; | ||||
|   height: 7px; | ||||
|   position: absolute; | ||||
|   right: 1.5rem; | ||||
|   top: 2.5rem; | ||||
|   width: 10px; | ||||
|   z-index: 1; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-send { | ||||
|     background-color: #83D01B; | ||||
|     color: #FFFFFF; | ||||
|     cursor: pointer; | ||||
|     font-size: 1rem; | ||||
|     transform: translateY(.5rem); | ||||
|     padding: 0.5rem; | ||||
|     transition: all 0.35s; | ||||
|   background-color: #83D01B; | ||||
|   color: #FFFFFF; | ||||
|   cursor: pointer; | ||||
|   font-size: 1rem; | ||||
|   margin-top: -1.5rem; | ||||
|   padding: 0.5rem; | ||||
|   transition: all 0.35s; | ||||
| } | ||||
| 
 | ||||
|     .esh-catalog-send:hover { | ||||
|         background-color: #4a760f; | ||||
|         transition: all 0.35s; | ||||
|     } | ||||
| .esh-catalog-send:hover { | ||||
|   background-color: #4a760f; | ||||
|   transition: all 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-items { | ||||
|     margin-top: 1rem; | ||||
|   margin-top: 1rem; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-item { | ||||
|     text-align: center; | ||||
|     margin-bottom: 1.5rem; | ||||
|     width: 33%; | ||||
|     display: inline-block; | ||||
|     float: none !important; | ||||
|   margin-bottom: 1.5rem; | ||||
|   text-align: center; | ||||
|   width: 33%; | ||||
|   display: inline-block; | ||||
|   float: none !important; | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 1024px) { | ||||
|     .esh-catalog-item { | ||||
|         width: 50%; | ||||
|     } | ||||
|   .esh-catalog-item { | ||||
|     width: 50%; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 768px) { | ||||
|     .esh-catalog-item { | ||||
|         width: 100%; | ||||
|     } | ||||
|   .esh-catalog-item { | ||||
|     width: 100%; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-thumbnail { | ||||
|     max-width: 370px; | ||||
|     width: 100%; | ||||
|   max-width: 370px; | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-button { | ||||
|     background-color: #83D01B; | ||||
|     border: none; | ||||
|     color: #FFFFFF; | ||||
|     cursor: pointer; | ||||
|     font-size: 1rem; | ||||
|     height: 3rem; | ||||
|     margin-top: 1rem; | ||||
|     transition: all 0.35s; | ||||
|     width: 80%; | ||||
|   background-color: #83D01B; | ||||
|   border: 0; | ||||
|   color: #FFFFFF; | ||||
|   cursor: pointer; | ||||
|   font-size: 1rem; | ||||
|   height: 3rem; | ||||
|   margin-top: 1rem; | ||||
|   transition: all 0.35s; | ||||
|   width: 80%; | ||||
| } | ||||
|     .esh-catalog-button.is-disabled { | ||||
|         opacity: .5; | ||||
|         pointer-events: none; | ||||
|     } | ||||
| 
 | ||||
|     .esh-catalog-button:hover { | ||||
|         background-color: #4a760f; | ||||
|         transition: all 0.35s; | ||||
|     } | ||||
| .esh-catalog-button.is-disabled { | ||||
|   opacity: .5; | ||||
|   pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-button:hover { | ||||
|   background-color: #4a760f; | ||||
|   transition: all 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-name { | ||||
|     font-size: 1rem; | ||||
|     font-weight: 300; | ||||
|     margin-top: .5rem; | ||||
|     text-align: center; | ||||
|     text-transform: uppercase; | ||||
|   font-size: 1rem; | ||||
|   font-weight: 300; | ||||
|   margin-top: .5rem; | ||||
|   text-align: center; | ||||
|   text-transform: uppercase; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-price { | ||||
|     text-align: center; | ||||
|     font-weight: 900; | ||||
|     font-size: 28px; | ||||
|   font-size: 28px; | ||||
|   font-weight: 900; | ||||
|   text-align: center; | ||||
| } | ||||
| 
 | ||||
| .esh-catalog-price::before { | ||||
|   content: '$'; | ||||
| } | ||||
| 
 | ||||
|     .esh-catalog-price::before { | ||||
|         content: '$'; | ||||
|     } | ||||
|  | ||||
							
								
								
									
										154
									
								
								src/Web/WebMVC/wwwroot/css/catalog/catalog.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								src/Web/WebMVC/wwwroot/css/catalog/catalog.component.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,154 @@ | ||||
| @import '../variables'; | ||||
| 
 | ||||
| .esh-catalog { | ||||
|     $banner-height: 260px; | ||||
| 
 | ||||
|     &-hero { | ||||
|         background-image: url($image-main_banner); | ||||
|         background-size: cover; | ||||
|         height: $banner-height; | ||||
|         width: 100%; | ||||
|     } | ||||
| 
 | ||||
|     &-title { | ||||
|         position: relative; | ||||
|         top: $banner-height / 3.5; | ||||
|     } | ||||
| 
 | ||||
|     $filter-height: 65px; | ||||
| 
 | ||||
|     &-filters { | ||||
|         background-color: $color-brand; | ||||
|         height: $filter-height; | ||||
|     } | ||||
| 
 | ||||
|     $filter-padding: .5rem; | ||||
| 
 | ||||
|     &-filter { | ||||
|         -webkit-appearance: none; | ||||
|         background-color: transparent; | ||||
|         border-color: $color-brand-bright; | ||||
|         color: $color-foreground-brighter; | ||||
|         cursor: pointer; | ||||
|         margin-right: 1rem; | ||||
|         margin-top: .5rem; | ||||
|         min-width: 140px; | ||||
|         outline-color: $color-secondary; | ||||
|         padding-bottom: 0; | ||||
|         padding-left: $filter-padding; | ||||
|         padding-right: $filter-padding; | ||||
|         padding-top: $filter-padding * 3; | ||||
| 
 | ||||
|         option { | ||||
|             background-color: $color-brand; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-label { | ||||
|         display: inline-block; | ||||
|         position: relative; | ||||
|         z-index: 0; | ||||
| 
 | ||||
|         &::before { | ||||
|             color: rgba($color-foreground-brighter, .5); | ||||
|             content: attr(data-title); | ||||
|             font-size: $font-size-xs; | ||||
|             margin-left: $filter-padding; | ||||
|             margin-top: $font-size-xs; | ||||
|             position: absolute; | ||||
|             text-transform: uppercase; | ||||
|             z-index: 1; | ||||
|         } | ||||
| 
 | ||||
|         &::after { | ||||
|             background-image: url($image-arrow_down); | ||||
|             content: ''; | ||||
|             height: 7px; //png height | ||||
|             position: absolute; | ||||
|             right: $filter-padding * 3; | ||||
|             top: $filter-padding * 5; | ||||
|             width: 10px; //png width | ||||
|             z-index: 1; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-send { | ||||
|         background-color: $color-secondary; | ||||
|         color: $color-foreground-brighter; | ||||
|         cursor: pointer; | ||||
|         font-size: $font-size-m; | ||||
|         margin-top: -$filter-padding * 3; | ||||
|         padding: $filter-padding; | ||||
|         transition: all $animation-speed-default; | ||||
| 
 | ||||
|         &:hover { | ||||
|             background-color: $color-secondary-darker; | ||||
|             transition: all $animation-speed-default; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-items { | ||||
|         margin-top: 1rem; | ||||
|     } | ||||
| 
 | ||||
|     &-item { | ||||
|         margin-bottom: 1.5rem; | ||||
|         text-align: center; | ||||
|         width: 33%; | ||||
|         display: inline-block; | ||||
|         float: none !important; | ||||
| 
 | ||||
|         @media screen and (max-width: $media-screen-m) { | ||||
|             width: 50%; | ||||
|         } | ||||
| 
 | ||||
|         @media screen and (max-width: $media-screen-s) { | ||||
|             width: 100%; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-thumbnail { | ||||
|         max-width: 370px; | ||||
|         width: 100%; | ||||
|     } | ||||
| 
 | ||||
|     &-button { | ||||
|         background-color: $color-secondary; | ||||
|         border: 0; | ||||
|         color: $color-foreground-brighter; | ||||
|         cursor: pointer; | ||||
|         font-size: $font-size-m; | ||||
|         height: 3rem; | ||||
|         margin-top: 1rem; | ||||
|         transition: all $animation-speed-default; | ||||
|         width: 80%; | ||||
| 
 | ||||
|         &.is-disabled { | ||||
|             opacity: .5; | ||||
|             pointer-events: none; | ||||
|         } | ||||
| 
 | ||||
|         &:hover { | ||||
|             background-color: $color-secondary-darker; | ||||
|             transition: all $animation-speed-default; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-name { | ||||
|         font-size: $font-size-m; | ||||
|         font-weight: $font-weight-semilight; | ||||
|         margin-top: .5rem; | ||||
|         text-align: center; | ||||
|         text-transform: uppercase; | ||||
|     } | ||||
| 
 | ||||
|     &-price { | ||||
|         font-size: 28px; | ||||
|         font-weight: 900; | ||||
|         text-align: center; | ||||
| 
 | ||||
|         &::before { | ||||
|             content: '$'; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,52 +1,53 @@ | ||||
| .esh-orders_detail { | ||||
|     min-height: 80vh; | ||||
| .esh-orders_detail { | ||||
|   min-height: 80vh; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_detail-section { | ||||
|     padding: 1rem 0; | ||||
|   padding: 1rem 0; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_detail-section--right { | ||||
|     text-align: right; | ||||
|   text-align: right; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_detail-titles { | ||||
|     padding-bottom: 1rem; | ||||
|     padding-top: 2rem; | ||||
|   padding-bottom: 1rem; | ||||
|   padding-top: 2rem; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_detail-title { | ||||
|     text-transform: uppercase; | ||||
|   text-transform: uppercase; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_detail-items--border { | ||||
|     border-bottom: 1px solid #EEEEEE; | ||||
|     padding: .5rem 0; | ||||
|   border-bottom: 1px solid #EEEEEE; | ||||
|   padding: .5rem 0; | ||||
| } | ||||
| 
 | ||||
|     .esh-orders_detail-items--border:last-of-type { | ||||
|         border-color: transparent; | ||||
|     } | ||||
| .esh-orders_detail-items--border:last-of-type { | ||||
|   border-color: transparent; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_detail-item { | ||||
|     font-size: 1rem; | ||||
|     font-weight: 300; | ||||
|   font-size: 1rem; | ||||
|   font-weight: 300; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_detail-item--middle { | ||||
|     line-height: 8rem; | ||||
|   line-height: 8rem; | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 768px) { | ||||
|     .esh-orders_detail-item--middle { | ||||
|         line-height: 1rem; | ||||
|     } | ||||
|   .esh-orders_detail-item--middle { | ||||
|     line-height: 1rem; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .esh-orders_detail-item--mark { | ||||
|     color: #83D01B; | ||||
|   color: #83D01B; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_detail-image { | ||||
|     height: 8rem; | ||||
|   height: 8rem; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,56 @@ | ||||
| @import '../../variables'; | ||||
| 
 | ||||
| .esh-orders_detail { | ||||
|     min-height: 80vh; | ||||
| 
 | ||||
|     &-section { | ||||
|         padding: 1rem 0; | ||||
| 
 | ||||
|         &--right { | ||||
|             text-align: right; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-titles { | ||||
|         padding-bottom: 1rem; | ||||
|         padding-top: 2rem; | ||||
|     } | ||||
| 
 | ||||
|     &-title { | ||||
|         text-transform: uppercase; | ||||
|     } | ||||
| 
 | ||||
|     &-items { | ||||
|         &--border { | ||||
|             border-bottom: $border-light solid $color-foreground-bright; | ||||
|             padding: .5rem 0; | ||||
| 
 | ||||
|             &:last-of-type { | ||||
|                 border-color: transparent; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     $item-height: 8rem; | ||||
| 
 | ||||
|     &-item { | ||||
|         font-size: $font-size-m; | ||||
|         font-weight: $font-weight-semilight; | ||||
| 
 | ||||
|         &--middle { | ||||
|             line-height: $item-height; | ||||
| 
 | ||||
|             @media screen and (max-width: $media-screen-s) { | ||||
|                 line-height: $font-size-m; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         &--mark { | ||||
|             color: $color-secondary; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-image { | ||||
|         height: $item-height; | ||||
|     } | ||||
| } | ||||
| @ -1,91 +1,96 @@ | ||||
| .esh-orders_new { | ||||
|     min-height: 80vh; | ||||
| .esh-orders_new { | ||||
|   min-height: 80vh; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-header { | ||||
|     background-color: #00A69C; | ||||
|     height: 4rem; | ||||
|   background-color: #00A69C; | ||||
|   height: 4rem; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-back { | ||||
|     color: rgba(255, 255, 255, 0.4); | ||||
|     line-height: 4rem; | ||||
|     text-decoration: none; | ||||
|     text-transform: uppercase; | ||||
|     transition: color 0.35s; | ||||
|   color: rgba(255, 255, 255, 0.4); | ||||
|   line-height: 4rem; | ||||
|   text-decoration: none; | ||||
|   text-transform: uppercase; | ||||
|   transition: color 0.35s; | ||||
| } | ||||
| 
 | ||||
|     .esh-orders_new-back:hover { | ||||
|         color: #FFFFFF; | ||||
|         transition: color 0.35s; | ||||
|     } | ||||
| .esh-orders_new-back:hover { | ||||
|   color: #FFFFFF; | ||||
|   transition: color 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-section { | ||||
|     padding: 1rem 0; | ||||
|   padding: 1rem 0; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-section--right { | ||||
|     text-align: right; | ||||
|   text-align: right; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-placeOrder { | ||||
|     background-color: #83D01B; | ||||
|     border: 0; | ||||
|     border-radius: 0; | ||||
|     color: #FFFFFF; | ||||
|     display: inline-block; | ||||
|     font-size: 1rem; | ||||
|     font-weight: 400; | ||||
|     margin-top: 1rem; | ||||
|     padding: 1rem 1.5rem; | ||||
|     text-align: center; | ||||
|     text-transform: uppercase; | ||||
|     transition: all 0.35s; | ||||
|   background-color: #83D01B; | ||||
|   border: 0; | ||||
|   border-radius: 0; | ||||
|   color: #FFFFFF; | ||||
|   display: inline-block; | ||||
|   font-size: 1rem; | ||||
|   font-weight: 400; | ||||
|   margin-top: 1rem; | ||||
|   padding: 1rem 1.5rem; | ||||
|   text-align: center; | ||||
|   text-transform: uppercase; | ||||
|   transition: all 0.35s; | ||||
| } | ||||
| 
 | ||||
|     .esh-orders_new-placeOrder:hover { | ||||
|         background-color: #4a760f; | ||||
|         transition: all 0.35s; | ||||
|     } | ||||
| .esh-orders_new-placeOrder:hover { | ||||
|   background-color: #4a760f; | ||||
|   transition: all 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-titles { | ||||
|     padding-bottom: 1rem; | ||||
|     padding-top: 2rem; | ||||
|   padding-bottom: 1rem; | ||||
|   padding-top: 2rem; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-title { | ||||
|     font-size: 1.25rem; | ||||
|     text-transform: uppercase; | ||||
|   font-size: 1.25rem; | ||||
|   text-transform: uppercase; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-items--border { | ||||
|     border-bottom: 1px solid #EEEEEE; | ||||
|     padding: .5rem 0; | ||||
|   border-bottom: 1px solid #EEEEEE; | ||||
|   padding: .5rem 0; | ||||
| } | ||||
| 
 | ||||
|     .esh-orders_new-items--border:last-of-type { | ||||
|         border-color: transparent; | ||||
|     } | ||||
| .esh-orders_new-items--border:last-of-type { | ||||
|   border-color: transparent; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-item { | ||||
|     font-size: 1rem; | ||||
|     font-weight: 300; | ||||
|   font-size: 1rem; | ||||
|   font-weight: 300; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-item--middle { | ||||
|     line-height: 8rem; | ||||
|   line-height: 8rem; | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 768px) { | ||||
|     .esh-orders_new-item--middle { | ||||
|         line-height: 1rem; | ||||
|     } | ||||
|   .esh-orders_new-item--middle { | ||||
|     line-height: 1rem; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-item--mark { | ||||
|     color: #83D01B; | ||||
|   color: #83D01B; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-image { | ||||
|     height: 8rem; | ||||
|   height: 8rem; | ||||
| } | ||||
| 
 | ||||
| .esh-orders_new-alert { | ||||
|   margin-top: 10px; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,101 @@ | ||||
| @import '../../variables'; | ||||
| 
 | ||||
| .esh-orders_new { | ||||
|     min-height: 80vh; | ||||
| 
 | ||||
|     $header-height: 4rem; | ||||
| 
 | ||||
|     &-header { | ||||
|         background-color: #00A69C; | ||||
|         height: $header-height; | ||||
|     } | ||||
| 
 | ||||
|     &-back { | ||||
|         color: rgba($color-foreground-brighter, .4); | ||||
|         line-height: $header-height; | ||||
|         text-decoration: none; | ||||
|         text-transform: uppercase; | ||||
|         transition: color $animation-speed-default; | ||||
| 
 | ||||
|         &:hover { | ||||
|             color: $color-foreground-brighter; | ||||
|             transition: color $animation-speed-default; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-section { | ||||
|         padding: 1rem 0; | ||||
| 
 | ||||
|         &--right { | ||||
|             text-align: right; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-placeOrder { | ||||
|         background-color: $color-secondary; | ||||
|         border: 0; | ||||
|         border-radius: 0; | ||||
|         color: $color-foreground-brighter; | ||||
|         display: inline-block; | ||||
|         font-size: 1rem; | ||||
|         font-weight: $font-weight-normal; | ||||
|         margin-top: 1rem; | ||||
|         padding: 1rem 1.5rem; | ||||
|         text-align: center; | ||||
|         text-transform: uppercase; | ||||
|         transition: all $animation-speed-default; | ||||
| 
 | ||||
|         &:hover { | ||||
|             background-color: $color-secondary-darker; | ||||
|             transition: all $animation-speed-default; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-titles { | ||||
|         padding-bottom: 1rem; | ||||
|         padding-top: 2rem; | ||||
|     } | ||||
| 
 | ||||
|     &-title { | ||||
|         font-size: $font-size-l; | ||||
|         text-transform: uppercase; | ||||
|     } | ||||
| 
 | ||||
|     &-items { | ||||
|         &--border { | ||||
|             border-bottom: $border-light solid $color-foreground-bright; | ||||
|             padding: .5rem 0; | ||||
| 
 | ||||
|             &:last-of-type { | ||||
|                 border-color: transparent; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     $item-height: 8rem; | ||||
| 
 | ||||
|     &-item { | ||||
|         font-size: $font-size-m; | ||||
|         font-weight: $font-weight-semilight; | ||||
| 
 | ||||
|         &--middle { | ||||
|             line-height: $item-height; | ||||
| 
 | ||||
|             @media screen and (max-width: $media-screen-s) { | ||||
|                 line-height: $font-size-m; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         &--mark { | ||||
|             color: $color-secondary; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-image { | ||||
|         height: $item-height; | ||||
|     } | ||||
| 
 | ||||
|     &-alert { | ||||
|        margin-top: 10px; | ||||
|     } | ||||
| } | ||||
| @ -1,74 +1,75 @@ | ||||
| .esh-orders { | ||||
|     min-height: 80vh; | ||||
|     overflow-x: hidden; | ||||
| .esh-orders { | ||||
|   min-height: 80vh; | ||||
|   overflow-x: hidden; | ||||
| } | ||||
| 
 | ||||
| .esh-orders-header { | ||||
|     background-color: #00A69C; | ||||
|     height: 4rem; | ||||
|   background-color: #00A69C; | ||||
|   height: 4rem; | ||||
| } | ||||
| 
 | ||||
| .esh-orders-back { | ||||
|     color: rgba(255, 255, 255, 0.4); | ||||
|     line-height: 4rem; | ||||
|     text-transform: uppercase; | ||||
|     text-decoration: none; | ||||
|     transition: color 0.35s; | ||||
|   color: rgba(255, 255, 255, 0.4); | ||||
|   line-height: 4rem; | ||||
|   text-decoration: none; | ||||
|   text-transform: uppercase; | ||||
|   transition: color 0.35s; | ||||
| } | ||||
| 
 | ||||
|     .esh-orders-back:hover { | ||||
|         color: #FFFFFF; | ||||
|         transition: color 0.35s; | ||||
|     } | ||||
| .esh-orders-back:hover { | ||||
|   color: #FFFFFF; | ||||
|   transition: color 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-orders-titles { | ||||
|     padding-bottom: 1rem; | ||||
|     padding-top: 2rem; | ||||
|   padding-bottom: 1rem; | ||||
|   padding-top: 2rem; | ||||
| } | ||||
| 
 | ||||
| .esh-orders-title { | ||||
|     text-transform: uppercase; | ||||
|   text-transform: uppercase; | ||||
| } | ||||
| 
 | ||||
| .esh-orders-items { | ||||
|     height: 2rem; | ||||
|     line-height: 2rem; | ||||
|     position: relative; | ||||
|   height: 2rem; | ||||
|   line-height: 2rem; | ||||
|   position: relative; | ||||
| } | ||||
| 
 | ||||
|     .esh-orders-items:nth-of-type(2n + 1):before { | ||||
|         background-color: #EEEEFF; | ||||
|         content: ''; | ||||
|         height: 100%; | ||||
|         left: 0; | ||||
|         margin-left: -100vw; | ||||
|         position: absolute; | ||||
|         top: 0; | ||||
|         width: 200vw; | ||||
|         z-index: -1; | ||||
|     } | ||||
| .esh-orders-items:nth-of-type(2n + 1):before { | ||||
|   background-color: #EEEEFF; | ||||
|   content: ''; | ||||
|   height: 100%; | ||||
|   left: 0; | ||||
|   margin-left: -100vw; | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   width: 200vw; | ||||
|   z-index: -1; | ||||
| } | ||||
| 
 | ||||
| .esh-orders-item { | ||||
|     font-weight: 300; | ||||
|   font-weight: 300; | ||||
| } | ||||
| 
 | ||||
| .esh-orders-item--hover { | ||||
|     opacity: 0; | ||||
|     pointer-events: none; | ||||
|   opacity: 0; | ||||
|   pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .esh-orders-items:hover .esh-orders-item--hover { | ||||
|     opacity: 1; | ||||
|     pointer-events: all; | ||||
|   opacity: 1; | ||||
|   pointer-events: all; | ||||
| } | ||||
| 
 | ||||
| .esh-orders-link { | ||||
|     color: #83D01B; | ||||
|     text-decoration: none; | ||||
|     transition: color 0.35s; | ||||
|   color: #83D01B; | ||||
|   text-decoration: none; | ||||
|   transition: color 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-orders-link:hover { | ||||
|   color: #75b918; | ||||
|   transition: color 0.35s; | ||||
| } | ||||
| 
 | ||||
|     .esh-orders-link:hover { | ||||
|         color: #75b918; | ||||
|         transition: color 0.35s; | ||||
|     } | ||||
|  | ||||
							
								
								
									
										81
									
								
								src/Web/WebMVC/wwwroot/css/orders/orders.component.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/Web/WebMVC/wwwroot/css/orders/orders.component.scss
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | ||||
| @import '../variables'; | ||||
| 
 | ||||
| .esh-orders { | ||||
|     min-height: 80vh; | ||||
|     overflow-x: hidden; | ||||
| 
 | ||||
|     $header-height: 4rem; | ||||
| 
 | ||||
|     &-header { | ||||
|         background-color: #00A69C; | ||||
|         height: $header-height; | ||||
|     } | ||||
| 
 | ||||
|     &-back { | ||||
|         color: rgba($color-foreground-brighter, .4); | ||||
|         line-height: $header-height; | ||||
|         text-decoration: none; | ||||
|         text-transform: uppercase; | ||||
|         transition: color $animation-speed-default; | ||||
| 
 | ||||
|         &:hover { | ||||
|             color: $color-foreground-brighter; | ||||
|             transition: color $animation-speed-default; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-titles { | ||||
|         padding-bottom: 1rem; | ||||
|         padding-top: 2rem; | ||||
|     } | ||||
| 
 | ||||
|     &-title { | ||||
|         text-transform: uppercase; | ||||
|     } | ||||
| 
 | ||||
|     &-items { | ||||
|         $height: 2rem; | ||||
|         height: $height; | ||||
|         line-height: $height; | ||||
|         position: relative; | ||||
| 
 | ||||
|         &:nth-of-type(2n + 1) { | ||||
|             &:before { | ||||
|                 background-color: $color-background-bright; | ||||
|                 content: ''; | ||||
|                 height: 100%; | ||||
|                 left: 0; | ||||
|                 margin-left: -100vw; | ||||
|                 position: absolute; | ||||
|                 top: 0; | ||||
|                 width: 200vw; | ||||
|                 z-index: -1; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-item { | ||||
|         font-weight: $font-weight-semilight; | ||||
| 
 | ||||
|         &--hover { | ||||
|             opacity: 0; | ||||
|             pointer-events: none; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-items:hover &-item--hover { | ||||
|         opacity: 1; | ||||
|         pointer-events: all; | ||||
|     } | ||||
| 
 | ||||
|     &-link { | ||||
|         color: $color-secondary; | ||||
|         text-decoration: none; | ||||
|         transition: color $animation-speed-default; | ||||
| 
 | ||||
|         &:hover { | ||||
|             color: $color-secondary-dark; | ||||
|             transition: color $animation-speed-default; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										6
									
								
								src/Web/WebMVC/wwwroot/css/override.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/Web/WebMVC/wwwroot/css/override.css
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| .esh-catalog-button { | ||||
|     background-color: #FF001b; | ||||
|     border: 2px; | ||||
|     color: #fff; | ||||
|     cursor: pointer; | ||||
| } | ||||
| @ -1,31 +1,18 @@ | ||||
| .esh-header { | ||||
|     background-color: #00A69C; | ||||
|     height: 4rem; | ||||
| .esh-header { | ||||
|   background-color: #00A69C; | ||||
|   height: 4rem; | ||||
| } | ||||
| 
 | ||||
| .esh-header-title { | ||||
|     color: rgba(255, 255, 255, 0.5) !important; | ||||
|     line-height: 4rem; | ||||
|     text-transform: uppercase; | ||||
|     text-decoration: none; | ||||
|     transition: color 0.35s; | ||||
|     margin-right: 15px; | ||||
| } | ||||
| 
 | ||||
|     .esh-header-title:hover { | ||||
|         color: #FFFFFF !important; | ||||
|         transition: color 0.35s; | ||||
|     } | ||||
| 
 | ||||
| .esh-header-back { | ||||
|     color: rgba(255, 255, 255, 0.5) !important; | ||||
|     line-height: 4rem; | ||||
|     text-transform: uppercase; | ||||
|     text-decoration: none; | ||||
|     transition: color 0.35s; | ||||
|   color: rgba(255, 255, 255, 0.5); | ||||
|   line-height: 4rem; | ||||
|   text-decoration: none; | ||||
|   text-transform: uppercase; | ||||
|   transition: color 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-header-back:hover { | ||||
|   color: #FFFFFF; | ||||
|   transition: color 0.35s; | ||||
| } | ||||
| 
 | ||||
|     .esh-header-back:hover { | ||||
|         color: #FFFFFF !important; | ||||
|         transition: color 0.35s; | ||||
|     } | ||||
|  | ||||
| @ -0,0 +1,21 @@ | ||||
| @import '../../../variables'; | ||||
| 
 | ||||
| .esh-header { | ||||
|     $header-height: 4rem; | ||||
| 
 | ||||
|     background-color: $color-brand; | ||||
|     height: $header-height; | ||||
| 
 | ||||
|     &-back { | ||||
|         color: rgba($color-foreground-brighter, .5); | ||||
|         line-height: $header-height; | ||||
|         text-decoration: none; | ||||
|         text-transform: uppercase; | ||||
|         transition: color $animation-speed-default; | ||||
| 
 | ||||
|         &:hover { | ||||
|             color: $color-foreground-brighter; | ||||
|             transition: color $animation-speed-default; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,57 +1,57 @@ | ||||
| .esh-identity { | ||||
|     line-height: 3rem; | ||||
|     position: relative; | ||||
|     text-align: right; | ||||
| .esh-identity { | ||||
|   line-height: 3rem; | ||||
|   position: relative; | ||||
|   text-align: right; | ||||
| } | ||||
| 
 | ||||
| .esh-identity-section { | ||||
|     display: inline-block; | ||||
|     width: 100%; | ||||
|   display: inline-block; | ||||
|   width: 100%; | ||||
| } | ||||
| 
 | ||||
| .esh-identity-name { | ||||
|     display: inline-block; | ||||
|   display: inline-block; | ||||
| } | ||||
| 
 | ||||
| .esh-identity-name--upper { | ||||
|     text-transform: uppercase; | ||||
|   text-transform: uppercase; | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 768px) { | ||||
|     .esh-identity-name { | ||||
|         font-size: 0.85rem; | ||||
|     } | ||||
|   .esh-identity-name { | ||||
|     font-size: 0.85rem; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .esh-identity-image { | ||||
|     display: inline-block; | ||||
|   display: inline-block; | ||||
| } | ||||
| 
 | ||||
| .esh-identity-drop { | ||||
|     background: #FFFFFF; | ||||
|     height: 0; | ||||
|     min-width: 14rem; | ||||
|     right: 0; | ||||
|     overflow: hidden; | ||||
|     padding: .5rem; | ||||
|     position: absolute; | ||||
|     top: 2.5rem; | ||||
|     transition: height 0.35s; | ||||
|   background: #FFFFFF; | ||||
|   height: 0; | ||||
|   min-width: 14rem; | ||||
|   overflow: hidden; | ||||
|   padding: .5rem; | ||||
|   position: absolute; | ||||
|   right: 0; | ||||
|   top: 2.5rem; | ||||
|   transition: height 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-identity:hover .esh-identity-drop { | ||||
|     border: 1px solid #EEEEEE; | ||||
|     height: 7rem; | ||||
|     transition: height 0.35s; | ||||
|   border: 1px solid #EEEEEE; | ||||
|   height: 7rem; | ||||
|   transition: height 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-identity-item { | ||||
|     cursor: pointer; | ||||
|     display: block; | ||||
|     transition: color 0.35s; | ||||
|   cursor: pointer; | ||||
|   transition: color 0.35s; | ||||
| } | ||||
| 
 | ||||
| .esh-identity-item:hover { | ||||
|   color: #75b918; | ||||
|   transition: color 0.35s; | ||||
| } | ||||
| 
 | ||||
|     .esh-identity-item:hover { | ||||
|         color: #75b918; | ||||
|         transition: color 0.35s; | ||||
|     } | ||||
|  | ||||
| @ -0,0 +1,56 @@ | ||||
| @import '../../../variables'; | ||||
| 
 | ||||
| .esh-identity { | ||||
|     line-height: 3rem; | ||||
|     position: relative; | ||||
|     text-align: right; | ||||
| 
 | ||||
|     &-section { | ||||
|         display: inline-block; | ||||
|         width: 100%; | ||||
|     } | ||||
| 
 | ||||
|     &-name { | ||||
|         display: inline-block; | ||||
| 
 | ||||
|         &--upper { | ||||
|             text-transform: uppercase; | ||||
|         } | ||||
| 
 | ||||
|         @media screen and (max-width: $media-screen-s) { | ||||
|             font-size: $font-size-s; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &-image { | ||||
|         display: inline-block; | ||||
|     } | ||||
| 
 | ||||
|     &-drop { | ||||
|         background: $color-background-brighter; | ||||
|         height: 0; | ||||
|         min-width: 14rem; | ||||
|         overflow: hidden; | ||||
|         padding: .5rem; | ||||
|         position: absolute; | ||||
|         right: 0; | ||||
|         top: 2.5rem; | ||||
|         transition: height $animation-speed-default; | ||||
|     } | ||||
| 
 | ||||
|     &:hover &-drop { | ||||
|         border: $border-light solid $color-foreground-bright; | ||||
|         height: 7rem; | ||||
|         transition: height $animation-speed-default; | ||||
|     } | ||||
| 
 | ||||
|     &-item { | ||||
|         cursor: pointer; | ||||
|         transition: color $animation-speed-default; | ||||
| 
 | ||||
|         &:hover { | ||||
|             color: $color-secondary-dark; | ||||
|             transition: color $animation-speed-default; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,34 +1,35 @@ | ||||
| .esh-pager-wrapper { | ||||
|     padding-top: 1rem; | ||||
|     text-align: center; | ||||
| .esh-pager-wrapper { | ||||
|   padding-top: 1rem; | ||||
|   text-align: center; | ||||
| } | ||||
| 
 | ||||
| .esh-pager-item { | ||||
|     margin: 0 5vw; | ||||
|   margin: 0 5vw; | ||||
| } | ||||
| 
 | ||||
| .esh-pager-item.is-disabled { | ||||
|   opacity: 0; | ||||
|   pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| .esh-pager-item--navigable { | ||||
|     display: inline-block; | ||||
|     cursor: pointer; | ||||
|   cursor: pointer; | ||||
|   display: inline-block; | ||||
| } | ||||
| 
 | ||||
|     .esh-pager-item--navigable.is-disabled { | ||||
|         opacity: 0; | ||||
|         pointer-events: none; | ||||
|     } | ||||
| 
 | ||||
|     .esh-pager-item--navigable:hover { | ||||
|         color: #83D01B; | ||||
|     } | ||||
| .esh-pager-item--navigable:hover { | ||||
|   color: #83D01B; | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 1280px) { | ||||
|     .esh-pager-item { | ||||
|         font-size: 0.85rem; | ||||
|     } | ||||
|   .esh-pager-item { | ||||
|     font-size: 0.85rem; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @media screen and (max-width: 1024px) { | ||||
|     .esh-pager-item { | ||||
|         margin: 0 4vw; | ||||
|     } | ||||
|   .esh-pager-item { | ||||
|     margin: 0 2.5vw; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -0,0 +1,36 @@ | ||||
| @import '../../../variables'; | ||||
| 
 | ||||
| .esh-pager { | ||||
| 
 | ||||
|     &-wrapper { | ||||
|         padding-top: 1rem; | ||||
|         text-align: center; | ||||
|     } | ||||
| 
 | ||||
|     &-item { | ||||
|         $margin: 5vw; | ||||
|         margin: 0 $margin; | ||||
| 
 | ||||
|         &.is-disabled { | ||||
|             opacity: 0; | ||||
|             pointer-events: none; | ||||
|         } | ||||
| 
 | ||||
|         &--navigable { | ||||
|             cursor: pointer; | ||||
|             display: inline-block; | ||||
| 
 | ||||
|             &:hover { | ||||
|                 color: $color-secondary; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         @media screen and (max-width: $media-screen-l) { | ||||
|             font-size: $font-size-s; | ||||
|         } | ||||
| 
 | ||||
|         @media screen and (max-width: $media-screen-m) { | ||||
|             margin: 0 $margin / 2; | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										2
									
								
								src/Web/WebMVC/wwwroot/css/site.min.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								src/Web/WebMVC/wwwroot/css/site.min.css
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/Web/WebMVC/wwwroot/images/main_footer_text.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/Web/WebMVC/wwwroot/images/main_footer_text.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.1 KiB | 
| @ -12,5 +12,6 @@ namespace eShopOnContainers.WebSPA | ||||
|         public string OrderingUrl { get; set; } | ||||
|         public string IdentityUrl { get; set; } | ||||
|         public string BasketUrl { get; set; } | ||||
|         public bool UseCustomizationData { get; set; } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								src/Web/WebSPA/Client/assets/images/main_footer_text.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/Web/WebSPA/Client/assets/images/main_footer_text.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.1 KiB | 
| @ -51,3 +51,8 @@ $media-screen-xxxl: 1920px; | ||||
| 
 | ||||
| // Borders | ||||
| $border-light: 1px; | ||||
| 
 | ||||
| // Images | ||||
| $image_path: '../../assets/images/'; | ||||
| $image-main_banner: '#{$image_path}main_banner.png'; | ||||
| $image-arrow_down: '#{$image_path}arrow-down.png'; | ||||
| @ -32,7 +32,7 @@ | ||||
|             </section> | ||||
| 
 | ||||
|             <section class="col-sm-6"> | ||||
|                 <div class="esh-app-footer-text hidden-xs"> e-ShoponContainers. All right reserved </div> | ||||
|                 <img class="esh-app-footer-text hidden-xs" src="assets/images/main_footer_text.png" width="335" height="26" alt="footer text image" /> | ||||
|             </section> | ||||
| 
 | ||||
|         </article> | ||||
|  | ||||
| @ -19,11 +19,5 @@ | ||||
|             width: 230px; | ||||
|         } | ||||
| 
 | ||||
|         &-text { | ||||
|             color: $color-secondary; | ||||
|             line-height: $height; | ||||
|             text-align: right; | ||||
|             width: 100%; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -4,7 +4,7 @@ | ||||
|     $banner-height: 260px; | ||||
| 
 | ||||
|     &-hero { | ||||
|         background-image: url('../../assets/images/main_banner.png'); | ||||
|         background-image: url($image-main_banner); | ||||
|         background-size: cover; | ||||
|         height: $banner-height; | ||||
|         width: 100%; | ||||
| @ -61,7 +61,7 @@ | ||||
|         } | ||||
| 
 | ||||
|         &::after { | ||||
|             background-image: url('../../assets/images/arrow-down.png'); | ||||
|             background-image: url($image-arrow_down); | ||||
|             content: ''; | ||||
|             height: 7px; //png height | ||||
|             position: absolute; | ||||
|  | ||||
							
								
								
									
										73
									
								
								src/Web/WebSPA/Server/Infrastructure/WebContextSeed.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/Web/WebSPA/Server/Infrastructure/WebContextSeed.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| using eShopOnContainers.WebSPA; | ||||
| using Microsoft.AspNetCore.Builder; | ||||
| using Microsoft.AspNetCore.Hosting; | ||||
| using Microsoft.Extensions.DependencyInjection; | ||||
| using Microsoft.Extensions.Logging; | ||||
| using Microsoft.Extensions.Options; | ||||
| using System; | ||||
| using System.IO; | ||||
| using System.IO.Compression; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace WebSPA.Infrastructure | ||||
| { | ||||
|     public class WebContextSeed | ||||
|     { | ||||
|         public static void Seed(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory) | ||||
|         { | ||||
|             var log = loggerFactory.CreateLogger("WebSPA seed"); | ||||
| 
 | ||||
|             var settings = (AppSettings)applicationBuilder | ||||
|                 .ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value; | ||||
| 
 | ||||
|             var useCustomizationData = settings.UseCustomizationData; | ||||
|             var contentRootPath = env.ContentRootPath; | ||||
|             var webroot = env.WebRootPath; | ||||
| 
 | ||||
|             if (useCustomizationData) | ||||
|             { | ||||
|                 GetPreconfiguredImages(contentRootPath, webroot, log); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger log) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip"); | ||||
|                 if (!File.Exists(imagesZipFile)) | ||||
|                 { | ||||
|                     log.LogError($" zip file '{imagesZipFile}' does not exists."); | ||||
|                     return; | ||||
|                 } | ||||
| 
 | ||||
|                 string imagePath = Path.Combine(webroot, "assets", "images"); | ||||
|                 string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray(); | ||||
| 
 | ||||
|                 using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read)) | ||||
|                 { | ||||
|                     foreach (ZipArchiveEntry entry in zip.Entries) | ||||
|                     { | ||||
|                         if (imageFiles.Contains(entry.Name)) | ||||
|                         { | ||||
|                             string destinationFilename = Path.Combine(imagePath, entry.Name); | ||||
|                             if (File.Exists(destinationFilename)) | ||||
|                             { | ||||
|                                 File.Delete(destinationFilename); | ||||
|                             } | ||||
|                             entry.ExtractToFile(destinationFilename); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             log.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'"); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 log.LogError($"Exception in method GetPreconfiguredImages WebSPA. Exception Message={ex.Message}"); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -11,6 +11,7 @@ using Microsoft.Extensions.HealthChecks; | ||||
| using Newtonsoft.Json.Serialization; | ||||
| using eShopOnContainers.WebSPA; | ||||
| using Microsoft.eShopOnContainers.BuildingBlocks; | ||||
| using WebSPA.Infrastructure; | ||||
| 
 | ||||
| namespace eShopConContainers.WebSPA | ||||
| { | ||||
| @ -99,6 +100,9 @@ namespace eShopConContainers.WebSPA | ||||
|             //     await next.Invoke(); | ||||
|             // }); | ||||
| 
 | ||||
|             //Seed Data | ||||
|             WebContextSeed.Seed(app, env, loggerFactory); | ||||
| 
 | ||||
|             app.Use(async (context, next) => | ||||
|             { | ||||
|                 await next(); | ||||
|  | ||||
| @ -16,6 +16,9 @@ | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <Compile Remove="node_modules\**\*;Client\**\*" /> | ||||
|     <Content Include="Setup\images.zip"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|     <Content Update="appsettings.json;"> | ||||
|       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> | ||||
|     </Content> | ||||
|  | ||||
| @ -4,6 +4,7 @@ | ||||
|   "BasketUrl": "http://localhost:5103", | ||||
|   "IdentityUrl": "http://localhost:5105", | ||||
|   "CallBackUrl": "http://localhost:5104/", | ||||
|   "UseCustomizationData": true, | ||||
|   "IsClusterEnv": "False", | ||||
|   "Logging": { | ||||
|     "IncludeScopes": false, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user