PR U-9: FluentSearch filter on grid pages
Accounts, BookingRules, BookingTexts, BankTransactions each get an <FluentSearch> above the grid, backed by a computed Filtered IQueryable over an in-memory source list. FluentSearch is Immediate, so filtering happens on each keystroke; data sizes are small enough for per-render LINQ. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
766295a119
commit
0c48539ce6
8 changed files with 69 additions and 24 deletions
|
|
@ -3,10 +3,13 @@
|
|||
|
||||
<PageTitle>Konten</PageTitle>
|
||||
|
||||
<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center">
|
||||
<FluentButton IconStart="@(new Icons.Regular.Size16.Add())" Appearance="Appearance.Neutral" OnClick="AddAsync">Neues Konto hinzufügen</FluentButton>
|
||||
<FluentSearch @bind-Value="filter" Immediate="true" Placeholder="Filter…" Style="width: 300px;"/>
|
||||
</FluentStack>
|
||||
<FluentPaginator State="@pagination" SummaryTemplate="@template" />
|
||||
|
||||
<FluentDataGrid Items="@accountQueryable" Pagination="@pagination" RowSize="DataGridRowSize.Medium">
|
||||
<FluentDataGrid Items="@Filtered" Pagination="@pagination" RowSize="DataGridRowSize.Medium">
|
||||
<PropertyColumn Property="@(p => p.Class)" Sortable="true" Width="100px" />
|
||||
<PropertyColumn Property="@(p => p.SubClass)" Sortable="true" Width="140px" />
|
||||
<PropertyColumn Property="@(p => p.Number)" Sortable="true" IsDefaultSortColumn="true" Width="140px" />
|
||||
|
|
|
|||
|
|
@ -17,11 +17,20 @@ public partial class Accounts : ComponentBase
|
|||
[Inject]
|
||||
private IToastService toastService { get; set; } = null!;
|
||||
|
||||
private IQueryable<Account>? accountQueryable;
|
||||
private IReadOnlyList<Account> source = [];
|
||||
private string filter = "";
|
||||
|
||||
private IQueryable<Account> Filtered =>
|
||||
string.IsNullOrWhiteSpace(filter)
|
||||
? source.AsQueryable()
|
||||
: source.AsQueryable().Where(a =>
|
||||
a.Name.Contains(filter, StringComparison.OrdinalIgnoreCase) ||
|
||||
a.Number.ToString().Contains(filter) ||
|
||||
(a.BankAccountNumber != null && a.BankAccountNumber.Contains(filter, StringComparison.OrdinalIgnoreCase)));
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
accountQueryable = (await accountRepository.GetAccountListAsync()).AsQueryable();
|
||||
source = await accountRepository.GetAccountListAsync();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +48,7 @@ public partial class Accounts : ComponentBase
|
|||
var result = await dialog.Result;
|
||||
if (!result.Cancelled && result.Data != null)
|
||||
{
|
||||
accountQueryable = (await accountRepository.GetAccountListAsync()).AsQueryable();
|
||||
source = await accountRepository.GetAccountListAsync();
|
||||
toastService.ShowToast(ToastIntent.Success, "Konto gespeichert", 3000);
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +67,7 @@ public partial class Accounts : ComponentBase
|
|||
var result = await dialog.Result;
|
||||
if (!result.Cancelled && result.Data != null)
|
||||
{
|
||||
accountQueryable = (await accountRepository.GetAccountListAsync()).AsQueryable();
|
||||
source = await accountRepository.GetAccountListAsync();
|
||||
toastService.ShowToast(ToastIntent.Success, "Konto gespeichert", 3000);
|
||||
}
|
||||
}
|
||||
|
|
@ -71,7 +80,7 @@ public partial class Accounts : ComponentBase
|
|||
if (!result.Cancelled)
|
||||
{
|
||||
await accountRepository.DeleteAccountAsync(id);
|
||||
accountQueryable = (await accountRepository.GetAccountListAsync()).AsQueryable();
|
||||
source = await accountRepository.GetAccountListAsync();
|
||||
toastService.ShowToast(ToastIntent.Success, "Konto gelöscht", 3000);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
@page "/banktransactions"
|
||||
<PageTitle>BankTransactions</PageTitle>
|
||||
|
||||
<FluentSearch @bind-Value="filter" Immediate="true" Placeholder="Filter…" Style="width: 300px;"/>
|
||||
<FluentPaginator State="@pagination" />
|
||||
|
||||
<div id="datagrid-container">
|
||||
<FluentDataGrid Items="@TransactionList" Pagination="@pagination" RowSize="DataGridRowSize.Small" AutoItemsPerPage="true" Style="overflow-y:hidden;">
|
||||
<FluentDataGrid Items="@Filtered" Pagination="@pagination" RowSize="DataGridRowSize.Small" AutoItemsPerPage="true" Style="overflow-y:hidden;">
|
||||
<PropertyColumn Title="Buchungsdatum" Width="170px" Property="@(p => p.BookingDate)" Format="dd.MM.yyyy" Sortable="true" IsDefaultSortColumn="true" InitialSortDirection="SortDirection.Descending" />
|
||||
<PropertyColumn Title="Valutadatum" Width="170px" Property="@(p => p.ValueDate)" Format="dd.MM.yyyy" Sortable="true" />
|
||||
<PropertyColumn Title="Text" Property="@(p => p.Text)" />
|
||||
|
|
|
|||
|
|
@ -8,11 +8,22 @@ public partial class BankTransactions : ComponentBase
|
|||
{
|
||||
[Inject]
|
||||
private IBankTransactionRepository bankTransactionRepository { get; set; } = null!;
|
||||
private IQueryable<BankTransaction>? TransactionList;
|
||||
|
||||
private IReadOnlyList<BankTransaction> source = [];
|
||||
private string filter = "";
|
||||
|
||||
private IQueryable<BankTransaction> Filtered =>
|
||||
string.IsNullOrWhiteSpace(filter)
|
||||
? source.AsQueryable()
|
||||
: source.AsQueryable().Where(t =>
|
||||
(t.Text != null && t.Text.Contains(filter, StringComparison.OrdinalIgnoreCase)) ||
|
||||
(t.Debtor != null && t.Debtor.Contains(filter, StringComparison.OrdinalIgnoreCase)) ||
|
||||
(t.Creditor != null && t.Creditor.Contains(filter, StringComparison.OrdinalIgnoreCase)) ||
|
||||
t.Value.ToString().Contains(filter));
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
TransactionList = (await bankTransactionRepository.GetBankTransactionListAsync()).AsQueryable();
|
||||
source = await bankTransactionRepository.GetBankTransactionListAsync();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@
|
|||
|
||||
<PageTitle>Buchungsregeln</PageTitle>
|
||||
|
||||
<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center">
|
||||
<FluentButton IconStart="@(new Icons.Regular.Size16.Add())" Appearance="Appearance.Neutral" OnClick="AddAsync">Neue Regel hinzufügen</FluentButton>
|
||||
<FluentSearch @bind-Value="filter" Immediate="true" Placeholder="Filter…" Style="width: 300px;"/>
|
||||
</FluentStack>
|
||||
<FluentPaginator State="@pagination" SummaryTemplate="@template" />
|
||||
|
||||
<FluentDataGrid Items="@bookingRulesQueryable" Pagination="@pagination" RowSize="DataGridRowSize.Medium">
|
||||
<FluentDataGrid Items="@Filtered" Pagination="@pagination" RowSize="DataGridRowSize.Medium">
|
||||
<PropertyColumn Property="@(p => p.LookupText)" Sortable="true" IsDefaultSortColumn="true" />
|
||||
<PropertyColumn Property="@(p => p.LookupValue)" Format="C2" Align="Align.End"/>
|
||||
<PropertyColumn Property="@(p => p.BookingText)" />
|
||||
|
|
|
|||
|
|
@ -21,11 +21,20 @@ public partial class BookingRules : ComponentBase
|
|||
[Inject]
|
||||
private IToastService toastService { get; set; } = null!;
|
||||
|
||||
private IQueryable<BookingRuleDataset>? bookingRulesQueryable;
|
||||
private IReadOnlyList<BookingRuleDataset> source = [];
|
||||
private string filter = "";
|
||||
|
||||
private IQueryable<BookingRuleDataset> Filtered =>
|
||||
string.IsNullOrWhiteSpace(filter)
|
||||
? source.AsQueryable()
|
||||
: source.AsQueryable().Where(r =>
|
||||
r.LookupText.Contains(filter, StringComparison.OrdinalIgnoreCase) ||
|
||||
(r.BookingText != null && r.BookingText.Contains(filter, StringComparison.OrdinalIgnoreCase)) ||
|
||||
(r.Account != null && r.Account.Contains(filter, StringComparison.OrdinalIgnoreCase)));
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
bookingRulesQueryable = (await viewService.GetBookingRuleViewListAsync()).AsQueryable();
|
||||
source = await viewService.GetBookingRuleViewListAsync();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
|
|
@ -43,7 +52,7 @@ public partial class BookingRules : ComponentBase
|
|||
var result = await dialog.Result;
|
||||
if (!result.Cancelled && result.Data != null)
|
||||
{
|
||||
bookingRulesQueryable = (await viewService.GetBookingRuleViewListAsync()).AsQueryable();
|
||||
source = await viewService.GetBookingRuleViewListAsync();
|
||||
toastService.ShowToast(ToastIntent.Success, "Buchungsregel gespeichert", 3000);
|
||||
}
|
||||
}
|
||||
|
|
@ -62,7 +71,7 @@ public partial class BookingRules : ComponentBase
|
|||
var result = await dialog.Result;
|
||||
if (!result.Cancelled && result.Data != null)
|
||||
{
|
||||
bookingRulesQueryable = (await viewService.GetBookingRuleViewListAsync()).AsQueryable();
|
||||
source = await viewService.GetBookingRuleViewListAsync();
|
||||
toastService.ShowToast(ToastIntent.Success, "Buchungsregel gespeichert", 3000);
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +84,7 @@ public partial class BookingRules : ComponentBase
|
|||
if (!result.Cancelled)
|
||||
{
|
||||
await bookingRuleRepository.DeleteBookingRuleAsync(id);
|
||||
bookingRulesQueryable = (await viewService.GetBookingRuleViewListAsync()).AsQueryable();
|
||||
source = await viewService.GetBookingRuleViewListAsync();
|
||||
toastService.ShowToast(ToastIntent.Success, "Buchungsregel gelöscht", 3000);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@
|
|||
|
||||
<PageTitle>Buchungstexte</PageTitle>
|
||||
|
||||
<FluentStack Orientation="Orientation.Horizontal" VerticalAlignment="VerticalAlignment.Center">
|
||||
<FluentButton IconStart="@(new Icons.Regular.Size16.Add())" Appearance="Appearance.Neutral" OnClick="AddAsync">Neuer Text hinzufügen</FluentButton>
|
||||
<FluentSearch @bind-Value="filter" Immediate="true" Placeholder="Filter…" Style="width: 300px;"/>
|
||||
</FluentStack>
|
||||
<FluentPaginator State="@pagination" SummaryTemplate="@template" />
|
||||
|
||||
<FluentDataGrid Items="@bookingTextQueryable" Pagination="@pagination" RowSize="DataGridRowSize.Medium">
|
||||
<FluentDataGrid Items="@Filtered" Pagination="@pagination" RowSize="DataGridRowSize.Medium">
|
||||
<PropertyColumn Property="@(p => p.Text)" Sortable="true" IsDefaultSortColumn="true" />
|
||||
<TemplateColumn Align="Align.Center" Width="50px">
|
||||
<FluentButton IconEnd="@(new Icons.Regular.Size16.Delete())" Appearance="Appearance.Neutral" OnClick="@(() => DeleteAsync(context.Id))" />
|
||||
|
|
|
|||
|
|
@ -17,11 +17,17 @@ public partial class BookingTexts : ComponentBase
|
|||
[Inject]
|
||||
private IToastService toastService { get; set; } = null!;
|
||||
|
||||
private IQueryable<BookingText>? bookingTextQueryable;
|
||||
private IReadOnlyList<BookingText> source = [];
|
||||
private string filter = "";
|
||||
|
||||
private IQueryable<BookingText> Filtered =>
|
||||
string.IsNullOrWhiteSpace(filter)
|
||||
? source.AsQueryable()
|
||||
: source.AsQueryable().Where(t => t.Text.Contains(filter, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
bookingTextQueryable = (await bookingTextRepository.GetBookingTextListAsync()).AsQueryable();
|
||||
source = await bookingTextRepository.GetBookingTextListAsync();
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +45,7 @@ public partial class BookingTexts : ComponentBase
|
|||
var result = await dialog.Result;
|
||||
if (!result.Cancelled && result.Data != null)
|
||||
{
|
||||
bookingTextQueryable = (await bookingTextRepository.GetBookingTextListAsync()).AsQueryable();
|
||||
source = await bookingTextRepository.GetBookingTextListAsync();
|
||||
toastService.ShowToast(ToastIntent.Success, "Buchungstext gespeichert", 3000);
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +64,7 @@ public partial class BookingTexts : ComponentBase
|
|||
var result = await dialog.Result;
|
||||
if (!result.Cancelled && result.Data != null)
|
||||
{
|
||||
bookingTextQueryable = (await bookingTextRepository.GetBookingTextListAsync()).AsQueryable();
|
||||
source = await bookingTextRepository.GetBookingTextListAsync();
|
||||
toastService.ShowToast(ToastIntent.Success, "Buchungstext gespeichert", 3000);
|
||||
}
|
||||
}
|
||||
|
|
@ -71,7 +77,7 @@ public partial class BookingTexts : ComponentBase
|
|||
if (!result.Cancelled)
|
||||
{
|
||||
await bookingTextRepository.DeleteBookingTextAsync(id);
|
||||
bookingTextQueryable = (await bookingTextRepository.GetBookingTextListAsync()).AsQueryable();
|
||||
source = await bookingTextRepository.GetBookingTextListAsync();
|
||||
toastService.ShowToast(ToastIntent.Success, "Buchungstext gelöscht", 3000);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue