diff --git a/API/Controllers/BodyController.cs b/API/Controllers/BodyController.cs index 0608f2b..00b66f5 100644 --- a/API/Controllers/BodyController.cs +++ b/API/Controllers/BodyController.cs @@ -27,10 +27,8 @@ public async Task AddBodyweightRecord(AddBodyWeightRecordCommand q } [HttpGet] - public async Task> GetBodyWeight() + public async Task> GetBodyWeight([FromQuery] GetBodyWeightQuery query) { - var query = new GetBodyWeightQuery(); - var result = await _mediator.Send(query); return Ok(result); diff --git a/API/Data/Repositories/BodyWeight/BodyWeightRepository.cs b/API/Data/Repositories/BodyWeight/BodyWeightRepository.cs index fb9b023..5229692 100644 --- a/API/Data/Repositories/BodyWeight/BodyWeightRepository.cs +++ b/API/Data/Repositories/BodyWeight/BodyWeightRepository.cs @@ -1,5 +1,6 @@  using API.Data.Dtos; +using API.Utilities; using AutoMapper; using AutoMapper.QueryableExtensions; using Microsoft.EntityFrameworkCore; @@ -15,11 +16,26 @@ public void AddRecord(int userId, BodyWeightRecord record) bodyWeight.WeightRecords.Add(record); } - public async Task GetBodyWeightAsync(int userId) + public async Task> GetBodyWeightRecordsAsync(int userId, int? pageNumber = null, int? pageSize = null) + { + var query = _dbSet + .Where(x => x.UserId == userId) + .SelectMany(x => x.WeightRecords) + .ProjectTo(_mapper.ConfigurationProvider); + + if (pageNumber != null && pageSize != null) + { + return await PagedResult.CreateFromQueryAsync(query, pageNumber.Value, pageSize.Value); + } + + return new PagedResult(await query.ToListAsync()); + } + + public async Task GetHeight(int userId) { return await _dbSet - .Where(x=>x.UserId == userId) - .ProjectTo(_mapper.ConfigurationProvider) + .Where(x => x.UserId == userId) + .Select(x => x.Height) .FirstOrDefaultAsync(); } diff --git a/API/Data/Repositories/BodyWeight/IBodyWeightRepository.cs b/API/Data/Repositories/BodyWeight/IBodyWeightRepository.cs index 8616ded..e5e0c28 100644 --- a/API/Data/Repositories/BodyWeight/IBodyWeightRepository.cs +++ b/API/Data/Repositories/BodyWeight/IBodyWeightRepository.cs @@ -1,4 +1,5 @@ using API.Data.Dtos; +using API.Utilities; namespace API.Data.Repositories { @@ -7,6 +8,8 @@ public interface IBodyWeightRepository : IRepository public void SetHeight(int height, int userId); public void AddRecord(int userId, BodyWeightRecord record); - public Task GetBodyWeightAsync(int userId); + public Task GetHeight(int userId); + + public Task> GetBodyWeightRecordsAsync(int userId, int? pageNumber = null, int? pageSize = null); } } diff --git a/API/Data/Repositories/Users/UsersRepository.cs b/API/Data/Repositories/Users/UsersRepository.cs index c4d60d7..bbbfd45 100644 --- a/API/Data/Repositories/Users/UsersRepository.cs +++ b/API/Data/Repositories/Users/UsersRepository.cs @@ -46,7 +46,6 @@ public async Task> GetUsersListAsync(int? pageNumber = null if(pageNumber != null && pageSize != null) { - query = query.Skip((pageNumber.Value - 1) * pageSize.Value).Take(pageSize.Value); return await PagedResult.CreateFromQueryAsync(query, pageNumber.Value, pageSize.Value); } diff --git a/API/Handlers/Accounts/List/GetAccountsListHandler.cs b/API/Handlers/Accounts/List/GetAccountsListHandler.cs index 1d8d4f3..8403b7e 100644 --- a/API/Handlers/Accounts/List/GetAccountsListHandler.cs +++ b/API/Handlers/Accounts/List/GetAccountsListHandler.cs @@ -15,7 +15,7 @@ public GetAccountsListHandler(IUnitOfWork unitOfWork) } public async Task> Handle(GetAccountsListQuery request, CancellationToken cancellationToken) { - return await _unitOfWork.UsersRepository.GetUsersListAsync(); + return await _unitOfWork.UsersRepository.GetUsersListAsync(request.PageNumber, request.PageSize); } } } diff --git a/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightHandler.cs b/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightHandler.cs index 13ca6d9..27fb627 100644 --- a/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightHandler.cs +++ b/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightHandler.cs @@ -18,10 +18,13 @@ public async Task Handle(GetBodyWeightQuery request, Canc { var user = await _userService.GetCurrentUserAsync(); - var bodyWeight = await _unitOfWork.BodyWeightRepository.GetBodyWeightAsync(user.Id); + var height = await _unitOfWork.BodyWeightRepository.GetHeight(user.Id); + + var bodyWeight = await _unitOfWork.BodyWeightRepository.GetBodyWeightRecordsAsync(user.Id, request.PageNumber, request.PageSize); return new GetBodyWeightResponse() { - BodyWeight = bodyWeight, + BodyWeightRecords = bodyWeight, + Height = height, GenderMale = user.Gender.Equals("male", StringComparison.OrdinalIgnoreCase), }; } diff --git a/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightQuery.cs b/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightQuery.cs index 017ae75..db48694 100644 --- a/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightQuery.cs +++ b/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightQuery.cs @@ -1,10 +1,10 @@ - -using API.Data.Dtos; -using MediatR; +using MediatR; namespace API.Handlers.BodyWeight.GetBodyWeight { public class GetBodyWeightQuery : IRequest { + public int? PageNumber { get; set; } + public int? PageSize { get; set; } } } diff --git a/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightResponse.cs b/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightResponse.cs index 4d265ce..4d2dc46 100644 --- a/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightResponse.cs +++ b/API/Handlers/BodyWeight/GetBodyWeight/GetBodyWeightResponse.cs @@ -1,10 +1,12 @@ using API.Data.Dtos; +using API.Utilities; namespace API.Handlers.BodyWeight.GetBodyWeight { public class GetBodyWeightResponse { public bool GenderMale { get; set; } - public BodyWeightDto BodyWeight { get; set; } + public int? Height { get; set; } + public PagedResult BodyWeightRecords { get; set; } } } diff --git a/API/Utilities/PagedResult.cs b/API/Utilities/PagedResult.cs index ef2ccfb..3d12d9e 100644 --- a/API/Utilities/PagedResult.cs +++ b/API/Utilities/PagedResult.cs @@ -1,10 +1,12 @@ using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory; namespace API.Utilities { public class PagedResult { + public PagedResult() {} public PagedResult(List items) { Items = items; @@ -26,9 +28,10 @@ public PagedResult(List items, int totalPages, int totalCount, int itemsFrom, public int TotalCount { get; set; } public int ItemsFrom { get; set; } public int ItemsTo { get; set; } - public static async Task> CreateFromQueryAsync(IQueryable items, int pageNumber, int pageSize) + public static async Task> CreateFromQueryAsync(IQueryable query, int pageNumber, int pageSize) { - var totalCount = items.Count(); + var totalCount = query.Count(); + var items = query.Skip((pageNumber - 1) * pageSize).Take(pageSize); var toatlPages = (int)Math.Ceiling(totalCount / (double)pageSize); var itemsFrom = (pageNumber - 1) * pageSize + 1; var itemsTo = itemsFrom + pageSize - 1 < totalCount ? itemsFrom + pageSize - 1 : totalCount; diff --git a/client/src/app/Accounts/list/accounts-list.component.html b/client/src/app/Accounts/list/accounts-list.component.html index 45bce35..64a850b 100644 --- a/client/src/app/Accounts/list/accounts-list.component.html +++ b/client/src/app/Accounts/list/accounts-list.component.html @@ -1,5 +1,23 @@ -
-
- +

Users from {{itemsFrom}} to {{itemsTo}} of {{totalCount}}

+ +
+
+
+ +

Page {{currentPage}} of {{totalPages}}

+ +
+ + + +
diff --git a/client/src/app/Accounts/list/accounts-list.component.ts b/client/src/app/Accounts/list/accounts-list.component.ts index abeb01d..a5b47e1 100644 --- a/client/src/app/Accounts/list/accounts-list.component.ts +++ b/client/src/app/Accounts/list/accounts-list.component.ts @@ -11,16 +11,49 @@ import {environment} from "../../../environments/environment"; }) export class AccountsListComponent implements OnInit { baseUrl = environment.baseUrl; - users : UserDto[] = new Array() + users : UserDto[] = new Array(); + totalPages?: number; + totalCount?: number; + itemsFrom?: number; + itemsTo?: number; + pageSize: number = 10; + currentPage: number = 1; constructor(private http: HttpClient) { } ngOnInit(): void { - this.http.get(this.baseUrl+'Accounts/List/').subscribe({ + this.getList(this.currentPage,this.pageSize) + } + + private getList(pageNumber: number, pageSize: number): void{ + this.http.get(this.baseUrl+'Accounts/List?' + 'PageNumber=' + pageNumber + '&PageSize=' + pageSize).subscribe({ next: response => { this.users = response.items; + this.totalPages = response.totalPages; + this.totalCount = response.totalCount; + this.itemsFrom = response.itemsFrom; + this.itemsTo = response.itemsTo; + this.currentPage = pageNumber; } }) } + + goToPage(pageNumber: number): void { + if (pageNumber >= 1 && pageNumber <= (this.totalPages || 1)) { + this.getList(pageNumber, this.pageSize); + } + } + + nextPage(): void { + if (this.currentPage < (this.totalPages || 1)) { + this.goToPage(this.currentPage + 1); + } + } + + previousPage(): void { + if (this.currentPage > 1) { + this.goToPage(this.currentPage - 1); + } + } } diff --git a/client/src/app/Body/body-main/body-main.component.html b/client/src/app/Body/body-main/body-main.component.html index 25a9ff2..c650e6d 100644 --- a/client/src/app/Body/body-main/body-main.component.html +++ b/client/src/app/Body/body-main/body-main.component.html @@ -1,7 +1,11 @@
- +
+

Loading data...

+
+ +
-

Height: {{bodyWeight.height}}

+

Height: {{height}}

@@ -24,14 +28,20 @@ - + {{record.date | date}} {{record.weight}} - {{ calculateBMI(record.weight, bodyWeight.height) }} - {{ getStatus(record.weight, bodyWeight.height) }} + {{ calculateBMI(record.weight, height) }} + {{ getStatus(record.weight, height) }} + +
+ + Page {{currentPage}} of {{bodyWeightRecords.totalPages}} + +
diff --git a/client/src/app/Body/body-main/body-main.component.ts b/client/src/app/Body/body-main/body-main.component.ts index 2859497..53f241e 100644 --- a/client/src/app/Body/body-main/body-main.component.ts +++ b/client/src/app/Body/body-main/body-main.component.ts @@ -1,7 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { BodyService } from '../../_services/body.service'; -import { BodyWeight } from '../models/BodyWeight'; +import {BodyWeightRecord} from "../models/BodyWeightRecord"; +import {PagedResult} from "../../_common/PagedResult"; @Component({ selector: 'app-body-main', @@ -9,36 +10,50 @@ import { BodyWeight } from '../models/BodyWeight'; styleUrls: ['./body-main.component.css'] }) export class BodyMainComponent implements OnInit { - bodyWeight?: BodyWeight; + bodyWeightRecords?: PagedResult; + height?: number; genderMale: boolean = false; isEditingHeight: boolean = true; newHeight?: number; + isLoaded: boolean = false; + currentPage = 1; + pageSize = 10; constructor(private bodyService: BodyService, private router: Router) {} ngOnInit(): void { - this.bodyService.getBodyWeight().subscribe({ + this.loadBodyWeightRecords(this.currentPage, this.pageSize) + } + + loadBodyWeightRecords(pageNumber: number, pageSize: number): void { + this.bodyService.getBodyWeight(pageNumber, pageSize).subscribe({ next: response => { - this.bodyWeight = response.bodyWeight; - if (this.bodyWeight.height) { + this.bodyWeightRecords = response.bodyWeightRecords; + console.log(this.bodyWeightRecords); + if (response.height) { + this.height = response.height; this.isEditingHeight = false; } this.genderMale = response.genderMale; + this.isLoaded = true; + }, + error: () => { + this.isLoaded = false; } }); } enableHeightEdit(): void { this.isEditingHeight = true; - this.newHeight = this.bodyWeight?.height || undefined; + this.newHeight = this.height || undefined; } saveHeight(): void { if (this.newHeight != null) { this.bodyService.saveHeight(this.newHeight).subscribe({ next: () => { - if (this.bodyWeight && this.newHeight) { - this.bodyWeight.height = this.newHeight; + if (this.bodyWeightRecords && this.newHeight) { + this.height = this.newHeight; } this.isEditingHeight = !this.isEditingHeight; } @@ -66,10 +81,24 @@ export class BodyMainComponent implements OnInit { } addRecord(): void { - this.router.navigate(['/body-record-details'], { queryParams: { genderMale: this.genderMale, height: this.bodyWeight?.height, isViewMode: false } }); + this.router.navigate(['/body-record-details'], { queryParams: { genderMale: this.genderMale, height: this.height, isViewMode: false } }); } viewDetails(record: any): void { - this.router.navigate(['/body-record-details'], { queryParams: { ...record, genderMale: this.genderMale, height: this.bodyWeight?.height, isViewMode: true } }); + this.router.navigate(['/body-record-details'], { queryParams: { ...record, genderMale: this.genderMale, height: this.height, isViewMode: true } }); + } + + goToPreviousPage(): void { + if (this.currentPage > 1) { + this.currentPage--; + this.loadBodyWeightRecords(this.currentPage, this.pageSize); + } + } + + goToNextPage(): void { + if (this.bodyWeightRecords && this.currentPage < this.bodyWeightRecords.totalPages) { + this.currentPage++; + this.loadBodyWeightRecords(this.currentPage, this.pageSize); + } } } diff --git a/client/src/app/Body/models/GetBodyWeightQueryResponse.ts b/client/src/app/Body/models/GetBodyWeightQueryResponse.ts index 03549c8..590fce1 100644 --- a/client/src/app/Body/models/GetBodyWeightQueryResponse.ts +++ b/client/src/app/Body/models/GetBodyWeightQueryResponse.ts @@ -1,6 +1,8 @@ -import {BodyWeight} from "./BodyWeight"; +import {BodyWeightRecord} from "./BodyWeightRecord"; +import {PagedResult} from "../../_common/PagedResult"; export interface GetBodyWeightQueryResponse { - bodyWeight: BodyWeight; + bodyWeightRecords: PagedResult + height: number; genderMale: boolean; } diff --git a/client/src/app/Plans/plans-market/plans-market.component.html b/client/src/app/Plans/plans-market/plans-market.component.html index cd21584..2033bad 100644 --- a/client/src/app/Plans/plans-market/plans-market.component.html +++ b/client/src/app/Plans/plans-market/plans-market.component.html @@ -13,9 +13,15 @@ {{plan.name}} {{plan.authorName}} - - - + + + + + + + + + @@ -28,4 +34,21 @@ + + +

Showing plans {{itemsFrom}} to {{itemsTo}} of {{totalCount}}

+ +
+ + + +
diff --git a/client/src/app/Plans/plans-market/plans-market.component.ts b/client/src/app/Plans/plans-market/plans-market.component.ts index 459a6be..d0b8e24 100644 --- a/client/src/app/Plans/plans-market/plans-market.component.ts +++ b/client/src/app/Plans/plans-market/plans-market.component.ts @@ -1,10 +1,9 @@ -import {Component, OnInit} from '@angular/core'; -import {PlanTemplate} from "../models/planTemplate"; -import {HttpClient} from "@angular/common/http"; -import {GetPlanTemplatesQueryResult} from "../models/GetPlanTemplatesQueryResult"; -import {Router} from "@angular/router"; -import {add} from "ngx-bootstrap/chronos"; -import {environment} from "../../../environments/environment"; +import { Component, OnInit } from '@angular/core'; +import { PlanTemplate } from "../models/planTemplate"; +import { HttpClient } from "@angular/common/http"; +import { GetPlanTemplatesQueryResult } from "../models/GetPlanTemplatesQueryResult"; +import { Router } from "@angular/router"; +import { environment } from "../../../environments/environment"; @Component({ selector: 'app-plans-market', @@ -13,25 +12,56 @@ import {environment} from "../../../environments/environment"; }) export class PlansMarketComponent implements OnInit { baseUrl = environment.baseUrl; - plans : Array = new Array(); + plans: Array = new Array(); + totalPages?: number; + totalCount?: number; + itemsFrom: number = 1; + itemsTo: number = 1; + pageSize: number = 10; + currentPage: number = 1; - constructor(private http : HttpClient, private router: Router) {} + constructor(private http: HttpClient, private router: Router) {} - ngOnInit(): void { - this.http.get(this.baseUrl + 'Plans/Templates/').subscribe({ - next: response => { - this.plans = response.items; - } - }) + ngOnInit(): void { + this.getList(this.currentPage, this.pageSize); + } + + getList(pageNumber: number, pageSize: number): void { + this.http.get(`${this.baseUrl}Plans/Templates?PageNumber=${pageNumber}&PageSize=${pageSize}`).subscribe({ + next: response => { + this.plans = response.items; + this.totalPages = response.totalPages; + this.totalCount = response.totalCount; + this.itemsFrom = (pageNumber - 1) * pageSize + 1; + this.itemsTo = Math.min(pageNumber * pageSize, this.totalCount || 0); + this.currentPage = pageNumber; + } + }); + } + + addPlan(id: number) { + this.http.get(this.baseUrl + 'Plans/add/' + id).subscribe({ + next: response => { + this.router.navigateByUrl('/plans/list'); + } + }); + } + + goToPage(pageNumber: number): void { + if (pageNumber >= 1 && pageNumber <= (this.totalPages || 1)) { + this.getList(pageNumber, this.pageSize); } + } - addPlan(id: number){ - this.http.get(this.baseUrl + 'Plans/add/'+id).subscribe({ - next: response => { - this.router.navigateByUrl('/plans/list') - } - }) + nextPage(): void { + if (this.currentPage < (this.totalPages || 1)) { + this.goToPage(this.currentPage + 1); } + } - protected readonly add = add; + previousPage(): void { + if (this.currentPage > 1) { + this.goToPage(this.currentPage - 1); + } + } } diff --git a/client/src/app/_common/PagedResult.ts b/client/src/app/_common/PagedResult.ts new file mode 100644 index 0000000..3cc6993 --- /dev/null +++ b/client/src/app/_common/PagedResult.ts @@ -0,0 +1,7 @@ +export interface PagedResult { + items: T[]; + totalPages: number; + totalCount: number; + itemsFrom: number; + itemsTo: number; +} diff --git a/client/src/app/_services/body.service.ts b/client/src/app/_services/body.service.ts index bb0da07..6cea788 100644 --- a/client/src/app/_services/body.service.ts +++ b/client/src/app/_services/body.service.ts @@ -12,8 +12,8 @@ export class BodyService { constructor(private http : HttpClient) { } - getBodyWeight(){ - return this.http.get(this.baseUrl+'Body'); + getBodyWeight(pageNumber : number, pageSize : number){ + return this.http.get(this.baseUrl+'Body?' + 'PageNumber=' + pageNumber + '&PageSize=' + pageSize); } saveHeight(height: number){