Cart Updates (#26)
* ardalis/cart-updates Updating how items are added to cart and displayed in cart. * Cleaning up UI
This commit is contained in:
@@ -8,19 +8,20 @@ namespace Microsoft.eShopWeb.ApplicationCore.Entities
|
|||||||
public string BuyerId { get; set; }
|
public string BuyerId { get; set; }
|
||||||
public List<BasketItem> Items { get; set; } = new List<BasketItem>();
|
public List<BasketItem> Items { get; set; } = new List<BasketItem>();
|
||||||
|
|
||||||
public void AddItem(int productId, decimal unitPrice, int quantity = 1)
|
public void AddItem(CatalogItem item, decimal unitPrice, int quantity = 1)
|
||||||
{
|
{
|
||||||
if(!Items.Any(i => i.ProductId == productId))
|
if(!Items.Any(i => i.Item.Id == item.Id))
|
||||||
{
|
{
|
||||||
Items.Add(new BasketItem()
|
Items.Add(new BasketItem()
|
||||||
{
|
{
|
||||||
ProductId = productId,
|
Item = item,
|
||||||
|
//ProductId = productId,
|
||||||
Quantity = quantity,
|
Quantity = quantity,
|
||||||
UnitPrice = unitPrice
|
UnitPrice = unitPrice
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var existingItem = Items.FirstOrDefault(i => i.ProductId == productId);
|
var existingItem = Items.FirstOrDefault(i => i.Item.Id == item.Id);
|
||||||
existingItem.Quantity += quantity;
|
existingItem.Quantity += quantity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
{
|
{
|
||||||
public class BasketItem : BaseEntity<string>
|
public class BasketItem : BaseEntity<string>
|
||||||
{
|
{
|
||||||
public int ProductId { get; set; }
|
//public int ProductId { get; set; }
|
||||||
public decimal UnitPrice { get; set; }
|
public decimal UnitPrice { get; set; }
|
||||||
public int Quantity { get; set; }
|
public int Quantity { get; set; }
|
||||||
|
public CatalogItem Item { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,5 @@
|
|||||||
public CatalogType CatalogType { get; set; }
|
public CatalogType CatalogType { get; set; }
|
||||||
public int CatalogBrandId { get; set; }
|
public int CatalogBrandId { get; set; }
|
||||||
public CatalogBrand CatalogBrand { get; set; }
|
public CatalogBrand CatalogBrand { get; set; }
|
||||||
public CatalogItem() { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,8 @@ namespace ApplicationCore.Interfaces
|
|||||||
Task<Basket> GetBasket(string basketId);
|
Task<Basket> GetBasket(string basketId);
|
||||||
Task<Basket> CreateBasket();
|
Task<Basket> CreateBasket();
|
||||||
Task<Basket> CreateBasketForUser(string userId);
|
Task<Basket> CreateBasketForUser(string userId);
|
||||||
Task UpdateBasket(Basket basket);
|
|
||||||
|
Task AddItemToBasket(Basket basket, int productId, int quantity);
|
||||||
|
//Task UpdateBasket(Basket basket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
8
src/ApplicationCore/Interfaces/IUriComposer.cs
Normal file
8
src/ApplicationCore/Interfaces/IUriComposer.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace ApplicationCore.Interfaces
|
||||||
|
{
|
||||||
|
|
||||||
|
public interface IUriComposer
|
||||||
|
{
|
||||||
|
string ComposePicUri(string uriTemplate);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/ApplicationCore/Services/UriComposer.cs
Normal file
20
src/ApplicationCore/Services/UriComposer.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using ApplicationCore.Interfaces;
|
||||||
|
using Microsoft.eShopWeb;
|
||||||
|
|
||||||
|
namespace ApplicationCore.Services
|
||||||
|
{
|
||||||
|
public class UriComposer : IUriComposer
|
||||||
|
{
|
||||||
|
private readonly CatalogSettings _catalogSettings;
|
||||||
|
|
||||||
|
public UriComposer(CatalogSettings catalogSettings)
|
||||||
|
{
|
||||||
|
_catalogSettings = catalogSettings;
|
||||||
|
}
|
||||||
|
public string ComposePicUri(string uriTemplate)
|
||||||
|
{
|
||||||
|
return uriTemplate.Replace("http://catalogbaseurltobereplaced", _catalogSettings.CatalogBaseUrl);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ using System.Threading.Tasks;
|
|||||||
using ApplicationCore.Interfaces;
|
using ApplicationCore.Interfaces;
|
||||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.eShopWeb.ViewModels;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb.Controllers
|
namespace Microsoft.eShopWeb.Controllers
|
||||||
{
|
{
|
||||||
@@ -11,12 +13,15 @@ namespace Microsoft.eShopWeb.Controllers
|
|||||||
private readonly IBasketService _basketService;
|
private readonly IBasketService _basketService;
|
||||||
//private readonly IIdentityParser<ApplicationUser> _appUserParser;
|
//private readonly IIdentityParser<ApplicationUser> _appUserParser;
|
||||||
private const string _basketSessionKey = "basketId";
|
private const string _basketSessionKey = "basketId";
|
||||||
|
private readonly IUriComposer _uriComposer;
|
||||||
|
|
||||||
public CartController(IBasketService basketService)
|
public CartController(IBasketService basketService,
|
||||||
|
IUriComposer uriComposer)
|
||||||
// IIdentityParser<ApplicationUser> appUserParser)
|
// IIdentityParser<ApplicationUser> appUserParser)
|
||||||
{
|
{
|
||||||
_basketService = basketService;
|
_basketService = basketService;
|
||||||
// _appUserParser = appUserParser;
|
_uriComposer = uriComposer;
|
||||||
|
// _appUserParser = appUserParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -26,7 +31,21 @@ namespace Microsoft.eShopWeb.Controllers
|
|||||||
//var user = _appUserParser.Parse(HttpContext.User);
|
//var user = _appUserParser.Parse(HttpContext.User);
|
||||||
var basket = await GetBasketFromSessionAsync();
|
var basket = await GetBasketFromSessionAsync();
|
||||||
|
|
||||||
return View(basket);
|
var viewModel = new BasketViewModel()
|
||||||
|
{
|
||||||
|
BuyerId = basket.BuyerId,
|
||||||
|
Items = basket.Items.Select(i => new BasketItemViewModel()
|
||||||
|
{
|
||||||
|
Id = i.Id,
|
||||||
|
UnitPrice = i.UnitPrice,
|
||||||
|
PictureUrl = _uriComposer.ComposePicUri(i.Item.PictureUri),
|
||||||
|
ProductId = i.Item.Id.ToString(),
|
||||||
|
ProductName = i.Item.Name,
|
||||||
|
Quantity = i.Quantity
|
||||||
|
}).ToList()
|
||||||
|
};
|
||||||
|
|
||||||
|
return View(viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET: /Cart/AddToCart
|
// GET: /Cart/AddToCart
|
||||||
@@ -39,9 +58,7 @@ namespace Microsoft.eShopWeb.Controllers
|
|||||||
}
|
}
|
||||||
var basket = await GetBasketFromSessionAsync();
|
var basket = await GetBasketFromSessionAsync();
|
||||||
|
|
||||||
basket.AddItem(productDetails.Id, productDetails.Price, 1);
|
await _basketService.AddItemToBasket(basket, productDetails.Id, 1);
|
||||||
|
|
||||||
await _basketService.UpdateBasket(basket);
|
|
||||||
|
|
||||||
return RedirectToAction("Index");
|
return RedirectToAction("Index");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Threading.Tasks;
|
|||||||
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||||
using Microsoft.eShopWeb.Infrastructure;
|
using Microsoft.eShopWeb.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Web.Services
|
namespace Web.Services
|
||||||
{
|
{
|
||||||
@@ -18,6 +19,7 @@ namespace Web.Services
|
|||||||
{
|
{
|
||||||
var basket = await _context.Baskets
|
var basket = await _context.Baskets
|
||||||
.Include(b => b.Items)
|
.Include(b => b.Items)
|
||||||
|
.ThenInclude(i => i.Item)
|
||||||
.FirstOrDefaultAsync(b => b.Id == basketId);
|
.FirstOrDefaultAsync(b => b.Id == basketId);
|
||||||
if (basket == null)
|
if (basket == null)
|
||||||
{
|
{
|
||||||
@@ -43,11 +45,19 @@ namespace Web.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task UpdateBasket(Basket basket)
|
//public async Task UpdateBasket(Basket basket)
|
||||||
|
//{
|
||||||
|
// // only need to save changes here
|
||||||
|
// await _context.SaveChangesAsync();
|
||||||
|
//}
|
||||||
|
|
||||||
|
public async Task AddItemToBasket(Basket basket, int productId, int quantity)
|
||||||
{
|
{
|
||||||
// only need to save changes here
|
var item = await _context.CatalogItems.FirstOrDefaultAsync(i => i.Id == productId);
|
||||||
|
|
||||||
|
basket.AddItem(item, item.Price, quantity);
|
||||||
|
|
||||||
await _context.SaveChangesAsync();
|
await _context.SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ using Infrastructure.FileSystem;
|
|||||||
using Infrastructure.Logging;
|
using Infrastructure.Logging;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Web.Services;
|
using Web.Services;
|
||||||
|
using ApplicationCore.Services;
|
||||||
|
|
||||||
namespace Microsoft.eShopWeb
|
namespace Microsoft.eShopWeb
|
||||||
{
|
{
|
||||||
@@ -71,7 +72,11 @@ namespace Microsoft.eShopWeb
|
|||||||
services.AddScoped<IBasketService, BasketService>();
|
services.AddScoped<IBasketService, BasketService>();
|
||||||
services.AddScoped<CatalogService>();
|
services.AddScoped<CatalogService>();
|
||||||
services.Configure<CatalogSettings>(Configuration);
|
services.Configure<CatalogSettings>(Configuration);
|
||||||
|
services.AddSingleton<IUriComposer>(new UriComposer(Configuration.Get<CatalogSettings>()));
|
||||||
|
|
||||||
|
// TODO: Remove
|
||||||
services.AddSingleton<IImageService, LocalFileImageService>();
|
services.AddSingleton<IImageService, LocalFileImageService>();
|
||||||
|
|
||||||
services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
|
services.AddScoped(typeof(IAppLogger<>), typeof(LoggerAdapter<>));
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
19
src/Web/ViewModels/BasketItemViewModel.cs
Normal file
19
src/Web/ViewModels/BasketItemViewModel.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Microsoft.eShopWeb.ApplicationCore.Entities;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.ViewModels
|
||||||
|
{
|
||||||
|
|
||||||
|
public class BasketItemViewModel
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string ProductId { get; set; }
|
||||||
|
public string ProductName { get; set; }
|
||||||
|
public decimal UnitPrice { get; set; }
|
||||||
|
public decimal OldUnitPrice { get; set; }
|
||||||
|
public int Quantity { get; set; }
|
||||||
|
public string PictureUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Web/ViewModels/BasketViewModel.cs
Normal file
18
src/Web/ViewModels/BasketViewModel.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.eShopWeb.ViewModels
|
||||||
|
{
|
||||||
|
|
||||||
|
public class BasketViewModel
|
||||||
|
{
|
||||||
|
public List<BasketItemViewModel> Items { get; set; } = new List<BasketItemViewModel>();
|
||||||
|
public string BuyerId { get; set; }
|
||||||
|
|
||||||
|
public decimal Total()
|
||||||
|
{
|
||||||
|
return Math.Round(Items.Sum(x => x.UnitPrice * x.Quantity), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
@using Microsoft.eShopWeb.ApplicationCore.Entities;
|
@using Microsoft.eShopWeb.ViewModels
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Catalog";
|
ViewData["Title"] = "Cart";
|
||||||
@model Basket
|
@model BasketViewModel
|
||||||
}
|
}
|
||||||
<section class="esh-catalog-hero">
|
<section class="esh-catalog-hero">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -13,11 +13,60 @@
|
|||||||
|
|
||||||
@if (Model.Items.Any())
|
@if (Model.Items.Any())
|
||||||
{
|
{
|
||||||
|
<article class="esh-basket-titles row">
|
||||||
|
<br />
|
||||||
|
<section class="esh-basket-title col-xs-3">Product</section>
|
||||||
|
<section class="esh-basket-title col-xs-3 hidden-lg-down"></section>
|
||||||
|
<section class="esh-basket-title col-xs-2">Price</section>
|
||||||
|
<section class="esh-basket-title col-xs-2">Quantity</section>
|
||||||
|
<section class="esh-basket-title col-xs-2">Cost</section>
|
||||||
|
</article>
|
||||||
<div class="esh-catalog-items row">
|
<div class="esh-catalog-items row">
|
||||||
@foreach (var item in Model.Items)
|
@foreach (var item in Model.Items)
|
||||||
{
|
{
|
||||||
<div class="esh-catalog-item col-md-4">
|
<article class="esh-basket-items row">
|
||||||
|
<div>
|
||||||
|
<section class="esh-basket-item esh-basket-item--middle col-lg-3 hidden-lg-down">
|
||||||
|
<img class="esh-basket-image" src="@item.PictureUrl" />
|
||||||
|
</section>
|
||||||
|
<section class="esh-basket-item esh-basket-item--middle col-xs-3">@item.ProductName</section>
|
||||||
|
<section class="esh-basket-item esh-basket-item--middle col-xs-2">$ @item.UnitPrice.ToString("N2")</section>
|
||||||
|
<section class="esh-basket-item esh-basket-item--middle col-xs-2">
|
||||||
|
<input type="hidden" name="@("quantities[" + item.Id +"].Key")" value="@item.Id" />
|
||||||
|
<input type="number" class="esh-basket-input" min="1" name="@("quantities[" + item.Id +"].Value")" value="@item.Quantity" />
|
||||||
|
</section>
|
||||||
|
<section class="esh-basket-item esh-basket-item--middle esh-basket-item--mark col-xs-2">$ @Math.Round(item.Quantity * item.UnitPrice, 2).ToString("N2")</section>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
@*<div class="esh-catalog-item col-md-4">
|
||||||
@item.ProductId
|
@item.ProductId
|
||||||
|
</div>*@
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<article class="esh-basket-titles esh-basket-titles--clean row">
|
||||||
|
<section class="esh-basket-title col-xs-10"></section>
|
||||||
|
<section class="esh-basket-title col-xs-2">Total</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-basket-items row">
|
||||||
|
<section class="esh-basket-item col-xs-10"></section>
|
||||||
|
<section class="esh-basket-item esh-basket-item--mark col-xs-2">$ @Model.Total()</section>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="esh-basket-items row">
|
||||||
|
<section class="esh-basket-item col-xs-7"></section>
|
||||||
|
<section class="esh-basket-item col-xs-2">
|
||||||
|
<button class="btn esh-basket-checkout" name="name" value="" type="submit">[ Update ]</button>
|
||||||
|
</section>
|
||||||
|
<section class="esh-basket-item col-xs-3">
|
||||||
|
<input type="submit"
|
||||||
|
class="btn esh-basket-checkout"
|
||||||
|
value="[ Checkout ]" name="action" />
|
||||||
|
</section>
|
||||||
|
</article>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>@ViewData["Title"] - Microsoft.eShopOnContainers.WebMVC</title>
|
<title>@ViewData["Title"] - Microsoft.eShopOnWeb</title>
|
||||||
|
|
||||||
<environment names="Development">
|
<environment names="Development">
|
||||||
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
|
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
|
||||||
|
|||||||
@@ -10,6 +10,13 @@
|
|||||||
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
|
<DockerComposeProjectPath>..\docker-compose.dcproj</DockerComposeProjectPath>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Pics\**" />
|
||||||
|
<Content Remove="Pics\**" />
|
||||||
|
<EmbeddedResource Remove="Pics\**" />
|
||||||
|
<None Remove="Pics\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Dapper" Version="1.50.2" />
|
<PackageReference Include="Dapper" Version="1.50.2" />
|
||||||
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.1" />
|
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.1" />
|
||||||
@@ -27,7 +34,6 @@
|
|||||||
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.2" />
|
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="1.1.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Pics\" />
|
|
||||||
<Folder Include="Views\Catalog\" />
|
<Folder Include="Views\Catalog\" />
|
||||||
<Folder Include="Views\Account\" />
|
<Folder Include="Views\Account\" />
|
||||||
<Folder Include="wwwroot\css\catalog\" />
|
<Folder Include="wwwroot\css\catalog\" />
|
||||||
|
|||||||
@@ -145,3 +145,84 @@
|
|||||||
.esh-catalog-price::before {
|
.esh-catalog-price::before {
|
||||||
content: '$';
|
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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user