From 0c48539ce61db58033f773ee0fc7a4d5b6fa6663 Mon Sep 17 00:00:00 2001 From: Claudio Schaad Date: Sat, 4 Jul 2026 10:10:43 +0200 Subject: [PATCH] PR U-9: FluentSearch filter on grid pages Accounts, BookingRules, BookingTexts, BankTransactions each get an 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 --- .../Components/Pages/Accounts.razor | 7 +++++-- .../Components/Pages/Accounts.razor.cs | 19 ++++++++++++++----- .../Components/Pages/BankTransactions.razor | 3 ++- .../Pages/BankTransactions.razor.cs | 15 +++++++++++++-- .../Components/Pages/BookingRules.razor | 7 +++++-- .../Components/Pages/BookingRules.razor.cs | 19 ++++++++++++++----- .../Components/Pages/BookingTexts.razor | 7 +++++-- .../Components/Pages/BookingTexts.razor.cs | 16 +++++++++++----- 8 files changed, 69 insertions(+), 24 deletions(-) diff --git a/Schaad.Accounting.UI/Components/Pages/Accounts.razor b/Schaad.Accounting.UI/Components/Pages/Accounts.razor index 03ebd38..97336c6 100644 --- a/Schaad.Accounting.UI/Components/Pages/Accounts.razor +++ b/Schaad.Accounting.UI/Components/Pages/Accounts.razor @@ -3,10 +3,13 @@ Konten -Neues Konto hinzufügen + + Neues Konto hinzufügen + + - + diff --git a/Schaad.Accounting.UI/Components/Pages/Accounts.razor.cs b/Schaad.Accounting.UI/Components/Pages/Accounts.razor.cs index df987ad..134833a 100644 --- a/Schaad.Accounting.UI/Components/Pages/Accounts.razor.cs +++ b/Schaad.Accounting.UI/Components/Pages/Accounts.razor.cs @@ -17,11 +17,20 @@ public partial class Accounts : ComponentBase [Inject] private IToastService toastService { get; set; } = null!; - private IQueryable? accountQueryable; + private IReadOnlyList source = []; + private string filter = ""; + + private IQueryable 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); } } diff --git a/Schaad.Accounting.UI/Components/Pages/BankTransactions.razor b/Schaad.Accounting.UI/Components/Pages/BankTransactions.razor index e72827e..83cb930 100644 --- a/Schaad.Accounting.UI/Components/Pages/BankTransactions.razor +++ b/Schaad.Accounting.UI/Components/Pages/BankTransactions.razor @@ -1,10 +1,11 @@ @page "/banktransactions" BankTransactions +
- + diff --git a/Schaad.Accounting.UI/Components/Pages/BankTransactions.razor.cs b/Schaad.Accounting.UI/Components/Pages/BankTransactions.razor.cs index b2ece4a..25f386d 100644 --- a/Schaad.Accounting.UI/Components/Pages/BankTransactions.razor.cs +++ b/Schaad.Accounting.UI/Components/Pages/BankTransactions.razor.cs @@ -8,11 +8,22 @@ public partial class BankTransactions : ComponentBase { [Inject] private IBankTransactionRepository bankTransactionRepository { get; set; } = null!; - private IQueryable? TransactionList; + + private IReadOnlyList source = []; + private string filter = ""; + + private IQueryable 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(); } } diff --git a/Schaad.Accounting.UI/Components/Pages/BookingRules.razor b/Schaad.Accounting.UI/Components/Pages/BookingRules.razor index 4f33e68..c2775ae 100644 --- a/Schaad.Accounting.UI/Components/Pages/BookingRules.razor +++ b/Schaad.Accounting.UI/Components/Pages/BookingRules.razor @@ -3,10 +3,13 @@ Buchungsregeln -Neue Regel hinzufügen + + Neue Regel hinzufügen + + - + diff --git a/Schaad.Accounting.UI/Components/Pages/BookingRules.razor.cs b/Schaad.Accounting.UI/Components/Pages/BookingRules.razor.cs index 31ecf0c..71436a0 100644 --- a/Schaad.Accounting.UI/Components/Pages/BookingRules.razor.cs +++ b/Schaad.Accounting.UI/Components/Pages/BookingRules.razor.cs @@ -21,11 +21,20 @@ public partial class BookingRules : ComponentBase [Inject] private IToastService toastService { get; set; } = null!; - private IQueryable? bookingRulesQueryable; + private IReadOnlyList source = []; + private string filter = ""; + + private IQueryable 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); } } diff --git a/Schaad.Accounting.UI/Components/Pages/BookingTexts.razor b/Schaad.Accounting.UI/Components/Pages/BookingTexts.razor index 74dc948..829c161 100644 --- a/Schaad.Accounting.UI/Components/Pages/BookingTexts.razor +++ b/Schaad.Accounting.UI/Components/Pages/BookingTexts.razor @@ -3,10 +3,13 @@ Buchungstexte -Neuer Text hinzufügen + + Neuer Text hinzufügen + + - + diff --git a/Schaad.Accounting.UI/Components/Pages/BookingTexts.razor.cs b/Schaad.Accounting.UI/Components/Pages/BookingTexts.razor.cs index e838057..7d35166 100644 --- a/Schaad.Accounting.UI/Components/Pages/BookingTexts.razor.cs +++ b/Schaad.Accounting.UI/Components/Pages/BookingTexts.razor.cs @@ -17,11 +17,17 @@ public partial class BookingTexts : ComponentBase [Inject] private IToastService toastService { get; set; } = null!; - private IQueryable? bookingTextQueryable; + private IReadOnlyList source = []; + private string filter = ""; + + private IQueryable 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); } }