Browse Source
* 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.main
committed by
GitHub
45 changed files with 1022 additions and 480 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,13 @@ |
|||||
|
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 string CatalogItemId { get; private set; } |
||||
|
public string ProductName { get; private set; } |
||||
|
public string PictureUri { get; private set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
using ApplicationCore.Interfaces; |
||||
|
using Microsoft.eShopWeb.ApplicationCore.Entities; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace ApplicationCore.Entities.OrderAggregate |
||||
|
{ |
||||
|
public class Order : BaseEntity, IAggregateRoot |
||||
|
{ |
||||
|
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 OrderAggrergateRoot.AddOrderItem() which includes behaviour.
|
||||
|
private readonly List<OrderItem> _orderItems; |
||||
|
|
||||
|
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 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); |
|
||||
} |
|
||||
} |
|
||||
@ -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,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,27 @@ |
|||||
|
using ApplicationCore.Interfaces; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.eShopWeb.ViewModels; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace Web.ViewComponents |
||||
|
{ |
||||
|
public class Basket : ViewComponent |
||||
|
{ |
||||
|
private readonly IBasketService _cartSvc; |
||||
|
|
||||
|
public Basket(IBasketService cartSvc) => _cartSvc = cartSvc; |
||||
|
|
||||
|
public async Task<IViewComponentResult> InvokeAsync(string userName) |
||||
|
{ |
||||
|
var vm = new BasketComponentViewModel(); |
||||
|
var itemsInCart = await ItemsInBasketAsync(userName); |
||||
|
vm.ItemsCount = itemsInCart; |
||||
|
return View(vm); |
||||
|
} |
||||
|
private async Task<int> ItemsInBasketAsync(string userName) |
||||
|
{ |
||||
|
var basket = await _cartSvc.GetOrCreateBasketForUser(userName); |
||||
|
return basket.Items.Count; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Microsoft.eShopWeb.ViewModels |
||||
|
{ |
||||
|
public class BasketComponentViewModel |
||||
|
{ |
||||
|
public int ItemsCount { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
@model BasketComponentViewModel |
||||
|
|
||||
|
@{ |
||||
|
ViewData["Title"] = "My Basket"; |
||||
|
} |
||||
|
|
||||
|
<a class="esh-basketstatus " |
||||
|
asp-area="" |
||||
|
asp-controller="Cart" |
||||
|
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: '$'; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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