From 3138c4e745f4f0f9396647446de46b754fb89aec Mon Sep 17 00:00:00 2001 From: Andre Foresti Date: Sat, 25 May 2024 16:43:38 +1000 Subject: [PATCH] Cashier UI remodel - Add support to use tailwind; - Show all available products as buttons in a grid - Add item to order when the product button is clicked - Create the order on the first item - Show summary of the order on the left side - Asks for customer name in a modal - Place the order when confirm the modal --- src/UI/NCafe.Web/App.razor | 1 + src/UI/NCafe.Web/Models/Order.cs | 16 ++ src/UI/NCafe.Web/Pages/Cashier/Index.razor | 224 ++++++++++----------- src/UI/NCafe.Web/Shared/Modal.razor | 42 ++++ 4 files changed, 160 insertions(+), 123 deletions(-) create mode 100644 src/UI/NCafe.Web/Shared/Modal.razor diff --git a/src/UI/NCafe.Web/App.razor b/src/UI/NCafe.Web/App.razor index 6f377a6..df5979f 100644 --- a/src/UI/NCafe.Web/App.razor +++ b/src/UI/NCafe.Web/App.razor @@ -10,5 +10,6 @@ + diff --git a/src/UI/NCafe.Web/Models/Order.cs b/src/UI/NCafe.Web/Models/Order.cs index ee4bc89..d5c1129 100644 --- a/src/UI/NCafe.Web/Models/Order.cs +++ b/src/UI/NCafe.Web/Models/Order.cs @@ -1,7 +1,23 @@ namespace NCafe.Web.Models; +public class Order +{ + public Guid Id { get; set; } + public string CustomerName { get; set; } + public List Items { get; set; } = new(); + public decimal Total => Items.Sum(x => x.Total); + public void Clear() + { + Items.Clear(); + Id = Guid.Empty; + CustomerName = string.Empty; + } +} + public class OrderItem { public Guid ProductId { get; set; } public int Quantity { get; set; } + public decimal Price { get; set; } + public decimal Total => Quantity * Price; } diff --git a/src/UI/NCafe.Web/Pages/Cashier/Index.razor b/src/UI/NCafe.Web/Pages/Cashier/Index.razor index d281981..1d7dd74 100644 --- a/src/UI/NCafe.Web/Pages/Cashier/Index.razor +++ b/src/UI/NCafe.Web/Pages/Cashier/Index.razor @@ -1,4 +1,4 @@ -@page "/cashier" +@page "/cashier" @using NCafe.Web.Models @inject HttpClient Http @inject IConfiguration Configuration @@ -7,141 +7,140 @@ Cashier - NCafe -

New Order

- -@if (products == null) +@if (_products == null) { + return; } -else if (!products.Any()) + +@if (!_products.Any()) { + return; } -else -{ - if (cashierState == CashierState.Idle) - { - - } - else if (cashierState == CashierState.OrderCreated) - { -
- - - - - - - - - -
- - - - - - } -} +
+
+

Order Summary

+
    + @foreach (var item in _order.Items) + { +
  • + @item.Quantity x @(_products.FirstOrDefault(p => p.Id == item.ProductId)?.Name) + @(item.Total.ToString("C")) +
  • + } +
+ @if (_order.Items.Any()) { +
+ Total + @(_order.Total.ToString("C")) +
+ + + } +
+
+ @foreach (var product in _products) + { + + } +
-@code { - private bool isLoading; - private CashierState cashierState = CashierState.Idle; +
- private Product[] products; + +
+ + + +
+ + - private AutoComplete productAutoComplete; - private string value; +
+ Total + @(_order.Total.ToString("C")) +
+
- private Guid orderId; - private string customer = string.Empty; - private OrderItem orderItem = new OrderItem { Quantity = 1 }; +@code { - private string baseAddress; + private Product[] _products; + private readonly Order _order = new(); + private string _baseAddress; + private bool _isModalVisible; protected override async Task OnInitializedAsync() { - baseAddress = Configuration["CashierBaseAddress"]; + _baseAddress = Configuration["CashierBaseAddress"]; - products = await Http.GetFromJsonAsync($"{baseAddress}/products"); + _products = await Http.GetFromJsonAsync($"{_baseAddress}/products"); } - void OnSelectionChange(AutoCompleteOption item) + void ShowModal() => _isModalVisible = true; + void HideModal() => _isModalVisible = false; + + void CancelOrder() { - if (item.Value is Product product) - { - orderItem.ProductId = product.Id; - } + _order.Clear(); + _isModalVisible = false; } - async Task CreateOrder() + async Task AddItemToOrder(Guid productId) { - isLoading = true; + if (_order.Id == Guid.Empty) + { + await CreateOrder(); + } + + var product = _products.FirstOrDefault(p => p.Id == productId); + var item = _order.Items.FirstOrDefault(i => i.ProductId == productId); + if (item == null) + { + item = new OrderItem { ProductId = productId, Price = product?.Price ?? 0, Quantity = 1 }; + _order.Items.Add(item); + } else + { + item.Quantity++; + } try { - var response = await Http.PostAsJsonAsync($"{baseAddress}/orders", new { CreatedBy = "cashier-1" }); + var response = await Http.PostAsJsonAsync( + $"{_baseAddress}/orders/add-item", + new { OrderId = _order.Id, item.ProductId, item.Quantity }); response.EnsureSuccessStatusCode(); - - orderId = await response.Content.ReadFromJsonAsync(); - - cashierState = CashierState.OrderCreated; } catch (Exception ex) { _ = NotificationService.Open(new NotificationConfig - { - Message = "Error", - Duration = 0, - Description = $"There was an error while attempting to save the product: {ex.Message}.", - NotificationType = NotificationType.Error - }); - } - finally - { - isLoading = false; + { + Message = "Error", + Duration = 0, + Description = $"There was an error while attempting to add item: {ex.Message}.", + NotificationType = NotificationType.Error + }); } } - async Task AddOrderItem() + async Task CreateOrder() { - isLoading = true; - try { - var response = await Http.PostAsJsonAsync($"{baseAddress}/orders/add-item", new { OrderId = orderId, orderItem.ProductId, orderItem.Quantity }); + var response = await Http.PostAsJsonAsync( + $"{_baseAddress}/orders", new { CreatedBy = "cashier-1" }); response.EnsureSuccessStatusCode(); - _ = NotificationService.Open(new NotificationConfig - { - Message = "Item Added", - Description = $"{orderItem.Quantity}x {productAutoComplete.SelectedValue} was added.", - NotificationType = NotificationType.Success - }); - - orderItem.Quantity = 1; - - // The next 2 lines will reset the product selection - value = ""; - productAutoComplete.SelectedValue = null; + _order.Id = await response.Content.ReadFromJsonAsync(); } catch (Exception ex) { @@ -149,39 +148,31 @@ else { Message = "Error", Duration = 0, - Description = $"There was an error while attempting to add item: {ex.Message}.", + Description = $"There was an error while attempting to create the order: {ex.Message}.", NotificationType = NotificationType.Error }); } - finally - { - isLoading = false; - } } async Task PlaceOrder() { - isLoading = true; - try { - var response = await Http.PostAsJsonAsync($"{baseAddress}/orders/place", new { OrderId = orderId, Customer = new { Name = customer } }); + var response = await Http.PostAsJsonAsync( + $"{_baseAddress}/orders/place", + new { OrderId = _order.Id, Customer = new { Name = _order.CustomerName } }); response.EnsureSuccessStatusCode(); _ = NotificationService.Open(new NotificationConfig { Message = "Order Created", - Description = $"Order created successfully.", + Description = "Order created successfully.", NotificationType = NotificationType.Success }); - orderId = Guid.Empty; - customer = string.Empty; - cashierState = CashierState.Idle; + _order.Clear(); + _isModalVisible = false; - // The next 2 lines will reset the product selection - value = ""; - productAutoComplete.SelectedValue = null; } catch (Exception ex) { @@ -193,20 +184,7 @@ else NotificationType = NotificationType.Error }); } - finally - { - isLoading = false; - } - } - - void GoAdmin() - { - NavigationManager.NavigateTo("admin"); } - public enum CashierState - { - Idle, - OrderCreated - } + void GoAdmin() => NavigationManager.NavigateTo("admin"); } diff --git a/src/UI/NCafe.Web/Shared/Modal.razor b/src/UI/NCafe.Web/Shared/Modal.razor new file mode 100644 index 0000000..e973e61 --- /dev/null +++ b/src/UI/NCafe.Web/Shared/Modal.razor @@ -0,0 +1,42 @@ +@code { + [Parameter] + public bool IsVisible { get; set; } + + [Parameter] + public EventCallback OnClose { get; set; } + + [Parameter] + public EventCallback OnConfirm { get; set; } + + [Parameter] + public EventCallback OnCancel { get; set; } + + [Parameter] + public string Title { get; set; } + + [Parameter] + public RenderFragment ChildContent { get; set; } + + private void Close() => OnClose.InvokeAsync(); + private void Confirm() => OnConfirm.InvokeAsync(); + private void Cancel() => OnCancel.InvokeAsync(); +} + +@if (IsVisible) +{ +
+
+
+
@Title
+ +
+
+ @ChildContent +
+
+ + +
+
+
+}