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.Extensions.Logging; |
|||
using Microsoft.AspNetCore.Hosting; |
|||
using Microsoft.AspNetCore; |
|||
|
|||
namespace Microsoft.eShopWeb |
|||
{ |
|||
public class Program |
|||
{ |
|||
|
|||
public static void Main(string[] args) |
|||
{ |
|||
var host = new WebHostBuilder() |
|||
.UseKestrel() |
|||
BuildWebHost(args).Run(); |
|||
} |
|||
|
|||
public static IWebHost BuildWebHost(string[] args) => |
|||
WebHost.CreateDefaultBuilder(args) |
|||
.UseUrls("http://0.0.0.0:5106") |
|||
.UseContentRoot(Directory.GetCurrentDirectory()) |
|||
.ConfigureLogging(factory => |
|||
{ |
|||
factory.AddConsole(LogLevel.Warning); |
|||
factory.AddDebug(); |
|||
}) |
|||
.UseIISIntegration() |
|||
.UseStartup<Startup>() |
|||
.UseApplicationInsights() |
|||
.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 { |
|||
background-image: url("../../images/main_banner.png"); |
|||
background-size: cover; |
|||
height: 260px; |
|||
width: 100%; |
|||
} |
|||
.esh-catalog-hero { |
|||
background-image: url("../../images/main_banner.png"); |
|||
background-size: cover; |
|||
height: 260px; |
|||
width: 100%; } |
|||
|
|||
.esh-catalog-title { |
|||
position: relative; |
|||
top: 74.28571px; |
|||
} |
|||
position: relative; |
|||
top: 74.28571px; } |
|||
|
|||
.esh-catalog-filters { |
|||
background-color: #00A69C; |
|||
height: 65px; |
|||
} |
|||
background-color: #00A69C; |
|||
height: 65px; } |
|||
|
|||
.esh-catalog-filter { |
|||
background-color: transparent; |
|||
border-color: #00d9cc; |
|||
color: #FFFFFF; |
|||
cursor: pointer; |
|||
margin-right: 1rem; |
|||
margin-top: .5rem; |
|||
outline-color: #83D01B; |
|||
padding-bottom: 0; |
|||
padding-left: 0.5rem; |
|||
padding-right: 0.5rem; |
|||
padding-top: 1.5rem; |
|||
min-width: 140px; |
|||
-webkit-appearance: none; |
|||
} |
|||
|
|||
.esh-catalog-filter option { |
|||
background-color: #00A69C; |
|||
} |
|||
-webkit-appearance: none; |
|||
background-color: transparent; |
|||
border-color: #00d9cc; |
|||
color: #FFFFFF; |
|||
cursor: pointer; |
|||
margin-right: 1rem; |
|||
margin-top: .5rem; |
|||
min-width: 140px; |
|||
outline-color: #83D01B; |
|||
padding-bottom: 0; |
|||
padding-left: 0.5rem; |
|||
padding-right: 0.5rem; |
|||
padding-top: 1.5rem; } |
|||
.esh-catalog-filter option { |
|||
background-color: #00A69C; } |
|||
|
|||
.esh-catalog-label { |
|||
display: inline-block; |
|||
position: relative; |
|||
z-index: 0; |
|||
} |
|||
|
|||
.esh-catalog-label::before { |
|||
color: rgba(255, 255, 255, 0.5); |
|||
content: attr(data-title); |
|||
font-size: 0.65rem; |
|||
margin-top: 0.65rem; |
|||
margin-left: 0.5rem; |
|||
position: absolute; |
|||
text-transform: uppercase; |
|||
z-index: 1; |
|||
} |
|||
|
|||
.esh-catalog-label::after { |
|||
background-image: url("../../images/arrow-down.png"); |
|||
height: 7px; |
|||
content: ''; |
|||
position: absolute; |
|||
right: 1.5rem; |
|||
top: 2.5rem; |
|||
width: 10px; |
|||
z-index: 1; |
|||
} |
|||
display: inline-block; |
|||
position: relative; |
|||
z-index: 0; } |
|||
.esh-catalog-label::before { |
|||
color: rgba(255, 255, 255, 0.5); |
|||
content: attr(data-title); |
|||
font-size: 0.65rem; |
|||
margin-left: 0.5rem; |
|||
margin-top: 0.65rem; |
|||
position: absolute; |
|||
text-transform: uppercase; |
|||
z-index: 1; } |
|||
.esh-catalog-label::after { |
|||
background-image: url("../../images/arrow-down.png"); |
|||
content: ''; |
|||
height: 7px; |
|||
position: absolute; |
|||
right: 1.5rem; |
|||
top: 2.5rem; |
|||
width: 10px; |
|||
z-index: 1; } |
|||
|
|||
.esh-catalog-send { |
|||
background-color: #83D01B; |
|||
color: #FFFFFF; |
|||
cursor: pointer; |
|||
font-size: 1rem; |
|||
transform: translateY(.5rem); |
|||
padding: 0.5rem; |
|||
transition: all 0.35s; |
|||
} |
|||
|
|||
.esh-catalog-send:hover { |
|||
background-color: #4a760f; |
|||
transition: all 0.35s; |
|||
} |
|||
background-color: #83D01B; |
|||
color: #FFFFFF; |
|||
cursor: pointer; |
|||
font-size: 1rem; |
|||
margin-top: -1.5rem; |
|||
padding: 0.5rem; |
|||
transition: all 0.35s; } |
|||
.esh-catalog-send:hover { |
|||
background-color: #4a760f; |
|||
transition: all 0.35s; } |
|||
|
|||
.esh-catalog-items { |
|||
margin-top: 1rem; |
|||
} |
|||
margin-top: 1rem; } |
|||
|
|||
.esh-catalog-item { |
|||
text-align: center; |
|||
margin-bottom: 1.5rem; |
|||
width: 33%; |
|||
display: inline-block; |
|||
float: none !important; |
|||
} |
|||
|
|||
@media screen and (max-width: 1024px) { |
|||
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) { |
|||
width: 50%; } } |
|||
@media screen and (max-width: 768px) { |
|||
.esh-catalog-item { |
|||
width: 100%; |
|||
} |
|||
} |
|||
width: 100%; } } |
|||
|
|||
.esh-catalog-thumbnail { |
|||
max-width: 370px; |
|||
width: 100%; |
|||
} |
|||
max-width: 370px; |
|||
width: 100%; } |
|||
|
|||
.esh-catalog-button { |
|||
background-color: #83D01B; |
|||
border: none; |
|||
color: #FFFFFF; |
|||
cursor: pointer; |
|||
font-size: 1rem; |
|||
height: 3rem; |
|||
margin-top: 1rem; |
|||
transition: all 0.35s; |
|||
width: 80%; |
|||
} |
|||
.esh-catalog-button.is-disabled { |
|||
opacity: .5; |
|||
pointer-events: none; |
|||
} |
|||
|
|||
.esh-catalog-button:hover { |
|||
background-color: #4a760f; |
|||
transition: all 0.35s; |
|||
} |
|||
background-color: #83D01B; |
|||
border: 0; |
|||
color: #FFFFFF; |
|||
cursor: pointer; |
|||
font-size: 1rem; |
|||
height: 3rem; |
|||
margin-top: 1rem; |
|||
transition: all 0.35s; |
|||
width: 80%; } |
|||
.esh-catalog-button.is-disabled { |
|||
opacity: .5; |
|||
pointer-events: none; } |
|||
.esh-catalog-button:hover { |
|||
background-color: #4a760f; |
|||
transition: all 0.35s; } |
|||
|
|||
.esh-catalog-name { |
|||
font-size: 1rem; |
|||
font-weight: 300; |
|||
margin-top: .5rem; |
|||
text-align: center; |
|||
text-transform: uppercase; |
|||
} |
|||
font-size: 1rem; |
|||
font-weight: 300; |
|||
margin-top: .5rem; |
|||
text-align: center; |
|||
text-transform: uppercase; } |
|||
|
|||
.esh-catalog-price { |
|||
text-align: center; |
|||
font-weight: 900; |
|||
font-size: 28px; |
|||
} |
|||
|
|||
.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; |
|||
} |
|||
font-size: 28px; |
|||
font-weight: 900; |
|||
text-align: center; } |
|||
.esh-catalog-price::before { |
|||
content: '$'; } |
|||
|
|||
.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