data and UI customization enhancements
This commit is contained in:
parent
d6c6582b51
commit
445101b376
@ -29,6 +29,7 @@ services:
|
|||||||
- ConnectionString=Server=sql.data;Database=Microsoft.eShopOnContainers.Services.CatalogDb;User Id=sa;Password=Pass@word
|
- 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"
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Catalog.API.Extensions
|
||||||
|
{
|
||||||
|
public static class LinqSelectExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector)
|
||||||
|
{
|
||||||
|
foreach (TSource element in enumerable)
|
||||||
|
{
|
||||||
|
SelectTryResult<TSource, TResult> returnedValue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex);
|
||||||
|
}
|
||||||
|
yield return returnedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler)
|
||||||
|
{
|
||||||
|
return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler)
|
||||||
|
{
|
||||||
|
return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SelectTryResult<TSource, TResult>
|
||||||
|
{
|
||||||
|
internal SelectTryResult(TSource source, TResult result, Exception exception)
|
||||||
|
{
|
||||||
|
Source = source;
|
||||||
|
Result = result;
|
||||||
|
CaughtException = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TSource Source { get; private set; }
|
||||||
|
public TResult Result { get; private set; }
|
||||||
|
public Exception CaughtException { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,47 +2,115 @@
|
|||||||
{
|
{
|
||||||
using EntityFrameworkCore;
|
using 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
8
src/Services/Catalog/Catalog.API/Setup/CatalogBrands.csv
Normal file
8
src/Services/Catalog/Catalog.API/Setup/CatalogBrands.csv
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
CatalogBrand
|
||||||
|
Azure
|
||||||
|
.NET
|
||||||
|
Visual Studio
|
||||||
|
SQL Server
|
||||||
|
Other
|
||||||
|
CatalogBrandTestOne
|
||||||
|
CatalogBrandTestTwo
|
|
13
src/Services/Catalog/Catalog.API/Setup/CatalogItems.csv
Normal file
13
src/Services/Catalog/Catalog.API/Setup/CatalogItems.csv
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
CatalogTypeName,CatalogBrandName,Description,Name,Price,PictureUri
|
||||||
|
T-Shirt,.NET,".NET Bot Black Hoodie, and more",.NET Bot Black Hoodie,19.5,1.png
|
||||||
|
Mug,.NET,.NET Black & White Mug,.NET Black & White Mug,8.50,2.png
|
||||||
|
T-Shirt,Other,Prism White T-Shirt,Prism White T-Shirt,12,3.png
|
||||||
|
T-Shirt,.NET,.NET Foundation T-shirt,.NET Foundation T-shirt,12,4.png
|
||||||
|
Sheet,Other,Roslyn Red Sheet,Roslyn Red Sheet,8.5,5.png
|
||||||
|
T-Shirt,.NET,.NET Blue Hoodie,.NET Blue Hoodie,12,6.png
|
||||||
|
T-Shirt,Other,Roslyn Red T-Shirt,Roslyn Red T-Shirt",12,7.png
|
||||||
|
T-Shirt,Other,Kudu Purple Hoodie,Kudu Purple Hoodie,8.5,8.png
|
||||||
|
Mug,Other,Cup<T> White Mug,Cup<T> White Mug,12,9.png
|
||||||
|
Sheet,.NET,.NET Foundation Sheet,.NET Foundation Sheet,12,10.png
|
||||||
|
Sheet,.NET,Cup<T> Sheet,Cup<T> Sheet,8.5,11.png
|
||||||
|
T-Shirt,Other,Prism White TShirt,Prism White TShirt,12,12.png
|
Can't render this file because it contains an unexpected character in line 8 and column 52.
|
7
src/Services/Catalog/Catalog.API/Setup/CatalogTypes.csv
Normal file
7
src/Services/Catalog/Catalog.API/Setup/CatalogTypes.csv
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
CatalogType
|
||||||
|
Mug
|
||||||
|
T-Shirt
|
||||||
|
Sheet
|
||||||
|
USB Memory Stick
|
||||||
|
CatalogTypeTestOne
|
||||||
|
CatalogTypeTestTwo
|
|
BIN
src/Services/Catalog/Catalog.API/Setup/Catalogitems.zip
Normal file
BIN
src/Services/Catalog/Catalog.API/Setup/Catalogitems.zip
Normal file
Binary file not shown.
@ -153,7 +153,7 @@
|
|||||||
var context = (CatalogContext)app
|
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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": {
|
||||||
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Identity.API.Extensions
|
||||||
|
{
|
||||||
|
public static class LinqSelectExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector)
|
||||||
|
{
|
||||||
|
foreach (TSource element in enumerable)
|
||||||
|
{
|
||||||
|
SelectTryResult<TSource, TResult> returnedValue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex);
|
||||||
|
}
|
||||||
|
yield return returnedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler)
|
||||||
|
{
|
||||||
|
return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler)
|
||||||
|
{
|
||||||
|
return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SelectTryResult<TSource, TResult>
|
||||||
|
{
|
||||||
|
internal SelectTryResult(TSource source, TResult result, Exception exception)
|
||||||
|
{
|
||||||
|
Source = source;
|
||||||
|
Result = result;
|
||||||
|
CaughtException = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TSource Source { get; private set; }
|
||||||
|
public TResult Result { get; private set; }
|
||||||
|
public Exception CaughtException { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,13 @@
|
|||||||
<PackageTargetFallback>$(PackageTargetFallback);dotnet5.6;portable-net45+win8</PackageTargetFallback>
|
<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>
|
||||||
|
2
src/Services/Identity/Identity.API/Setup/Users.csv
Normal file
2
src/Services/Identity/Identity.API/Setup/Users.csv
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
CardHolderName,CardNumber,CardType,City,Country,Email,Expiration,LastName,Name,PhoneNumber,UserName,ZipCode,State,Street,SecurityNumber,NormalizedEmail,NormalizedUserName,Password
|
||||||
|
DemoUser,4012888888881881,1,Redmond,U.S.,demouser@microsoft.com,12/20,DemoLastName,DemoUser,1234567890,demouser@microsoft.com,98052,WA,15703 NE 61st Ct,535,DEMOUSER@MICROSOFT.COM,DEMOUSER@MICROSOFT.COM,Pass@word1
|
|
@ -153,7 +153,7 @@ namespace eShopOnContainers.Identity
|
|||||||
|
|
||||||
//Seed Data
|
//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)
|
||||||
|
@ -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": {
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Ordering.API.Extensions
|
||||||
|
{
|
||||||
|
public static class LinqSelectExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<SelectTryResult<TSource, TResult>> SelectTry<TSource, TResult>(this IEnumerable<TSource> enumerable, Func<TSource, TResult> selector)
|
||||||
|
{
|
||||||
|
foreach (TSource element in enumerable)
|
||||||
|
{
|
||||||
|
SelectTryResult<TSource, TResult> returnedValue;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
returnedValue = new SelectTryResult<TSource, TResult>(element, selector(element), null);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
returnedValue = new SelectTryResult<TSource, TResult>(element, default(TResult), ex);
|
||||||
|
}
|
||||||
|
yield return returnedValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<Exception, TResult> exceptionHandler)
|
||||||
|
{
|
||||||
|
return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.CaughtException));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TResult> OnCaughtException<TSource, TResult>(this IEnumerable<SelectTryResult<TSource, TResult>> enumerable, Func<TSource, Exception, TResult> exceptionHandler)
|
||||||
|
{
|
||||||
|
return enumerable.Select(x => x.CaughtException == null ? x.Result : exceptionHandler(x.Source, x.CaughtException));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SelectTryResult<TSource, TResult>
|
||||||
|
{
|
||||||
|
internal SelectTryResult(TSource source, TResult result, Exception exception)
|
||||||
|
{
|
||||||
|
Source = source;
|
||||||
|
Result = result;
|
||||||
|
CaughtException = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TSource Source { get; private set; }
|
||||||
|
public TResult Result { get; private set; }
|
||||||
|
public Exception CaughtException { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,40 +8,175 @@
|
|||||||
using System.Threading.Tasks;
|
using 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
7
src/Services/Ordering/Ordering.API/OrderingSettings.cs
Normal file
7
src/Services/Ordering/Ordering.API/OrderingSettings.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Microsoft.eShopOnContainers.Services.Ordering.API
|
||||||
|
{
|
||||||
|
public class OrderingSettings
|
||||||
|
{
|
||||||
|
public bool UseCustomizationData { get; set; }
|
||||||
|
}
|
||||||
|
}
|
5
src/Services/Ordering/Ordering.API/Setup/CardTypes.csv
Normal file
5
src/Services/Ordering/Ordering.API/Setup/CardTypes.csv
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
CardType
|
||||||
|
Amex
|
||||||
|
Visa
|
||||||
|
MasterCard
|
||||||
|
Capital One
|
|
7
src/Services/Ordering/Ordering.API/Setup/OrderStatus.csv
Normal file
7
src/Services/Ordering/Ordering.API/Setup/OrderStatus.csv
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
OrderStatus
|
||||||
|
Submitted
|
||||||
|
AwaitingValidation
|
||||||
|
StockConfirmed
|
||||||
|
Paid
|
||||||
|
Shipped
|
||||||
|
Cancelled
|
|
@ -87,6 +87,8 @@
|
|||||||
ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request)
|
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);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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": {
|
||||||
|
@ -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
|
||||||
|
97
src/Web/WebMVC/Infrastructure/WebContextSeed.cs
Normal file
97
src/Web/WebMVC/Infrastructure/WebContextSeed.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.eShopOnContainers.WebMVC;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace WebMVC.Infrastructure
|
||||||
|
{
|
||||||
|
public class WebContextSeed
|
||||||
|
{
|
||||||
|
public static void Seed(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
var log = loggerFactory.CreateLogger("WebMVC seed");
|
||||||
|
|
||||||
|
var settings = (AppSettings)applicationBuilder
|
||||||
|
.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value;
|
||||||
|
|
||||||
|
var useCustomizationData = settings.UseCustomizationData;
|
||||||
|
var contentRootPath = env.ContentRootPath;
|
||||||
|
var webroot = env.WebRootPath;
|
||||||
|
|
||||||
|
if (useCustomizationData)
|
||||||
|
{
|
||||||
|
GetPreconfiguredImages(contentRootPath, webroot, log);
|
||||||
|
|
||||||
|
GetPreconfiguredCSS(contentRootPath, webroot, log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetPreconfiguredCSS(string contentRootPath, string webroot, ILogger log)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string overrideCssFile = Path.Combine(contentRootPath, "Setup", "override.css");
|
||||||
|
if (!File.Exists(overrideCssFile))
|
||||||
|
{
|
||||||
|
log.LogError($" override css file '{overrideCssFile}' does not exists.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string destinationFilename = Path.Combine(webroot, "css", "override.css");
|
||||||
|
File.Copy(overrideCssFile, destinationFilename, true );
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.LogError($"Exception in method GetPreconfiguredCSS WebMVC. Exception Message={ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger log)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip");
|
||||||
|
if (!File.Exists(imagesZipFile))
|
||||||
|
{
|
||||||
|
log.LogError($" zip file '{imagesZipFile}' does not exists.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string imagePath = Path.Combine(webroot, "images");
|
||||||
|
string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray();
|
||||||
|
|
||||||
|
using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read))
|
||||||
|
{
|
||||||
|
foreach (ZipArchiveEntry entry in zip.Entries)
|
||||||
|
{
|
||||||
|
if (imageFiles.Contains(entry.Name))
|
||||||
|
{
|
||||||
|
string destinationFilename = Path.Combine(imagePath, entry.Name);
|
||||||
|
if (File.Exists(destinationFilename))
|
||||||
|
{
|
||||||
|
File.Delete(destinationFilename);
|
||||||
|
}
|
||||||
|
entry.ExtractToFile(destinationFilename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch ( Exception ex )
|
||||||
|
{
|
||||||
|
log.LogError($"Exception in method GetPreconfiguredImages WebMVC. Exception Message={ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
src/Web/WebMVC/Setup/images.zip
Normal file
BIN
src/Web/WebMVC/Setup/images.zip
Normal file
Binary file not shown.
6
src/Web/WebMVC/Setup/override.css
Normal file
6
src/Web/WebMVC/Setup/override.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.esh-catalog-button {
|
||||||
|
background-color: #FF001b;
|
||||||
|
border: 2px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
@ -12,6 +12,7 @@ using Microsoft.Extensions.HealthChecks;
|
|||||||
using Microsoft.Extensions.Logging;
|
using 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);
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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\**" />
|
||||||
|
@ -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": {
|
||||||
|
42
src/Web/WebMVC/compilerconfig.json
Normal file
42
src/Web/WebMVC/compilerconfig.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/orders/orders.component.css",
|
||||||
|
"inputFile": "wwwroot/css/orders/orders.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/orders/orders-new/orders-new.component.css",
|
||||||
|
"inputFile": "wwwroot/css/orders/orders-new/orders-new.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/orders/orders-detail/orders-detail.component.css",
|
||||||
|
"inputFile": "wwwroot/css/orders/orders-detail/orders-detail.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/catalog/catalog.component.css",
|
||||||
|
"inputFile": "wwwroot/css/catalog/catalog.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/basket/basket.component.css",
|
||||||
|
"inputFile": "wwwroot/css/basket/basket.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/basket/basket-status/basket-status.component.css",
|
||||||
|
"inputFile": "wwwroot/css/basket/basket-status/basket-status.component.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/shared/components/header/header.css",
|
||||||
|
"inputFile": "wwwroot/css/shared/components/header/header.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/shared/components/identity/identity.css",
|
||||||
|
"inputFile": "wwwroot/css/shared/components/identity/identity.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/shared/components/pager/pager.css",
|
||||||
|
"inputFile": "wwwroot/css/shared/components/pager/pager.scss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFile": "wwwroot/css/app.component.css",
|
||||||
|
"inputFile": "wwwroot/css/app.component.scss"
|
||||||
|
}
|
||||||
|
]
|
49
src/Web/WebMVC/compilerconfig.json.defaults
Normal file
49
src/Web/WebMVC/compilerconfig.json.defaults
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"compilers": {
|
||||||
|
"less": {
|
||||||
|
"autoPrefix": "",
|
||||||
|
"cssComb": "none",
|
||||||
|
"ieCompat": true,
|
||||||
|
"strictMath": false,
|
||||||
|
"strictUnits": false,
|
||||||
|
"relativeUrls": true,
|
||||||
|
"rootPath": "",
|
||||||
|
"sourceMapRoot": "",
|
||||||
|
"sourceMapBasePath": "",
|
||||||
|
"sourceMap": false
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"includePath": "",
|
||||||
|
"indentType": "space",
|
||||||
|
"indentWidth": 2,
|
||||||
|
"outputStyle": "expanded",
|
||||||
|
"Precision": 5,
|
||||||
|
"relativeUrls": true,
|
||||||
|
"sourceMapRoot": "",
|
||||||
|
"sourceMap": false
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"sourceMap": false
|
||||||
|
},
|
||||||
|
"babel": {
|
||||||
|
"sourceMap": false
|
||||||
|
},
|
||||||
|
"coffeescript": {
|
||||||
|
"bare": false,
|
||||||
|
"runtimeMode": "node",
|
||||||
|
"sourceMap": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minifiers": {
|
||||||
|
"css": {
|
||||||
|
"enabled": true,
|
||||||
|
"termSemicolons": true,
|
||||||
|
"gzip": false
|
||||||
|
},
|
||||||
|
"javascript": {
|
||||||
|
"enabled": true,
|
||||||
|
"termSemicolons": true,
|
||||||
|
"gzip": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
58
src/Web/WebMVC/wwwroot/css/_variables.scss
Normal file
58
src/Web/WebMVC/wwwroot/css/_variables.scss
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// Colors
|
||||||
|
$color-brand: #00A69C;
|
||||||
|
$color-brand-dark: darken($color-brand, 10%);
|
||||||
|
$color-brand-darker: darken($color-brand, 20%);
|
||||||
|
$color-brand-bright: lighten($color-brand, 10%);
|
||||||
|
$color-brand-brighter: lighten($color-brand, 20%);
|
||||||
|
|
||||||
|
$color-secondary: #83D01B;
|
||||||
|
$color-secondary-dark: darken($color-secondary, 5%);
|
||||||
|
$color-secondary-darker: darken($color-secondary, 20%);
|
||||||
|
$color-secondary-bright: lighten($color-secondary, 10%);
|
||||||
|
$color-secondary-brighter: lighten($color-secondary, 20%);
|
||||||
|
|
||||||
|
$color-background-dark: #333333;
|
||||||
|
$color-background-darker: #000000;
|
||||||
|
$color-background-bright: #EEEEFF;
|
||||||
|
$color-background-brighter: #FFFFFF;
|
||||||
|
|
||||||
|
$color-foreground-dark: #333333;
|
||||||
|
$color-foreground-darker: #000000;
|
||||||
|
$color-foreground-bright: #EEEEEE;
|
||||||
|
$color-foreground-brighter: #FFFFFF;
|
||||||
|
|
||||||
|
// Animations
|
||||||
|
$animation-speed-default: .35s;
|
||||||
|
$animation-speed-slow: .5s;
|
||||||
|
$animation-speed-fast: .15s;
|
||||||
|
|
||||||
|
// Fonts
|
||||||
|
$font-weight-light: 200;
|
||||||
|
$font-weight-semilight: 300;
|
||||||
|
$font-weight-normal: 400;
|
||||||
|
$font-weight-semibold: 600;
|
||||||
|
$font-weight-bold: 700;
|
||||||
|
|
||||||
|
$font-size-xs: .65rem; // 10.4px
|
||||||
|
$font-size-s: .85rem; // 13.6px
|
||||||
|
$font-size-m: 1rem; // 16px
|
||||||
|
$font-size-l: 1.25rem; // 20px
|
||||||
|
$font-size-xl: 1.5rem; // 24px
|
||||||
|
|
||||||
|
// Medias
|
||||||
|
$media-screen-xxs: 360px;
|
||||||
|
$media-screen-xs: 640px;
|
||||||
|
$media-screen-s: 768px;
|
||||||
|
$media-screen-m: 1024px;
|
||||||
|
$media-screen-l: 1280px;
|
||||||
|
$media-screen-xl: 1440px;
|
||||||
|
$media-screen-xxl: 1680px;
|
||||||
|
$media-screen-xxxl: 1920px;
|
||||||
|
|
||||||
|
// Borders
|
||||||
|
$border-light: 1px;
|
||||||
|
|
||||||
|
// Images
|
||||||
|
$image_path: '../../images/';
|
||||||
|
$image-main_banner: '#{$image_path}main_banner.png';
|
||||||
|
$image-arrow_down: '#{$image_path}arrow-down.png';
|
@ -1,20 +1,14 @@
|
|||||||
.esh-app-footer {
|
.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%;
|
|
||||||
}
|
|
||||||
|
23
src/Web/WebMVC/wwwroot/css/app.component.scss
Normal file
23
src/Web/WebMVC/wwwroot/css/app.component.scss
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
@import './variables';
|
||||||
|
|
||||||
|
.esh-app {
|
||||||
|
&-footer {
|
||||||
|
$margin: 2.5rem;
|
||||||
|
$padding: 2.5rem;
|
||||||
|
|
||||||
|
background-color: $color-background-darker;
|
||||||
|
border-top: $border-light solid $color-foreground-bright;
|
||||||
|
margin-top: $margin;
|
||||||
|
padding-bottom: $padding;
|
||||||
|
padding-top: $padding;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
$height: 50px;
|
||||||
|
|
||||||
|
&-brand {
|
||||||
|
height: $height;
|
||||||
|
width: 230px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,38 +1,39 @@
|
|||||||
.esh-basketstatus {
|
.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
@import '../../variables';
|
||||||
|
|
||||||
|
.esh-basketstatus {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
position: relative;
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
opacity: .5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-image {
|
||||||
|
height: 36px;
|
||||||
|
margin-top: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-badge {
|
||||||
|
$size: 1.5rem;
|
||||||
|
background-color: $color-secondary;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: $color-foreground-brighter;
|
||||||
|
display: block;
|
||||||
|
height: $size;
|
||||||
|
left: 50%;
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
top: 0;
|
||||||
|
transform: translateX(-38%);
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
width: $size;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover &-badge {
|
||||||
|
background-color: transparent;
|
||||||
|
color: $color-secondary-dark;
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
}
|
||||||
|
}
|
@ -1,78 +1,79 @@
|
|||||||
.esh-basket {
|
.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;
|
|
||||||
}
|
|
||||||
|
89
src/Web/WebMVC/wwwroot/css/basket/basket.component.scss
Normal file
89
src/Web/WebMVC/wwwroot/css/basket/basket.component.scss
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@import '../variables';
|
||||||
|
|
||||||
|
@mixin margin-left($distance) {
|
||||||
|
margin-left: $distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esh-basket {
|
||||||
|
min-height: 80vh;
|
||||||
|
|
||||||
|
&-titles {
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
|
||||||
|
&--clean {
|
||||||
|
padding-bottom: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-items {
|
||||||
|
&--border {
|
||||||
|
border-bottom: $border-light solid $color-foreground-bright;
|
||||||
|
padding: .5rem 0;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-margin-left1 {
|
||||||
|
@include margin-left(1px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$item-height: 8rem;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
font-size: $font-size-m;
|
||||||
|
font-weight: $font-weight-semilight;
|
||||||
|
|
||||||
|
&--middle {
|
||||||
|
line-height: $item-height;
|
||||||
|
|
||||||
|
@media screen and (max-width: $media-screen-m) {
|
||||||
|
line-height: $font-size-m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--mark {
|
||||||
|
color: $color-brand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-image {
|
||||||
|
height: $item-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-input {
|
||||||
|
line-height: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-checkout {
|
||||||
|
background-color: $color-secondary;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
color: $color-foreground-brighter;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: $font-weight-normal;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color-secondary-darker;
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,147 +1,149 @@
|
|||||||
.esh-catalog-hero {
|
.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: '$';
|
|
||||||
}
|
|
||||||
|
154
src/Web/WebMVC/wwwroot/css/catalog/catalog.component.scss
Normal file
154
src/Web/WebMVC/wwwroot/css/catalog/catalog.component.scss
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
@import '../variables';
|
||||||
|
|
||||||
|
.esh-catalog {
|
||||||
|
$banner-height: 260px;
|
||||||
|
|
||||||
|
&-hero {
|
||||||
|
background-image: url($image-main_banner);
|
||||||
|
background-size: cover;
|
||||||
|
height: $banner-height;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
position: relative;
|
||||||
|
top: $banner-height / 3.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filter-height: 65px;
|
||||||
|
|
||||||
|
&-filters {
|
||||||
|
background-color: $color-brand;
|
||||||
|
height: $filter-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
$filter-padding: .5rem;
|
||||||
|
|
||||||
|
&-filter {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: $color-brand-bright;
|
||||||
|
color: $color-foreground-brighter;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 1rem;
|
||||||
|
margin-top: .5rem;
|
||||||
|
min-width: 140px;
|
||||||
|
outline-color: $color-secondary;
|
||||||
|
padding-bottom: 0;
|
||||||
|
padding-left: $filter-padding;
|
||||||
|
padding-right: $filter-padding;
|
||||||
|
padding-top: $filter-padding * 3;
|
||||||
|
|
||||||
|
option {
|
||||||
|
background-color: $color-brand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-label {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
z-index: 0;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
color: rgba($color-foreground-brighter, .5);
|
||||||
|
content: attr(data-title);
|
||||||
|
font-size: $font-size-xs;
|
||||||
|
margin-left: $filter-padding;
|
||||||
|
margin-top: $font-size-xs;
|
||||||
|
position: absolute;
|
||||||
|
text-transform: uppercase;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
background-image: url($image-arrow_down);
|
||||||
|
content: '';
|
||||||
|
height: 7px; //png height
|
||||||
|
position: absolute;
|
||||||
|
right: $filter-padding * 3;
|
||||||
|
top: $filter-padding * 5;
|
||||||
|
width: 10px; //png width
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-send {
|
||||||
|
background-color: $color-secondary;
|
||||||
|
color: $color-foreground-brighter;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: $font-size-m;
|
||||||
|
margin-top: -$filter-padding * 3;
|
||||||
|
padding: $filter-padding;
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color-secondary-darker;
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-items {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
width: 33%;
|
||||||
|
display: inline-block;
|
||||||
|
float: none !important;
|
||||||
|
|
||||||
|
@media screen and (max-width: $media-screen-m) {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $media-screen-s) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-thumbnail {
|
||||||
|
max-width: 370px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-button {
|
||||||
|
background-color: $color-secondary;
|
||||||
|
border: 0;
|
||||||
|
color: $color-foreground-brighter;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: $font-size-m;
|
||||||
|
height: 3rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
width: 80%;
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
opacity: .5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color-secondary-darker;
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
font-size: $font-size-m;
|
||||||
|
font-weight: $font-weight-semilight;
|
||||||
|
margin-top: .5rem;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-price {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 900;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '$';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,52 +1,53 @@
|
|||||||
.esh-orders_detail {
|
.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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
@import '../../variables';
|
||||||
|
|
||||||
|
.esh-orders_detail {
|
||||||
|
min-height: 80vh;
|
||||||
|
|
||||||
|
&-section {
|
||||||
|
padding: 1rem 0;
|
||||||
|
|
||||||
|
&--right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-titles {
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-items {
|
||||||
|
&--border {
|
||||||
|
border-bottom: $border-light solid $color-foreground-bright;
|
||||||
|
padding: .5rem 0;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$item-height: 8rem;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
font-size: $font-size-m;
|
||||||
|
font-weight: $font-weight-semilight;
|
||||||
|
|
||||||
|
&--middle {
|
||||||
|
line-height: $item-height;
|
||||||
|
|
||||||
|
@media screen and (max-width: $media-screen-s) {
|
||||||
|
line-height: $font-size-m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--mark {
|
||||||
|
color: $color-secondary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-image {
|
||||||
|
height: $item-height;
|
||||||
|
}
|
||||||
|
}
|
@ -1,91 +1,96 @@
|
|||||||
.esh-orders_new {
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,101 @@
|
|||||||
|
@import '../../variables';
|
||||||
|
|
||||||
|
.esh-orders_new {
|
||||||
|
min-height: 80vh;
|
||||||
|
|
||||||
|
$header-height: 4rem;
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
background-color: #00A69C;
|
||||||
|
height: $header-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-back {
|
||||||
|
color: rgba($color-foreground-brighter, .4);
|
||||||
|
line-height: $header-height;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: color $animation-speed-default;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $color-foreground-brighter;
|
||||||
|
transition: color $animation-speed-default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-section {
|
||||||
|
padding: 1rem 0;
|
||||||
|
|
||||||
|
&--right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-placeOrder {
|
||||||
|
background-color: $color-secondary;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
color: $color-foreground-brighter;
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: $font-weight-normal;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color-secondary-darker;
|
||||||
|
transition: all $animation-speed-default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-titles {
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
font-size: $font-size-l;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-items {
|
||||||
|
&--border {
|
||||||
|
border-bottom: $border-light solid $color-foreground-bright;
|
||||||
|
padding: .5rem 0;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$item-height: 8rem;
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
font-size: $font-size-m;
|
||||||
|
font-weight: $font-weight-semilight;
|
||||||
|
|
||||||
|
&--middle {
|
||||||
|
line-height: $item-height;
|
||||||
|
|
||||||
|
@media screen and (max-width: $media-screen-s) {
|
||||||
|
line-height: $font-size-m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--mark {
|
||||||
|
color: $color-secondary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-image {
|
||||||
|
height: $item-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-alert {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
@ -1,74 +1,75 @@
|
|||||||
.esh-orders {
|
.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;
|
|
||||||
}
|
|
||||||
|
81
src/Web/WebMVC/wwwroot/css/orders/orders.component.scss
Normal file
81
src/Web/WebMVC/wwwroot/css/orders/orders.component.scss
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
@import '../variables';
|
||||||
|
|
||||||
|
.esh-orders {
|
||||||
|
min-height: 80vh;
|
||||||
|
overflow-x: hidden;
|
||||||
|
|
||||||
|
$header-height: 4rem;
|
||||||
|
|
||||||
|
&-header {
|
||||||
|
background-color: #00A69C;
|
||||||
|
height: $header-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-back {
|
||||||
|
color: rgba($color-foreground-brighter, .4);
|
||||||
|
line-height: $header-height;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: color $animation-speed-default;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $color-foreground-brighter;
|
||||||
|
transition: color $animation-speed-default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-titles {
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
padding-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-items {
|
||||||
|
$height: 2rem;
|
||||||
|
height: $height;
|
||||||
|
line-height: $height;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&:nth-of-type(2n + 1) {
|
||||||
|
&:before {
|
||||||
|
background-color: $color-background-bright;
|
||||||
|
content: '';
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
margin-left: -100vw;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 200vw;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
font-weight: $font-weight-semilight;
|
||||||
|
|
||||||
|
&--hover {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-items:hover &-item--hover {
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-link {
|
||||||
|
color: $color-secondary;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: color $animation-speed-default;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $color-secondary-dark;
|
||||||
|
transition: color $animation-speed-default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
src/Web/WebMVC/wwwroot/css/override.css
Normal file
6
src/Web/WebMVC/wwwroot/css/override.css
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
.esh-catalog-button {
|
||||||
|
background-color: #FF001b;
|
||||||
|
border: 2px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
@ -1,31 +1,18 @@
|
|||||||
.esh-header {
|
.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;
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
@import '../../../variables';
|
||||||
|
|
||||||
|
.esh-header {
|
||||||
|
$header-height: 4rem;
|
||||||
|
|
||||||
|
background-color: $color-brand;
|
||||||
|
height: $header-height;
|
||||||
|
|
||||||
|
&-back {
|
||||||
|
color: rgba($color-foreground-brighter, .5);
|
||||||
|
line-height: $header-height;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: color $animation-speed-default;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $color-foreground-brighter;
|
||||||
|
transition: color $animation-speed-default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,57 +1,57 @@
|
|||||||
.esh-identity {
|
.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;
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
@import '../../../variables';
|
||||||
|
|
||||||
|
.esh-identity {
|
||||||
|
line-height: 3rem;
|
||||||
|
position: relative;
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
&-section {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-name {
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&--upper {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $media-screen-s) {
|
||||||
|
font-size: $font-size-s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-image {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-drop {
|
||||||
|
background: $color-background-brighter;
|
||||||
|
height: 0;
|
||||||
|
min-width: 14rem;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: .5rem;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 2.5rem;
|
||||||
|
transition: height $animation-speed-default;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover &-drop {
|
||||||
|
border: $border-light solid $color-foreground-bright;
|
||||||
|
height: 7rem;
|
||||||
|
transition: height $animation-speed-default;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color $animation-speed-default;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $color-secondary-dark;
|
||||||
|
transition: color $animation-speed-default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +1,35 @@
|
|||||||
.esh-pager-wrapper {
|
.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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
@import '../../../variables';
|
||||||
|
|
||||||
|
.esh-pager {
|
||||||
|
|
||||||
|
&-wrapper {
|
||||||
|
padding-top: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-item {
|
||||||
|
$margin: 5vw;
|
||||||
|
margin: 0 $margin;
|
||||||
|
|
||||||
|
&.is-disabled {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--navigable {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $color-secondary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $media-screen-l) {
|
||||||
|
font-size: $font-size-s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: $media-screen-m) {
|
||||||
|
margin: 0 $margin / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
src/Web/WebMVC/wwwroot/css/site.min.css
vendored
2
src/Web/WebMVC/wwwroot/css/site.min.css
vendored
File diff suppressed because one or more lines are too long
BIN
src/Web/WebMVC/wwwroot/images/main_footer_text.png
Normal file
BIN
src/Web/WebMVC/wwwroot/images/main_footer_text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
@ -12,5 +12,6 @@ namespace eShopOnContainers.WebSPA
|
|||||||
public string OrderingUrl { get; set; }
|
public string 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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
src/Web/WebSPA/Client/assets/images/main_footer_text.png
Normal file
BIN
src/Web/WebSPA/Client/assets/images/main_footer_text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
@ -51,3 +51,8 @@ $media-screen-xxxl: 1920px;
|
|||||||
|
|
||||||
// Borders
|
// 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';
|
@ -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>
|
||||||
|
@ -19,11 +19,5 @@
|
|||||||
width: 230px;
|
width: 230px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-text {
|
|
||||||
color: $color-secondary;
|
|
||||||
line-height: $height;
|
|
||||||
text-align: right;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
73
src/Web/WebSPA/Server/Infrastructure/WebContextSeed.cs
Normal file
73
src/Web/WebSPA/Server/Infrastructure/WebContextSeed.cs
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
using eShopOnContainers.WebSPA;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace WebSPA.Infrastructure
|
||||||
|
{
|
||||||
|
public class WebContextSeed
|
||||||
|
{
|
||||||
|
public static void Seed(IApplicationBuilder applicationBuilder, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||||
|
{
|
||||||
|
var log = loggerFactory.CreateLogger("WebSPA seed");
|
||||||
|
|
||||||
|
var settings = (AppSettings)applicationBuilder
|
||||||
|
.ApplicationServices.GetRequiredService<IOptions<AppSettings>>().Value;
|
||||||
|
|
||||||
|
var useCustomizationData = settings.UseCustomizationData;
|
||||||
|
var contentRootPath = env.ContentRootPath;
|
||||||
|
var webroot = env.WebRootPath;
|
||||||
|
|
||||||
|
if (useCustomizationData)
|
||||||
|
{
|
||||||
|
GetPreconfiguredImages(contentRootPath, webroot, log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetPreconfiguredImages(string contentRootPath, string webroot, ILogger log)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string imagesZipFile = Path.Combine(contentRootPath, "Setup", "images.zip");
|
||||||
|
if (!File.Exists(imagesZipFile))
|
||||||
|
{
|
||||||
|
log.LogError($" zip file '{imagesZipFile}' does not exists.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string imagePath = Path.Combine(webroot, "assets", "images");
|
||||||
|
string[] imageFiles = Directory.GetFiles(imagePath).Select(file => Path.GetFileName(file)).ToArray();
|
||||||
|
|
||||||
|
using (ZipArchive zip = ZipFile.Open(imagesZipFile, ZipArchiveMode.Read))
|
||||||
|
{
|
||||||
|
foreach (ZipArchiveEntry entry in zip.Entries)
|
||||||
|
{
|
||||||
|
if (imageFiles.Contains(entry.Name))
|
||||||
|
{
|
||||||
|
string destinationFilename = Path.Combine(imagePath, entry.Name);
|
||||||
|
if (File.Exists(destinationFilename))
|
||||||
|
{
|
||||||
|
File.Delete(destinationFilename);
|
||||||
|
}
|
||||||
|
entry.ExtractToFile(destinationFilename);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.LogWarning($"Skip file '{entry.Name}' in zipfile '{imagesZipFile}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.LogError($"Exception in method GetPreconfiguredImages WebSPA. Exception Message={ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ using Microsoft.Extensions.HealthChecks;
|
|||||||
using Newtonsoft.Json.Serialization;
|
using 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();
|
||||||
|
@ -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>
|
||||||
|
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user