data and UI customization enhancements

This commit is contained in:
Shaun Walker 2017-06-20 12:54:32 -07:00
parent d6c6582b51
commit 445101b376
72 changed files with 2207 additions and 446 deletions

View File

@ -29,6 +29,7 @@ services:
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word - 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. - 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 - EventBusConnection=rabbitmq
- UseCustomizationData=True
ports: ports:
- "5101:80" - "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 - 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 - 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. - 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: ports:
- "5105:80" - "5105:80"
@ -50,6 +52,7 @@ services:
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word - 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. - identityUrl=http://identity.api #Local: You need to open your local dev-machine firewall at range 5100-5105. at range 5100-5105.
- EventBusConnection=rabbitmq - EventBusConnection=rabbitmq
- UseCustomizationData=True
ports: ports:
- "5102:80" - "5102:80"
@ -77,6 +80,7 @@ services:
- OrderingUrlHC=http://ordering.api/hc - 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. - 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 - BasketUrlHC=http://basket.api/hc
- UseCustomizationData=True
ports: ports:
- "5104:80" - "5104:80"
@ -88,7 +92,7 @@ services:
- OrderingUrl=http://ordering.api - OrderingUrl=http://ordering.api
- BasketUrl=http://basket.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. - 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: ports:
- "5100:80" - "5100:80"

View File

@ -21,6 +21,11 @@
<Content Include="Pics\**\*;"> <Content Include="Pics\**\*;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content> </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" /> <Compile Include="IntegrationEvents\EventHandling\AnyFutureIntegrationEventHandler.cs.txt" />
<Content Update="web.config;"> <Content Update="web.config;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
@ -50,6 +55,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="1.0.0" />
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -72,6 +78,9 @@
<None Update="Pics\*"> <None Update="Pics\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="Setup\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -5,5 +5,7 @@
public string ExternalCatalogBaseUrl {get;set;} public string ExternalCatalogBaseUrl {get;set;}
public string EventBusConnection { get; set; } public string EventBusConnection { get; set; }
public bool UseCustomizationData { get; set; }
} }
} }

View File

@ -235,7 +235,10 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
items.ForEach(x => 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; return items;

View File

@ -15,16 +15,58 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
_env = env; _env = env;
} }
[HttpGet("{id}")] [HttpGet("{filename}")]
// GET: /<controller>/ // GET: /<controller>/
public IActionResult GetImage(int id) public IActionResult GetImage(string filename)
{ {
var webRoot = _env.WebRootPath; var webRoot = _env.WebRootPath;
var path = Path.Combine(webRoot, id + ".png"); var path = Path.Combine(webRoot, filename);
var buffer = System.IO.File.ReadAllBytes(path); string imageFileExtension = Path.GetExtension(filename);
string mimetype = GetImageMimeTypeFromImageFileExtension(imageFileExtension);
return File(buffer, "image/png");
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;
} }
} }
} }

View File

@ -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; }
}
}
}

View File

@ -2,47 +2,115 @@
{ {
using EntityFrameworkCore; using EntityFrameworkCore;
using Extensions.Logging; using Extensions.Logging;
using global::Catalog.API.Extensions;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Model; using Model;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
public class CatalogContextSeed 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 var context = (CatalogContext)applicationBuilder
.ApplicationServices.GetService(typeof(CatalogContext)); .ApplicationServices.GetService(typeof(CatalogContext));
context.Database.Migrate(); 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()) if (!context.CatalogBrands.Any())
{ {
context.CatalogBrands.AddRange( context.CatalogBrands.AddRange(useCustomizationData
GetPreconfiguredCatalogBrands()); ? GetCatalogBrandsFromFile(contentRootPath, log)
: GetPreconfiguredCatalogBrands()
);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
if (!context.CatalogTypes.Any()) if (!context.CatalogTypes.Any())
{ {
context.CatalogTypes.AddRange( context.CatalogTypes.AddRange(useCustomizationData
GetPreconfiguredCatalogTypes()); ? GetCatalogTypesFromFile(contentRootPath, log)
: GetPreconfiguredCatalogTypes()
);
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
if (!context.CatalogItems.Any()) if (!context.CatalogItems.Any())
{ {
context.CatalogItems.AddRange( context.CatalogItems.AddRange(useCustomizationData
GetPreconfiguredItems()); ? GetCatalogItemsFromFile(contentRootPath, context, log)
: GetPreconfiguredItems()
);
await context.SaveChangesAsync(); 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() static IEnumerable<CatalogBrand> GetPreconfiguredCatalogBrands()
{ {
return new List<CatalogBrand>() return new List<CatalogBrand>()
@ -50,11 +118,52 @@
new CatalogBrand() { Brand = "Azure"}, new CatalogBrand() { Brand = "Azure"},
new CatalogBrand() { Brand = ".NET" }, new CatalogBrand() { Brand = ".NET" },
new CatalogBrand() { Brand = "Visual Studio" }, new CatalogBrand() { Brand = "Visual Studio" },
new CatalogBrand() { Brand = "SQL Server" }, new CatalogBrand() { Brand = "SQL Server" },
new CatalogBrand() { Brand = "Other" } 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() static IEnumerable<CatalogType> GetPreconfiguredCatalogTypes()
{ {
return new List<CatalogType>() 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() static IEnumerable<CatalogItem> GetPreconfiguredItems()
{ {
return new List<CatalogItem>() 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=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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/2", AvailableStock = 100 }, 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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/3", AvailableStock = 100 }, 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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/4", AvailableStock = 100 }, 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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/5", AvailableStock = 100 }, 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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/6", AvailableStock = 100 }, 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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/7", AvailableStock = 100 }, 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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/8", AvailableStock = 100 }, 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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/9", AvailableStock = 100 }, 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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/10", AvailableStock = 100 }, 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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/11", AvailableStock = 100 }, 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 = "http://externalcatalogbaseurltobereplaced/api/v1/pic/12", AvailableStock = 100 } 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);
}
} }
} }

View File

@ -0,0 +1,8 @@
CatalogBrand
Azure
.NET
Visual Studio
SQL Server
Other
CatalogBrandTestOne
CatalogBrandTestTwo
1 CatalogBrand
2 Azure
3 .NET
4 Visual Studio
5 SQL Server
6 Other
7 CatalogBrandTestOne
8 CatalogBrandTestTwo

View 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.

View File

@ -0,0 +1,7 @@
CatalogType
Mug
T-Shirt
Sheet
USB Memory Stick
CatalogTypeTestOne
CatalogTypeTestTwo
1 CatalogType
2 Mug
3 T-Shirt
4 Sheet
5 USB Memory Stick
6 CatalogTypeTestOne
7 CatalogTypeTestTwo

View File

@ -153,7 +153,7 @@
var context = (CatalogContext)app var context = (CatalogContext)app
.ApplicationServices.GetService(typeof(CatalogContext)); .ApplicationServices.GetService(typeof(CatalogContext));
WaitForSqlAvailabilityAsync(context, loggerFactory, app).Wait(); WaitForSqlAvailabilityAsync(context, loggerFactory, app, env).Wait();
ConfigureEventBus(app); ConfigureEventBus(app);
@ -165,13 +165,13 @@
integrationEventLogContext.Database.Migrate(); 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 logger = loggerFactory.CreateLogger(nameof(Startup));
var policy = CreatePolicy(retries, logger, nameof (WaitForSqlAvailabilityAsync)); var policy = CreatePolicy(retries, logger, nameof (WaitForSqlAvailabilityAsync));
await policy.ExecuteAsync(async () => await policy.ExecuteAsync(async () =>
{ {
await CatalogContextSeed.SeedAsync(app, loggerFactory); await CatalogContextSeed.SeedAsync(app, env, loggerFactory);
}); });
} }

View File

@ -1,6 +1,7 @@
{ {
"ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word", "ConnectionString": "Server=tcp:127.0.0.1,5433;Initial Catalog=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word",
"ExternalCatalogBaseUrl": "http://localhost:5101", "ExternalCatalogBaseUrl": "http://localhost:5101",
"UseCustomizationData": true,
"Logging": { "Logging": {
"IncludeScopes": false, "IncludeScopes": false,
"LogLevel": { "LogLevel": {

View File

@ -8,5 +8,6 @@ namespace eShopOnContainers.Identity
public class AppSettings public class AppSettings
{ {
public string MvcClient { get; set; } public string MvcClient { get; set; }
public bool UseCustomizationData { get; set; }
} }
} }

View File

@ -3,13 +3,20 @@
using AspNetCore.Identity; using AspNetCore.Identity;
using EntityFrameworkCore; using EntityFrameworkCore;
using Extensions.Logging; using Extensions.Logging;
using global::eShopOnContainers.Identity;
using global::Identity.API.Data; using global::Identity.API.Data;
using global::Identity.API.Models; using global::Identity.API.Models;
using Identity.API.Extensions;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
public class ApplicationContextSeed public class ApplicationContextSeed
@ -21,20 +28,29 @@
_passwordHasher = passwordHasher; _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; int retryForAvaiability = retry.Value;
try try
{ {
var log = loggerFactory.CreateLogger("application seed");
var context = (ApplicationDbContext)applicationBuilder var context = (ApplicationDbContext)applicationBuilder
.ApplicationServices.GetService(typeof(ApplicationDbContext)); .ApplicationServices.GetService(typeof(ApplicationDbContext));
context.Database.Migrate(); context.Database.Migrate();
var settings = (AppSettings)applicationBuilder
.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value;
var useCustomizationData = settings.UseCustomizationData;
var contentRootPath = env.ContentRootPath;
if (!context.Users.Any()) if (!context.Users.Any())
{ {
context.Users.AddRange( context.Users.AddRange(useCustomizationData
GetDefaultUser()); ? GetUsersFromFile(contentRootPath, log)
: GetDefaultUser());
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
@ -46,14 +62,93 @@
retryForAvaiability++; retryForAvaiability++;
var log = loggerFactory.CreateLogger("catalog seed"); var log = loggerFactory.CreateLogger("catalog seed");
log.LogError(ex.Message); 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() new ApplicationUser()
{ {
CardHolderName = "DemoUser", CardHolderName = "DemoUser",
@ -63,23 +158,46 @@
Country = "U.S.", Country = "U.S.",
Email = "demouser@microsoft.com", Email = "demouser@microsoft.com",
Expiration = "12/20", Expiration = "12/20",
Id = Guid.NewGuid().ToString(), Id = Guid.NewGuid().ToString(),
LastName = "DemoLastName", LastName = "DemoLastName",
Name = "DemoUser", Name = "DemoUser",
PhoneNumber = "1234567890", PhoneNumber = "1234567890",
UserName = "demouser@microsoft.com", UserName = "demouser@microsoft.com",
ZipCode = "98052", ZipCode = "98052",
State = "WA", State = "WA",
Street = "15703 NE 61st Ct", Street = "15703 NE 61st Ct",
SecurityNumber = "535", SecurityNumber = "535",
NormalizedEmail = "DEMOUSER@MICROSOFT.COM", NormalizedEmail = "DEMOUSER@MICROSOFT.COM",
NormalizedUserName = "DEMOUSER@MICROSOFT.COM", NormalizedUserName = "DEMOUSER@MICROSOFT.COM",
SecurityStamp = Guid.NewGuid().ToString("D") SecurityStamp = Guid.NewGuid().ToString("D"),
}; };
user.PasswordHash = _passwordHasher.HashPassword(user, "Pass@word1"); 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;
} }
} }
} }

View File

@ -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; }
}
}
}

View File

@ -8,7 +8,13 @@
<PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback> <PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
<DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Content Include="Setup\**\*;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" /> <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="4.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.2" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="1.1.2" />
@ -70,6 +76,9 @@
<None Update="Dockerfile"> <None Update="Dockerfile">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="Setup\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View 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
1 CardHolderName CardNumber CardType City Country Email Expiration LastName Name PhoneNumber UserName ZipCode State Street SecurityNumber NormalizedEmail NormalizedUserName Password
2 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

View File

@ -153,7 +153,7 @@ namespace eShopOnContainers.Identity
//Seed Data //Seed Data
var hasher = new PasswordHasher<ApplicationUser>(); 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) private async Task InitializeGrantStoreAndConfiguration(IApplicationBuilder app)

View File

@ -6,6 +6,7 @@
"MvcClient": "http://localhost:5100", "MvcClient": "http://localhost:5100",
"SpaClient": "http://localhost:5104", "SpaClient": "http://localhost:5104",
"XamarinCallback": "http://localhost:5105/xamarincallback", "XamarinCallback": "http://localhost:5105/xamarincallback",
"UseCustomizationData": true,
"Logging": { "Logging": {
"IncludeScopes": false, "IncludeScopes": false,
"LogLevel": { "LogLevel": {

View File

@ -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; }
}
}
}

View File

@ -8,40 +8,175 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate; using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.BuyerAggregate;
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.OrderAggregate; 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 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 var context = (OrderingContext)applicationBuilder
.ApplicationServices.GetService(typeof(OrderingContext)); .ApplicationServices.GetService(typeof(OrderingContext));
var settings = applicationBuilder
.ApplicationServices.GetRequiredService<IOptions<OrderingSettings>>().Value;
var useCustomizationData = settings.UseCustomizationData;
var contentRootPath = env.ContentRootPath;
using (context) using (context)
{ {
context.Database.Migrate(); context.Database.Migrate();
if (!context.CardTypes.Any()) if (!context.CardTypes.Any())
{ {
context.CardTypes.Add(CardType.Amex); context.CardTypes.AddRange(useCustomizationData
context.CardTypes.Add(CardType.Visa); ? GetCardTypesFromFile(contentRootPath, log)
context.CardTypes.Add(CardType.MasterCard); : GetPredefinedCardTypes());
await context.SaveChangesAsync(); await context.SaveChangesAsync();
} }
if (!context.OrderStatus.Any()) if (!context.OrderStatus.Any())
{ {
context.OrderStatus.Add(OrderStatus.Submitted); context.OrderStatus.AddRange(useCustomizationData
context.OrderStatus.Add(OrderStatus.AwaitingValidation); ? GetOrderStatusFromFile(contentRootPath, log)
context.OrderStatus.Add(OrderStatus.StockConfirmed); : GetPredefinedOrderStatus());
context.OrderStatus.Add(OrderStatus.Paid);
context.OrderStatus.Add(OrderStatus.Shipped);
context.OrderStatus.Add(OrderStatus.Cancelled);
} }
await context.SaveChangesAsync(); 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;
}
} }
} }

View File

@ -16,6 +16,9 @@
<Content Include=".dockerignore;"> <Content Include=".dockerignore;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content> </Content>
<Content Include="Setup\**\*;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -73,6 +76,9 @@
<None Update="Dockerfile"> <None Update="Dockerfile">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
<None Update="Setup\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -0,0 +1,7 @@
namespace Microsoft.eShopOnContainers.Services.Ordering.API
{
public class OrderingSettings
{
public bool UseCustomizationData { get; set; }
}
}

View File

@ -0,0 +1,5 @@
CardType
Amex
Visa
MasterCard
Capital One
1 CardType
2 Amex
3 Visa
4 MasterCard
5 Capital One

View File

@ -0,0 +1,7 @@
OrderStatus
Submitted
AwaitingValidation
StockConfirmed
Paid
Shipped
Cancelled
1 OrderStatus
2 Submitted
3 AwaitingValidation
4 StockConfirmed
5 Paid
6 Shipped
7 Cancelled

View File

@ -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) 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 => services.AddSwaggerGen(options =>
{ {
options.DescribeAllEnumsAsStrings(); options.DescribeAllEnumsAsStrings();
@ -159,7 +161,7 @@
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"); c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
}); });
WaitForSqlAvailabilityAsync(loggerFactory, app).Wait(); WaitForSqlAvailabilityAsync(loggerFactory, app, env).Wait();
ConfigureEventBus(app); ConfigureEventBus(app);
var integrationEventLogContext = new IntegrationEventLogContext( 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 logger = loggerFactory.CreateLogger(nameof(Startup));
var policy = CreatePolicy(retries, logger, nameof(WaitForSqlAvailabilityAsync)); var policy = CreatePolicy(retries, logger, nameof(WaitForSqlAvailabilityAsync));
await policy.ExecuteAsync(async () => await policy.ExecuteAsync(async () =>
{ {
await OrderingContextSeed.SeedAsync(app); await OrderingContextSeed.SeedAsync(app, env, loggerFactory);
}); });
} }

View File

@ -1,6 +1,7 @@
{ {
"ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;", "ConnectionString": "Server=tcp:127.0.0.1,5433;Database=Microsoft.eShopOnContainers.Services.OrderingDb;User Id=sa;Password=Pass@word;",
"IdentityUrl": "http://localhost:5105", "IdentityUrl": "http://localhost:5105",
"UseCustomizationData": true,
"Logging": { "Logging": {
"IncludeScopes": false, "IncludeScopes": false,
"LogLevel": { "LogLevel": {

View File

@ -12,6 +12,7 @@ namespace Microsoft.eShopOnContainers.WebMVC
public string OrderingUrl { get; set; } public string OrderingUrl { get; set; }
public string BasketUrl { get; set; } public string BasketUrl { get; set; }
public Logging Logging { get; set; } public Logging Logging { get; set; }
public bool UseCustomizationData { get; set; }
} }
public class Connectionstrings public class Connectionstrings

View 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}");
}
}
}
}

Binary file not shown.

View File

@ -0,0 +1,6 @@
.esh-catalog-button {
background-color: #FF001b;
border: 2px;
color: #fff;
cursor: pointer;
}

View File

@ -12,6 +12,7 @@ using Microsoft.Extensions.HealthChecks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System; using System;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using WebMVC.Infrastructure;
namespace Microsoft.eShopOnContainers.WebMVC namespace Microsoft.eShopOnContainers.WebMVC
{ {
@ -128,6 +129,9 @@ namespace Microsoft.eShopOnContainers.WebMVC
Scope = { "openid", "profile", "orders", "basket" } Scope = { "openid", "profile", "orders", "basket" }
}; };
//Seed Data
WebContextSeed.Seed(app, env, loggerFactory);
//Wait untill identity service is ready on compose. //Wait untill identity service is ready on compose.
app.UseOpenIdConnectAuthentication(oidcOptions); app.UseOpenIdConnectAuthentication(oidcOptions);

View File

@ -18,12 +18,14 @@
<link rel="stylesheet" href="~/css/orders/orders.component.css" /> <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-detail/orders-detail.component.css" />
<link rel="stylesheet" href="~/css/orders/orders-new/orders-new.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>
<environment names="Staging,Production"> <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" <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-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> 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/site.min.css" asp-append-version="true" />
<link rel="stylesheet" href="~/css/override.css" type="text/css" />
</environment> </environment>
</head> </head>
<body> <body>
@ -55,7 +57,7 @@
</section> </section>
<section class="col-sm-6"> <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> </section>
</article> </article>

View File

@ -9,6 +9,25 @@
<DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath> <DockerComposeProjectPath>..\..\..\docker-compose.dcproj</DockerComposeProjectPath>
</PropertyGroup> </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> <!--<ItemGroup>
<Compile Remove="wwwroot\lib\bootstrap\**" /> <Compile Remove="wwwroot\lib\bootstrap\**" />
<Content Remove="wwwroot\lib\bootstrap\**" /> <Content Remove="wwwroot\lib\bootstrap\**" />

View File

@ -6,6 +6,7 @@
"CallBackUrl": "http://localhost:5100/", "CallBackUrl": "http://localhost:5100/",
"IsClusterEnv": "False", "IsClusterEnv": "False",
"UseResilientHttp": "True", "UseResilientHttp": "True",
"UseCustomizationData": true,
"Logging": { "Logging": {
"IncludeScopes": false, "IncludeScopes": false,
"LogLevel": { "LogLevel": {

View 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"
}
]

View 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
}
}
}

View 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';

View File

@ -1,20 +1,14 @@
.esh-app-footer { .esh-app-footer {
background-color: #000000; background-color: #000000;
border-top: 1px solid #EEEEEE; border-top: 1px solid #EEEEEE;
margin-top: 2.5rem; margin-top: 2.5rem;
padding-bottom: 2.5rem; padding-bottom: 2.5rem;
padding-top: 2.5rem; padding-top: 2.5rem;
width: 100%; width: 100%;
} }
.esh-app-footer-brand { .esh-app-footer-brand {
height: 50px; height: 50px;
width: 230px; width: 230px;
} }
.esh-app-footer-text {
color: #83D01B;
line-height: 50px;
text-align: right;
width: 100%;
}

View 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;
}
}
}

View File

@ -1,38 +1,39 @@
.esh-basketstatus { .esh-basketstatus {
cursor: pointer; cursor: pointer;
display: inline-block; display: inline-block;
float: right; float: right;
position: relative; position: relative;
transition: all 0.35s; transition: all 0.35s;
} }
.esh-basketstatus.is-disabled { .esh-basketstatus.is-disabled {
opacity: .5; opacity: .5;
pointer-events: none; pointer-events: none;
} }
.esh-basketstatus-image { .esh-basketstatus-image {
height: 36px; height: 36px;
margin-top: .5rem; margin-top: .5rem;
} }
.esh-basketstatus-badge { .esh-basketstatus-badge {
background-color: #83D01B; background-color: #83D01B;
border-radius: 50%; border-radius: 50%;
color: #FFFFFF; color: #FFFFFF;
display: block; display: block;
height: 1.5rem; height: 1.5rem;
left: 50%; left: 50%;
position: absolute; position: absolute;
text-align: center; text-align: center;
top: 0; top: 0;
transform: translateX(-38%); transform: translateX(-38%);
transition: all 0.35s; transition: all 0.35s;
width: 1.5rem; width: 1.5rem;
} }
.esh-basketstatus:hover .esh-basketstatus-badge { .esh-basketstatus:hover .esh-basketstatus-badge {
background-color: transparent; background-color: transparent;
color: #75b918; color: #75b918;
transition: all 0.35s; transition: all 0.35s;
} }

View File

@ -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;
}
}

View File

@ -1,78 +1,79 @@
.esh-basket { .esh-basket {
min-height: 80vh; min-height: 80vh;
} }
.esh-basket-titles { .esh-basket-titles {
padding-bottom: 1rem; padding-bottom: 1rem;
padding-top: 2rem; padding-top: 2rem;
} }
.esh-basket-titles--clean { .esh-basket-titles--clean {
padding-bottom: 0; padding-bottom: 0;
padding-top: 0; padding-top: 0;
} }
.esh-basket-title { .esh-basket-title {
text-transform: uppercase; text-transform: uppercase;
} }
.esh-basket-items--border { .esh-basket-items--border {
border-bottom: 1px solid #EEEEEE; border-bottom: 1px solid #EEEEEE;
padding: .5rem 0; padding: .5rem 0;
} }
.esh-basket-items--border:last-of-type { .esh-basket-items--border:last-of-type {
border-color: transparent; border-color: transparent;
} }
.esh-basket-items-margin-left1 {
margin-left: 1px;
}
.esh-basket-item { .esh-basket-item {
font-size: 1rem; font-size: 1rem;
font-weight: 300; font-weight: 300;
} }
.esh-basket-item--middle { .esh-basket-item--middle {
line-height: 8rem; line-height: 8rem;
} }
@media screen and (max-width: 1024px) { @media screen and (max-width: 1024px) {
.esh-basket-item--middle { .esh-basket-item--middle {
line-height: 1rem; line-height: 1rem;
} }
} }
.esh-basket-item--mark { .esh-basket-item--mark {
color: #00A69C; color: #00A69C;
} }
.esh-basket-image { .esh-basket-image {
height: 8rem; height: 8rem;
} }
.esh-basket-input { .esh-basket-input {
line-height: 1rem; line-height: 1rem;
width: 100%; width: 100%;
} }
.esh-basket-checkout { .esh-basket-checkout {
border: none; background-color: #83D01B;
border-radius: 0; border: 0;
background-color: #83D01B; border-radius: 0;
color: #FFFFFF; color: #FFFFFF;
display: inline-block; display: inline-block;
font-size: 1rem; font-size: 1rem;
font-weight: 400; font-weight: 400;
margin-top: 1rem; margin-top: 1rem;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
text-align: center; text-align: center;
text-transform: uppercase; text-transform: uppercase;
transition: all 0.35s; transition: all 0.35s;
} }
.esh-basket-checkout:hover { .esh-basket-checkout:hover {
background-color: #4a760f; background-color: #4a760f;
transition: all 0.35s; transition: all 0.35s;
} }
.esh-basket-margin12{
margin-left: 12px;
}

View 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;
}
}
}

View File

@ -1,147 +1,149 @@
.esh-catalog-hero { .esh-catalog-hero {
background-image: url("../../images/main_banner.png"); background-image: url("../../images/main_banner.png");
background-size: cover; background-size: cover;
height: 260px; height: 260px;
width: 100%; width: 100%;
} }
.esh-catalog-title { .esh-catalog-title {
position: relative; position: relative;
top: 74.28571px; top: 74.28571px;
} }
.esh-catalog-filters { .esh-catalog-filters {
background-color: #00A69C; background-color: #00A69C;
height: 65px; height: 65px;
} }
.esh-catalog-filter { .esh-catalog-filter {
background-color: transparent; -webkit-appearance: none;
border-color: #00d9cc; background-color: transparent;
color: #FFFFFF; border-color: #00d9cc;
cursor: pointer; color: #FFFFFF;
margin-right: 1rem; cursor: pointer;
margin-top: .5rem; margin-right: 1rem;
outline-color: #83D01B; margin-top: .5rem;
padding-bottom: 0; min-width: 140px;
padding-left: 0.5rem; outline-color: #83D01B;
padding-right: 0.5rem; padding-bottom: 0;
padding-top: 1.5rem; padding-left: 0.5rem;
min-width: 140px; padding-right: 0.5rem;
-webkit-appearance: none; padding-top: 1.5rem;
} }
.esh-catalog-filter option { .esh-catalog-filter option {
background-color: #00A69C; background-color: #00A69C;
} }
.esh-catalog-label { .esh-catalog-label {
display: inline-block; display: inline-block;
position: relative; position: relative;
z-index: 0; z-index: 0;
} }
.esh-catalog-label::before { .esh-catalog-label::before {
color: rgba(255, 255, 255, 0.5); color: rgba(255, 255, 255, 0.5);
content: attr(data-title); content: attr(data-title);
font-size: 0.65rem; font-size: 0.65rem;
margin-top: 0.65rem; margin-left: 0.5rem;
margin-left: 0.5rem; margin-top: 0.65rem;
position: absolute; position: absolute;
text-transform: uppercase; text-transform: uppercase;
z-index: 1; z-index: 1;
} }
.esh-catalog-label::after { .esh-catalog-label::after {
background-image: url("../../images/arrow-down.png"); background-image: url("../../images/arrow-down.png");
height: 7px; content: '';
content: ''; height: 7px;
position: absolute; position: absolute;
right: 1.5rem; right: 1.5rem;
top: 2.5rem; top: 2.5rem;
width: 10px; width: 10px;
z-index: 1; z-index: 1;
} }
.esh-catalog-send { .esh-catalog-send {
background-color: #83D01B; background-color: #83D01B;
color: #FFFFFF; color: #FFFFFF;
cursor: pointer; cursor: pointer;
font-size: 1rem; font-size: 1rem;
transform: translateY(.5rem); margin-top: -1.5rem;
padding: 0.5rem; padding: 0.5rem;
transition: all 0.35s; transition: all 0.35s;
} }
.esh-catalog-send:hover { .esh-catalog-send:hover {
background-color: #4a760f; background-color: #4a760f;
transition: all 0.35s; transition: all 0.35s;
} }
.esh-catalog-items { .esh-catalog-items {
margin-top: 1rem; margin-top: 1rem;
} }
.esh-catalog-item { .esh-catalog-item {
text-align: center; margin-bottom: 1.5rem;
margin-bottom: 1.5rem; text-align: center;
width: 33%; width: 33%;
display: inline-block; display: inline-block;
float: none !important; float: none !important;
} }
@media screen and (max-width: 1024px) { @media screen and (max-width: 1024px) {
.esh-catalog-item { .esh-catalog-item {
width: 50%; width: 50%;
} }
} }
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
.esh-catalog-item { .esh-catalog-item {
width: 100%; width: 100%;
} }
} }
.esh-catalog-thumbnail { .esh-catalog-thumbnail {
max-width: 370px; max-width: 370px;
width: 100%; width: 100%;
} }
.esh-catalog-button { .esh-catalog-button {
background-color: #83D01B; background-color: #83D01B;
border: none; border: 0;
color: #FFFFFF; color: #FFFFFF;
cursor: pointer; cursor: pointer;
font-size: 1rem; font-size: 1rem;
height: 3rem; height: 3rem;
margin-top: 1rem; margin-top: 1rem;
transition: all 0.35s; transition: all 0.35s;
width: 80%; width: 80%;
} }
.esh-catalog-button.is-disabled {
opacity: .5;
pointer-events: none;
}
.esh-catalog-button:hover { .esh-catalog-button.is-disabled {
background-color: #4a760f; opacity: .5;
transition: all 0.35s; pointer-events: none;
} }
.esh-catalog-button:hover {
background-color: #4a760f;
transition: all 0.35s;
}
.esh-catalog-name { .esh-catalog-name {
font-size: 1rem; font-size: 1rem;
font-weight: 300; font-weight: 300;
margin-top: .5rem; margin-top: .5rem;
text-align: center; text-align: center;
text-transform: uppercase; text-transform: uppercase;
} }
.esh-catalog-price { .esh-catalog-price {
text-align: center; font-size: 28px;
font-weight: 900; font-weight: 900;
font-size: 28px; text-align: center;
}
.esh-catalog-price::before {
content: '$';
} }
.esh-catalog-price::before {
content: '$';
}

View 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: '$';
}
}
}

View File

@ -1,52 +1,53 @@
.esh-orders_detail { .esh-orders_detail {
min-height: 80vh; min-height: 80vh;
} }
.esh-orders_detail-section { .esh-orders_detail-section {
padding: 1rem 0; padding: 1rem 0;
} }
.esh-orders_detail-section--right { .esh-orders_detail-section--right {
text-align: right; text-align: right;
} }
.esh-orders_detail-titles { .esh-orders_detail-titles {
padding-bottom: 1rem; padding-bottom: 1rem;
padding-top: 2rem; padding-top: 2rem;
} }
.esh-orders_detail-title { .esh-orders_detail-title {
text-transform: uppercase; text-transform: uppercase;
} }
.esh-orders_detail-items--border { .esh-orders_detail-items--border {
border-bottom: 1px solid #EEEEEE; border-bottom: 1px solid #EEEEEE;
padding: .5rem 0; padding: .5rem 0;
} }
.esh-orders_detail-items--border:last-of-type { .esh-orders_detail-items--border:last-of-type {
border-color: transparent; border-color: transparent;
} }
.esh-orders_detail-item { .esh-orders_detail-item {
font-size: 1rem; font-size: 1rem;
font-weight: 300; font-weight: 300;
} }
.esh-orders_detail-item--middle { .esh-orders_detail-item--middle {
line-height: 8rem; line-height: 8rem;
} }
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
.esh-orders_detail-item--middle { .esh-orders_detail-item--middle {
line-height: 1rem; line-height: 1rem;
} }
} }
.esh-orders_detail-item--mark { .esh-orders_detail-item--mark {
color: #83D01B; color: #83D01B;
} }
.esh-orders_detail-image { .esh-orders_detail-image {
height: 8rem; height: 8rem;
} }

View File

@ -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;
}
}

View File

@ -1,91 +1,96 @@
.esh-orders_new { .esh-orders_new {
min-height: 80vh; min-height: 80vh;
} }
.esh-orders_new-header { .esh-orders_new-header {
background-color: #00A69C; background-color: #00A69C;
height: 4rem; height: 4rem;
} }
.esh-orders_new-back { .esh-orders_new-back {
color: rgba(255, 255, 255, 0.4); color: rgba(255, 255, 255, 0.4);
line-height: 4rem; line-height: 4rem;
text-decoration: none; text-decoration: none;
text-transform: uppercase; text-transform: uppercase;
transition: color 0.35s; transition: color 0.35s;
} }
.esh-orders_new-back:hover { .esh-orders_new-back:hover {
color: #FFFFFF; color: #FFFFFF;
transition: color 0.35s; transition: color 0.35s;
} }
.esh-orders_new-section { .esh-orders_new-section {
padding: 1rem 0; padding: 1rem 0;
} }
.esh-orders_new-section--right { .esh-orders_new-section--right {
text-align: right; text-align: right;
} }
.esh-orders_new-placeOrder { .esh-orders_new-placeOrder {
background-color: #83D01B; background-color: #83D01B;
border: 0; border: 0;
border-radius: 0; border-radius: 0;
color: #FFFFFF; color: #FFFFFF;
display: inline-block; display: inline-block;
font-size: 1rem; font-size: 1rem;
font-weight: 400; font-weight: 400;
margin-top: 1rem; margin-top: 1rem;
padding: 1rem 1.5rem; padding: 1rem 1.5rem;
text-align: center; text-align: center;
text-transform: uppercase; text-transform: uppercase;
transition: all 0.35s; transition: all 0.35s;
} }
.esh-orders_new-placeOrder:hover { .esh-orders_new-placeOrder:hover {
background-color: #4a760f; background-color: #4a760f;
transition: all 0.35s; transition: all 0.35s;
} }
.esh-orders_new-titles { .esh-orders_new-titles {
padding-bottom: 1rem; padding-bottom: 1rem;
padding-top: 2rem; padding-top: 2rem;
} }
.esh-orders_new-title { .esh-orders_new-title {
font-size: 1.25rem; font-size: 1.25rem;
text-transform: uppercase; text-transform: uppercase;
} }
.esh-orders_new-items--border { .esh-orders_new-items--border {
border-bottom: 1px solid #EEEEEE; border-bottom: 1px solid #EEEEEE;
padding: .5rem 0; padding: .5rem 0;
} }
.esh-orders_new-items--border:last-of-type { .esh-orders_new-items--border:last-of-type {
border-color: transparent; border-color: transparent;
} }
.esh-orders_new-item { .esh-orders_new-item {
font-size: 1rem; font-size: 1rem;
font-weight: 300; font-weight: 300;
} }
.esh-orders_new-item--middle { .esh-orders_new-item--middle {
line-height: 8rem; line-height: 8rem;
} }
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
.esh-orders_new-item--middle { .esh-orders_new-item--middle {
line-height: 1rem; line-height: 1rem;
} }
} }
.esh-orders_new-item--mark { .esh-orders_new-item--mark {
color: #83D01B; color: #83D01B;
} }
.esh-orders_new-image { .esh-orders_new-image {
height: 8rem; height: 8rem;
} }
.esh-orders_new-alert {
margin-top: 10px;
}

View File

@ -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;
}
}

View File

@ -1,74 +1,75 @@
.esh-orders { .esh-orders {
min-height: 80vh; min-height: 80vh;
overflow-x: hidden; overflow-x: hidden;
} }
.esh-orders-header { .esh-orders-header {
background-color: #00A69C; background-color: #00A69C;
height: 4rem; height: 4rem;
} }
.esh-orders-back { .esh-orders-back {
color: rgba(255, 255, 255, 0.4); color: rgba(255, 255, 255, 0.4);
line-height: 4rem; line-height: 4rem;
text-transform: uppercase; text-decoration: none;
text-decoration: none; text-transform: uppercase;
transition: color 0.35s; transition: color 0.35s;
} }
.esh-orders-back:hover { .esh-orders-back:hover {
color: #FFFFFF; color: #FFFFFF;
transition: color 0.35s; transition: color 0.35s;
} }
.esh-orders-titles { .esh-orders-titles {
padding-bottom: 1rem; padding-bottom: 1rem;
padding-top: 2rem; padding-top: 2rem;
} }
.esh-orders-title { .esh-orders-title {
text-transform: uppercase; text-transform: uppercase;
} }
.esh-orders-items { .esh-orders-items {
height: 2rem; height: 2rem;
line-height: 2rem; line-height: 2rem;
position: relative; position: relative;
} }
.esh-orders-items:nth-of-type(2n + 1):before { .esh-orders-items:nth-of-type(2n + 1):before {
background-color: #EEEEFF; background-color: #EEEEFF;
content: ''; content: '';
height: 100%; height: 100%;
left: 0; left: 0;
margin-left: -100vw; margin-left: -100vw;
position: absolute; position: absolute;
top: 0; top: 0;
width: 200vw; width: 200vw;
z-index: -1; z-index: -1;
} }
.esh-orders-item { .esh-orders-item {
font-weight: 300; font-weight: 300;
} }
.esh-orders-item--hover { .esh-orders-item--hover {
opacity: 0; opacity: 0;
pointer-events: none; pointer-events: none;
} }
.esh-orders-items:hover .esh-orders-item--hover { .esh-orders-items:hover .esh-orders-item--hover {
opacity: 1; opacity: 1;
pointer-events: all; pointer-events: all;
} }
.esh-orders-link { .esh-orders-link {
color: #83D01B; color: #83D01B;
text-decoration: none; text-decoration: none;
transition: color 0.35s; transition: color 0.35s;
}
.esh-orders-link:hover {
color: #75b918;
transition: color 0.35s;
} }
.esh-orders-link:hover {
color: #75b918;
transition: color 0.35s;
}

View 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;
}
}
}

View File

@ -0,0 +1,6 @@
.esh-catalog-button {
background-color: #FF001b;
border: 2px;
color: #fff;
cursor: pointer;
}

View File

@ -1,31 +1,18 @@
.esh-header { .esh-header {
background-color: #00A69C; background-color: #00A69C;
height: 4rem; 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 { .esh-header-back {
color: rgba(255, 255, 255, 0.5) !important; color: rgba(255, 255, 255, 0.5);
line-height: 4rem; line-height: 4rem;
text-transform: uppercase; text-decoration: none;
text-decoration: none; text-transform: uppercase;
transition: color 0.35s; 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;
}

View File

@ -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;
}
}
}

View File

@ -1,57 +1,57 @@
.esh-identity { .esh-identity {
line-height: 3rem; line-height: 3rem;
position: relative; position: relative;
text-align: right; text-align: right;
} }
.esh-identity-section { .esh-identity-section {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
} }
.esh-identity-name { .esh-identity-name {
display: inline-block; display: inline-block;
} }
.esh-identity-name--upper { .esh-identity-name--upper {
text-transform: uppercase; text-transform: uppercase;
} }
@media screen and (max-width: 768px) { @media screen and (max-width: 768px) {
.esh-identity-name { .esh-identity-name {
font-size: 0.85rem; font-size: 0.85rem;
} }
} }
.esh-identity-image { .esh-identity-image {
display: inline-block; display: inline-block;
} }
.esh-identity-drop { .esh-identity-drop {
background: #FFFFFF; background: #FFFFFF;
height: 0; height: 0;
min-width: 14rem; min-width: 14rem;
right: 0; overflow: hidden;
overflow: hidden; padding: .5rem;
padding: .5rem; position: absolute;
position: absolute; right: 0;
top: 2.5rem; top: 2.5rem;
transition: height 0.35s; transition: height 0.35s;
} }
.esh-identity:hover .esh-identity-drop { .esh-identity:hover .esh-identity-drop {
border: 1px solid #EEEEEE; border: 1px solid #EEEEEE;
height: 7rem; height: 7rem;
transition: height 0.35s; transition: height 0.35s;
} }
.esh-identity-item { .esh-identity-item {
cursor: pointer; cursor: pointer;
display: block; transition: color 0.35s;
transition: color 0.35s; }
.esh-identity-item:hover {
color: #75b918;
transition: color 0.35s;
} }
.esh-identity-item:hover {
color: #75b918;
transition: color 0.35s;
}

View File

@ -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;
}
}
}

View File

@ -1,34 +1,35 @@
.esh-pager-wrapper { .esh-pager-wrapper {
padding-top: 1rem; padding-top: 1rem;
text-align: center; text-align: center;
} }
.esh-pager-item { .esh-pager-item {
margin: 0 5vw; margin: 0 5vw;
}
.esh-pager-item.is-disabled {
opacity: 0;
pointer-events: none;
} }
.esh-pager-item--navigable { .esh-pager-item--navigable {
display: inline-block; cursor: pointer;
cursor: pointer; display: inline-block;
} }
.esh-pager-item--navigable.is-disabled { .esh-pager-item--navigable:hover {
opacity: 0; color: #83D01B;
pointer-events: none; }
}
.esh-pager-item--navigable:hover {
color: #83D01B;
}
@media screen and (max-width: 1280px) { @media screen and (max-width: 1280px) {
.esh-pager-item { .esh-pager-item {
font-size: 0.85rem; font-size: 0.85rem;
} }
} }
@media screen and (max-width: 1024px) { @media screen and (max-width: 1024px) {
.esh-pager-item { .esh-pager-item {
margin: 0 4vw; margin: 0 2.5vw;
} }
} }

View File

@ -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;
}
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -12,5 +12,6 @@ namespace eShopOnContainers.WebSPA
public string OrderingUrl { get; set; } public string OrderingUrl { get; set; }
public string IdentityUrl { get; set; } public string IdentityUrl { get; set; }
public string BasketUrl { get; set; } public string BasketUrl { get; set; }
public bool UseCustomizationData { get; set; }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -51,3 +51,8 @@ $media-screen-xxxl: 1920px;
// Borders // Borders
$border-light: 1px; $border-light: 1px;
// Images
$image_path: '../../assets/images/';
$image-main_banner: '#{$image_path}main_banner.png';
$image-arrow_down: '#{$image_path}arrow-down.png';

View File

@ -32,7 +32,7 @@
</section> </section>
<section class="col-sm-6"> <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> </section>
</article> </article>

View File

@ -19,11 +19,5 @@
width: 230px; width: 230px;
} }
&-text {
color: $color-secondary;
line-height: $height;
text-align: right;
width: 100%;
}
} }
} }

View File

@ -4,7 +4,7 @@
$banner-height: 260px; $banner-height: 260px;
&-hero { &-hero {
background-image: url('../../assets/images/main_banner.png'); background-image: url($image-main_banner);
background-size: cover; background-size: cover;
height: $banner-height; height: $banner-height;
width: 100%; width: 100%;
@ -61,7 +61,7 @@
} }
&::after { &::after {
background-image: url('../../assets/images/arrow-down.png'); background-image: url($image-arrow_down);
content: ''; content: '';
height: 7px; //png height height: 7px; //png height
position: absolute; position: absolute;

View 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}");
}
}
}
}

View File

@ -11,6 +11,7 @@ using Microsoft.Extensions.HealthChecks;
using Newtonsoft.Json.Serialization; using Newtonsoft.Json.Serialization;
using eShopOnContainers.WebSPA; using eShopOnContainers.WebSPA;
using Microsoft.eShopOnContainers.BuildingBlocks; using Microsoft.eShopOnContainers.BuildingBlocks;
using WebSPA.Infrastructure;
namespace eShopConContainers.WebSPA namespace eShopConContainers.WebSPA
{ {
@ -99,6 +100,9 @@ namespace eShopConContainers.WebSPA
// await next.Invoke(); // await next.Invoke();
// }); // });
//Seed Data
WebContextSeed.Seed(app, env, loggerFactory);
app.Use(async (context, next) => app.Use(async (context, next) =>
{ {
await next(); await next();

View File

@ -16,6 +16,9 @@
<ItemGroup> <ItemGroup>
<Compile Remove="node_modules\**\*;Client\**\*" /> <Compile Remove="node_modules\**\*;Client\**\*" />
<Content Include="Setup\images.zip">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="appsettings.json;"> <Content Update="appsettings.json;">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content> </Content>

View File

@ -4,6 +4,7 @@
"BasketUrl": "http://localhost:5103", "BasketUrl": "http://localhost:5103",
"IdentityUrl": "http://localhost:5105", "IdentityUrl": "http://localhost:5105",
"CallBackUrl": "http://localhost:5104/", "CallBackUrl": "http://localhost:5104/",
"UseCustomizationData": true,
"IsClusterEnv": "False", "IsClusterEnv": "False",
"Logging": { "Logging": {
"IncludeScopes": false, "IncludeScopes": false,