Refactor Stores page to Razor components

This commit is contained in:
2023-12-02 20:31:32 +00:00
parent d8c6e502a8
commit f1d65f590f
12 changed files with 266 additions and 111 deletions

View File

@ -1,52 +0,0 @@
@using Groceries.Stores
@model StoreListModel
@{
ViewBag.Title = "Stores";
}
<div class="row">
<h1 class="row__fill">Stores</h1>
<form method="get" data-controller="search-form" data-turbo-frame="table" data-turbo-action="advance">
<input type="hidden" name="page" value="1" />
<div class="form-field">
<div class="form-field__control input input--leading-inset input--trailing-addon">
<div class="input__inset">
@* Search icon *@
<svg class="icon icon--sm" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0V0z" fill="none" /><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" /></svg>
</div>
<input class="input__control" type="search" name="search" value="@Model.Search" placeholder="Search" autocomplete="off" data-action="search-form#input" />
<button class="input__addon button" data-search-form-target="button">Search</button>
</div>
</div>
</form>
<a class="button button--primary" asp-action="NewStore" data-turbo-frame="modal">New store</a>
</div>
<turbo-frame id="table" target="_top">
<section class="table">
<table>
<thead>
<tr>
<th scope="col" class="table__header table__header--shaded">Retailer</th>
<th scope="col" class="table__header table__header--shaded" style="width: 100%">Name</th>
<th scope="col" class="table__header table__header--shaded">Transactions</th>
<th scope="col" class="table__header table__header--shaded"></th>
</tr>
</thead>
<tbody>
@foreach (var store in Model.Stores)
{
<tr>
<td class="table__cell">@store.Retailer</td>
<td class="table__cell">@store.Name</td>
<td class="table__cell table__cell--numeric">@store.TotalTransactions</td>
<td class="table__cell">
<a class="link" asp-action="EditStore" asp-route-id="@store.Id" data-turbo-frame="modal">Edit</a>
</td>
</tr>
}
</tbody>
</table>
<partial name="_TablePaginator" model="Model.Stores" />
</section>
</turbo-frame>

View File

@ -1,9 +0,0 @@
namespace Groceries.Stores;
public record StoreListModel(string? Search, ListPageModel<StoreListModel.Store> Stores)
{
public record Store(Guid Id, string Retailer, string Name)
{
public int TotalTransactions { get; init; }
}
}

View File

@ -2,6 +2,7 @@ namespace Groceries.Stores;
using Groceries.Common;
using Groceries.Data;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@ -16,31 +17,9 @@ public class StoresController : Controller
}
[HttpGet]
public async Task<IActionResult> Index(int page, string? search)
public IResult Index()
{
var storesQuery = dbContext.Stores.AsQueryable();
if (!string.IsNullOrEmpty(search))
{
var searchPattern = $"%{search}%";
storesQuery = storesQuery.Where(store => EF.Functions.ILike(store.Retailer!.Name + ' ' + store.Name, searchPattern));
}
var stores = await storesQuery
.OrderBy(store => store.Retailer!.Name)
.ThenBy(store => store.Name)
.Select(store => new StoreListModel.Store(store.Id, store.Retailer!.Name, store.Name)
{
TotalTransactions = store.Transactions!.Count(),
})
.ToListPageModelAsync(page, cancellationToken: HttpContext.RequestAborted);
if (stores.Page != page)
{
return RedirectToAction(nameof(Index), new { page = stores.Page, search });
}
var model = new StoreListModel(search, stores);
return View(model);
return new RazorComponentResult<StoresPage>();
}
[HttpGet("new")]

View File

@ -0,0 +1,80 @@
@using Groceries.Data
@using Microsoft.EntityFrameworkCore
@layout Layout
@inject AppDbContext DbContext
@inject NavigationManager Navigation
@inject IHttpContextAccessor HttpContextAccessor
<PageTitle>Groceries &ndash; Stores</PageTitle>
<div class="row">
<h1 class="row__fill">Stores</h1>
<SearchForm data-turbo-frame="table" data-turbo-action="advance">
<input type="hidden" name="page" value="1" />
</SearchForm>
<a class="button button--primary" href="/stores/new" data-turbo-frame="modal">New store</a>
</div>
<turbo-frame id="table" target="top">
<section class="table">
<table>
<thead>
<tr>
<th scope="col" class="table__header table__header--shaded">Retailer</th>
<th scope="col" class="table__header table__header--shaded" style="width: 100%">Name</th>
<th scope="col" class="table__header table__header--shaded">Transactions</th>
<th scope="col" class="table__header table__header--shaded"></th>
</tr>
</thead>
<tbody>
@foreach (var store in stores)
{
<tr>
<td class="table__cell">@store.Retailer</td>
<td class="table__cell">@store.Name</td>
<td class="table__cell table__cell--numeric">@store.TransactionsCount</td>
<td class="table__cell">
<a class="link" href="/stores/edit/@store.Id" data-turbo-frame="modal">Edit</a>
</td>
</tr>
}
</tbody>
</table>
<TablePaginator Model="stores" />
</section>
</turbo-frame>
@code {
[SupplyParameterFromQuery]
public int? Page { get; set; }
[SupplyParameterFromQuery]
public string? Search { get; set; }
private record StoreModel(Guid Id, string Retailer, string Name, int TransactionsCount);
private ListPageModel<StoreModel> stores = ListPageModel.Empty<StoreModel>();
protected override async Task OnParametersSetAsync()
{
var storesQuery = DbContext.Stores.AsQueryable();
if (!string.IsNullOrEmpty(Search))
{
var searchPattern = $"%{Search}%";
storesQuery = storesQuery.Where(store => EF.Functions.ILike(store.Retailer!.Name + ' ' + store.Name, searchPattern));
}
stores = await storesQuery
.OrderBy(store => store.Retailer!.Name)
.ThenBy(store => store.Name)
.Select(store => new StoreModel(store.Id, store.Retailer!.Name, store.Name, store.Transactions!.Count()))
.ToListPageModelAsync(Page.GetValueOrDefault(), cancellationToken: HttpContextAccessor.HttpContext!.RequestAborted);
if (stores.Page != Page)
{
Navigation.NavigateTo(Navigation.GetUriWithQueryParameter("page", stores.Page));
}
}
}