Browse Source

Merge pull request #55 from abpframework/gterdem/basket_ui_enhancements

eShopOnAbp: Design Basket UI
pull/58/head
Galip Tolga Erdem 5 years ago
committed by GitHub
parent
commit
4369ae71f3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 86
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/Default.cshtml
  2. 67
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Purchase/Default.cshtml
  3. 1
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Basket.cshtml
  4. 130
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket/basket-widget.css
  5. 44
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket/basket-widget.js
  6. 12
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/purchase/purchase-widget.css
  7. 11
      shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/en.json
  8. 11
      shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/tr.json

86
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/Default.cshtml

@ -8,47 +8,75 @@
@if (!Model.Items.Any()) @if (!Model.Items.Any())
{ {
<div class="basket-list"> <div class="basket-list">
<p class="text-center"> <abp-row>
<strong> <abp-card>
<em>Your basket is empty. Click to a product to add it!</em> <abp-card-body>
</strong> <h3 style="text-align: center">@L["EmptyBasket"]</h3>
</p> <br/>
<div style="text-align: center">
<span>
@L["CheckOtherProducts"] <a href="/">@L["ContinueShopping"]</a>
</span>
</div>
</abp-card-body>
</abp-card>
</abp-row>
</div> </div>
} }
else else
{ {
<div class="row"> <div class="row">
<div class="col-lg-9"> <div class="col-lg-9">
<div class="basket-list"> <div class="basket-list flex-container vertical">
@foreach (var item in Model.Items) @foreach (var item in Model.Items)
{ {
<div class="basket-list-item" data-product-id="@item.ProductId"> string imageName = item.ImageName.IsNullOrEmpty() ? "/product-images/noimage.jpg" : $"/product-images/{item.ImageName}";
@if (!item.ImageName.IsNullOrEmpty()) var price = (item.TotalPrice * item.Count).ToString("0.00");
{ <div class="card basket-list-item" data-product-id="@item.ProductId">
<img src="/product-images/@item.ImageName"/> <div class="card-body">
} <div class="flex-container horizontal">
else <div class="image-container flex-item">
{ <img class="basket-item-image" src="@imageName"/>
<img src="/product-images/noimage.jpg"/> </div>
} <div class="flex-item">
@item.ProductName <div class="flex-container vertical description-container">
@if (item.Count > 1) <div class="flex-item description-header">
{ <span>@item.ProductName</span>
<text> (@("x" + item.Count))</text> </div>
} <div class="flex-item description-price">
- $@item.TotalPrice.ToString("0.00") <span>$@price</span>
<i class="fa fa-remove basket-item-remove" title="@L["RemoveFromBasket"]"></i> </div>
<div class="flex-item description-cargo">
<span><i class="fa fa-truck"></i> @L["FreeCargo"]</span>
</div>
</div>
</div>
<div class="remove-container hide flex-item">
<i class="fa fa-trash basket-item-remove" title="@L["RemoveFromBasket"]"></i>
</div>
<div class="actions-container flex-item">
<a href="" class="basket-item-increase">
<i class="fa fa-plus-circle"></i>
</a>
<span class="basket-item-count">@item.Count</span>
<a href="" class="basket-item-decrease">
@{
string removeIcon = item.Count <= 1 ? "fa-trash" : "fa-minus-circle";
}
<i class="fa @removeIcon"></i>
</a>
</div>
</div>
</div>
</div> </div>
<hr/>
} }
<hr/>
<div class="basket-total-area">
@L["Total"]: $@Model.TotalPrice.ToString("0.00")
</div>
</div> </div>
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3 sticky">
@await Component.InvokeAsync(typeof(PurchaseWidgetViewComponent), @await Component.InvokeAsync(typeof(PurchaseWidgetViewComponent),
new { Basket = Model, ButtonDescription = "CompletePurchase"}) new {Basket = Model, ButtonDescription = "CompletePurchase"})
</div> </div>
</div> </div>
} }

67
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Purchase/Default.cshtml

@ -4,18 +4,59 @@
@model PurchaseWidgetViewComponent.PurchaseViewModel @model PurchaseWidgetViewComponent.PurchaseViewModel
@inject IStringLocalizer<EShopOnAbpResource> L @inject IStringLocalizer<EShopOnAbpResource> L
<div> @{
<div class="row"> var totalCount = Model?.Basket.Items.Count ?? 0;
<h4>Total Selected (@Model.Basket.Items.Count)</h4> var totalPrice = Model.Basket.TotalPrice;
</div> var demoDiscountPercentage = 10;
<div class="row"> var discountTotal = totalPrice * demoDiscountPercentage / 100;
<h3>$@Model.Basket.TotalPrice.ToString("0.00")</h3> var grandTotal = totalPrice - discountTotal;
</div> }
<div class="row"> <div class="card">
<form method="post"> <div class="card-body">
@Html.AntiForgeryToken() <div style="text-align: center; padding-top: 25px" >
<abp-button type="submit" size="Large" class="basket-purchase-button" text="@L[Model.ButtonDescription]" button-type="Primary"/> <div class="row">
</form> <h5>@L["TotalSelected", totalCount]</h5>
</div>
<div class="row">
<h3>$@totalPrice.ToString("0.00")</h3>
</div>
</div>
<hr/>
<div class="row">
<h3>@L["Purchase:OrderSummary"]</h3>
</div>
<div class="summary">
<div class="row">
<div class="col-md-7">@L["Purchase:Products"]:</div>
<div class="col-md-5 m-result">@totalPrice.ToString("0.00") $</div>
</div>
<div class="row">
<div class="col-md-7">@L["Purchase:CargoAndPackaging"]:</div>
<div class="col-md-5 m-result">0.00 $</div>
</div>
<div class="row">
<div class="col-md-7">@L["Purchase:Total"]:</div>
<div class="col-md-5 m-result">@totalPrice.ToString("0.00") $</div>
</div>
<div class="row">
<div class="col-md-7">@L["Purchase:GiftCards"]:</div>
<div class="col-md-5 m-result">@discountTotal.ToString("0.00") $</div>
</div>
</div>
<hr/>
<div class="row">
<div class="col-md-7">
<h5>@L["Purchase:OrderTotal"]:</h5>
</div>
<div class="col-md-5 m-result order-total">
<h5>@grandTotal.ToString("0.00") $</h5>
</div>
</div>
<div class="row" style="padding: 25px; text-align: center">
<form method="post">
@Html.AntiForgeryToken()
<abp-button type="submit" size="Large" class="basket-purchase-button" text="@L[Model.ButtonDescription]" button-type="Warning"/>
</form>
</div>
</div> </div>
</div> </div>

1
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Basket.cshtml

@ -11,7 +11,6 @@
@inject IStringLocalizer<EShopOnAbpResource> L @inject IStringLocalizer<EShopOnAbpResource> L
<div class="row"> <div class="row">
@* <h3 class="pt-5 pb-4 text-center">@L["YourBasket"]</h3> *@
<div id="BasketArea" class="pt-5"> <div id="BasketArea" class="pt-5">
@await Component.InvokeAsync(typeof(BasketWidgetViewComponent)) @await Component.InvokeAsync(typeof(BasketWidgetViewComponent))
</div> </div>

130
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket/basket-widget.css

@ -1,42 +1,132 @@
.basket-list .basket-list {
{
}
.basket-list-item {
width:-webkit-fill-available;
background-color: #fff; background-color: #fff;
padding: 1em; border: 1px solid #eee;
border: 1px solid #666;
border-radius: 3px; border-radius: 3px;
box-shadow: 0 0 10px rgba(0, 0, 0, .05); box-shadow: 0 0 10px rgba(0, 0, 0, .05);
margin-bottom: 0.5em;
transition: all .2s; transition: all .2s;
cursor: default;
} }
.basket-list:hover { .basket-list-item:hover {
box-shadow: 0 0 20px rgba(0, 0, 0, .1); box-shadow: 0 0 20px rgba(0, 0, 0, .1);
} }
.basket-list-item { .flex-container {
margin-top: 1em; display: flex;
justify-content: space-between;
align-items: center;
}
.horizontal {
flex-direction: row;
width: 100%;
} }
.basket-list-item:first-child{ .horizontal .flex-item {
margin-top: 0; flex: 1;
} }
.basket-list-item img { .vertical {
height: 1em; flex-direction: column;
} }
.basket-total-area .flex-item {
{ /*background: tomato;*/
margin-bottom: 1em; padding: 5px;
max-width: 100vh;
margin: 5px;
font-size: 3em;
text-align: center;
}
.image-container {
border: 2px solid #ccc !important;
border-radius: 10px;
background-color: white;
max-width: 3em;
}
.basket-item-image {
min-width: 1em;
width: 2em;
max-width: 80%;
}
.description-container {
align-items: baseline;
}
.description-header {
line-height: 50px;
text-align: left;
font-weight: 500;
font-size: 1.2rem;
margin-bottom: 0.5rem;
overflow: hidden;
}
.description-header > span {
line-height: 20px;
vertical-align: top;
}
.description-price {
text-align: center;
font-size: 1.1rem;
font-weight: bold; font-weight: bold;
line-height: 30px;
color: rgb(72, 72, 72);
} }
.basket-item-remove { .description-price > span {
cursor: pointer; line-height: 20px;
color: red; vertical-align: top;
opacity: 0.25; }
.description-cargo{
color: #189c38;
text-align: center;
font-size: 1rem;
} }
.description-cargo> span {
line-height: 20px;
vertical-align: top;
}
.basket-list-item:hover .basket-item-remove { .remove-container {
color: #bb2d3b;
max-width: 2em;
font-size: 1.5rem;
line-height: initial;
}
.actions-container {
font-weight: 600;
color: #523e02;
max-width: 2em;
font-size: 1.25rem;
line-height: 40px;
}
.hide {
opacity: 0;
-webkit-transition: all 500ms ease-in-out;
-moz-transition: all 500ms ease-in-out;
-ms-transition: all 500ms ease-in-out;
-o-transition: all 500ms ease-in-out;
transition: all 500ms ease-in-out;
}
.basket-list-item:hover .hide {
opacity: 1.0; opacity: 1.0;
}
.sticky {
position: -webkit-sticky; /* for Safari */
position: sticky;
top: 1rem;
align-self: flex-start; /* <-- this is the fix */
} }

44
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket/basket-widget.js

@ -1,8 +1,6 @@
(function () { (function () {
let anonymousId = abp.utils.getCookieValue("anonymous_id"); let anonymousId = abp.utils.getCookieValue("anonymous_id");
abp.widgets.BasketWidget = function ($wrapper) { abp.widgets.BasketWidget = function ($wrapper) {
var widgetManager = $wrapper.data('abp-widget-manager'); var widgetManager = $wrapper.data('abp-widget-manager');
@ -10,23 +8,55 @@
$wrapper $wrapper
.find('.basket-item-remove') .find('.basket-item-remove')
.click(function () { .click(function () {
var $this = $(this); let $this = $(this);
var productId = $this.parent('.basket-list-item').attr('data-product-id'); let productId = $this.parents('.basket-list-item').attr('data-product-id');
eShopOnAbp.basketService.basket.removeProduct({ eShopOnAbp.basketService.basket.removeProduct({
productId: productId, productId: productId,
anonymousId: anonymousId anonymousId: anonymousId
}).then(function () { }).then(function () {
widgetManager.refresh(); widgetManager.refresh();
$('.abp-widget-wrapper[data-widget-name="CartWidget"]') $('.abp-widget-wrapper[data-widget-name="CartWidget"]')
.data('abp-widget-manager') .data('abp-widget-manager')
.refresh(); .refresh();
abp.notify.info("Removed the product from your basket.", "Removed basket item"); abp.notify.info("Removed the product from your basket.", "Removed basket item");
}); });
}); });
$wrapper
.find('.basket-item-increase')
.click(function (e) {
e.preventDefault();
let $this = $(this);
let productId = $this.parents('.basket-list-item').attr('data-product-id');
eShopOnAbp.basketService.basket.addProduct({
productId: productId,
count: 1,
anonymousId: anonymousId
}).then(function () {
widgetManager.refresh();
$('.abp-widget-wrapper[data-widget-name="CartWidget"]')
.data('abp-widget-manager')
.refresh();
});
});
$wrapper
.find('.basket-item-decrease')
.click(function (e) {
e.preventDefault();
let $this = $(this);
let productId = $this.parents('.basket-list-item').attr('data-product-id');
eShopOnAbp.basketService.basket.removeProduct({
productId: productId,
count: 1,
anonymousId: anonymousId
}).then(function () {
widgetManager.refresh();
$('.abp-widget-wrapper[data-widget-name="CartWidget"]')
.data('abp-widget-manager')
.refresh();
});
});
}; };
return { return {

12
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/purchase/purchase-widget.css

@ -0,0 +1,12 @@
.m-result {
text-align: end;
}
.summary {
color: #0F1111;
font-size: 1rem;
}
.order-total {
font-size: 1.2rem;
}

11
shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/en.json

@ -4,8 +4,19 @@
"Menu:Home": "Home", "Menu:Home": "Home",
"Login": "Login", "Login": "Login",
"YourBasket": "Your Basket", "YourBasket": "Your Basket",
"EmptyBasket": "Empyt basket",
"CheckOtherProducts": "You can check the recommended products below or",
"ContinueShopping": "continue shopping.",
"Total": "Total", "Total": "Total",
"TotalSelected": "Total Selected ({0})",
"OurProducts": "Our Products", "OurProducts": "Our Products",
"FreeCargo": "Free Cargo",
"Purchase:GiftCards":"Gift Cards",
"Purchase:CargoAndPackaging": "Cargo & Packaging",
"Purchase:Total": "Total",
"Purchase:OrderSummary": "Order Summary",
"Purchase:Products": "Products",
"Purchase:OrderTotal": "Order Total",
"CatalogServiceNotAvailable": "Catalog Service is unavailable right now! Try again later...", "CatalogServiceNotAvailable": "Catalog Service is unavailable right now! Try again later...",
"StockCount": "Stock Count", "StockCount": "Stock Count",
"RemoveFromBasket": "Remove from basket", "RemoveFromBasket": "Remove from basket",

11
shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/tr.json

@ -4,8 +4,19 @@
"Menu:Home": "Ana Sayfa", "Menu:Home": "Ana Sayfa",
"Login": "Giriş", "Login": "Giriş",
"YourBasket": "Sepetiniz", "YourBasket": "Sepetiniz",
"EmptyBasket": "Sepetiniz boş",
"CheckOtherProducts": "Aşağıdaki önerilen öğeleri takip edebilirsiniz veya",
"ContinueShopping": "alışverişe devam edin.",
"Total": "Toplam", "Total": "Toplam",
"TotalSelected": "Seçilen Ürünler ({0})",
"OurProducts": "Ürünlerimiz", "OurProducts": "Ürünlerimiz",
"FreeCargo": "Ücretsiz Kargo",
"Purchase:GiftCards":"Hediye Çekleri",
"Purchase:CargoAndPackaging": "Kargo ve Paketleme",
"Purchase:Total": "Toplam",
"Purchase:OrderSummary": "Sipariş Özeti",
"Purchase:Products": "Ürünler",
"Purchase:OrderTotal": "Sipariş Toplamı",
"CatalogServiceNotAvailable": "Ürün servisine şu anda ulaşılamıyor! Lütfen sonra tekrar deneyiniz...", "CatalogServiceNotAvailable": "Ürün servisine şu anda ulaşılamıyor! Lütfen sonra tekrar deneyiniz...",
"StockCount": "Stokta", "StockCount": "Stokta",
"RemoveFromBasket": "Sepetten kaldır", "RemoveFromBasket": "Sepetten kaldır",

Loading…
Cancel
Save