Refactor tables to Razor components

This commit is contained in:
2023-12-10 20:36:48 +00:00
parent fd05501fad
commit 8223568ed6
15 changed files with 624 additions and 417 deletions

View File

@ -2,7 +2,6 @@
@using Microsoft.EntityFrameworkCore
@layout Layout
@inject AppDbContext DbContext
<PageTitle>Groceries &ndash; New Transaction</PageTitle>
@ -14,57 +13,45 @@
</div>
<form method="post">
<div class="card form-field">
<section class="card form-field">
<div class="card__header row">
<h2 class="row__fill">Items</h2>
<a class="button button--primary" href="/transactions/new/items/new" autofocus data-turbo-frame="modal">New item</a>
<a class="button button--primary" href="/transactions/new/items/new" autofocus data-turbo-frame="modal">
New item
</a>
</div>
<div class="card__content card__content--table">
<table>
<thead>
<tr>
<th scope="col" class="table__header" style="width: 100%">Name</th>
<th scope="col" class="table__header">Price</th>
<th scope="col" class="table__header"><abbr title="Quantity">Qty</abbr></th>
<th scope="col" class="table__header">Amount</th>
<th scope="col" class="table__header"></th>
</tr>
</thead>
<tbody>
@foreach (var item in Transaction.Items)
{
<tr>
<td class="table__cell table__cell--compact">
@itemNames.GetValueOrDefault(item.ItemId)
</td>
<td class="table__cell table__cell--compact table__cell--numeric">
@item.Price.ToString("c")
</td>
<td class="table__cell table__cell--compact table__cell--numeric">
@item.Quantity
</td>
<td class="table__cell table__cell--compact table__cell--numeric">
@item.Amount.ToString("c")
</td>
<td class="table__cell table__cell--compact">
<a class="link" href="/transactions/new/items/edit/@item.ItemId" data-turbo-frame="modal">Edit</a>
</td>
</tr>
}
</tbody>
<tfoot>
<Table Items="Transaction.Items.AsQueryable()" CellClass="table__cell--compact">
<ChildContent>
<TemplateTableColumn Title="Name" Fill="true" Context="item">
@itemNames.GetValueOrDefault(item.ItemId)
</TemplateTableColumn>
<PropertyTableColumn Property="i => i.Price" Format="c" />
<PropertyTableColumn Property="i => i.Quantity">
<HeaderContent>
<abbr title="Quantity">Qty</abbr>
</HeaderContent>
</PropertyTableColumn>
<PropertyTableColumn Property="i => i.Amount" Format="c" />
<TemplateTableColumn Context="item">
<a class="link" href="/transactions/new/items/edit/@item.ItemId" data-turbo-frame="modal">
Edit
</a>
</TemplateTableColumn>
</ChildContent>
<FooterContent>
<tr>
<td class="table__cell table__cell--compact table__cell--total" colspan="3">Subtotal</td>
<td class="table__cell table__cell--compact table__cell--numeric table__cell--total">
<td class="table__cell table__cell--compact table__cell--total table__cell--align-end">
@Transaction.Items.Sum(item => item.Amount).ToString("c")
</td>
<td class="table__cell table__cell--compact"></td>
</tr>
</tfoot>
</table>
</FooterContent>
</Table>
</div>
</div>
</section>
<div class="row">
<button class="button button--primary" type="submit" disabled="@(Transaction.Items.Count == 0)">Next</button>
@ -73,12 +60,12 @@
</form>
@code {
[Parameter]
public required Transaction Transaction { get; set; }
private string store = string.Empty;
private Dictionary<Guid, string> itemNames = new();
[Parameter]
public required Transaction Transaction { get; set; }
protected override async Task OnParametersSetAsync()
{
store = await DbContext.Stores

View File

@ -2,7 +2,6 @@
@using Microsoft.EntityFrameworkCore
@layout Layout
@inject AppDbContext DbContext
<PageTitle>Groceries &ndash; New Transaction</PageTitle>
@ -14,55 +13,42 @@
</div>
<form method="post">
<div class="card form-field">
<div class="card__header row">
<section class="card form-field">
<header class="card__header row">
<h2 class="row__fill">Promotions</h2>
<a class="button button--primary" href="/transactions/new/promotions/new" autofocus data-turbo-frame="modal">
New promotion
</a>
</div>
</header>
<div class="card__content card__content--table">
<table>
<thead>
<tr>
<th scope="col" class="table__header" style="width: 100%">Name</th>
<th scope="col" class="table__header">Items</th>
<th scope="col" class="table__header">Amount</th>
<th scope="col" class="table__header"></th>
</tr>
</thead>
<tbody>
@foreach (var promotion in Transaction.Promotions)
{
<tr>
<td class="table__cell table__cell--compact">
@promotion.Name
</td>
<td class="table__cell table__cell--compact table__cell--numeric">
@promotion.Items.Sum(item => Transaction.Items.Single(i => i.ItemId == item.Id).Quantity)
</td>
<td class="table__cell table__cell--compact table__cell--numeric">
@((-promotion.Amount).ToString("c"))
</td>
<td class="table__cell table__cell--compact">
<a class="link" href="/transactions/new/promotions/edit/@promotion.Id" data-turbo-frame="modal">Edit</a>
</td>
</tr>
}
</tbody>
<tfoot>
<Table Items="Transaction.Promotions.AsQueryable()" CellClass="table__cell--compact">
<ChildContent>
<PropertyTableColumn Property="p => p.Name" Fill="true" />
<TemplateTableColumn Title="Items" Align="Align.End" Context="promotion">
@promotion.Items.Sum(item => Transaction.Items.Single(i => i.ItemId == item.Id).Quantity)
</TemplateTableColumn>
<PropertyTableColumn Property="p => p.Amount" Context="amount">
@((-amount).ToString("c"))
</PropertyTableColumn>
<TemplateTableColumn Context="promotion">
<a class="link" href="/transactions/new/promotions/edit/@promotion.Id" data-turbo-frame="modal">
Edit
</a>
</TemplateTableColumn>
</ChildContent>
<FooterContent>
<tr>
<td class="table__cell table__cell--compact table__cell--total" colspan="2">Total</td>
<td class="table__cell table__cell--compact table__cell--numeric table__cell--total">
<td class="table__cell table__cell--compact table__cell--total table__cell--align-end">
@Transaction.Total.ToString("c")
</td>
<td class="table__cell table__cell--compact"></td>
</tr>
</tfoot>
</table>
</FooterContent>
</Table>
</div>
</div>
</section>
<div class="row">
<button class="button button--primary" type="submit" disabled="@(Transaction.Items.Count == 0)">Save</button>
@ -71,11 +57,11 @@
</form>
@code {
private string store = string.Empty;
[Parameter]
public required Transaction Transaction { get; set; }
private string store = string.Empty;
protected override async Task OnParametersSetAsync()
{
store = await DbContext.Stores

View File

@ -1,139 +1,58 @@
@using Groceries.Data
@layout Layout
@layout Layout
@inject AppDbContext DbContext
@inject NavigationManager Navigation
@inject IHttpContextAccessor HttpContextAccessor
<PageTitle>Groceries &ndash; Transactions</PageTitle>
<div class="row">
<header class="row">
<h1 class="row__fill">Transactions</h1>
<a class="button button--primary form-field" href="/transactions/new">New transaction</a>
</div>
</header>
<section class="table">
<table>
<thead>
<tr>
<th scope="col" class="table__header table__header--shaded table__header--sortable">
<a href="@GetColumnHeaderUri("date")" data-dir="@GetColumnSortDirection("date")">
Date
</a>
</th>
<th scope="col" class="table__header table__header--shaded" style="width: 100%">Store</th>
<th scope="col" class="table__header table__header--shaded table__header--sortable">
<a href="@GetColumnHeaderUri("items")" data-dir="@GetColumnSortDirection("items")">
Items
</a>
</th>
<th scope="col" class="table__header table__header--shaded table__header--sortable">
<a href="@GetColumnHeaderUri("amount")" data-dir="@GetColumnSortDirection("amount")">
Amount
</a>
</th>
@* <th scope="col" class="table__header table__header--shaded"></th> *@
</tr>
</thead>
<tbody>
@foreach (var transaction in transactions)
{
<tr>
<td class="table__cell">
<time datetime="@transaction.CreatedAt.ToString("o")">@transaction.CreatedAt.ToLongDateString()</time>
</td>
<td class="table__cell">@transaction.Store</td>
<td class="table__cell table__cell--numeric">@transaction.TotalItems</td>
<td class="table__cell table__cell--numeric">@transaction.TotalAmount.ToString("c")</td>
@*<td class="table__cell">View</td>*@
</tr>
}
</tbody>
</table>
<TablePaginator Model="transactions" />
<Table Items="transactions" Pagination="pagination" HeaderClass="table__header--shaded">
<PropertyTableColumn Property="t => t.CreatedAt" Title="Date" Sortable="true" Context="createdAt">
<time datetime="@createdAt.ToString("o")">@createdAt.ToLongDateString()</time>
</PropertyTableColumn>
<PropertyTableColumn Property="t => t.Store" Fill="true" Sortable="true" />
<PropertyTableColumn Property="t => t.TotalItems" Title="Items" Sortable="true" />
<PropertyTableColumn Property="t => t.TotalAmount" Title="Amount" Format="c" Sortable="true" />
@* <TemplateTableColumn>
<a class="link" href="/transactions/@context.Id">View</a>
</TemplateTableColumn> *@
</Table>
<TablePaginator State="pagination" />
</section>
@code {
[SupplyParameterFromQuery]
public int? Page { get; set; }
[SupplyParameterFromQuery(Name = "sort")]
public string? SortColumnName { get; set; }
[SupplyParameterFromQuery(Name = "dir")]
public string? SortDirection { get; set; }
private record TransactionModel(Guid Id, DateTime CreatedAt, string Store, decimal TotalAmount, int TotalItems);
private ListPageModel<TransactionModel> transactions = ListPageModel.Empty<TransactionModel>();
protected override async Task OnParametersSetAsync()
private record TransactionModel
{
var transactionsQuery = DbContext.Transactions
public Guid Id { get; init; }
public DateTime CreatedAt { get; init; }
public required string Store { get; init; }
public decimal TotalAmount { get; init; }
public int TotalItems { get; init; }
}
private IQueryable<TransactionModel> transactions = null!;
private PaginationState pagination = new();
protected override void OnParametersSet()
{
transactions = DbContext.Transactions
.Join(
DbContext.TransactionTotals,
transaction => transaction.Id,
transactionTotal => transactionTotal.TransactionId,
(transaction, transactionTotal) => new
(transaction, transactionTotal) => new TransactionModel
{
transaction.Id,
transaction.CreatedAt,
Id = transaction.Id,
CreatedAt = transaction.CreatedAt,
Store = string.Concat(transaction.Store!.Retailer!.Name, " ", transaction.Store.Name),
TotalAmount = transactionTotal.Total,
TotalItems = transaction.Items.Sum(item => item.Quantity),
});
transactionsQuery = SortColumnName?.ToLowerInvariant() switch
{
"date" when SortDirection == "desc" => transactionsQuery.OrderByDescending(transaction => transaction.CreatedAt),
"amount" when SortDirection == "desc" => transactionsQuery.OrderByDescending(transaction => transaction.TotalAmount),
"items" when SortDirection == "desc" => transactionsQuery.OrderByDescending(transaction => transaction.TotalItems),
"date" => transactionsQuery.OrderBy(transaction => transaction.CreatedAt),
"amount" => transactionsQuery.OrderBy(transaction => transaction.TotalAmount),
"items" => transactionsQuery.OrderBy(transaction => transaction.TotalItems),
_ => transactionsQuery.OrderByDescending(transaction => transaction.CreatedAt),
};
transactions = await transactionsQuery
.Select(transaction => new TransactionModel(
transaction.Id,
transaction.CreatedAt,
transaction.Store,
transaction.TotalAmount,
transaction.TotalItems))
.ToListPageModelAsync(Page.GetValueOrDefault(), cancellationToken: HttpContextAccessor.HttpContext!.RequestAborted);
if (transactions.Page != Page)
{
Navigation.NavigateTo(Navigation.GetUriWithQueryParameter("page", transactions.Page));
}
}
private string GetColumnHeaderUri(string name)
{
var nextSortDirecton = name == SortColumnName
? SortDirection switch
{
null or "" => "asc",
"asc" => "desc",
_ => null,
}
: "asc";
return Navigation.GetUriWithQueryParameters(new Dictionary<string, object?>
{
{ "sort", nextSortDirecton != null ? name : null },
{ "dir", nextSortDirecton },
{ "page", 1 },
});
}
private string? GetColumnSortDirection(string name)
{
return SortDirection switch
{
"asc" or "desc" when name == SortColumnName => SortDirection,
_ => null,
};
})
.OrderByDescending(transaction => transaction.CreatedAt);
}
}