Skip to content

Commit ee7e362

Browse files
committed
[GEN-1863] Wallets pagination + filtering
Add GetPaginatedAsync method to WalletRepository and IWalletRepository interface with comprehensive filtering options including name, status, wallet type, date range, and various boolean filters. Update Wallets page UI to support paginated display with filter controls for improved wallet management and navigation. stack-info: PR: #484, branch: Jossec101/stack/17
1 parent d327e43 commit ee7e362

3 files changed

Lines changed: 220 additions & 38 deletions

File tree

src/Data/Repositories/Interfaces/IWalletRepository.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ public interface IWalletRepository
2727
Task<Wallet?> GetById(int id);
2828

2929
Task<List<Wallet>> GetAll();
30+
31+
Task<(List<Wallet> wallets, int totalCount)> GetPaginatedAsync(
32+
int pageNumber,
33+
int pageSize,
34+
string? nameFilter = null,
35+
string? statusFilter = null,
36+
bool archivedFilter = false,
37+
bool compromisedFilter = false,
38+
bool hotWalletFilter = false,
39+
bool coldWalletFilter = false,
40+
bool finalisedFilter = false,
41+
DateTimeOffset? fromDate = null,
42+
DateTimeOffset? toDate = null);
3043

3144
Task<List<Wallet>> GetAvailableByType(WALLET_TYPE type);
3245

src/Data/Repositories/WalletRepository.cs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,90 @@ public async Task<List<Wallet>> GetAll()
7070
return await applicationDbContext.Wallets.Include(x => x.InternalWallet).Include(x => x.Keys).ToListAsync();
7171
}
7272

73+
public async Task<(List<Wallet> wallets, int totalCount)> GetPaginatedAsync(
74+
int pageNumber,
75+
int pageSize,
76+
string? nameFilter = null,
77+
string? statusFilter = null,
78+
bool archivedFilter = false,
79+
bool compromisedFilter = false,
80+
bool hotWalletFilter = false,
81+
bool coldWalletFilter = false,
82+
bool finalisedFilter = false,
83+
DateTimeOffset? fromDate = null,
84+
DateTimeOffset? toDate = null)
85+
{
86+
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();
87+
88+
var query = applicationDbContext.Wallets
89+
.Include(x => x.InternalWallet)
90+
.Include(x => x.Keys)
91+
.AsQueryable();
92+
93+
if (!string.IsNullOrWhiteSpace(nameFilter))
94+
{
95+
query = query.Where(w => w.Name.Contains(nameFilter));
96+
}
97+
98+
switch (statusFilter)
99+
{
100+
case "Finalised":
101+
query = query.Where(w => w.IsFinalised);
102+
break;
103+
case "Not Finalised":
104+
query = query.Where(w => !w.IsFinalised);
105+
break;
106+
case "Archived":
107+
query = query.Where(w => w.IsArchived);
108+
break;
109+
case "Not Archived":
110+
query = query.Where(w => !w.IsArchived);
111+
break;
112+
}
113+
114+
if (fromDate.HasValue)
115+
{
116+
query = query.Where(w => w.CreationDatetime >= fromDate.Value);
117+
}
118+
119+
if (toDate.HasValue)
120+
{
121+
query = query.Where(w => w.CreationDatetime <= toDate.Value);
122+
}
123+
124+
if (!archivedFilter && statusFilter != "Archived")
125+
{
126+
query = query.Where(w => !w.IsArchived);
127+
}
128+
129+
if (!compromisedFilter)
130+
{
131+
query = query.Where(w => !w.IsCompromised);
132+
}
133+
134+
var anyPropertyFilter = archivedFilter || compromisedFilter || hotWalletFilter || coldWalletFilter || finalisedFilter;
135+
if (anyPropertyFilter)
136+
{
137+
query = query.Where(w =>
138+
(archivedFilter && w.IsArchived) ||
139+
(compromisedFilter && w.IsCompromised) ||
140+
(hotWalletFilter && w.IsHotWallet) ||
141+
(coldWalletFilter && !w.IsHotWallet) ||
142+
(finalisedFilter && w.IsFinalised));
143+
}
144+
145+
query = query.OrderByDescending(w => w.CreationDatetime);
146+
147+
var totalCount = await query.CountAsync();
148+
149+
var wallets = await query
150+
.Skip((pageNumber - 1) * pageSize)
151+
.Take(pageSize)
152+
.ToListAsync();
153+
154+
return (wallets, totalCount);
155+
}
156+
73157
public async Task<List<Wallet>> GetAvailableByType(WALLET_TYPE type)
74158
{
75159
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();

src/Pages/Wallets.razor

Lines changed: 123 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,65 @@
3131
<h3 class="custom-primary">Treasury Wallets</h3>
3232
<Row>
3333
<Column ColumnSize="ColumnSize.Is12">
34+
<Row Class="mb-3">
35+
<Column ColumnSize="ColumnSize.Is2">
36+
<Field>
37+
<FieldLabel>Name</FieldLabel>
38+
<TextEdit Text="@_nameFilter" TextChanged="async text => { _nameFilter = text; await OnFiltersChanged(); }" Placeholder="All" />
39+
</Field>
40+
</Column>
41+
<Column ColumnSize="ColumnSize.Is2">
42+
<Field>
43+
<FieldLabel>Properties</FieldLabel>
44+
<Dropdown>
45+
<DropdownToggle>Select</DropdownToggle>
46+
<DropdownMenu>
47+
<div class="checkbox-drop">
48+
<Check TValue="bool" CheckedChanged="async _ => { _hotWalletFilter = !_hotWalletFilter; await OnFiltersChanged(); }">🔥 Hot Wallet</Check>
49+
<Check TValue="bool" CheckedChanged="async _ => { _coldWalletFilter = !_coldWalletFilter; await OnFiltersChanged(); }">❄️ Cold Wallet</Check>
50+
<Check TValue="bool" CheckedChanged="async _ => { _finalisedWalletFilter = !_finalisedWalletFilter; await OnFiltersChanged(); }">🏁 Finalised</Check>
51+
<Check TValue="bool" CheckedChanged="async _ => { _archivedWalletFilter = !_archivedWalletFilter; await OnFiltersChanged(); }">📂 Archived</Check>
52+
<Check TValue="bool" CheckedChanged="async _ => { _compromisedWalletFilter = !_compromisedWalletFilter; await OnFiltersChanged(); }">⛔️ Compromised</Check>
53+
</div>
54+
</DropdownMenu>
55+
</Dropdown>
56+
</Field>
57+
</Column>
58+
<Column ColumnSize="ColumnSize.Is2">
59+
<Field>
60+
<FieldLabel>Status</FieldLabel>
61+
<Select TValue="string" @bind-SelectedValue="@_walletStatusFilter" @bind-SelectedValue:after="OnFiltersChanged">
62+
@foreach (var option in _walletStatusOptions)
63+
{
64+
<SelectItem Value="@option">@option</SelectItem>
65+
}
66+
</Select>
67+
</Field>
68+
</Column>
69+
<Column ColumnSize="ColumnSize.Is1">
70+
<Field>
71+
<FieldLabel>From</FieldLabel>
72+
<DatePicker TValue="DateTime?" @bind-Date="@_fromDateFilter" @bind-Date:after="OnFiltersChanged" Placeholder="Start" />
73+
</Field>
74+
</Column>
75+
<Column ColumnSize="ColumnSize.Is1">
76+
<Field>
77+
<FieldLabel>To</FieldLabel>
78+
<DatePicker TValue="DateTime?" @bind-Date="@_toDateFilter" @bind-Date:after="OnFiltersChanged" Placeholder="End" />
79+
</Field>
80+
</Column>
81+
<Column ColumnSize="ColumnSize.Is2" Class="d-flex align-items-end pb-3">
82+
<Button Color="Color.Secondary" Clicked="ClearAllFilters">
83+
<Icon Name="IconName.Times" /> Clear
84+
</Button>
85+
</Column>
86+
</Row>
3487
<DataGrid TItem="Wallet"
3588
@ref="_walletsDataGrid"
3689
@bind-SelectedRow="_selectedWallet"
3790
Data="_wallets"
91+
ReadData="@OnReadData"
92+
TotalItems="@_totalItems"
3893
Editable="true"
3994
EditMode="DataGridEditMode.Popup"
4095
Responsive="true"
@@ -48,9 +103,9 @@
48103
ShowPager="true"
49104
ShowPageSizes="true"
50105
PageSize="25"
51-
Filterable="true"
106+
Filterable="false"
52107
UseValidation="true"
53-
CustomFilter="OnWalletCustomFilter">
108+
>
54109
<PopupTitleTemplate>
55110
<h2>@(context.EditState) wallet</h2>
56111
</PopupTitleTemplate>
@@ -130,21 +185,7 @@
130185
<DataGridColumn TItem="Wallet" Displayable="false" Filterable="true" Caption="Finalised" Field="@nameof(Wallet.IsFinalised)" Editable="false"/>
131186
<DataGridColumn TItem="Wallet" Displayable="false" Filterable="true" Caption="Archived" Field="@nameof(Wallet.IsArchived)" Editable="true" CellsEditableOnNewCommand="false"/>
132187
<DataGridColumn TItem="Wallet" Displayable="false" Filterable="true" Caption="Compromised" Field="@nameof(Wallet.IsCompromised)" Editable="true" CellsEditableOnNewCommand="false"/>
133-
<DataGridSelectColumn TItem="Wallet" Sortable="false" Filterable="true" Displayable="@IsColumnVisible(WalletsColumnName.WalletProperties)">
134-
<FilterTemplate>
135-
<Dropdown>
136-
<DropdownToggle>Select</DropdownToggle>
137-
<DropdownMenu>
138-
<div class="checkbox-drop">
139-
<Check TValue="bool" CheckedChanged="@(() => { _hotWalletFilter = !_hotWalletFilter; OnCheckedChanged(true); })">🔥 Hot Wallet</Check>
140-
<Check TValue="bool" CheckedChanged="@(() => { _coldWalletFilter = !_coldWalletFilter; OnCheckedChanged(true); })">❄️ Cold Wallet</Check>
141-
<Check TValue="bool" CheckedChanged="@(() => { _finalisedWalletFilter = !_finalisedWalletFilter; OnCheckedChanged(true); })">🏁 finalised</Check>
142-
<Check TValue="bool" CheckedChanged="@(() => { _archivedWalletFilter = !_archivedWalletFilter; OnCheckedChanged(true); })">📂 Archived</Check>
143-
<Check TValue="bool" CheckedChanged="@(() => { _compromisedWalletFilter = !_compromisedWalletFilter; OnCheckedChanged(true); })">⛔️ Compromised</Check>
144-
</div>
145-
</DropdownMenu>
146-
</Dropdown>
147-
</FilterTemplate>
188+
<DataGridSelectColumn TItem="Wallet" Sortable="false" Filterable="false" Displayable="@IsColumnVisible(WalletsColumnName.WalletProperties)">
148189
<CaptionTemplate>
149190
<span>
150191
Wallet Properties
@@ -828,7 +869,20 @@ OnSubmit="TransferFundsHotWallet"/>
828869
private bool _finalisedWalletFilter = false;
829870
private bool _archivedWalletFilter = false;
830871
private bool _compromisedWalletFilter = false;
872+
private string _nameFilter = string.Empty;
873+
private string _walletStatusFilter = "All";
874+
private DateTime? _fromDateFilter;
875+
private DateTime? _toDateFilter;
876+
private List<string> _walletStatusOptions = new()
877+
{
878+
"All",
879+
"Finalised",
880+
"Not Finalised",
881+
"Archived",
882+
"Not Archived"
883+
};
831884
private DataGrid<Wallet> _walletsDataGrid;
885+
private int _totalItems;
832886
833887
private ColumnLayout<WalletsColumnName> WalletsColumnLayout;
834888
private Dictionary<string, bool> WalletsColumns = new();
@@ -888,7 +942,6 @@ OnSubmit="TransferFundsHotWallet"/>
888942
889943
private async Task GetData()
890944
{
891-
_wallets = await WalletRepository.GetAll();
892945
var financeManagers = (await ApplicationUserRepository.GetUsersInRole(ApplicationUserRole.FinanceManager));
893946
_financeManagers = financeManagers.Where(x => x.Keys.Any()).ToList();
894947
@@ -898,6 +951,51 @@ OnSubmit="TransferFundsHotWallet"/>
898951
_selectedFinanceManagerAvailableKeys = await FilterKeys(_selectedFinanceManager.Keys);
899952
}
900953
954+
private async Task OnReadData(DataGridReadDataEventArgs<Wallet> e)
955+
{
956+
var fromDate = _fromDateFilter.HasValue
957+
? new DateTimeOffset(DateTime.SpecifyKind(_fromDateFilter.Value.Date, DateTimeKind.Local)).ToUniversalTime()
958+
: (DateTimeOffset?)null;
959+
var toDate = _toDateFilter.HasValue
960+
? new DateTimeOffset(DateTime.SpecifyKind(_toDateFilter.Value.Date.AddDays(1).AddTicks(-1), DateTimeKind.Local)).ToUniversalTime()
961+
: (DateTimeOffset?)null;
962+
963+
var (wallets, totalCount) = await WalletRepository.GetPaginatedAsync(
964+
e.Page,
965+
e.PageSize,
966+
nameFilter: _nameFilter,
967+
statusFilter: _walletStatusFilter,
968+
archivedFilter: _archivedWalletFilter,
969+
compromisedFilter: _compromisedWalletFilter,
970+
hotWalletFilter: _hotWalletFilter,
971+
coldWalletFilter: _coldWalletFilter,
972+
finalisedFilter: _finalisedWalletFilter,
973+
fromDate: fromDate,
974+
toDate: toDate);
975+
976+
_wallets = wallets;
977+
_totalItems = totalCount;
978+
}
979+
980+
private async Task OnFiltersChanged()
981+
{
982+
await _walletsDataGrid.Reload();
983+
}
984+
985+
private async Task ClearAllFilters()
986+
{
987+
_hotWalletFilter = false;
988+
_coldWalletFilter = false;
989+
_finalisedWalletFilter = false;
990+
_archivedWalletFilter = false;
991+
_compromisedWalletFilter = false;
992+
_nameFilter = string.Empty;
993+
_walletStatusFilter = "All";
994+
_fromDateFilter = null;
995+
_toDateFilter = null;
996+
await _walletsDataGrid.Reload();
997+
}
998+
901999
9021000
private async Task OnRowInserted(SavedRowItem<Wallet, Dictionary<string, object>> arg)
9031001
{
@@ -917,7 +1015,7 @@ OnSubmit="TransferFundsHotWallet"/>
9171015
AuditObjectType.Wallet,
9181016
arg.Item.Id.ToString(),
9191017
new { Name = arg.Item.Name, IsHotWallet = arg.Item.IsHotWallet, MofN = arg.Item.MofN });
920-
await GetData();
1018+
await _walletsDataGrid.Reload();
9211019
}
9221020
else
9231021
{
@@ -963,7 +1061,7 @@ OnSubmit="TransferFundsHotWallet"/>
9631061
AuditObjectType.Wallet,
9641062
arg.Item.Id.ToString(),
9651063
new { Name = arg.Item.Name });
966-
await GetData();
1064+
await _walletsDataGrid.Reload();
9671065
}
9681066
}
9691067
}
@@ -1012,7 +1110,7 @@ OnSubmit="TransferFundsHotWallet"/>
10121110
new { Name = arg.Item.Name, Error = updateResult.Item2 });
10131111
}
10141112
1015-
await GetData();
1113+
await _walletsDataGrid.Reload();
10161114
}
10171115
10181116
private async Task CloseModal()
@@ -1091,7 +1189,7 @@ OnSubmit="TransferFundsHotWallet"/>
10911189
10921190
CleanModal();
10931191
1094-
await GetData();
1192+
await _walletsDataGrid.Reload();
10951193
10961194
await _modalRef.Close(CloseReason.UserClosing);
10971195
}
@@ -1320,7 +1418,7 @@ OnSubmit="TransferFundsHotWallet"/>
13201418
13211419
await _finaliseModalRef.Close(CloseReason.UserClosing);
13221420
1323-
await GetData();
1421+
await _walletsDataGrid.Reload();
13241422
}
13251423
13261424
private async Task CloseAndCleanFinaliseModal()
@@ -1403,7 +1501,7 @@ OnSubmit="TransferFundsHotWallet"/>
14031501
}
14041502
14051503
//Load data
1406-
await GetData();
1504+
await _walletsDataGrid.Reload();
14071505
14081506
//Success
14091507
ToastService.ShowSuccess("Wallet imported successfully");
@@ -1725,19 +1823,6 @@ OnSubmit="TransferFundsHotWallet"/>
17251823
await CloseTextModal();
17261824
}
17271825
1728-
private bool OnWalletCustomFilter(Wallet wallet)
1729-
{
1730-
bool anyCheck = _archivedWalletFilter || _compromisedWalletFilter || _hotWalletFilter || _coldWalletFilter || _finalisedWalletFilter;
1731-
if (!anyCheck)
1732-
return true;
1733-
1734-
return _archivedWalletFilter && wallet.IsArchived
1735-
|| _compromisedWalletFilter && wallet.IsCompromised
1736-
|| _hotWalletFilter && wallet.IsHotWallet
1737-
|| _coldWalletFilter && !wallet.IsHotWallet
1738-
|| _finalisedWalletFilter && wallet.IsFinalised;
1739-
}
1740-
17411826
private void OnColumnLayoutUpdate()
17421827
{
17431828
StateHasChanged();
@@ -1754,7 +1839,7 @@ OnSubmit="TransferFundsHotWallet"/>
17541839
17551840
private Task OnCheckedChanged(bool value)
17561841
{
1757-
return _walletsDataGrid.Reload();
1842+
return Task.CompletedTask;
17581843
}
17591844
17601845
private async Task ValidateOutputDescriptor(ValidatorEventArgs arg1, CancellationToken arg2)

0 commit comments

Comments
 (0)