diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Basket/UserBasketProvider.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Basket/UserBasketProvider.cs new file mode 100644 index 00000000..2fd7e5de --- /dev/null +++ b/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 logger; + protected readonly IGuidGenerator guidGenerator; + protected readonly IBasketAppService basketAppService; + protected readonly ICurrentUser currentUser; + + public UserBasketProvider( + IHttpContextAccessor httpContextAccessor, + ILogger 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 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; + } + } + } +} diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/BasketWidgetViewComponent.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/BasketWidgetViewComponent.cs index 71e36800..6f8ed944 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/BasketWidgetViewComponent.cs +++ b/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 InvokeAsync() { - var basketDto = await _basketAppService.GetAsync(); - return View("~/Components/Basket/Default.cshtml", basketDto); + + return View( + "~/Components/Basket/Default.cshtml", + await userBasketProvider.GetBasketAsync()); } } \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/Default.cshtml b/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/Default.cshtml index ef5edc9b..e0057a76 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Basket/Default.cshtml +++ b/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 L +
@if (!Model.Items.Any()) { @@ -26,15 +33,15 @@ (@("x" + item.Count)) } - $@item.TotalPrice.ToString("0.00") - +
}
- Total: $@Model.TotalPrice.ToString("0.00") + @L["Total"]: $@Model.TotalPrice.ToString("0.00")
- +
} diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetScriptContributor.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetScriptContributor.cs new file mode 100644 index 00000000..a1066256 --- /dev/null +++ b/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) + { + } +} \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetStyleContributor.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetStyleContributor.cs new file mode 100644 index 00000000..d0b42f47 --- /dev/null +++ b/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"); + } +} \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetViewComponent.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/CartWidgetViewComponent.cs new file mode 100644 index 00000000..ce214c76 --- /dev/null +++ b/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 InvokeAsync() + { + return View( + "~/Components/Toolbar/Cart/Default.cshtml", + await userBasketProvider.GetBasketAsync()); + } +} \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/Default.cshtml b/apps/public-web/src/EShopOnAbp.PublicWeb/Components/Toolbar/Cart/Default.cshtml new file mode 100644 index 00000000..74d306ec --- /dev/null +++ b/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 L +@model EShopOnAbp.BasketService.BasketDto + \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Controllers/WidgetsController.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Controllers/WidgetsController.cs index c3f1098c..f55a5dfd 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/Controllers/WidgetsController.cs +++ b/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)); + } } \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/CookieConstants.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/CookieConstants.cs new file mode 100644 index 00000000..1994218b --- /dev/null +++ b/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"; +} \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenuContributor.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenuContributor.cs index f78e4c34..f2ea9b52 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenuContributor.cs +++ b/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; } diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenus.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenus.cs index 0c9dc4e4..cb80e44b 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebMenus.cs +++ b/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"; } } diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebToolbarContributor.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebToolbarContributor.cs index cf1a59a0..21a9f84e 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/Menus/EShopOnAbpPublicWebToolbarContributor.cs +++ b/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().IsAuthenticated) { context.Toolbar.Items.Add(new ToolbarItem(typeof(LoginLinkViewComponent))); diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Basket.cshtml b/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Basket.cshtml new file mode 100644 index 00000000..e30d044d --- /dev/null +++ b/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 { + + +} + +@inject IStringLocalizer L + +
+

@L["YourBasket"]

+
+ @await Component.InvokeAsync(typeof(BasketWidgetViewComponent)) +
+
\ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Basket.cshtml.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Basket.cshtml.cs new file mode 100644 index 00000000..8de8b3c4 --- /dev/null +++ b/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() + { + + } +} \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.cs index e7874572..c762a0d5 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.cs +++ b/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; diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.js b/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.js deleted file mode 100644 index fefd8436..00000000 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.cshtml.js +++ /dev/null @@ -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"); - }); - }); - }); -})(); \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml b/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml index 5c9faabd..9589b904 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml +++ b/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 L - - - - - - - - - +@{ + const int productsColumnSize = 4; // TODO: update abp-column helper to plain html for proper row and columns css +} -
-
-
- -

Heading

-

Vestibulum id ligula porta.

-

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.

-

View details »

-
-
- -

Heading

-

Nulla non metus auctor.

-

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.

-

View details »

-
-
- -

Heading

-

Metus auctor fringilla.

-

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.

-

View details »

-
-
+@section styles{ + +} +@section scripts { + + +} -
-
-

First featurette heading. It'll blow your mind.

-

Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper.

-

Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.

-
-
- -
-
- -
-
-

Oh yeah, it's that good. See for yourself.

-

Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper.

-

Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.

-
-
- +
+
+

@L["OurProducts"]

+
+ @for (int i = 0; i < Math.Ceiling(Model.Products.Count / (double)productsColumnSize); i++) + { + + @for (int j = 0; j < productsColumnSize; j++) + { + + @if ((i * productsColumnSize) + j < Model.Products.Count) + { + var product = Model.Products[(i * productsColumnSize) + j]; +
+ @if (!product.ImageName.IsNullOrEmpty()) + { + + } + else + { + + } +
+
+
+
@product.Name
+ @product.Code - @L["StockCount"]: @product.StockCount +
+
+
+ $@product.Price +
+
+
+ } +
+ } +
+ }
- -
-
-

And lastly, this one. Checkmate.

-

Donec ullamcorper nulla non metus auctor fringilla. Vestibulum id ligula porta felis euismod semper.

-

Praesent commodo cursus magna, vel scelerisque nisl consectetur. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo.

-
-
- -
-
- -
+
\ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml.cs b/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml.cs index 22c79292..6191dd43 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Index.cshtml.cs +++ b/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 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"); } } -} +} \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.css b/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/index.css similarity index 100% rename from apps/public-web/src/EShopOnAbp.PublicWeb/Pages/Catalog.css rename to apps/public-web/src/EShopOnAbp.PublicWeb/Pages/index.css diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/index.js b/apps/public-web/src/EShopOnAbp.PublicWeb/Pages/index.js new file mode 100644 index 00000000..9af51771 --- /dev/null +++ b/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"); + }); + }); + }); +})(); \ No newline at end of file diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/client-proxies/basket-proxy.js b/apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/client-proxies/basket-proxy.js index c3877617..de10ce04 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/client-proxies/basket-proxy.js +++ b/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)); }; diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket-widget.js b/apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket-widget.js index f709fc65..c71356ba 100644 --- a/apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/basket-widget.js +++ b/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 }); diff --git a/apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/cart/cart-widget.css b/apps/public-web/src/EShopOnAbp.PublicWeb/wwwroot/components/cart/cart-widget.css new file mode 100644 index 00000000..083ca3ea --- /dev/null +++ b/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); +} \ No newline at end of file diff --git a/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/AddProductDto.cs b/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/AddProductDto.cs index 890bb338..f569c44e 100644 --- a/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/AddProductDto.cs +++ b/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; } } \ No newline at end of file diff --git a/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IBasketAppService.cs b/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IBasketAppService.cs index 00266aa7..55a4ba0a 100644 --- a/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IBasketAppService.cs +++ b/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 GetAsync(); + Task GetByAnonymousUserIdAsync(Guid id); Task AddProductAsync(AddProductDto input); Task RemoveProductAsync(RemoveProductDto input); Task PurchaseAsync(); diff --git a/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IHasAnonymousId.cs b/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IHasAnonymousId.cs new file mode 100644 index 00000000..64c27687 --- /dev/null +++ b/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/IHasAnonymousId.cs @@ -0,0 +1,9 @@ +using System; + +namespace EShopOnAbp.BasketService +{ + public interface IHasAnonymousId + { + Guid? AnonymousId { get; } + } +} diff --git a/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/RemoveProductDto.cs b/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/RemoveProductDto.cs index b1b2ba2f..e8cf0a2f 100644 --- a/services/basket/src/EShopOnAbp.BasketService.Application.Contracts/RemoveProductDto.cs +++ b/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; } } \ No newline at end of file diff --git a/services/basket/src/EShopOnAbp.BasketService.Application/BasketAppService.cs b/services/basket/src/EShopOnAbp.BasketService.Application/BasketAppService.cs index 76d77784..2aab9990 100644 --- a/services/basket/src/EShopOnAbp.BasketService.Application/BasketAppService.cs +++ b/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 GetAsync() { var basket = await _basketRepository.GetAsync(CurrentUser.GetId()); return await GetBasketDtoAsync(basket); } + public async Task GetByAnonymousUserIdAsync(Guid id) + { + var basket = await _basketRepository.GetAsync(id); + _anonymousUserId = id; + return await GetBasketDtoAsync(basket); + } + public async Task 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 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; } } \ No newline at end of file diff --git a/services/basket/src/EShopOnAbp.BasketService.Domain.Shared/Localization/BasketService/en.json b/services/basket/src/EShopOnAbp.BasketService.Domain.Shared/Localization/BasketService/en.json index d2a6a983..d1b6949a 100644 --- a/services/basket/src/EShopOnAbp.BasketService.Domain.Shared/Localization/BasketService/en.json +++ b/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!" } } diff --git a/services/basket/src/EShopOnAbp.BasketService.Domain.Shared/Localization/BasketService/tr.json b/services/basket/src/EShopOnAbp.BasketService.Domain.Shared/Localization/BasketService/tr.json index 2cc911e4..60577366 100644 --- a/services/basket/src/EShopOnAbp.BasketService.Domain.Shared/Localization/BasketService/tr.json +++ b/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ı!" } } \ No newline at end of file diff --git a/services/basket/src/EShopOnAbp.BasketService.Domain/Basket.cs b/services/basket/src/EShopOnAbp.BasketService.Domain/Basket.cs index 61064707..30fd4abb 100644 --- a/services/basket/src/EShopOnAbp.BasketService.Domain/Basket.cs +++ b/services/basket/src/EShopOnAbp.BasketService.Domain/Basket.cs @@ -11,10 +11,10 @@ public class Basket : AggregateRoot private Basket() { - + } - - public Basket(Guid id) + + public Basket(Guid id) : base(id) { Items = new List(); @@ -37,20 +37,20 @@ public class Basket : AggregateRoot 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); diff --git a/services/basket/src/EShopOnAbp.BasketService.Domain/BasketServiceDomainModule.cs b/services/basket/src/EShopOnAbp.BasketService.Domain/BasketServiceDomainModule.cs index e3992597..39230157 100644 --- a/services/basket/src/EShopOnAbp.BasketService.Domain/BasketServiceDomainModule.cs +++ b/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) { diff --git a/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/BasketServiceHttpApiClientModule.cs b/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/BasketServiceHttpApiClientModule.cs index 36b13872..093c7352 100644 --- a/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/BasketServiceHttpApiClientModule.cs +++ b/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/BasketServiceHttpApiClientModule.cs @@ -26,4 +26,4 @@ namespace EShopOnAbp.BasketService }); } } -} +} \ No newline at end of file diff --git a/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/BasketClientProxy.Generated.cs b/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/BasketClientProxy.Generated.cs index 014328ed..6a723592 100644 --- a/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/BasketClientProxy.Generated.cs +++ b/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/BasketClientProxy.Generated.cs @@ -20,6 +20,14 @@ namespace EShopOnAbp.BasketService.ClientProxies return await RequestAsync(nameof(GetAsync)); } + public virtual async Task GetByAnonymousUserIdAsync(Guid id) + { + return await RequestAsync(nameof(GetByAnonymousUserIdAsync), new ClientProxyRequestTypeValue + { + { typeof(Guid), id } + }); + } + public virtual async Task AddProductAsync(AddProductDto input) { return await RequestAsync(nameof(AddProductAsync), new ClientProxyRequestTypeValue diff --git a/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/basket-generate-proxy.json b/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/basket-generate-proxy.json index c6f742a4..34f02bdc 100644 --- a/services/basket/src/EShopOnAbp.BasketService.HttpApi.Client/ClientProxies/basket-generate-proxy.json +++ b/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" } } diff --git a/shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/en.json b/shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/en.json index ab1b0396..44e7594e 100644 --- a/shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/en.json +++ b/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" } } diff --git a/shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/tr.json b/shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/tr.json index 8450b569..b803def2 100644 --- a/shared/EShopOnAbp.Shared.Localization/Localization/EShopOnAbp/tr.json +++ b/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" } }