diff --git a/src/Data/Repositories/ChannelOperationRequestRepository.cs b/src/Data/Repositories/ChannelOperationRequestRepository.cs index a98bfb0d..34744987 100644 --- a/src/Data/Repositories/ChannelOperationRequestRepository.cs +++ b/src/Data/Repositories/ChannelOperationRequestRepository.cs @@ -235,5 +235,86 @@ public async Task> GetPendingRequests() .Include(x => x.Utxos).AsSplitQuery() .ToListAsync(); } + + public async Task<(List requests, int totalCount)> GetPaginatedAsync( + int pageNumber, + int pageSize, + ChannelOperationRequestStatus? status = null, + OperationRequestType? requestType = null, + int? sourceNodeId = null, + int? destNodeId = null, + int? walletId = null, + string? userId = null, + DateTimeOffset? fromDate = null, + DateTimeOffset? toDate = null, + IEnumerable? excludedIds = null) + { + await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync(); + + var query = applicationDbContext.ChannelOperationRequests + .Include(request => request.Wallet) + .Include(request => request.SourceNode) + .Include(request => request.DestNode) + .Include(request => request.ChannelOperationRequestPsbts) + .Include(request => request.User) + .Include(x => x.Utxos) + .AsSplitQuery() + .AsQueryable(); + + if (status.HasValue) + { + query = query.Where(r => r.Status == status.Value); + } + + if (requestType.HasValue) + { + query = query.Where(r => r.RequestType == requestType.Value); + } + + if (sourceNodeId.HasValue) + { + query = query.Where(r => r.SourceNodeId == sourceNodeId.Value); + } + + if (destNodeId.HasValue) + { + query = query.Where(r => r.DestNodeId == destNodeId.Value); + } + + if (walletId.HasValue) + { + query = query.Where(r => r.WalletId == walletId.Value); + } + + if (!string.IsNullOrEmpty(userId)) + { + query = query.Where(r => r.UserId == userId); + } + + if (fromDate.HasValue) + { + query = query.Where(r => r.CreationDatetime >= fromDate.Value); + } + + if (toDate.HasValue) + { + query = query.Where(r => r.CreationDatetime <= toDate.Value); + } + + if (excludedIds != null && excludedIds.Any()) + { + query = query.Where(r => !excludedIds.Contains(r.Id)); + } + + var totalCount = await query.CountAsync(); + + var requests = await query + .OrderByDescending(r => r.CreationDatetime) + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToListAsync(); + + return (requests, totalCount); + } } } \ No newline at end of file diff --git a/src/Data/Repositories/Interfaces/IChannelOperationRequestRepository.cs b/src/Data/Repositories/Interfaces/IChannelOperationRequestRepository.cs index 34d050a6..56dbcb64 100644 --- a/src/Data/Repositories/Interfaces/IChannelOperationRequestRepository.cs +++ b/src/Data/Repositories/Interfaces/IChannelOperationRequestRepository.cs @@ -44,4 +44,17 @@ public interface IChannelOperationRequestRepository : IBitcoinRequestRepository /// /// Task> GetPendingRequests(); + + Task<(List requests, int totalCount)> GetPaginatedAsync( + int pageNumber, + int pageSize, + ChannelOperationRequestStatus? status = null, + OperationRequestType? requestType = null, + int? sourceNodeId = null, + int? destNodeId = null, + int? walletId = null, + string? userId = null, + DateTimeOffset? fromDate = null, + DateTimeOffset? toDate = null, + IEnumerable? excludedIds = null); } \ No newline at end of file diff --git a/src/Pages/ChannelRequests.razor b/src/Pages/ChannelRequests.razor index f522affa..09aa05aa 100644 --- a/src/Pages/ChannelRequests.razor +++ b/src/Pages/ChannelRequests.razor @@ -4,6 +4,7 @@ @using Quartz @using Humanizer @using NBitcoin +@using Blazorise.Components @using NodeGuard.Jobs @using Google.Protobuf @using NBXplorer.Models @@ -287,10 +288,127 @@
+ + + + Status + + + + + + Type + + + + + + Source Node + + + + + + Dest Node + + + + + + Wallet + + + + + + User + + + + + + From + + + + + + To + + + + + + + +@* TODO: Convert this grid to paginated ReadData to avoid loading all records in memory. *@ @@ -455,10 +573,30 @@ @inject IPriceConversionService PriceConversionService @inject IAuditService AuditService @inject IHttpContextAccessor HttpContextAccessor +@inject IApplicationUserRepository ApplicationUserRepository @code { private List? _channelRequests; private List? _allRequests; + private int _totalAllRequests; + private List _availableUsers = new(); + private List _availableNodes = new(); + + // Filter state + private ChannelOperationRequestStatus? _statusFilter; + private OperationRequestType? _requestTypeFilter; + private int? _sourceNodeFilter; + private int? _destNodeFilter; + private int? _walletFilter; + private string? _userFilter; + private int _filtersResetKey; + private DateTime? _fromDate; + private DateTime? _toDate; + + // Filter options + private List _statusOptions = new() { null }; + private List _requestTypeOptions = new() { null }; + private ChannelOperationRequest? _selectedRequest; private ChannelOperationRequestStatus _selectedStatus; private ChannelOperationRequest _selectedRequestForMarkingAsFailed; @@ -1136,8 +1274,65 @@ private async Task RefreshChannelRequestsInformation() { await FetchRequests(); + if (_allRequestsDatagrid != null) + { + await _allRequestsDatagrid.Reload(); + } StateHasChanged(); } + + private async Task OnAllRequestsReadData(DataGridReadDataEventArgs e) + { + if (!e.CancellationToken.IsCancellationRequested) + { + var fromDateOffset = _fromDate.HasValue ? new DateTimeOffset(_fromDate.Value, TimeSpan.Zero) : (DateTimeOffset?)null; + var toDateOffset = _toDate.HasValue ? new DateTimeOffset(_toDate.Value.AddDays(1).AddSeconds(-1), TimeSpan.Zero) : (DateTimeOffset?)null; + + var excludedIds = _channelRequests?.Select(r => r.Id) ?? Enumerable.Empty(); + + var (requests, totalCount) = await ChannelOperationRequestRepository.GetPaginatedAsync( + e.Page, + e.PageSize, + _statusFilter, + _requestTypeFilter, + _sourceNodeFilter, + _destNodeFilter, + _walletFilter, + _userFilter, + fromDateOffset, + toDateOffset, + excludedIds); + + _allRequests = requests; + _totalAllRequests = totalCount; + } + } + + private async Task OnFiltersChanged() + { + if (_allRequestsDatagrid != null) + { + await _allRequestsDatagrid.Reload(); + } + } + + private async Task ClearAllFilters() + { + _statusFilter = null; + _requestTypeFilter = null; + _sourceNodeFilter = null; + _destNodeFilter = null; + _walletFilter = null; + _userFilter = null; + _fromDate = null; + _toDate = null; + _filtersResetKey++; + + if (_allRequestsDatagrid != null) + { + await _allRequestsDatagrid.Reload(); + } + } private async Task OnChangelessChanged(bool value) {