diff --git a/src/FrontOffice.Main/Pages/DiscountStore/Products.razor b/src/FrontOffice.Main/Pages/DiscountStore/Products.razor index 3527a69..6cb49e8 100644 --- a/src/FrontOffice.Main/Pages/DiscountStore/Products.razor +++ b/src/FrontOffice.Main/Pages/DiscountStore/Products.razor @@ -64,7 +64,7 @@ در حال بارگذاری... } - else if (_result.Products.Count == 0) + else if (_products.Count == 0) { محصولی یافت نشد. } @@ -72,7 +72,7 @@ { - @foreach (var p in _result.Products) + @foreach (var p in _products) { @@ -119,15 +119,29 @@ } - - @if (_result.TotalPages > 1) + @* Lazy Load — بارگذاری بیشتر *@ + @if (_loadingMore) { -
- + + + بارگذاری بیشتر... + + } + else if (_hasMore) + { +
+ + نمایش محصولات بیشتر +
} + else if (_products.Count > 0) + { + + همه @(_products.Count) محصول نمایش داده شد + + } } diff --git a/src/FrontOffice.Main/Pages/DiscountStore/Products.razor.cs b/src/FrontOffice.Main/Pages/DiscountStore/Products.razor.cs index e84463c..9bdf7e9 100644 --- a/src/FrontOffice.Main/Pages/DiscountStore/Products.razor.cs +++ b/src/FrontOffice.Main/Pages/DiscountStore/Products.razor.cs @@ -13,9 +13,12 @@ public partial class Products : ComponentBase, IDisposable private long? _selectedCategoryId; private int _currentPage = 1; private bool _loading; + private bool _loadingMore; + private bool _hasMore = true; + private int _totalCount; private const int PageSize = 12; - private DiscountProductListResult _result = new(new(), 0, 0, 1); + private List _products = new(); private List _categories = new(); protected override async Task OnInitializedAsync() @@ -27,45 +30,66 @@ public partial class Products : ComponentBase, IDisposable var productsTask = ProductService.GetProductsAsync(page: 1, pageSize: PageSize); await Task.WhenAll(categoriesTask, productsTask); _categories = categoriesTask.Result; - _result = productsTask.Result; + var result = productsTask.Result; + _products = result.Products; + _totalCount = result.TotalCount; + _hasMore = result.CurrentPage < result.TotalPages; _loading = false; } - private async Task LoadProductsAsync() + private async Task LoadInitial() { _loading = true; + _currentPage = 1; + _products.Clear(); StateHasChanged(); - _result = await ProductService.GetProductsAsync( + + var result = await ProductService.GetProductsAsync( + page: 1, + pageSize: PageSize, + search: string.IsNullOrWhiteSpace(_search) ? null : _search, + categoryId: _selectedCategoryId); + _products = result.Products; + _totalCount = result.TotalCount; + _hasMore = result.CurrentPage < result.TotalPages; + _loading = false; + StateHasChanged(); + } + + private async Task LoadMore() + { + if (_loadingMore || !_hasMore) return; + + _loadingMore = true; + StateHasChanged(); + + _currentPage++; + var result = await ProductService.GetProductsAsync( page: _currentPage, pageSize: PageSize, search: string.IsNullOrWhiteSpace(_search) ? null : _search, categoryId: _selectedCategoryId); - _loading = false; + _products.AddRange(result.Products); + _hasMore = result.CurrentPage < result.TotalPages; + + _loadingMore = false; StateHasChanged(); } - private async Task SearchProducts() => await LoadProductsAsync(); + private async Task SearchProducts() => await LoadInitial(); private async Task OnSearchKeyUp(KeyboardEventArgs e) { if (e.Key == "Enter") { - _currentPage = 1; - await LoadProductsAsync(); + await LoadInitial(); } } private async Task OnCategoryChanged(long? value) { _selectedCategoryId = value; - _currentPage = 1; - await LoadProductsAsync(); - } - - private async Task OnPageChanged(int page) - { - _currentPage = page; - await LoadProductsAsync(); + await LoadInitial(); } private async Task AddToCart(DiscountProductCard p) diff --git a/src/FrontOffice.Main/Pages/Store/Products.razor b/src/FrontOffice.Main/Pages/Store/Products.razor index 370abbc..a00d6f3 100644 --- a/src/FrontOffice.Main/Pages/Store/Products.razor +++ b/src/FrontOffice.Main/Pages/Store/Products.razor @@ -94,7 +94,7 @@ { - @(_products.Count) محصول + @(_totalCount) محصول } @@ -159,5 +159,30 @@ } + + @* Lazy Load — بارگذاری بیشتر *@ + @if (_loadingMore) + { + + + بارگذاری بیشتر... + + } + else if (_hasMore) + { +
+ + نمایش محصولات بیشتر + +
+ } + else if (_products.Count > 0) + { + + همه @(_products.Count) محصول نمایش داده شد + + } } diff --git a/src/FrontOffice.Main/Pages/Store/Products.razor.cs b/src/FrontOffice.Main/Pages/Store/Products.razor.cs index 5c8163d..4394fa2 100644 --- a/src/FrontOffice.Main/Pages/Store/Products.razor.cs +++ b/src/FrontOffice.Main/Pages/Store/Products.razor.cs @@ -24,6 +24,11 @@ public partial class Products : ComponentBase, IDisposable private string _query = string.Empty; private bool _loading; + private bool _loadingMore; + private bool _hasMore = true; + private int _currentPage = 1; + private int _totalCount; + private const int PageSize = 12; private List _products = new(); private long? _activeCategoryId; private string? _activeCategoryTitle; @@ -36,7 +41,7 @@ public partial class Products : ComponentBase, IDisposable await Cart.EnsureInitializedAsync(); Cart.OnChange += StateHasChanged; Navigation.LocationChanged += HandleLocationChanged; - await Load(); + await LoadInitial(); } protected override async Task OnAfterRenderAsync(bool firstRender) @@ -49,18 +54,40 @@ public partial class Products : ComponentBase, IDisposable await base.OnAfterRenderAsync(firstRender); } - private async Task Load() + private async Task LoadInitial() { _loading = true; + _currentPage = 1; + _products.Clear(); UpdateCategoryFilterFromUri(); var sortBy = GetSortByValue(); - _products = await ProductService.GetProductsAsync(_query, _activeCategoryId, sortBy); + var result = await ProductService.GetProductsPagedAsync(_query, _activeCategoryId, sortBy, _currentPage, PageSize); + _products = result.Products; + _hasMore = result.HasNext; + _totalCount = result.TotalCount; _activeCategoryTitle = _activeCategoryId is { } categoryId ? (await CategoryService.GetByIdAsync(categoryId))?.Title : null; _loading = false; } + private async Task LoadMore() + { + if (_loadingMore || !_hasMore) return; + + _loadingMore = true; + StateHasChanged(); + + _currentPage++; + var sortBy = GetSortByValue(); + var result = await ProductService.GetProductsPagedAsync(_query, _activeCategoryId, sortBy, _currentPage, PageSize); + _products.AddRange(result.Products); + _hasMore = result.HasNext; + + _loadingMore = false; + StateHasChanged(); + } + private string GetSortByValue() { return _sortOption switch @@ -75,12 +102,12 @@ public partial class Products : ComponentBase, IDisposable private async Task OnSortChanged() { - await Load(); + await LoadInitial(); } private async Task OnQueryChanged(KeyboardEventArgs _) { - await Load(); + await LoadInitial(); } private async Task AddToCart(Product p) @@ -98,7 +125,7 @@ public partial class Products : ComponentBase, IDisposable private void HandleLocationChanged(object? sender, LocationChangedEventArgs args) { - _ = InvokeAsync(Load); + _ = InvokeAsync(LoadInitial); } private void UpdateCategoryFilterFromUri() diff --git a/src/FrontOffice.Main/Utilities/ProductService.cs b/src/FrontOffice.Main/Utilities/ProductService.cs index 81309ad..0640bee 100644 --- a/src/FrontOffice.Main/Utilities/ProductService.cs +++ b/src/FrontOffice.Main/Utilities/ProductService.cs @@ -26,6 +26,13 @@ public record Product( = []; } +public record ProductListResult( + List Products, + int TotalCount, + int TotalPages, + int CurrentPage, + bool HasNext); + public record ProductGalleryImage( long ProductGalleryId, long ProductImageId, @@ -59,11 +66,24 @@ public class ProductService } public async Task> GetProductsAsync(string? query = null, long? categoryId = null, string? sortBy = null) + { + var result = await GetProductsPagedAsync(query: query, categoryId: categoryId, sortBy: sortBy, page: 1, pageSize: 500); + return result.Products; + } + + public async Task GetProductsPagedAsync( + string? query = null, long? categoryId = null, string? sortBy = null, + int page = 1, int pageSize = 12) { try { var request = new GetAllProductsByFilterRequest { + PaginationState = new CMSMicroservice.Protobuf.Protos.PaginationState + { + PageNumber = page, + PageSize = pageSize + }, Filter = new GetAllProductsByFilterFilter { Title = query ?? string.Empty, @@ -84,7 +104,12 @@ public class ProductService } var resp = await _client.GetAllProductsByFilterAsync(request); - return MapAndCache(resp.Models); + var products = MapAndCache(resp.Models); + var totalCount = (int)(resp.MetaData?.TotalCount ?? 0); + var totalPages = (int)(resp.MetaData?.TotalPage ?? 0); + var hasNext = resp.MetaData?.HasNext ?? false; + + return new ProductListResult(products, totalCount, totalPages, page, hasNext); } catch { @@ -97,7 +122,8 @@ public class ProductService p.Description.Contains(q, StringComparison.OrdinalIgnoreCase)); } - return list.OrderBy(p => p.Id).ToList(); + var all = list.OrderBy(p => p.Id).ToList(); + return new ProductListResult(all, all.Count, 1, 1, false); } }