diff --git a/LiteCharms.Features.MidrandBooks.Seed/LiteCharms.Features.MidrandBooks.Seed.csproj b/LiteCharms.Features.MidrandBooks.Seed/LiteCharms.Features.MidrandBooks.Seed.csproj index bc9d6e2..86652ed 100644 --- a/LiteCharms.Features.MidrandBooks.Seed/LiteCharms.Features.MidrandBooks.Seed.csproj +++ b/LiteCharms.Features.MidrandBooks.Seed/LiteCharms.Features.MidrandBooks.Seed.csproj @@ -11,7 +11,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -116,8 +116,8 @@ - - + + diff --git a/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj b/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj index 09ba93d..a8bef3a 100644 --- a/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj +++ b/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj @@ -32,7 +32,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -136,8 +136,8 @@ - - + + diff --git a/LiteCharms.Features.MidrandBooks/Orders/CartService.cs b/LiteCharms.Features.MidrandBooks/Orders/CartService.cs new file mode 100644 index 0000000..ae3f8c7 --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Orders/CartService.cs @@ -0,0 +1,128 @@ +using LiteCharms.Features.Abstractions; +using LiteCharms.Features.MidrandBooks.Orders.Models; +using LiteCharms.Features.MidrandBooks.Products.Models; + +namespace LiteCharms.Features.MidrandBooks.Orders; + +public sealed class CartService : IService +{ + private Cart cart = new(); + + public Cart GetCart() => cart; + + public void LoadCart(Cart savedCart) => cart = savedCart; + + public void AddItem(ProductPrice productPrice) + { + var itemExists = false; + + for (var i = 0; i < cart.Items.Count; i++) + { + if (cart.Items[i].Price!.Id == productPrice.Id) + { + cart.Items[i].Quantity++; + cart.Items[i].Amount += productPrice.Amount; + + itemExists = true; + + break; + } + } + + if (!itemExists) + cart.Items.Add(new CartItem + { + Price = productPrice, + Amount = productPrice.Amount, + Quantity = 1, + }); + + CalculateTotalPrice(); + } + + public void UpdateQuantity(long productPriceId, int newQuantity) + { + if (newQuantity <= 0) + { + RemoveAllSameItem(productPriceId); + + return; + } + + for (var i = 0; i < cart.Items.Count; i++) + { + if (cart.Items[i].Price!.Id == productPriceId) + { + var oldQuantity = cart.Items[i].Quantity; + var pricePerUnit = cart.Items[i].Price!.Amount; + + cart.Items[i].Quantity = newQuantity; + cart.Items[i].Amount = pricePerUnit * newQuantity; + break; + } + } + + CalculateTotalPrice(); + } + + public void RemoveOneItem(long productPriceId) + { + for (var i = 0; i < cart.Items.Count; i++) + { + if (cart.Items[i].Price!.Id == productPriceId) + { + if (cart.Items[i].Quantity <= 1) + { + cart.Items.RemoveAt(i); + } + else + { + cart.Items[i].Quantity--; + cart.Items[i].Amount -= cart.Items[i].Price!.Amount; + } + + break; + } + } + + CalculateTotalPrice(); + } + + public void RemoveAllSameItem(long productPriceId) + { + if (cart.Items.Count == 0) return; + + var item = cart.Items.FirstOrDefault(i => i.Price?.Id == productPriceId); + + if (item is not null) cart.Items.Remove(item); + + CalculateTotalPrice(); + } + + public void Clear() + { + if(cart.CustomerId is not null || cart.OrderId is not null) + { + cart.TotalPrice = 0; + cart.TotalVat = 0; + cart.Items.Clear(); + + return; + } + + cart = new Cart(); + } + + public decimal CalculateTotalPrice() + { + if (cart.Items.Count == 0) return 0; + + var gross = cart.Items.Sum(i => i.Amount); + + if (!cart.IsVatInclusive) cart.TotalVat = gross * cart.VatRate; + + cart.TotalPrice = gross + cart.TotalVat; + + return cart.TotalPrice; + } +} diff --git a/LiteCharms.Features.MidrandBooks/Orders/Models/Cart.cs b/LiteCharms.Features.MidrandBooks/Orders/Models/Cart.cs new file mode 100644 index 0000000..1d1fd1e --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Orders/Models/Cart.cs @@ -0,0 +1,18 @@ +namespace LiteCharms.Features.MidrandBooks.Orders.Models; + +public sealed class Cart +{ + public long? CustomerId { get; set; } + + public long? OrderId { get; set; } + + public decimal TotalPrice { get; set; } + + public decimal TotalVat { get; set; } + + public decimal VatRate { get; set; } = 0.15m; + + public bool IsVatInclusive { get; set; } = true; + + public IList Items { get; set; } = []; +} diff --git a/LiteCharms.Features.MidrandBooks/Orders/Models/CartItem.cs b/LiteCharms.Features.MidrandBooks/Orders/Models/CartItem.cs new file mode 100644 index 0000000..5d73afe --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Orders/Models/CartItem.cs @@ -0,0 +1,12 @@ +using LiteCharms.Features.MidrandBooks.Products.Models; + +namespace LiteCharms.Features.MidrandBooks.Orders.Models; + +public sealed class CartItem +{ + public ProductPrice? Price { get; set; } + + public long Quantity { get; set; } + + public decimal Amount { get; set; } +} diff --git a/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj b/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj index 4cd7426..5f01765 100644 --- a/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj +++ b/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj @@ -136,8 +136,8 @@ - - + + diff --git a/LiteCharms.Features/Browser/LocalStorageService.cs b/LiteCharms.Features/Browser/LocalStorageService.cs new file mode 100644 index 0000000..f5168ac --- /dev/null +++ b/LiteCharms.Features/Browser/LocalStorageService.cs @@ -0,0 +1,80 @@ +using LiteCharms.Features.Abstractions; + +namespace LiteCharms.Features.Browser; + +public sealed class LocalStorageService(ProtectedLocalStorage storage) : IService +{ + public async ValueTask DeleteAsync(string key) + { + try + { + await storage.DeleteAsync(key); + + return Result.Ok(); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask SaveAsync(string key, string value) + { + try + { + await storage.SetAsync(key, value); + + return Result.Ok(); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask SaveAsync(string key, TValue value) where TValue : class + { + try + { + await storage.SetAsync(key, value); + + return Result.Ok(); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetAsync(string key) + { + try + { + var retrieval = await storage.GetAsync(key); + + return retrieval.Success && !string.IsNullOrWhiteSpace(retrieval.Value) + ? Result.Ok(retrieval.Value) + : Result.Fail($"Could not find object by key {key}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetAsync(string key) where TValue : class + { + try + { + var retrieval = await storage.GetAsync(key); + + return retrieval.Success && retrieval.Value is not null + ? Result.Ok(retrieval.Value) + : Result.Fail($"Could not find object by key {key}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/LiteCharms.Features.csproj b/LiteCharms.Features/LiteCharms.Features.csproj index 98a074e..fc6cc2f 100644 --- a/LiteCharms.Features/LiteCharms.Features.csproj +++ b/LiteCharms.Features/LiteCharms.Features.csproj @@ -67,7 +67,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -171,8 +171,8 @@ - - + + @@ -182,6 +182,7 @@ +