@using Groceries.Data @using Microsoft.EntityFrameworkCore @inject AppDbContext DbContext <form method="post" @attributes="AdditionalAttributes"> @* Ensure form action/method are used for implicit submission instead of barcode button *@ <button type="submit" hidden></button> <div data-controller="transaction-item-form"> <div class="form-field" data-transaction-item-form-target="barcodeFormField" hidden> <label class="form-field__label" for="transactionItemBarcode">Barcode</label> <div class="form-field__control input"> <input type="hidden" name="barcodeFormat" value="@barcode?.Format" data-transaction-item-form-target="barcodeFormat" /> <input class="input__control" id="transactionItemBarcode" name="barcodeData" value="@barcode?.BarcodeData" data-transaction-item-form-target="barcodeData" /> <button class="input__addon button" type="submit" formmethod="get" formnovalidate data-action="transaction-item-form#scanBarcode" data-transaction-item-form-target="barcodeButton"> @* Barcode scanner icon *@ <svg class="icon icon--sm" xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M40-120v-200h80v120h120v80H40Zm680 0v-80h120v-120h80v200H720ZM160-240v-480h80v480h-80Zm120 0v-480h40v480h-40Zm120 0v-480h80v480h-80Zm120 0v-480h120v480H520Zm160 0v-480h40v480h-40Zm80 0v-480h40v480h-40ZM40-640v-200h200v80H120v120H40Zm800 0v-120H720v-80h200v200h-80Z" /></svg> </button> </div> </div> <fieldset class="form-field"> <legend class="form-field__label">Item</legend> <div class="form-field__control input"> <input class="input__control flex-2" name="brand" value="@selectedItem?.Brand" placeholder="Brand" list="itemBrands" autocomplete="off" required autofocus data-action="transaction-item-form#filterNames transaction-item-form#setPriceAndQuantity" data-transaction-item-form-target="brand" /> <input class="input__control flex-5" name="name" value="@selectedItem?.Name" placeholder="Name" list="itemNames" autocomplete="off" required data-action="transaction-item-form#setPriceAndQuantity" /> <datalist id="itemBrands"> @foreach (var item in items.DistinctBy(item => item.Brand)) { <option value="@item.Brand" /> } </datalist> <datalist id="itemNames"> @foreach (var item in items) { <option value="@item.Name" data-transaction-item-form-target="option" data-brand="@item.Brand" data-price="@item.Price" data-quantity="@item.Quantity" /> } </datalist> </div> </fieldset> <div class="form-field"> <label class="form-field__label" for="transactionItemPrice">Price</label> <div class="form-field__control input"> @*<span class="input__inset">@CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol</span>*@ <input class="input__control" id="transactionItemPrice" name="price" value="@price" type="number" min="0" step="0.01" required data-transaction-item-form-target="price" /> </div> </div> <div class="form-field"> <label class="form-field__label" for="transactionItemQuantity">Quantity</label> <div class="form-field__control input"> <input class="input__control" id="transactionItemQuantity" name="quantity" value="@quantity" type="number" min="1" required data-transaction-item-form-target="quantity" /> </div> </div> </div> @ChildContent </form> @code { [Parameter] public TransactionItem? TransactionItem { get; set; } [Parameter] public RenderFragment? ChildContent { get; set; } [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; } private record ItemModel(Guid Id, string Brand, string Name, decimal? Price, int? Quantity); private ItemBarcode? barcode; private ItemModel[] items = []; private ItemModel? selectedItem; private decimal? price; private int quantity; protected override async Task OnParametersSetAsync() { barcode = TransactionItem?.Item?.Barcodes.FirstOrDefault(); items = await DbContext.Items .OrderBy(item => item.Brand) .ThenBy(item => item.Name) .GroupJoin( DbContext.ItemPurchases.Where(purchase => purchase.IsLastPurchase), item => item.Id, lastPurchase => lastPurchase.ItemId, (item, purchases) => new { item, purchases }) .SelectMany( group => group.purchases.DefaultIfEmpty(), (group, lastPurchase) => new ItemModel( group.item.Id, group.item.Brand, group.item.Name, lastPurchase != null ? lastPurchase.Price : null, lastPurchase != null ? lastPurchase.Quantity : null)) .ToArrayAsync(); selectedItem = items.SingleOrDefault(item => item.Id == TransactionItem?.ItemId); price = TransactionItem?.Price >= 0 ? TransactionItem.Price : selectedItem?.Price; quantity = TransactionItem?.Quantity >= 1 ? TransactionItem.Quantity : (selectedItem?.Quantity ?? 1); } }