Browse Source
* Ardalis/upgrade1 (#44) * Upgrading to netcore 2.0 Updating repository to support async options and refactoring to use it. * Starting work on tracking customer orders feature. * Cleaning up some bugs Working on basket view component implementation * Fixing up styles, especially for basket in header. * Adding Order Features (#47) * Working on order model binding from checkout page - WIP * Small layout tweaks (#43) * Updating quantities implemented. * Fixed basket widget count * Order History (#49) * working on creating and viewing orders. * Working on wiring up listing of orders * List orders page works as expected. Needed to support ThenInclude scenarios. Currently using strings.main
committed by
GitHub
70 changed files with 1752 additions and 510 deletions
@ -0,0 +1,26 @@ |
|||||
|
using ApplicationCore.Interfaces; |
||||
|
using Microsoft.eShopWeb.ApplicationCore.Entities; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace ApplicationCore.Entities.BuyerAggregate |
||||
|
{ |
||||
|
public class Buyer : BaseEntity, IAggregateRoot |
||||
|
{ |
||||
|
public string IdentityGuid { get; private set; } |
||||
|
|
||||
|
private List<PaymentMethod> _paymentMethods = new List<PaymentMethod>(); |
||||
|
|
||||
|
public IEnumerable<PaymentMethod> PaymentMethods => _paymentMethods.AsReadOnly(); |
||||
|
|
||||
|
protected Buyer() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Buyer(string identity) : this() |
||||
|
{ |
||||
|
IdentityGuid = !string.IsNullOrWhiteSpace(identity) ? identity : throw new ArgumentNullException(nameof(identity)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
using Microsoft.eShopWeb.ApplicationCore.Entities; |
||||
|
|
||||
|
namespace ApplicationCore.Entities.BuyerAggregate |
||||
|
{ |
||||
|
public class PaymentMethod : BaseEntity |
||||
|
{ |
||||
|
public string Alias { get; set; } |
||||
|
public string CardId { get; set; } // actual card data must be stored in a PCI compliant system, like Stripe
|
||||
|
public string Last4 { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace ApplicationCore.Entities.OrderAggregate |
||||
|
{ |
||||
|
public class Address // ValueObject
|
||||
|
{ |
||||
|
public String Street { get; private set; } |
||||
|
|
||||
|
public String City { get; private set; } |
||||
|
|
||||
|
public String State { get; private set; } |
||||
|
|
||||
|
public String Country { get; private set; } |
||||
|
|
||||
|
public String ZipCode { get; private set; } |
||||
|
|
||||
|
private Address() { } |
||||
|
|
||||
|
public Address(string street, string city, string state, string country, string zipcode) |
||||
|
{ |
||||
|
Street = street; |
||||
|
City = city; |
||||
|
State = state; |
||||
|
Country = country; |
||||
|
ZipCode = zipcode; |
||||
|
} |
||||
|
|
||||
|
//protected override IEnumerable<object> GetAtomicValues()
|
||||
|
//{
|
||||
|
// yield return Street;
|
||||
|
// yield return City;
|
||||
|
// yield return State;
|
||||
|
// yield return Country;
|
||||
|
// yield return ZipCode;
|
||||
|
//}
|
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
namespace ApplicationCore.Entities.OrderAggregate |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Represents the item that was ordered. If catalog item details change, details of
|
||||
|
/// the item that was part of a completed order should not change.
|
||||
|
/// </summary>
|
||||
|
public class CatalogItemOrdered // ValueObject
|
||||
|
{ |
||||
|
public CatalogItemOrdered(int catalogItemId, string productName, string pictureUri) |
||||
|
{ |
||||
|
CatalogItemId = catalogItemId; |
||||
|
ProductName = productName; |
||||
|
PictureUri = pictureUri; |
||||
|
} |
||||
|
private CatalogItemOrdered() |
||||
|
{ |
||||
|
// required by EF
|
||||
|
} |
||||
|
public int CatalogItemId { get; private set; } |
||||
|
public string ProductName { get; private set; } |
||||
|
public string PictureUri { get; private set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
using ApplicationCore.Interfaces; |
||||
|
using Microsoft.eShopWeb.ApplicationCore.Entities; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace ApplicationCore.Entities.OrderAggregate |
||||
|
{ |
||||
|
public class Order : BaseEntity, IAggregateRoot |
||||
|
{ |
||||
|
private Order() |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Order(string buyerId, Address shipToAddress, List<OrderItem> items) |
||||
|
{ |
||||
|
ShipToAddress = shipToAddress; |
||||
|
_orderItems = items; |
||||
|
BuyerId = buyerId; |
||||
|
} |
||||
|
public string BuyerId { get; private set; } |
||||
|
|
||||
|
public DateTimeOffset OrderDate { get; private set; } = DateTimeOffset.Now; |
||||
|
public Address ShipToAddress { get; private set; } |
||||
|
|
||||
|
// DDD Patterns comment
|
||||
|
// Using a private collection field, better for DDD Aggregate's encapsulation
|
||||
|
// so OrderItems cannot be added from "outside the AggregateRoot" directly to the collection,
|
||||
|
// but only through the method Order.AddOrderItem() which includes behavior.
|
||||
|
private readonly List<OrderItem> _orderItems = new List<OrderItem>(); |
||||
|
|
||||
|
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems; |
||||
|
// Using List<>.AsReadOnly()
|
||||
|
// This will create a read only wrapper around the private list so is protected against "external updates".
|
||||
|
// It's much cheaper than .ToList() because it will not have to copy all items in a new collection. (Just one heap alloc for the wrapper instance)
|
||||
|
//https://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.110).aspx
|
||||
|
|
||||
|
public decimal Total() |
||||
|
{ |
||||
|
var total = 0m; |
||||
|
foreach (var item in _orderItems) |
||||
|
{ |
||||
|
total += item.UnitPrice * item.Units; |
||||
|
} |
||||
|
return total; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
using Microsoft.eShopWeb.ApplicationCore.Entities; |
||||
|
|
||||
|
namespace ApplicationCore.Entities.OrderAggregate |
||||
|
{ |
||||
|
|
||||
|
public class OrderItem : BaseEntity |
||||
|
{ |
||||
|
public CatalogItemOrdered ItemOrdered { get; private set; } |
||||
|
public decimal UnitPrice { get; private set; } |
||||
|
public int Units { get; private set; } |
||||
|
|
||||
|
protected OrderItem() |
||||
|
{ |
||||
|
} |
||||
|
public OrderItem(CatalogItemOrdered itemOrdered, decimal unitPrice, int units) |
||||
|
{ |
||||
|
ItemOrdered = itemOrdered; |
||||
|
UnitPrice = unitPrice; |
||||
|
Units = units; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,5 @@ |
|||||
|
namespace ApplicationCore.Interfaces |
||||
|
{ |
||||
|
public interface IAggregateRoot |
||||
|
{ } |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using Microsoft.eShopWeb.ApplicationCore.Entities; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace ApplicationCore.Interfaces |
||||
|
{ |
||||
|
public interface IAsyncRepository<T> where T : BaseEntity |
||||
|
{ |
||||
|
Task<T> GetByIdAsync(int id); |
||||
|
Task<List<T>> ListAllAsync(); |
||||
|
Task<List<T>> ListAsync(ISpecification<T> spec); |
||||
|
Task<T> AddAsync(T entity); |
||||
|
Task UpdateAsync(T entity); |
||||
|
Task DeleteAsync(T entity); |
||||
|
} |
||||
|
} |
||||
@ -1,7 +0,0 @@ |
|||||
namespace ApplicationCore.Interfaces |
|
||||
{ |
|
||||
public interface IImageService |
|
||||
{ |
|
||||
byte[] GetImageBytesById(int id); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,12 @@ |
|||||
|
using ApplicationCore.Entities.OrderAggregate; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace ApplicationCore.Interfaces |
||||
|
{ |
||||
|
|
||||
|
public interface IOrderRepository : IRepository<Order>, IAsyncRepository<Order> |
||||
|
{ |
||||
|
Order GetByIdWithItems(int id); |
||||
|
Task<Order> GetByIdWithItemsAsync(int id); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
using ApplicationCore.Entities.OrderAggregate; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace ApplicationCore.Interfaces |
||||
|
{ |
||||
|
public interface IOrderService |
||||
|
{ |
||||
|
Task CreateOrderAsync(int basketId, Address shippingAddress); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
using ApplicationCore.Interfaces; |
||||
|
using System; |
||||
|
using System.Linq.Expressions; |
||||
|
using System.Collections.Generic; |
||||
|
using ApplicationCore.Entities.OrderAggregate; |
||||
|
|
||||
|
namespace ApplicationCore.Specifications |
||||
|
{ |
||||
|
public class CustomerOrdersWithItemsSpecification : ISpecification<Order> |
||||
|
{ |
||||
|
private readonly string _buyerId; |
||||
|
|
||||
|
public CustomerOrdersWithItemsSpecification(string buyerId) |
||||
|
{ |
||||
|
_buyerId = buyerId; |
||||
|
AddInclude(o => o.OrderItems); |
||||
|
AddInclude("OrderItems.ItemOrdered"); |
||||
|
} |
||||
|
|
||||
|
public Expression<Func<Order, bool>> Criteria => o => o.BuyerId == _buyerId; |
||||
|
|
||||
|
public List<Expression<Func<Order, object>>> Includes { get; } = new List<Expression<Func<Order, object>>>(); |
||||
|
public List<string> IncludeStrings { get; } = new List<string>(); |
||||
|
|
||||
|
public void AddInclude(Expression<Func<Order, object>> includeExpression) |
||||
|
{ |
||||
|
Includes.Add(includeExpression); |
||||
|
} |
||||
|
|
||||
|
public void AddInclude(string includeString) |
||||
|
{ |
||||
|
IncludeStrings.Add(includeString); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
using ApplicationCore.Entities.OrderAggregate; |
||||
|
using ApplicationCore.Interfaces; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Infrastructure.Data |
||||
|
{ |
||||
|
public class OrderRepository : EfRepository<Order>, IOrderRepository |
||||
|
{ |
||||
|
public OrderRepository(CatalogContext dbContext) : base(dbContext) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Order GetByIdWithItems(int id) |
||||
|
{ |
||||
|
return _dbContext.Orders |
||||
|
.Include(o => o.OrderItems) |
||||
|
.Include("OrderItems.ItemOrdered") |
||||
|
.FirstOrDefault(); |
||||
|
} |
||||
|
|
||||
|
public Task<Order> GetByIdWithItemsAsync(int id) |
||||
|
{ |
||||
|
return _dbContext.Orders |
||||
|
.Include(o => o.OrderItems) |
||||
|
.Include("OrderItems.ItemOrdered") |
||||
|
.FirstOrDefaultAsync(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,30 +0,0 @@ |
|||||
using ApplicationCore.Exceptions; |
|
||||
using ApplicationCore.Interfaces; |
|
||||
using Microsoft.AspNetCore.Hosting; |
|
||||
using System.IO; |
|
||||
|
|
||||
namespace Infrastructure.FileSystem |
|
||||
{ |
|
||||
public class LocalFileImageService : IImageService |
|
||||
{ |
|
||||
private readonly IHostingEnvironment _env; |
|
||||
|
|
||||
public LocalFileImageService(IHostingEnvironment env) |
|
||||
{ |
|
||||
_env = env; |
|
||||
} |
|
||||
public byte[] GetImageBytesById(int id) |
|
||||
{ |
|
||||
try |
|
||||
{ |
|
||||
var contentRoot = _env.ContentRootPath + "//Pics"; |
|
||||
var path = Path.Combine(contentRoot, id + ".png"); |
|
||||
return File.ReadAllBytes(path); |
|
||||
} |
|
||||
catch (FileNotFoundException ex) |
|
||||
{ |
|
||||
throw new CatalogImageMissingException(ex); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,30 +1,26 @@ |
|||||
<Project Sdk="Microsoft.NET.Sdk"> |
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
<PropertyGroup> |
<PropertyGroup> |
||||
<TargetFramework>netstandard1.4</TargetFramework> |
<TargetFramework>netcoreapp2.0</TargetFramework> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<RuntimeFrameworkVersion>2.0.0</RuntimeFrameworkVersion> |
||||
</PropertyGroup> |
</PropertyGroup> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" /> |
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" /> |
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="1.1.2" /> |
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" /> |
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="1.1.2" /> |
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" PrivateAssets="All" /> |
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="1.1.2" /> |
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer.Design" Version="1.1.2" PrivateAssets="All" /> |
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.1" PrivateAssets="All" /> |
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="1.1.2" /> |
|
||||
<PackageReference Include="StructureMap.Microsoft.DependencyInjection" Version="1.3.0" /> |
|
||||
</ItemGroup> |
</ItemGroup> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<ProjectReference Include="..\ApplicationCore\ApplicationCore.csproj" /> |
<ProjectReference Include="..\ApplicationCore\ApplicationCore.csproj" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools" Version="1.1.1" /> |
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" /> |
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" /> |
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" /> |
||||
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Design" Version="1.1.2" /> |
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0" /> |
||||
</ItemGroup> |
</ItemGroup> |
||||
<ItemGroup> |
<ItemGroup> |
||||
<Folder Include="Data\Migrations\" /> |
<Folder Include="Data\Migrations\" /> |
||||
<Folder Include="Services\" /> |
|
||||
</ItemGroup> |
</ItemGroup> |
||||
|
|
||||
</Project> |
</Project> |
||||
@ -0,0 +1,40 @@ |
|||||
|
using ApplicationCore.Interfaces; |
||||
|
using ApplicationCore.Entities.OrderAggregate; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.eShopWeb.ApplicationCore.Entities; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Infrastructure.Services |
||||
|
{ |
||||
|
public class OrderService : IOrderService |
||||
|
{ |
||||
|
private readonly IAsyncRepository<Order> _orderRepository; |
||||
|
private readonly IAsyncRepository<Basket> _basketRepository; |
||||
|
private readonly IAsyncRepository<CatalogItem> _itemRepository; |
||||
|
|
||||
|
public OrderService(IAsyncRepository<Basket> basketRepository, |
||||
|
IAsyncRepository<CatalogItem> itemRepository, |
||||
|
IAsyncRepository<Order> orderRepository) |
||||
|
{ |
||||
|
_orderRepository = orderRepository; |
||||
|
_basketRepository = basketRepository; |
||||
|
_itemRepository = itemRepository; |
||||
|
} |
||||
|
|
||||
|
public async Task CreateOrderAsync(int basketId, Address shippingAddress) |
||||
|
{ |
||||
|
var basket = await _basketRepository.GetByIdAsync(basketId); |
||||
|
var items = new List<OrderItem>(); |
||||
|
foreach (var item in basket.Items) |
||||
|
{ |
||||
|
var catalogItem = await _itemRepository.GetByIdAsync(item.CatalogItemId); |
||||
|
var itemOrdered = new CatalogItemOrdered(catalogItem.Id, catalogItem.Name, catalogItem.PictureUri); |
||||
|
var orderItem = new OrderItem(itemOrdered, item.UnitPrice, item.Quantity); |
||||
|
items.Add(orderItem); |
||||
|
} |
||||
|
var order = new Order(basket.BuyerId, shippingAddress, items); |
||||
|
|
||||
|
await _orderRepository.AddAsync(order); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,96 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Authorization; |
||||
|
using Microsoft.eShopWeb.ViewModels; |
||||
|
using System; |
||||
|
using ApplicationCore.Entities.OrderAggregate; |
||||
|
using ApplicationCore.Interfaces; |
||||
|
using System.Linq; |
||||
|
using ApplicationCore.Specifications; |
||||
|
|
||||
|
namespace Microsoft.eShopWeb.Controllers |
||||
|
{ |
||||
|
[Authorize] |
||||
|
[Route("[controller]/[action]")]
|
||||
|
public class OrderController : Controller |
||||
|
{ |
||||
|
private readonly IOrderRepository _orderRepository; |
||||
|
|
||||
|
public OrderController(IOrderRepository orderRepository) { |
||||
|
_orderRepository = orderRepository; |
||||
|
} |
||||
|
|
||||
|
public async Task<IActionResult> Index() |
||||
|
{ |
||||
|
var orders = await _orderRepository.ListAsync(new CustomerOrdersWithItemsSpecification(User.Identity.Name)); |
||||
|
|
||||
|
var viewModel = orders |
||||
|
.Select(o => new OrderViewModel() |
||||
|
{ |
||||
|
OrderDate = o.OrderDate, |
||||
|
OrderItems = o.OrderItems?.Select(oi => new OrderItemViewModel() |
||||
|
{ |
||||
|
Discount = 0, |
||||
|
PictureUrl = oi.ItemOrdered.PictureUri, |
||||
|
ProductId = oi.ItemOrdered.CatalogItemId, |
||||
|
ProductName = oi.ItemOrdered.ProductName, |
||||
|
UnitPrice = oi.UnitPrice, |
||||
|
Units = oi.Units |
||||
|
}).ToList(), |
||||
|
OrderNumber = o.Id, |
||||
|
ShippingAddress = o.ShipToAddress, |
||||
|
Status = "Pending", |
||||
|
Total = o.Total() |
||||
|
|
||||
|
}); |
||||
|
return View(viewModel); |
||||
|
} |
||||
|
|
||||
|
[HttpGet("{orderId}")] |
||||
|
public async Task<IActionResult> Detail(int orderId) |
||||
|
{ |
||||
|
var order = await _orderRepository.GetByIdWithItemsAsync(orderId); |
||||
|
var viewModel = new OrderViewModel() |
||||
|
{ |
||||
|
OrderDate = order.OrderDate, |
||||
|
OrderItems = order.OrderItems.Select(oi => new OrderItemViewModel() |
||||
|
{ |
||||
|
Discount = 0, |
||||
|
PictureUrl = oi.ItemOrdered.PictureUri, |
||||
|
ProductId = oi.ItemOrdered.CatalogItemId, |
||||
|
ProductName = oi.ItemOrdered.ProductName, |
||||
|
UnitPrice = oi.UnitPrice, |
||||
|
Units = oi.Units |
||||
|
}).ToList(), |
||||
|
OrderNumber = order.Id, |
||||
|
ShippingAddress = order.ShipToAddress, |
||||
|
Status = "Pending", |
||||
|
Total = order.Total() |
||||
|
}; |
||||
|
return View(viewModel); |
||||
|
} |
||||
|
|
||||
|
private OrderViewModel GetOrder() |
||||
|
{ |
||||
|
var order = new OrderViewModel() |
||||
|
{ |
||||
|
OrderDate = DateTimeOffset.Now.AddDays(-1), |
||||
|
OrderNumber = 12354, |
||||
|
Status = "Submitted", |
||||
|
Total = 123.45m, |
||||
|
ShippingAddress = new Address("123 Main St.", "Kent", "OH", "United States", "44240") |
||||
|
}; |
||||
|
|
||||
|
order.OrderItems.Add(new OrderItemViewModel() |
||||
|
{ |
||||
|
ProductId = 1, |
||||
|
PictureUrl = "", |
||||
|
ProductName = "Something", |
||||
|
UnitPrice = 5.05m, |
||||
|
Units = 2 |
||||
|
}); |
||||
|
|
||||
|
return order; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,28 +1,20 @@ |
|||||
using System.IO; |
using Microsoft.AspNetCore.Hosting; |
||||
using Microsoft.AspNetCore.Hosting; |
using Microsoft.AspNetCore; |
||||
using Microsoft.Extensions.Logging; |
|
||||
|
|
||||
namespace Microsoft.eShopWeb |
namespace Microsoft.eShopWeb |
||||
{ |
{ |
||||
public class Program |
public class Program |
||||
{ |
{ |
||||
|
|
||||
public static void Main(string[] args) |
public static void Main(string[] args) |
||||
{ |
{ |
||||
var host = new WebHostBuilder() |
BuildWebHost(args).Run(); |
||||
.UseKestrel() |
} |
||||
|
|
||||
|
public static IWebHost BuildWebHost(string[] args) => |
||||
|
WebHost.CreateDefaultBuilder(args) |
||||
.UseUrls("http://0.0.0.0:5106") |
.UseUrls("http://0.0.0.0:5106") |
||||
.UseContentRoot(Directory.GetCurrentDirectory()) |
|
||||
.ConfigureLogging(factory => |
|
||||
{ |
|
||||
factory.AddConsole(LogLevel.Warning); |
|
||||
factory.AddDebug(); |
|
||||
}) |
|
||||
.UseIISIntegration() |
|
||||
.UseStartup<Startup>() |
.UseStartup<Startup>() |
||||
.UseApplicationInsights() |
|
||||
.Build(); |
.Build(); |
||||
|
|
||||
host.Run(); |
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,52 @@ |
|||||
|
using ApplicationCore.Interfaces; |
||||
|
using Infrastructure.Identity; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Identity; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.eShopWeb.ViewModels; |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Web.ViewComponents |
||||
|
{ |
||||
|
public class Basket : ViewComponent |
||||
|
{ |
||||
|
private readonly IBasketService _basketService; |
||||
|
private readonly SignInManager<ApplicationUser> _signInManager; |
||||
|
|
||||
|
public Basket(IBasketService basketService, |
||||
|
SignInManager<ApplicationUser> signInManager) |
||||
|
{ |
||||
|
_basketService = basketService; |
||||
|
_signInManager = signInManager; |
||||
|
} |
||||
|
|
||||
|
public async Task<IViewComponentResult> InvokeAsync(string userName) |
||||
|
{ |
||||
|
var vm = new BasketComponentViewModel(); |
||||
|
vm.ItemsCount = (await GetBasketViewModelAsync()).Items.Sum(i => i.Quantity); |
||||
|
return View(vm); |
||||
|
} |
||||
|
|
||||
|
private async Task<BasketViewModel> GetBasketViewModelAsync() |
||||
|
{ |
||||
|
if (_signInManager.IsSignedIn(HttpContext.User)) |
||||
|
{ |
||||
|
return await _basketService.GetOrCreateBasketForUser(User.Identity.Name); |
||||
|
} |
||||
|
string anonymousId = GetBasketIdFromCookie(); |
||||
|
if (anonymousId == null) return new BasketViewModel(); |
||||
|
return await _basketService.GetOrCreateBasketForUser(anonymousId); |
||||
|
} |
||||
|
|
||||
|
private string GetBasketIdFromCookie() |
||||
|
{ |
||||
|
if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME)) |
||||
|
{ |
||||
|
return Request.Cookies[Constants.BASKET_COOKIENAME]; |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Microsoft.eShopWeb.ViewModels |
||||
|
{ |
||||
|
public class BasketComponentViewModel |
||||
|
{ |
||||
|
public int ItemsCount { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
|
||||
|
namespace Microsoft.eShopWeb.ViewModels |
||||
|
{ |
||||
|
|
||||
|
public class OrderItemViewModel |
||||
|
{ |
||||
|
public int ProductId { get; set; } |
||||
|
|
||||
|
public string ProductName { get; set; } |
||||
|
|
||||
|
public decimal UnitPrice { get; set; } |
||||
|
|
||||
|
public decimal Discount { get; set; } |
||||
|
|
||||
|
public int Units { get; set; } |
||||
|
|
||||
|
public string PictureUrl { get; set; } |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using ApplicationCore.Entities.OrderAggregate; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Microsoft.eShopWeb.ViewModels |
||||
|
{ |
||||
|
|
||||
|
public class OrderViewModel |
||||
|
{ |
||||
|
public int OrderNumber { get; set; } |
||||
|
public DateTimeOffset OrderDate { get; set; } |
||||
|
public decimal Total { get; set; } |
||||
|
public string Status { get; set; } |
||||
|
|
||||
|
public Address ShippingAddress { get; set; } |
||||
|
|
||||
|
public List<OrderItemViewModel> OrderItems { get; set; } = new List<OrderItemViewModel>(); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,88 @@ |
|||||
|
@using Microsoft.eShopWeb.ViewModels |
||||
|
@model OrderViewModel |
||||
|
@{ |
||||
|
ViewData["Title"] = "My Order History"; |
||||
|
} |
||||
|
@{ |
||||
|
ViewData["Title"] = "Order Detail"; |
||||
|
} |
||||
|
|
||||
|
<div class="esh-orders_detail"> |
||||
|
<div class="container"> |
||||
|
<section class="esh-orders_detail-section"> |
||||
|
<article class="esh-orders_detail-titles row"> |
||||
|
<section class="esh-orders_detail-title col-xs-3">Order number</section> |
||||
|
<section class="esh-orders_detail-title col-xs-3">Date</section> |
||||
|
<section class="esh-orders_detail-title col-xs-3">Total</section> |
||||
|
<section class="esh-orders_detail-title col-xs-3">Status</section> |
||||
|
</article> |
||||
|
|
||||
|
<article class="esh-orders_detail-items row"> |
||||
|
<section class="esh-orders_detail-item col-xs-3">@Model.OrderNumber</section> |
||||
|
<section class="esh-orders_detail-item col-xs-3">@Model.OrderDate</section> |
||||
|
<section class="esh-orders_detail-item col-xs-3">$@Model.Total</section> |
||||
|
<section class="esh-orders_detail-title col-xs-3">@Model.Status</section> |
||||
|
</article> |
||||
|
</section> |
||||
|
|
||||
|
@*<section class="esh-orders_detail-section"> |
||||
|
<article class="esh-orders_detail-titles row"> |
||||
|
<section class="esh-orders_detail-title col-xs-12">Description</section> |
||||
|
</article> |
||||
|
|
||||
|
<article class="esh-orders_detail-items row"> |
||||
|
<section class="esh-orders_detail-item col-xs-12">@Model.Description</section> |
||||
|
</article> |
||||
|
</section>*@ |
||||
|
|
||||
|
<section class="esh-orders_detail-section"> |
||||
|
<article class="esh-orders_detail-titles row"> |
||||
|
<section class="esh-orders_detail-title col-xs-12">Shipping Address</section> |
||||
|
</article> |
||||
|
|
||||
|
<article class="esh-orders_detail-items row"> |
||||
|
<section class="esh-orders_detail-item col-xs-12">@Model.ShippingAddress.Street</section> |
||||
|
</article> |
||||
|
|
||||
|
<article class="esh-orders_detail-items row"> |
||||
|
<section class="esh-orders_detail-item col-xs-12">@Model.ShippingAddress.City</section> |
||||
|
</article> |
||||
|
|
||||
|
<article class="esh-orders_detail-items row"> |
||||
|
<section class="esh-orders_detail-item col-xs-12">@Model.ShippingAddress.Country</section> |
||||
|
</article> |
||||
|
</section> |
||||
|
|
||||
|
<section class="esh-orders_detail-section"> |
||||
|
<article class="esh-orders_detail-titles row"> |
||||
|
<section class="esh-orders_detail-title col-xs-12">ORDER DETAILS</section> |
||||
|
</article> |
||||
|
|
||||
|
@for (int i = 0; i < Model.OrderItems.Count; i++) |
||||
|
{ |
||||
|
var item = Model.OrderItems[i]; |
||||
|
<article class="esh-orders_detail-items esh-orders_detail-items--border row"> |
||||
|
<section class="esh-orders_detail-item col-md-4 hidden-md-down"> |
||||
|
<img class="esh-orders_detail-image" src="@item.PictureUrl"> |
||||
|
</section> |
||||
|
<section class="esh-orders_detail-item esh-orders_detail-item--middle col-xs-4">@item.ProductName</section> |
||||
|
<section class="esh-orders_detail-item esh-orders_detail-item--middle col-xs-1">$ @item.UnitPrice.ToString("N2")</section> |
||||
|
<section class="esh-orders_detail-item esh-orders_detail-item--middle col-xs-1">@item.Units</section> |
||||
|
<section class="esh-orders_detail-item esh-orders_detail-item--middle col-xs-2">$ @Math.Round(item.Units * item.UnitPrice, 2).ToString("N2")</section> |
||||
|
</article> |
||||
|
} |
||||
|
</section> |
||||
|
|
||||
|
<section class="esh-orders_detail-section esh-orders_detail-section--right"> |
||||
|
<article class="esh-orders_detail-titles esh-basket-titles--clean row"> |
||||
|
<section class="esh-orders_detail-title col-xs-9"></section> |
||||
|
<section class="esh-orders_detail-title col-xs-2">TOTAL</section> |
||||
|
</article> |
||||
|
|
||||
|
<article class="esh-orders_detail-items row"> |
||||
|
<section class="esh-orders_detail-item col-xs-9"></section> |
||||
|
<section class="esh-orders_detail-item esh-orders_detail-item--mark col-xs-2">$ @Model.Total</section> |
||||
|
</article> |
||||
|
</section> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,39 @@ |
|||||
|
@using Microsoft.eShopWeb.ViewModels |
||||
|
@model IEnumerable<OrderViewModel> |
||||
|
@{ |
||||
|
ViewData["Title"] = "My Order History"; |
||||
|
} |
||||
|
|
||||
|
<div class="esh-orders"> |
||||
|
<div class="container"> |
||||
|
<h1>@ViewData["Title"]</h1> |
||||
|
<article class="esh-orders-titles row"> |
||||
|
<section class="esh-orders-title col-xs-2">Order number</section> |
||||
|
<section class="esh-orders-title col-xs-4">Date</section> |
||||
|
<section class="esh-orders-title col-xs-2">Total</section> |
||||
|
<section class="esh-orders-title col-xs-2">Status</section> |
||||
|
<section class="esh-orders-title col-xs-2"></section> |
||||
|
</article> |
||||
|
@if (Model != null && Model.Any()) |
||||
|
{ |
||||
|
@foreach (var item in Model) |
||||
|
{ |
||||
|
<article class="esh-orders-items row"> |
||||
|
<section class="esh-orders-item col-xs-2">@Html.DisplayFor(modelItem => item.OrderNumber)</section> |
||||
|
<section class="esh-orders-item col-xs-4">@Html.DisplayFor(modelItem => item.OrderDate)</section> |
||||
|
<section class="esh-orders-item col-xs-2">$ @Html.DisplayFor(modelItem => item.Total)</section> |
||||
|
<section class="esh-orders-item col-xs-2">@Html.DisplayFor(modelItem => item.Status)</section> |
||||
|
<section class="esh-orders-item col-xs-1"> |
||||
|
<a class="esh-orders-link" asp-controller="Order" asp-action="Detail" asp-route-orderId="@item.OrderNumber">Detail</a> |
||||
|
</section> |
||||
|
<section class="esh-orders-item col-xs-1"> |
||||
|
@if (item.Status.ToLower() == "submitted") |
||||
|
{ |
||||
|
<a class="esh-orders-link" asp-controller="Order" asp-action="cancel" asp-route-orderId="@item.OrderNumber">Cancel</a> |
||||
|
} |
||||
|
</section> |
||||
|
</article> |
||||
|
} |
||||
|
} |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,17 @@ |
|||||
|
@model BasketComponentViewModel |
||||
|
|
||||
|
@{ |
||||
|
ViewData["Title"] = "My Basket"; |
||||
|
} |
||||
|
|
||||
|
<a class="esh-basketstatus " |
||||
|
asp-area="" |
||||
|
asp-controller="Basket" |
||||
|
asp-action="Index"> |
||||
|
<div class="esh-basketstatus-image"> |
||||
|
<img src="~/images/cart.png" /> |
||||
|
</div> |
||||
|
<div class="esh-basketstatus-badge"> |
||||
|
@Model.ItemsCount |
||||
|
</div> |
||||
|
</a> |
||||
@ -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" |
||||
|
} |
||||
|
] |
||||
@ -0,0 +1,65 @@ |
|||||
|
// 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-warning: #ff0000; |
||||
|
$color-warning-dark: darken($color-warning, 5%); |
||||
|
$color-warning-darker: darken($color-warning, 20%); |
||||
|
$color-warning-bright: lighten($color-warning, 10%); |
||||
|
$color-warning-brighter: lighten($color-warning, 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'; |
||||
@ -0,0 +1,11 @@ |
|||||
|
.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; } |
||||
|
|
||||
@ -0,0 +1 @@ |
|||||
|
.esh-app-footer{background-color:#000;border-top:1px solid #eee;margin-top:2.5rem;padding-bottom:2.5rem;padding-top:2.5rem;width:100%}.esh-app-footer-brand{height:50px;width:230px} |
||||
@ -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; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
.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-image { |
||||
|
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; } |
||||
|
.esh-basketstatus-badge-inoperative { |
||||
|
background-color: #ff0000; |
||||
|
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; } |
||||
|
|
||||
@ -0,0 +1 @@ |
|||||
|
.esh-basketstatus{cursor:pointer;display:inline-block;float:right;position:relative;transition:all .35s}.esh-basketstatus.is-disabled{opacity:.5;pointer-events:none}.esh-basketstatus-image{height:36px;margin-top:.5rem}.esh-basketstatus-badge{background-color:#83d01b;border-radius:50%;color:#fff;display:block;height:1.5rem;left:50%;position:absolute;text-align:center;top:0;transform:translateX(-38%);transition:all .35s;width:1.5rem}.esh-basketstatus-badge-inoperative{background-color:#f00;border-radius:50%;color:#fff;display:block;height:1.5rem;left:50%;position:absolute;text-align:center;top:0;transform:translateX(-38%);transition:all .35s;width:1.5rem}.esh-basketstatus:hover .esh-basketstatus-badge{background-color:transparent;color:#75b918;transition:all .35s} |
||||
@ -0,0 +1,57 @@ |
|||||
|
@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; |
||||
|
} |
||||
|
|
||||
|
&-badge-inoperative { |
||||
|
$size: 1.5rem; |
||||
|
background-color: $color-warning; |
||||
|
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; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,49 @@ |
|||||
|
.esh-basket { |
||||
|
min-height: 80vh; } |
||||
|
.esh-basket-titles { |
||||
|
padding-bottom: 1rem; |
||||
|
padding-top: 2rem; } |
||||
|
.esh-basket-titles--clean { |
||||
|
padding-bottom: 0; |
||||
|
padding-top: 0; } |
||||
|
.esh-basket-title { |
||||
|
text-transform: uppercase; } |
||||
|
.esh-basket-items--border { |
||||
|
border-bottom: 1px solid #EEEEEE; |
||||
|
padding: .5rem 0; } |
||||
|
.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; } |
||||
|
.esh-basket-item--middle { |
||||
|
line-height: 8rem; } |
||||
|
@media screen and (max-width: 1024px) { |
||||
|
.esh-basket-item--middle { |
||||
|
line-height: 1rem; } } |
||||
|
.esh-basket-item--mark { |
||||
|
color: #00A69C; } |
||||
|
.esh-basket-image { |
||||
|
height: 8rem; } |
||||
|
.esh-basket-input { |
||||
|
line-height: 1rem; |
||||
|
width: 100%; } |
||||
|
.esh-basket-checkout { |
||||
|
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; } |
||||
|
|
||||
@ -0,0 +1 @@ |
|||||
|
.esh-basket{min-height:80vh}.esh-basket-titles{padding-bottom:1rem;padding-top:2rem}.esh-basket-titles--clean{padding-bottom:0;padding-top:0}.esh-basket-title{text-transform:uppercase}.esh-basket-items--border{border-bottom:1px solid #eee;padding:.5rem 0}.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}.esh-basket-item--middle{line-height:8rem}@media screen and (max-width:1024px){.esh-basket-item--middle{line-height:1rem}}.esh-basket-item--mark{color:#00a69c}.esh-basket-image{height:8rem}.esh-basket-input{line-height:1rem;width:100%}.esh-basket-checkout{background-color:#83d01b;border:0;border-radius:0;color:#fff;display:inline-block;font-size:1rem;font-weight:400;margin-top:1rem;padding:1rem 1.5rem;text-align:center;text-transform:uppercase;transition:all .35s}.esh-basket-checkout:hover{background-color:#4a760f;transition:all .35s} |
||||
@ -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,228 +1,117 @@ |
|||||
.esh-catalog-hero { |
.esh-catalog-hero { |
||||
background-image: url("../../images/main_banner.png"); |
background-image: url("../../images/main_banner.png"); |
||||
background-size: cover; |
background-size: cover; |
||||
height: 260px; |
height: 260px; |
||||
width: 100%; |
width: 100%; } |
||||
} |
|
||||
|
|
||||
.esh-catalog-title { |
.esh-catalog-title { |
||||
position: relative; |
position: relative; |
||||
top: 74.28571px; |
top: 74.28571px; } |
||||
} |
|
||||
|
|
||||
.esh-catalog-filters { |
.esh-catalog-filters { |
||||
background-color: #00A69C; |
background-color: #00A69C; |
||||
height: 65px; |
height: 65px; } |
||||
} |
|
||||
|
|
||||
.esh-catalog-filter { |
.esh-catalog-filter { |
||||
background-color: transparent; |
-webkit-appearance: none; |
||||
border-color: #00d9cc; |
background-color: transparent; |
||||
color: #FFFFFF; |
border-color: #00d9cc; |
||||
cursor: pointer; |
color: #FFFFFF; |
||||
margin-right: 1rem; |
cursor: pointer; |
||||
margin-top: .5rem; |
margin-right: 1rem; |
||||
outline-color: #83D01B; |
margin-top: .5rem; |
||||
padding-bottom: 0; |
min-width: 140px; |
||||
padding-left: 0.5rem; |
outline-color: #83D01B; |
||||
padding-right: 0.5rem; |
padding-bottom: 0; |
||||
padding-top: 1.5rem; |
padding-left: 0.5rem; |
||||
min-width: 140px; |
padding-right: 0.5rem; |
||||
-webkit-appearance: none; |
padding-top: 1.5rem; } |
||||
} |
.esh-catalog-filter option { |
||||
|
background-color: #00A69C; } |
||||
.esh-catalog-filter option { |
|
||||
background-color: #00A69C; |
|
||||
} |
|
||||
|
|
||||
.esh-catalog-label { |
.esh-catalog-label { |
||||
display: inline-block; |
display: inline-block; |
||||
position: relative; |
position: relative; |
||||
z-index: 0; |
z-index: 0; } |
||||
} |
.esh-catalog-label::before { |
||||
|
color: rgba(255, 255, 255, 0.5); |
||||
.esh-catalog-label::before { |
content: attr(data-title); |
||||
color: rgba(255, 255, 255, 0.5); |
font-size: 0.65rem; |
||||
content: attr(data-title); |
margin-left: 0.5rem; |
||||
font-size: 0.65rem; |
margin-top: 0.65rem; |
||||
margin-top: 0.65rem; |
position: absolute; |
||||
margin-left: 0.5rem; |
text-transform: uppercase; |
||||
position: absolute; |
z-index: 1; } |
||||
text-transform: uppercase; |
.esh-catalog-label::after { |
||||
z-index: 1; |
background-image: url("../../images/arrow-down.png"); |
||||
} |
content: ''; |
||||
|
height: 7px; |
||||
.esh-catalog-label::after { |
position: absolute; |
||||
background-image: url("../../images/arrow-down.png"); |
right: 1.5rem; |
||||
height: 7px; |
top: 2.5rem; |
||||
content: ''; |
width: 10px; |
||||
position: absolute; |
z-index: 1; } |
||||
right: 1.5rem; |
|
||||
top: 2.5rem; |
|
||||
width: 10px; |
|
||||
z-index: 1; |
|
||||
} |
|
||||
|
|
||||
.esh-catalog-send { |
.esh-catalog-send { |
||||
background-color: #83D01B; |
background-color: #83D01B; |
||||
color: #FFFFFF; |
color: #FFFFFF; |
||||
cursor: pointer; |
cursor: pointer; |
||||
font-size: 1rem; |
font-size: 1rem; |
||||
transform: translateY(.5rem); |
margin-top: -1.5rem; |
||||
padding: 0.5rem; |
padding: 0.5rem; |
||||
transition: all 0.35s; |
transition: all 0.35s; } |
||||
} |
.esh-catalog-send:hover { |
||||
|
background-color: #4a760f; |
||||
.esh-catalog-send:hover { |
transition: all 0.35s; } |
||||
background-color: #4a760f; |
|
||||
transition: all 0.35s; |
|
||||
} |
|
||||
|
|
||||
.esh-catalog-items { |
.esh-catalog-items { |
||||
margin-top: 1rem; |
margin-top: 1rem; } |
||||
} |
|
||||
|
|
||||
.esh-catalog-item { |
.esh-catalog-item { |
||||
text-align: center; |
margin-bottom: 1.5rem; |
||||
margin-bottom: 1.5rem; |
text-align: center; |
||||
width: 33%; |
width: 33%; |
||||
display: inline-block; |
display: inline-block; |
||||
float: none !important; |
float: none !important; } |
||||
} |
@media screen and (max-width: 1024px) { |
||||
|
|
||||
@media screen and (max-width: 1024px) { |
|
||||
.esh-catalog-item { |
.esh-catalog-item { |
||||
width: 50%; |
width: 50%; } } |
||||
} |
@media screen and (max-width: 768px) { |
||||
} |
|
||||
|
|
||||
@media screen and (max-width: 768px) { |
|
||||
.esh-catalog-item { |
.esh-catalog-item { |
||||
width: 100%; |
width: 100%; } } |
||||
} |
|
||||
} |
|
||||
|
|
||||
.esh-catalog-thumbnail { |
.esh-catalog-thumbnail { |
||||
max-width: 370px; |
max-width: 370px; |
||||
width: 100%; |
width: 100%; } |
||||
} |
|
||||
|
|
||||
.esh-catalog-button { |
.esh-catalog-button { |
||||
background-color: #83D01B; |
background-color: #83D01B; |
||||
border: none; |
border: 0; |
||||
color: #FFFFFF; |
color: #FFFFFF; |
||||
cursor: pointer; |
cursor: pointer; |
||||
font-size: 1rem; |
font-size: 1rem; |
||||
height: 3rem; |
height: 3rem; |
||||
margin-top: 1rem; |
margin-top: 1rem; |
||||
transition: all 0.35s; |
transition: all 0.35s; |
||||
width: 80%; |
width: 80%; } |
||||
} |
.esh-catalog-button.is-disabled { |
||||
.esh-catalog-button.is-disabled { |
opacity: .5; |
||||
opacity: .5; |
pointer-events: none; } |
||||
pointer-events: none; |
.esh-catalog-button:hover { |
||||
} |
background-color: #4a760f; |
||||
|
transition: all 0.35s; } |
||||
.esh-catalog-button:hover { |
|
||||
background-color: #4a760f; |
|
||||
transition: all 0.35s; |
|
||||
} |
|
||||
|
|
||||
.esh-catalog-name { |
.esh-catalog-name { |
||||
font-size: 1rem; |
font-size: 1rem; |
||||
font-weight: 300; |
font-weight: 300; |
||||
margin-top: .5rem; |
margin-top: .5rem; |
||||
text-align: center; |
text-align: center; |
||||
text-transform: uppercase; |
text-transform: uppercase; } |
||||
} |
|
||||
|
|
||||
.esh-catalog-price { |
.esh-catalog-price { |
||||
text-align: center; |
font-size: 28px; |
||||
font-weight: 900; |
font-weight: 900; |
||||
font-size: 28px; |
text-align: center; } |
||||
} |
.esh-catalog-price::before { |
||||
|
content: '$'; } |
||||
.esh-catalog-price::before { |
|
||||
content: '$'; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
|
||||
.esh-basket { |
|
||||
min-height: 80vh; |
|
||||
} |
|
||||
|
|
||||
.esh-basket-titles { |
|
||||
padding-bottom: 1rem; |
|
||||
padding-top: 2rem; |
|
||||
} |
|
||||
|
|
||||
.esh-basket-titles--clean { |
|
||||
padding-bottom: 0; |
|
||||
padding-top: 0; |
|
||||
} |
|
||||
|
|
||||
.esh-basket-title { |
|
||||
text-transform: uppercase; |
|
||||
} |
|
||||
|
|
||||
.esh-basket-items--border { |
|
||||
border-bottom: 1px solid #EEEEEE; |
|
||||
padding: .5rem 0; |
|
||||
} |
|
||||
|
|
||||
.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; |
|
||||
} |
|
||||
|
|
||||
.esh-basket-item--middle { |
|
||||
line-height: 8rem; |
|
||||
} |
|
||||
|
|
||||
@media screen and (max-width: 1024px) { |
|
||||
.esh-basket-item--middle { |
|
||||
line-height: 1rem; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.esh-basket-item--mark { |
|
||||
color: #00A69C; |
|
||||
} |
|
||||
|
|
||||
.esh-basket-image { |
|
||||
height: 8rem; |
|
||||
} |
|
||||
|
|
||||
.esh-basket-input { |
|
||||
line-height: 1rem; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
.esh-basket-checkout { |
|
||||
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; |
|
||||
} |
|
||||
@ -0,0 +1 @@ |
|||||
|
.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}.esh-catalog-filters{background-color:#00a69c;height:65px}.esh-catalog-filter{-webkit-appearance:none;background-color:transparent;border-color:#00d9cc;color:#fff;cursor:pointer;margin-right:1rem;margin-top:.5rem;min-width:140px;outline-color:#83d01b;padding-bottom:0;padding-left:.5rem;padding-right:.5rem;padding-top:1.5rem}.esh-catalog-filter option{background-color:#00a69c}.esh-catalog-label{display:inline-block;position:relative;z-index:0}.esh-catalog-label::before{color:rgba(255,255,255,.5);content:attr(data-title);font-size:.65rem;margin-left:.5rem;margin-top:.65rem;position:absolute;text-transform:uppercase;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:#fff;cursor:pointer;font-size:1rem;margin-top:-1.5rem;padding:.5rem;transition:all .35s}.esh-catalog-send:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-items{margin-top:1rem}.esh-catalog-item{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%}}@media screen and (max-width:768px){.esh-catalog-item{width:100%}}.esh-catalog-thumbnail{max-width:370px;width:100%}.esh-catalog-button{background-color:#83d01b;border:0;color:#fff;cursor:pointer;font-size:1rem;height:3rem;margin-top:1rem;transition:all .35s;width:80%}.esh-catalog-button.is-disabled{opacity:.5;pointer-events:none}.esh-catalog-button:hover{background-color:#4a760f;transition:all .35s}.esh-catalog-name{font-size:1rem;font-weight:300;margin-top:.5rem;text-align:center;text-transform:uppercase}.esh-catalog-price{font-size:28px;font-weight:900;text-align:center}.esh-catalog-price::before{content:'$'} |
||||
@ -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: '$'; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,50 @@ |
|||||
|
.esh-orders { |
||||
|
min-height: 80vh; |
||||
|
overflow-x: hidden; } |
||||
|
.esh-orders-header { |
||||
|
background-color: #00A69C; |
||||
|
height: 4rem; } |
||||
|
.esh-orders-back { |
||||
|
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-titles { |
||||
|
padding-bottom: 1rem; |
||||
|
padding-top: 2rem; } |
||||
|
.esh-orders-title { |
||||
|
text-transform: uppercase; } |
||||
|
.esh-orders-items { |
||||
|
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-item { |
||||
|
font-weight: 300; } |
||||
|
.esh-orders-item--hover { |
||||
|
opacity: 0; |
||||
|
pointer-events: none; } |
||||
|
.esh-orders-items:hover .esh-orders-item--hover { |
||||
|
opacity: 1; |
||||
|
pointer-events: all; } |
||||
|
.esh-orders-link { |
||||
|
color: #83D01B; |
||||
|
text-decoration: none; |
||||
|
transition: color 0.35s; } |
||||
|
.esh-orders-link:hover { |
||||
|
color: #75b918; |
||||
|
transition: color 0.35s; } |
||||
|
|
||||
@ -0,0 +1 @@ |
|||||
|
.esh-orders{min-height:80vh;overflow-x:hidden}.esh-orders-header{background-color:#00a69c;height:4rem}.esh-orders-back{color:rgba(255,255,255,.4);line-height:4rem;text-decoration:none;text-transform:uppercase;transition:color .35s}.esh-orders-back:hover{color:#fff;transition:color .35s}.esh-orders-titles{padding-bottom:1rem;padding-top:2rem}.esh-orders-title{text-transform:uppercase}.esh-orders-items{height:2rem;line-height:2rem;position:relative}.esh-orders-items:nth-of-type(2n+1):before{background-color:#eef;content:'';height:100%;left:0;margin-left:-100vw;position:absolute;top:0;width:200vw;z-index:-1}.esh-orders-item{font-weight:300}.esh-orders-item--hover{opacity:0;pointer-events:none}.esh-orders-items:hover .esh-orders-item--hover{opacity:1;pointer-events:all}.esh-orders-link{color:#83d01b;text-decoration:none;transition:color .35s}.esh-orders-link:hover{color:#75b918;transition:color .35s} |
||||
@ -0,0 +1,91 @@ |
|||||
|
@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; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
Before Width: | Height: | Size: 5.1 KiB |
@ -1,47 +0,0 @@ |
|||||
using Infrastructure.FileSystem; |
|
||||
using Microsoft.AspNetCore.Hosting; |
|
||||
using System.IO; |
|
||||
using Xunit; |
|
||||
using Moq; |
|
||||
|
|
||||
namespace IntegrationTests.Infrastructure.File |
|
||||
{ |
|
||||
public class LocalFileImageServiceGetImageBytesById |
|
||||
{ |
|
||||
private byte[] _testBytes = new byte[] { 0x01, 0x02, 0x03 }; |
|
||||
private readonly Mock<IHostingEnvironment> _mockEnvironment = new Mock<IHostingEnvironment>(); |
|
||||
private int _testImageId = 123; |
|
||||
private string _testFileName = "123.png"; |
|
||||
|
|
||||
public LocalFileImageServiceGetImageBytesById() |
|
||||
{ |
|
||||
// create folder if necessary
|
|
||||
Directory.CreateDirectory(Path.Combine(GetFileDirectory(), "Pics")); |
|
||||
|
|
||||
string filePath = GetFilePath(_testFileName); |
|
||||
System.IO.File.WriteAllBytes(filePath, _testBytes); |
|
||||
_mockEnvironment.SetupGet<string>(m => m.ContentRootPath).Returns(GetFileDirectory()); |
|
||||
} |
|
||||
|
|
||||
private string GetFilePath(string fileName) |
|
||||
{ |
|
||||
return Path.Combine(GetFileDirectory(), "Pics", fileName); |
|
||||
} |
|
||||
|
|
||||
private string GetFileDirectory() |
|
||||
{ |
|
||||
var location = System.Reflection.Assembly.GetEntryAssembly().Location; |
|
||||
return Path.GetDirectoryName(location); |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void ReturnsFileContentResultGivenValidId() |
|
||||
{ |
|
||||
var fileService = new LocalFileImageService(_mockEnvironment.Object); |
|
||||
|
|
||||
var result = fileService.GetImageBytesById(_testImageId); |
|
||||
|
|
||||
Assert.Equal(_testBytes, result); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue