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