Browse Source

Merge pull request #43 from abpframework/gterdem/catalog_update

eShopOnAbp: Catalog and basket updates
ipaddr
Galip Tolga Erdem 5 years ago
committed by GitHub
parent
commit
87ed273183
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 76
      apps/public-web/src/EShopOnAbp.PublicWeb/Basket/UserBasketProvider.cs
  2. 20
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/BasketWidgetViewComponent.cs
  3. 13
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/Default.cshtml
  4. 14
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetScriptContributor.cs
  5. 12
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetStyleContributor.cs
  6. 30
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetViewComponent.cs
  7. 16
      apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/Default.cshtml
  8. 8
      apps/public-web/src/EShopOnAbp.PublicWeb/Controllers/WidgetsController.cs
  9. 8
      apps/public-web/src/EShopOnAbp.PublicWeb/CookieConstants.cs
  10. 10
      apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenuContributor.cs
  11. 1
      apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenus.cs
  12. 3
      apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebToolbarContributor.cs
  13. 19
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Basket.cshtml
  14. 11
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Basket.cshtml.cs
  15. 2
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.cs
  16. 17
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.js
  17. 118
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml
  18. 16
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml.cs
  19. 0
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/index.css
  20. 26
      apps/public-web/src/EShopOnAbp.PublicWeb/Pages/index.js
  21. 9
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/client-proxies/basket-proxy.js
  22. 35
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket-widget.js
  23. 4
      apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/cart/cart-widget.css
  24. 4
      services/basket/src/EShopOnAbp.BasketService.Application.Contracts/AddProductDto.cs
  25. 4
      services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IBasketAppService.cs
  26. 9
      services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IHasAnonymousId.cs
  27. 4
      services/basket/src/EShopOnAbp.BasketService.Application.Contracts/RemoveProductDto.cs
  28. 35
      services/basket/src/EShopOnAbp.BasketService.Application/BasketAppService.cs
  29. 4
      services/basket/src/EShopOnAbp.BasketService.Domain.Shared/Localization/BasketService/en.json
  30. 4
      services/basket/src/EShopOnAbp.BasketService.Domain.Shared/Localization/BasketService/tr.json
  31. 12
      services/basket/src/EShopOnAbp.BasketService.Domain/Basket.cs
  32. 2
      services/basket/src/EShopOnAbp.BasketService.Domain/BasketServiceDomainModule.cs
  33. 2
      services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/BasketServiceHttpApiClientModule.cs
  34. 8
      services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/BasketClientProxy.Generated.cs
  35. 57
      services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/basket-generate-proxy.json
  36. 8
      shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/en.json
  37. 9
      shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/tr.json

76
apps/public-web/src/EShopOnAbp.PublicWeb/Basket/UserBasketProvider.cs

@ -0,0 +1,76 @@
using Castle.Core.Logging;
using EShopOnAbp.BasketService;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Users;
namespace EShopOnAbp.PublicWeb.Basket
{
public class UserBasketProvider : ITransientDependency
{
protected HttpContext HttpContext => httpContextAccessor.HttpContext;
private readonly IHttpContextAccessor httpContextAccessor;
protected readonly ILogger<UserBasketProvider> logger;
protected readonly IGuidGenerator guidGenerator;
protected readonly IBasketAppService basketAppService;
protected readonly ICurrentUser currentUser;
public UserBasketProvider(
IHttpContextAccessor httpContextAccessor,
ILogger<UserBasketProvider> logger,
IGuidGenerator guidGenerator,
IBasketAppService basketAppService,
ICurrentUser currentUser)
{
this.httpContextAccessor = httpContextAccessor;
this.logger = logger;
this.guidGenerator = guidGenerator;
this.basketAppService = basketAppService;
this.currentUser = currentUser;
}
public virtual async Task<BasketDto> GetBasketAsync()
{
try
{
// Get anonymous user id from cookie
HttpContext.Request.Cookies.TryGetValue(CookieConstants.AnonymousUserCookieName,
out string anonymousUserId);
logger.LogInformation($"========= Anonymous User Id from Cookie:{anonymousUserId} ========= ");
// Generate new id for anonymous user
if (string.IsNullOrEmpty(anonymousUserId))
{
anonymousUserId = guidGenerator.Create().ToString();
HttpContext.Response.Cookies.Append(CookieConstants.AnonymousUserCookieName, anonymousUserId);
logger.LogInformation($"========= Generated new User Id:{anonymousUserId} ========= APPENDED TO COOKIE ====== ");
}
if (!currentUser.IsAuthenticated)
{
logger.LogInformation(
$"========= Get Basket for Anonymous UserId:{anonymousUserId} ========= APPENDED TO COOKIE ====== ");
return await basketAppService.GetByAnonymousUserIdAsync(Guid.Parse(anonymousUserId));
}
else
{
//TODO: Merge with anonymously stored cart if exist
return await basketAppService.GetAsync();
}
}
catch (Exception ex)
{
logger.LogError(ex, ex.Message);
return null;
}
}
}
}

20
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/BasketWidgetViewComponent.cs

@ -1,29 +1,31 @@
using System.Threading.Tasks;
using EShopOnAbp.PublicWeb.Basket;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
using EShopOnAbp.BasketService;
namespace EShopOnAbp.PublicWeb.Components.Basket;
[Widget(
AutoInitialize = true,
RefreshUrl = "/Widgets/Basket",
StyleFiles = new[] { "/components/basket-widget.css" },
ScriptTypes = new[] { typeof(BasketWidgetScriptContributor) }
StyleFiles = new[] {"/components/basket-widget.css"},
ScriptTypes = new[] {typeof(BasketWidgetScriptContributor)}
)]
public class BasketWidgetViewComponent : AbpViewComponent
{
private readonly IBasketAppService _basketAppService;
private readonly UserBasketProvider userBasketProvider;
public BasketWidgetViewComponent(IBasketAppService basketAppService)
public BasketWidgetViewComponent(UserBasketProvider userBasketProvider)
{
_basketAppService = basketAppService;
this.userBasketProvider = userBasketProvider;
}
public async Task<IViewComponentResult> InvokeAsync()
{
var basketDto = await _basketAppService.GetAsync();
return View("~/Components/Basket/Default.cshtml", basketDto);
return View(
"~/Components/Basket/Default.cshtml",
await userBasketProvider.GetBasketAsync());
}
}

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

@ -1,4 +1,11 @@
@using EShopOnAbp.Localization
@using Microsoft.Extensions.Localization
@model EShopOnAbp.BasketService.BasketDto
@inject IStringLocalizer<EShopOnAbpResource> L
<div>
@if (!Model.Items.Any())
{
@ -26,15 +33,15 @@
<text> (@("x" + item.Count))</text>
}
- $@item.TotalPrice.ToString("0.00")
<i class="fa fa-remove basket-item-remove" title="Remove from basket"></i>
<i class="fa fa-remove basket-item-remove" title="@L["RemoveFromBasket"]"></i>
</div>
}
<hr/>
<div class="basket-total-area">
Total: $@Model.TotalPrice.ToString("0.00")
@L["Total"]: $@Model.TotalPrice.ToString("0.00")
</div>
<div>
<abp-button class="basket-purchase-button" text="Purchase" button-type="Primary" />
<abp-button class="basket-purchase-button" text="@L["Purchase"]" button-type="Primary" />
</div>
</div>
}

14
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetScriptContributor.cs

@ -0,0 +1,14 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.AspNetCore.Mvc.UI.Packages.SignalR;
using Volo.Abp.Modularity;
namespace EShopOnAbp.PublicWeb.Components.Toolbar.Cart;
[DependsOn(typeof(SignalRBrowserScriptContributor))]
public class CartWidgetScriptContributor: BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
}
}

12
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetStyleContributor.cs

@ -0,0 +1,12 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace EShopOnAbp.PublicWeb.Components.Toolbar.Cart;
public class CartWidgetStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddIfNotContains("/components/cart/cart-widget.css");
}
}

30
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetViewComponent.cs

@ -0,0 +1,30 @@
using EShopOnAbp.PublicWeb.Basket;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc.UI.Widgets;
namespace EShopOnAbp.PublicWeb.Components.Toolbar.Cart;
[Widget(
AutoInitialize = true,
RefreshUrl = "/Widgets/Cart",
StyleTypes = new[] {typeof(CartWidgetStyleContributor)},
ScriptTypes = new[] {typeof(CartWidgetScriptContributor)}
)]
public class CartWidgetViewComponent : AbpViewComponent
{
private readonly UserBasketProvider userBasketProvider;
public CartWidgetViewComponent(UserBasketProvider userBasketProvider)
{
this.userBasketProvider = userBasketProvider;
}
public async Task<IViewComponentResult> InvokeAsync()
{
return View(
"~/Components/Toolbar/Cart/Default.cshtml",
await userBasketProvider.GetBasketAsync());
}
}

16
apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/Default.cshtml

@ -0,0 +1,16 @@
@using EShopOnAbp.BasketService.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@inject IHtmlLocalizer<BasketServiceResource> L
@model EShopOnAbp.BasketService.BasketDto
<div id="shopping-cart" class="nav-link">
@if (Model == null)
{
<span class="cart-error" style="vertical-align: -webkit-baseline-middle;color: white">@L["Toolbar:BasketNotLoaded"] </span>
}
else
{
<a class="btn nav-link" style="padding:0px" role="button" href="~/Basket">
<span abp-badge="Info" data-cart-count>@Model.Items.Count</span> @L["Toolbar:Cart"]
</a>
}
</div>

8
apps/public-web/src/EShopOnAbp.PublicWeb/Controllers/WidgetsController.cs

@ -1,4 +1,5 @@
using EShopOnAbp.PublicWeb.Components.Basket;
using EShopOnAbp.PublicWeb.Components.Toolbar.Cart;
using Microsoft.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Mvc;
@ -13,4 +14,11 @@ public class WidgetsController : AbpController
{
return ViewComponent(typeof(BasketWidgetViewComponent));
}
[HttpGet]
[Route("Cart")]
public IActionResult GetCartWidget()
{
return ViewComponent(typeof(CartWidgetViewComponent));
}
}

8
apps/public-web/src/EShopOnAbp.PublicWeb/CookieConstants.cs

@ -0,0 +1,8 @@
using System;
namespace EShopOnAbp.PublicWeb;
public static class CookieConstants
{
public const string AnonymousUserCookieName = "eshop_anonymousId";
}

10
apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenuContributor.cs

@ -44,16 +44,6 @@ namespace EShopOnAbp.PublicWeb.Menus
order: 0
)
);
// Products
context.Menu.AddItem(
new ApplicationMenuItem(
EShopOnAbpPublicWebMenus.Catalog,
l["Catalog"],
"/Catalog",
icon: "fa fa-product-hunt",
order: 1
)
);
return Task.CompletedTask;
}

1
apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenus.cs

@ -5,6 +5,5 @@
private const string Prefix = "EShopOnAbp.Public";
public const string HomePage = Prefix + ".HomePage";
public const string Catalog = Prefix + ".Catalog";
}
}

3
apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebToolbarContributor.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using EShopOnAbp.PublicWeb.Components.Toolbar.Cart;
using EShopOnAbp.PublicWeb.Components.Toolbar.LoginLink;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars;
@ -15,6 +16,8 @@ namespace EShopOnAbp.PublicWeb.Menus
return Task.CompletedTask;
}
context.Toolbar.Items.Add(new ToolbarItem(typeof(CartWidgetViewComponent)));
if (!context.ServiceProvider.GetRequiredService<ICurrentUser>().IsAuthenticated)
{
context.Toolbar.Items.Add(new ToolbarItem(typeof(LoginLinkViewComponent)));

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

@ -0,0 +1,19 @@
@page
@using EShopOnAbp.Localization
@using EShopOnAbp.PublicWeb.Components.Basket
@using Microsoft.Extensions.Localization
@model EShopOnAbp.PublicWeb.Pages.Basket
@section scripts {
<abp-script src="/client-proxies/basket-proxy.js" />
<abp-script src="/Pages/index.js" />
}
@inject IStringLocalizer<EShopOnAbpResource> L
<div class="col-lg-12">
<h3 class="pt-5 pb-4 text-center">@L["YourBasket"]</h3>
<div id="BasketArea">
@await Component.InvokeAsync(typeof(BasketWidgetViewComponent))
</div>
</div>

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

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace EShopOnAbp.PublicWeb.Pages;
public class Basket : PageModel
{
public void OnGet()
{
}
}

2
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.cs

@ -14,7 +14,7 @@ public class Catalog : PageModel
{
_productAppService = productAppService;
}
public async Task OnGetAsync()
{
Products = (await _productAppService.GetListAsync()).Items;

17
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.js

@ -1,17 +0,0 @@
(function(){
$(function(){
var basketWidget = new abp.WidgetManager('#BasketArea');
$('.product-list-item').click(function(){
var $this = $(this);
var productId = $this.attr('data-product-id');
eShopOnAbp.basketService.basket.addProduct({
productId: productId
}).then(function(){
basketWidget.refresh();
abp.notify.success("Added product to your basket.", "Successfully added");
});
});
});
})();

118
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml

@ -1,72 +1,60 @@
@page
@using EShopOnAbp.Localization
@using Microsoft.Extensions.Localization
@model EShopOnAbp.PublicWeb.Pages.IndexModel
@inject IStringLocalizer<EShopOnAbpResource> L
<abp-card Class=" overflow-hidden">
<abp-card-body Class="p-0">
<abp-carousel style="width: 100%;" indicators="true" controls="true">
<abp-carousel-item src="https://i.picsum.photos/id/1002/1200/600.jpg?hmac=vEtEOHyKsKb7OglkR2kK2kT3SNT5KHJ4wtetm49EDIY" style="height: 600px;" class="bg-primary" caption-title="Maecenas a enim purus" caption="Sed sed tristique diam. Sed malesuada eu ex eu facilisis. Donec commodo placerat enim, vel mollis sem consequat eget. Pellentesque eget cursus sapien."></abp-carousel-item>
<abp-carousel-item src="https://i.picsum.photos/id/1070/1200/600.jpg?hmac=MvQe1lH2C5sz8TXGRo0pMg2Pcbvri9rpG_rvoBFpqxw" style="height: 600px;" class="bg-primary" caption-title="Sed sed tristique diam. " caption="Sed malesuada eu ex eu facilisis. Donec commodo placerat enim, vel mollis sem consequat eget. Pellentesque eget cursus sapien."></abp-carousel-item>
<abp-carousel-item src="https://i.picsum.photos/id/841/1200/600.jpg?hmac=1dnXZo7goR6mFJqBs-5o3jUdMke1v-JCGCl0on-cng8" style="height: 600px;" class="bg-primary" caption-title="Donec commodo placerat" caption="Sed sed tristique diam. Sed malesuada eu ex eu facilisis. Vel mollis sem consequat eget. Pellentesque eget cursus sapien."></abp-carousel-item>
</abp-carousel>
</abp-card-body>
</abp-card>
@{
const int productsColumnSize = 4; // TODO: update abp-column helper to plain html for proper row and columns css
}
<div class="container">
<div class="row text-center py-5">
<div class="col-lg-4 py-4">
<i class="fas fa-chess-rook fa-4x text-primary d-block my-3"></i>
<h2>Heading</h2>
<p class="lead">Vestibulum id ligula porta.</p>
<p>Donec sed odio dui. Etiam porta sem malesuada magna mollis euismod. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magna.</p>
<p><a class="btn btn-outline-primary" href="#" role="button">View details »</a></p>
</div><!-- /.col-lg-4 -->
<div class="col-lg-4 py-4">
<i class="fas fa-bullhorn fa-4x text-info d-block my-3"></i>
<h2>Heading</h2>
<p class="lead">Nulla non metus auctor.</p>
<p>Duis mollis, est non commodo luctus, nisi erat porttitor ligula, eget lacinia odio sem nec elit.Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh.</p>
<p><a class="btn btn-outline-info" href="#" role="button">View details »</a></p>
</div><!-- /.col-lg-4 -->
<div class="col-lg-4 py-4">
<i class="fas fa-balance-scale fa-4x text-success d-block my-3"></i>
<h2>Heading</h2>
<p class="lead">Metus auctor fringilla.</p>
<p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, sit amet risus.</p>
<p><a class="btn btn-outline-success" href="#" role="button">View details »</a></p>
</div><!-- /.col-lg-4 -->
</div><!-- /.row -->
@section styles{
<abp-style src="/Pages/index.css"/>
}
@section scripts {
<abp-script src="/client-proxies/basket-proxy.js" />
<abp-script src="/Pages/index.js" />
}
<div class="row my-5 py-md-5">
<div class="col-md-6 pr-lg-5">
<h2 class="mt-5">First featurette heading. <span class="text-primary d-block">It'll blow your mind.</span></h2>
<p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. </p>
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.</p>
</div>
<div class="col-md-6">
<img src="https://i.picsum.photos/id/59/600/400.jpg?hmac=hCS_vJsQfuK1hUlwEUlGbUAnb_pFljGkD-2jdYxB1y8" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;"/>
</div>
</div>
<div class="row my-5 py-md-5">
<div class="col-md-6 order-md-2 pl-lg-5">
<h2 class="mt-5">Oh yeah, it's that good. <span class="text-primary d-block">See for yourself.</span></h2>
<p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. </p>
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.</p>
</div>
<div class="col-md-6 order-md-1">
<img src="https://i.picsum.photos/id/437/600/400.jpg?hmac=JNv4AQJrQn53c1Xh1C7q0NBNoJgRXpdKtzIDJ1w2lqA" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;"/>
<div class="row">
<div class="col-lg-12">
<h3 class="pt-5 pb-4 text-center">@L["OurProducts"]</h3>
<div class="product-list">
@for (int i = 0; i < Math.Ceiling(Model.Products.Count / (double)productsColumnSize); i++)
{
<abp-row>
@for (int j = 0; j < productsColumnSize; j++)
{
<abp-column size-lg="_3" size-md="_4" size-sm="_6">
@if ((i * productsColumnSize) + j < Model.Products.Count)
{
var product = Model.Products[(i * productsColumnSize) + j];
<div class="product-list-item" data-product-id="@product.Id.ToString()">
@if (!product.ImageName.IsNullOrEmpty())
{
<img src="/product-images/@product.ImageName"/>
}
else
{
<img src="/product-images/noimage.jpg"/>
}
<div class="row mt-5">
<div class="col">
<div class="product-info-box">
<h6>@product.Name</h6>
<em class="d-block text-muted">@product.Code - @L["StockCount"]: @product.StockCount</em>
</div>
</div>
<div class="col-auto text-right text-danger">
<strong class="product-price">$@product.Price</strong>
</div>
</div>
</div>
}
</abp-column>
}
</abp-row>
}
</div>
</div>
<div class="row my-5 py-md-5">
<div class="col-md-6 pr-lg-5">
<h2 class="mt-5">And lastly, this one. <span class="text-primary d-block">Checkmate.</span></h2>
<p class="lead">Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper. </p>
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.</p>
</div>
<div class="col-md-6">
<img src="https://i.picsum.photos/id/835/600/400.jpg?hmac=HR6Bi8aji8auDURA6j9xiZC6QgcFwwBTMJ8X4Cf6bPg" class="rounded shadow-sm my-4 my-md-0" style="max-width: 100%;"/>
</div>
</div>
</div>
</div>

16
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml.cs

@ -1,4 +1,6 @@
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Threading.Tasks;
using EShopOnAbp.CatalogService.Products;
using Microsoft.AspNetCore.Authentication;
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages;
@ -6,9 +8,17 @@ namespace EShopOnAbp.PublicWeb.Pages
{
public class IndexModel : AbpPageModel
{
public void OnGet()
public IReadOnlyList<ProductDto> Products { get; private set; }
private readonly IPublicProductAppService _productAppService;
public IndexModel(IPublicProductAppService productAppService)
{
_productAppService = productAppService;
}
public async Task OnGet()
{
Products = (await _productAppService.GetListAsync()).Items;
}
public async Task OnPostLoginAsync()
@ -16,4 +26,4 @@ namespace EShopOnAbp.PublicWeb.Pages
await HttpContext.ChallengeAsync("oidc");
}
}
}
}

0
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.css → apps/public-web/src/EShopOnAbp.PublicWeb/Pages/index.css

26
apps/public-web/src/EShopOnAbp.PublicWeb/Pages/index.js

@ -0,0 +1,26 @@
(function () {
//set anonymous user
let anonymousId = abp.utils.getCookieValue("eshop_anonymousId");
if (anonymousId != null) {
console.info("Anonymous user set:" + anonymousId);
}
$(function () {
$('.product-list-item').click(function () {
var $this = $(this);
var productId = $this.attr('data-product-id');
eShopOnAbp.basketService.basket.addProduct({
productId: productId,
anonymousId: anonymousId,
}).then(function () {
$('.abp-widget-wrapper[data-widget-name="CartWidget"]')
.data('abp-widget-manager')
.refresh();
abp.notify.success("Added product to your basket.", "Successfully added");
});
});
});
})();

9
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/client-proxies/basket-proxy.js

@ -18,6 +18,13 @@
}, ajaxParams));
};
eShopOnAbp.basketService.basket.getByAnonymousUserId = function(id, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/basket/basket/' + id + '/by-anonymous-user-id',
type: 'GET'
}, ajaxParams));
};
eShopOnAbp.basketService.basket.addProduct = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/basket/basket/product',
@ -28,7 +35,7 @@
eShopOnAbp.basketService.basket.removeProduct = function(input, ajaxParams) {
return abp.ajax($.extend(true, {
url: abp.appPath + 'api/basket/basket/product' + abp.utils.buildQueryString([{ name: 'productId', value: input.productId }, { name: 'count', value: input.count }]) + '',
url: abp.appPath + 'api/basket/basket/product' + abp.utils.buildQueryString([{ name: 'productId', value: input.productId }, { name: 'count', value: input.count }, { name: 'anonymousId', value: input.anonymousId }]) + '',
type: 'DELETE'
}, ajaxParams));
};

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

@ -1,29 +1,40 @@
(function (){
(function () {
let anonymousId = abp.utils.getCookieValue("eshop_anonymousId");
abp.widgets.BasketWidget = function ($wrapper) {
var widgetManager = $wrapper.data('abp-widget-manager');
var init = function (filters) {
$wrapper
.find('.basket-item-remove')
.click(function(){
.click(function () {
var $this = $(this);
var productId = $this.parent('.basket-list-item').attr('data-product-id');
eShopOnAbp.basketService.basket.removeProduct({
productId: productId
}).then(function(){
productId: productId,
anonymousId: anonymousId
}).then(function () {
widgetManager.refresh();
$('.abp-widget-wrapper[data-widget-name="CartWidget"]')
.data('abp-widget-manager')
.refresh();
abp.notify.info("Removed the product from your basket.", "Removed basket item");
});
});
$wrapper
.find('.basket-purchase-button')
.click(function(){
eShopOnAbp.basketService.basket.purchase().then(function(){
.click(function () {
eShopOnAbp.basketService.basket.purchase().then(function () {
widgetManager.refresh();
abp.notify.info("Thank you for the payment!", "Purchase completed!");
});
});
});
};
@ -35,13 +46,13 @@
var connection = new signalR.HubConnectionBuilder()
.withUrl("/signalr-hubs/basket")
.build();
connection.on("BasketProductUpdated", function (data) {
$('.basket-list')
.closest('.abp-widget-wrapper')
.each(function(){
.each(function () {
var $wrapper = $(this);
if ($wrapper.find('[data-product-id=' + data.productId + ']').length){
if ($wrapper.find('[data-product-id=' + data.productId + ']').length) {
var widgetManager = new abp.WidgetManager({
wrapper: $wrapper
});

4
apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/cart/cart-widget.css

@ -0,0 +1,4 @@
.cart-error {
vertical-align: -webkit-baseline-middle;
color: rgba(255, 255, 255, 0.75);
}

4
services/basket/src/EShopOnAbp.BasketService.Application.Contracts/AddProductDto.cs

@ -3,10 +3,12 @@ using System.ComponentModel.DataAnnotations;
namespace EShopOnAbp.BasketService;
public class AddProductDto
public class AddProductDto : IHasAnonymousId
{
public Guid ProductId { get; set; }
[Range(1, int.MaxValue)]
public int Count { get; set; } = 1;
public Guid? AnonymousId { get; set; }
}

4
services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IBasketAppService.cs

@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System;
using System.Threading.Tasks;
using Volo.Abp.Application.Services;
namespace EShopOnAbp.BasketService;
@ -6,6 +7,7 @@ namespace EShopOnAbp.BasketService;
public interface IBasketAppService : IApplicationService
{
Task<BasketDto> GetAsync();
Task<BasketDto> GetByAnonymousUserIdAsync(Guid id);
Task<BasketDto> AddProductAsync(AddProductDto input);
Task<BasketDto> RemoveProductAsync(RemoveProductDto input);
Task PurchaseAsync();

9
services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IHasAnonymousId.cs

@ -0,0 +1,9 @@
using System;
namespace EShopOnAbp.BasketService
{
public interface IHasAnonymousId
{
Guid? AnonymousId { get; }
}
}

4
services/basket/src/EShopOnAbp.BasketService.Application.Contracts/RemoveProductDto.cs

@ -2,9 +2,11 @@ using System;
namespace EShopOnAbp.BasketService;
public class RemoveProductDto
public class RemoveProductDto : IHasAnonymousId
{
public Guid ProductId { get; set; }
public int? Count { get; set; }
public Guid? AnonymousId { get; set; }
}

35
services/basket/src/EShopOnAbp.BasketService.Application/BasketAppService.cs

@ -10,12 +10,12 @@ using Volo.Abp.EventBus.Distributed;
namespace EShopOnAbp.BasketService;
[Authorize]
public class BasketAppService : BasketServiceAppService, IBasketAppService
{
private readonly IBasketRepository _basketRepository;
private readonly IBasketProductService _basketProductService;
private readonly IDistributedEventBus _distributedEventBus;
private Guid _anonymousUserId { get; set; }
public BasketAppService(
IBasketRepository basketRepository,
@ -26,39 +26,52 @@ public class BasketAppService : BasketServiceAppService, IBasketAppService
_basketProductService = basketProductService;
_distributedEventBus = distributedEventBus;
}
public async Task<BasketDto> GetAsync()
{
var basket = await _basketRepository.GetAsync(CurrentUser.GetId());
return await GetBasketDtoAsync(basket);
}
public async Task<BasketDto> GetByAnonymousUserIdAsync(Guid id)
{
var basket = await _basketRepository.GetAsync(id);
_anonymousUserId = id;
return await GetBasketDtoAsync(basket);
}
public async Task<BasketDto> AddProductAsync(AddProductDto input)
{
var basket = await _basketRepository.GetAsync(CurrentUser.GetId());
Console.WriteLine("========================= AnonymousId: "+input.AnonymousId);
Guid userId = CurrentUser.IsAuthenticated ? CurrentUser.GetId() : input.AnonymousId.GetValueOrDefault();
var basket = await _basketRepository.GetAsync(userId);
var product = await _basketProductService.GetAsync(input.ProductId);
if (basket.GetProductCount(product.Id) >= product.StockCount)
{
throw new UserFriendlyException("There is not enough product in stock, sorry :(");
}
basket.AddProduct(product.Id);
await _basketRepository.UpdateAsync(basket);
return await GetBasketDtoAsync(basket);
}
public async Task<BasketDto> RemoveProductAsync(RemoveProductDto input)
{
var basket = await _basketRepository.GetAsync(CurrentUser.GetId());
Guid userId = CurrentUser.IsAuthenticated ? CurrentUser.GetId() : input.AnonymousId.GetValueOrDefault();
var basket = await _basketRepository.GetAsync(userId);
var product = await _basketProductService.GetAsync(input.ProductId);
basket.RemoveProduct(product.Id, input.Count);
await _basketRepository.UpdateAsync(basket);
return await GetBasketDtoAsync(basket);
}
@ -85,7 +98,7 @@ public class BasketAppService : BasketServiceAppService, IBasketAppService
productDto = await _basketProductService.GetAsync(basketItem.ProductId);
products[productDto.Id] = productDto;
}
//Removing the products if not available in the stock
if (basketItem.Count > productDto.StockCount)
{
@ -110,7 +123,7 @@ public class BasketAppService : BasketServiceAppService, IBasketAppService
{
await _basketRepository.UpdateAsync(basket);
}
return basketDto;
}
}

4
services/basket/src/EShopOnAbp.BasketService.Domain.Shared/Localization/BasketService/en.json

@ -3,6 +3,8 @@
"texts": {
"Menu:Home": "Home",
"Welcome": "Welcome",
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io."
"LongWelcomeMessage": "Welcome to the application. This is a startup project based on the ABP framework. For more information, visit abp.io.",
"Toolbar:Cart": "My Cart",
"Toolbar:BasketNotLoaded": "Couldn't Load!"
}
}

4
services/basket/src/EShopOnAbp.BasketService.Domain.Shared/Localization/BasketService/tr.json

@ -3,6 +3,8 @@
"texts": {
"Menu:Home": "Ana sayfa",
"Welcome": "Hoşgeldiniz",
"LongWelcomeMessage": "Uygulamaya hoşgeldiniz. Bu, ABP framework'ü üzerine bina edilmiş bir başlangıç projesidir. Daha fazla bilgi için abp.io adresini ziyaret edebilirsiniz."
"LongWelcomeMessage": "Uygulamaya hoşgeldiniz. Bu, ABP framework'ü üzerine bina edilmiş bir başlangıç projesidir. Daha fazla bilgi için abp.io adresini ziyaret edebilirsiniz.",
"Toolbar:Cart": "Sepetim",
"Toolbar:BasketNotLoaded": "Sepet Hatası!"
}
}

12
services/basket/src/EShopOnAbp.BasketService.Domain/Basket.cs

@ -11,10 +11,10 @@ public class Basket : AggregateRoot<Guid>
private Basket()
{
}
public Basket(Guid id)
public Basket(Guid id)
: base(id)
{
Items = new List<BasketItem>();
@ -37,20 +37,20 @@ public class Basket : AggregateRoot<Guid>
item.Count += count;
}
}
public void RemoveProduct(Guid productId, int? count = null)
{
if (count is < 1)
{
throw new ArgumentOutOfRangeException(nameof(count), "Product count should be null, 1 or more!");
}
var item = Items.FirstOrDefault(x => x.ProductId == productId);
if (item == null)
{
return;
}
if (count == null || item.Count <= count)
{
Items.Remove(item);

2
services/basket/src/EShopOnAbp.BasketService.Domain/BasketServiceDomainModule.cs

@ -11,7 +11,7 @@ namespace EShopOnAbp.BasketService
typeof(AbpDddDomainModule),
typeof(AbpCachingModule)
)]
public class BasketServiceDomainModule : AbpModule
public class BasketServiceDomainModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{

2
services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/BasketServiceHttpApiClientModule.cs

@ -26,4 +26,4 @@ namespace EShopOnAbp.BasketService
});
}
}
}
}

8
services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/BasketClientProxy.Generated.cs

@ -20,6 +20,14 @@ namespace EShopOnAbp.BasketService.ClientProxies
return await RequestAsync<BasketDto>(nameof(GetAsync));
}
public virtual async Task<BasketDto> GetByAnonymousUserIdAsync(Guid id)
{
return await RequestAsync<BasketDto>(nameof(GetByAnonymousUserIdAsync), new ClientProxyRequestTypeValue
{
{ typeof(Guid), id }
});
}
public virtual async Task<BasketDto> AddProductAsync(AddProductDto input)
{
return await RequestAsync<BasketDto>(nameof(AddProductAsync), new ClientProxyRequestTypeValue

57
services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/basket-generate-proxy.json

@ -35,7 +35,44 @@
"type": "EShopOnAbp.BasketService.BasketDto",
"typeSimple": "EShopOnAbp.BasketService.BasketDto"
},
"allowAnonymous": false,
"allowAnonymous": null,
"implementFrom": "EShopOnAbp.BasketService.IBasketAppService"
},
"GetByAnonymousUserIdAsyncById": {
"uniqueName": "GetByAnonymousUserIdAsyncById",
"name": "GetByAnonymousUserIdAsync",
"httpMethod": "GET",
"url": "api/basket/basket/{id}/by-anonymous-user-id",
"supportedVersions": [],
"parametersOnMethod": [
{
"name": "id",
"typeAsString": "System.Guid, System.Private.CoreLib",
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null
}
],
"parameters": [
{
"nameOnMethod": "id",
"name": "id",
"jsonName": null,
"type": "System.Guid",
"typeSimple": "string",
"isOptional": false,
"defaultValue": null,
"constraintTypes": [],
"bindingSourceId": "Path",
"descriptorName": ""
}
],
"returnValue": {
"type": "EShopOnAbp.BasketService.BasketDto",
"typeSimple": "EShopOnAbp.BasketService.BasketDto"
},
"allowAnonymous": null,
"implementFrom": "EShopOnAbp.BasketService.IBasketAppService"
},
"AddProductAsyncByInput": {
@ -72,7 +109,7 @@
"type": "EShopOnAbp.BasketService.BasketDto",
"typeSimple": "EShopOnAbp.BasketService.BasketDto"
},
"allowAnonymous": false,
"allowAnonymous": null,
"implementFrom": "EShopOnAbp.BasketService.IBasketAppService"
},
"RemoveProductAsyncByInput": {
@ -115,13 +152,25 @@
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
},
{
"nameOnMethod": "input",
"name": "AnonymousId",
"jsonName": null,
"type": "System.Guid?",
"typeSimple": "string?",
"isOptional": false,
"defaultValue": null,
"constraintTypes": null,
"bindingSourceId": "ModelBinding",
"descriptorName": "input"
}
],
"returnValue": {
"type": "EShopOnAbp.BasketService.BasketDto",
"typeSimple": "EShopOnAbp.BasketService.BasketDto"
},
"allowAnonymous": false,
"allowAnonymous": null,
"implementFrom": "EShopOnAbp.BasketService.IBasketAppService"
},
"PurchaseAsync": {
@ -136,7 +185,7 @@
"type": "System.Void",
"typeSimple": "System.Void"
},
"allowAnonymous": false,
"allowAnonymous": null,
"implementFrom": "EShopOnAbp.BasketService.IBasketAppService"
}
}

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

@ -3,6 +3,12 @@
"texts": {
"Menu:Home": "Home",
"Login": "Login",
"Catalog": "Catalog"
"Catalog": "Catalog",
"YourBasket": "Your Basket",
"Purchase": "Purchase",
"Total": "Total",
"OurProducts": "Our Products",
"StockCount": "Stock Count",
"RemoveFromBasket": "Remove from basket"
}
}

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

@ -2,6 +2,13 @@
"culture": "tr",
"texts": {
"Menu:Home": "Ana Sayfa",
"Login": "Giriş"
"Login": "Giriş",
"Catalog": "Katalog",
"YourBasket": "Sepetiniz",
"Purchase": "Alışverişi tamamla",
"Total": "Toplam",
"OurProducts": "Ürünlerimiz",
"StockCount": "Stokta",
"RemoveFromBasket": "Sepetten kaldır"
}
}

Loading…
Cancel
Save