Skip to content

Commit

Permalink
Merge pull request #234 from bcgov/fix/ss-788-fix-performance-issue-l…
Browse files Browse the repository at this point in the history
…oading-profile-details

SS-788: fix performance issue loading profile details
  • Loading branch information
WadeBarnes committed May 31, 2024
2 parents c670e8b + 811ed53 commit 0dd7608
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 116 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/api-dotnetcore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v4
with:
dotnet-version: 3.1.101
dotnet-version: 5.x
- name: Install dependencies
run: dotnet restore
working-directory: ${{env.working-directory}}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/app-vue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm install && npm ci
working-directory: ${{env.working-directory}}
- run: npm run build --if-present
working-directory: ${{env.working-directory}}
96 changes: 93 additions & 3 deletions api/controllers/usermanagement/SheriffController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class SheriffController : UserController
// ReSharper disable once InconsistentNaming
private readonly long _uploadPhotoSizeLimitKB;

public SheriffController(SheriffService sheriffService, DutyRosterService dutyRosterService, ShiftService shiftService, UserService userUserService,TrainingService trainingService, IConfiguration configuration, SheriffDbContext db) : base(userUserService)
public SheriffController(SheriffService sheriffService, DutyRosterService dutyRosterService, ShiftService shiftService, UserService userUserService, TrainingService trainingService, IConfiguration configuration, SheriffDbContext db) : base(userUserService)
{
SheriffService = sheriffService;
ShiftService = shiftService;
Expand Down Expand Up @@ -92,6 +92,96 @@ public async Task<ActionResult<SheriffWithIdirDto>> GetSheriffForTeam(Guid id)
return Ok(sheriffDto);
}

/// <summary>
/// Get Sheriff Identification data.
/// </summary>
/// <param name="id">Guid of the userid.</param>
/// <returns>SheriffWithIdirDto</returns>
[HttpGet]
[PermissionClaimAuthorize(perm: Permission.Login)]
[Route("{id}/identification")]
public async Task<ActionResult<SheriffWithIdirDto>> GetSheriffIdentification(Guid id)
{
Sheriff sheriffIdentification = await SheriffService.GetSheriffIdentification(id);
if (sheriffIdentification == null) return NotFound(CouldNotFindSheriffError);
if (!PermissionDataFiltersExtensions.HasAccessToLocation(User, Db, sheriffIdentification.HomeLocationId)) return Forbid();

SheriffWithIdirDto sheriffDto = sheriffIdentification.Adapt<SheriffWithIdirDto>();
//Prevent exposing Idirs to regular users.
sheriffDto.IdirName = User.HasPermission(Permission.EditIdir) ? sheriffIdentification.IdirName : null;
return Ok(sheriffDto);
}

/// <summary>
/// Get Sheriff Leaves.
/// </summary>
/// <param name="id">Guid of the userid.</param>
/// <returns>SheriffLeaveDto[]</returns>
[HttpGet]
[PermissionClaimAuthorize(perm: Permission.Login)]
[Route("{id}/leaves")]
public async Task<ActionResult<List<SheriffLeaveDto>>> GetSheriffLeaves(Guid id)
{
List<SheriffLeave> sheriffLeave = await SheriffService.GetSheriffLeaves(id);
return Ok(sheriffLeave.Adapt<List<SheriffLeaveDto>>());
}

/// <summary>
/// Get Sheriff AwayLocations.
/// </summary>
/// <param name="id">Guid of the userid.</param>
/// <returns>SheriffAwayLocationDto[]</returns>
[HttpGet]
[PermissionClaimAuthorize(perm: Permission.Login)]
[Route("{id}/awaylocations")]
public async Task<ActionResult<List<SheriffAwayLocationDto>>> GetSheriffAwayLocations(Guid id)
{
List<SheriffAwayLocation> sheriffAwayLocations = await SheriffService.GetSheriffAwayLocations(id);
return Ok(sheriffAwayLocations.Adapt<List<SheriffAwayLocationDto>>());
}

/// <summary>
/// Get Sheriff Ranks.
/// </summary>
/// <param name="id">Guid of the userid.</param>
/// <returns>SheriffActingRankDto[]</returns>
[HttpGet]
[PermissionClaimAuthorize(perm: Permission.Login)]
[Route("{id}/actingranks")]
public async Task<ActionResult<List<SheriffActingRankDto>>> GetSheriffActingRanks(Guid id)
{
List<SheriffActingRank> sheriffActingRanks = await SheriffService.GetSheriffActingRanks(id);
return Ok(sheriffActingRanks.Adapt<List<SheriffActingRankDto>>());
}

/// <summary>
/// Get Sheriff Roles.
/// </summary>
/// <param name="id">Guid of the userid.</param>
/// <returns>UserRoleDto[]</returns>
[HttpGet]
[PermissionClaimAuthorize(perm: Permission.Login)]
[Route("{id}/roles")]
public async Task<ActionResult<List<UserRoleDto>>> GetSheriffRoles(Guid id)
{
List<UserRole> sheriffRoles = await SheriffService.GetSheriffRoles(id);
return Ok(sheriffRoles.Adapt<List<UserRoleDto>>());
}

/// <summary>
/// Get Sheriff Trainings.
/// </summary>
/// <param name="id">Guid of the userid.</param>
/// <returns>SheriffTrainingDto[]</returns>
[HttpGet]
[PermissionClaimAuthorize(perm: Permission.Login)]
[Route("{id}/trainings")]
public async Task<ActionResult<List<SheriffTrainingDto>>> GetSheriffTrainings(Guid id)
{
List<SheriffTraining> sheriffTrainings = await SheriffService.GetSheriffTrainings(id);
return Ok(sheriffTrainings.Adapt<List<SheriffTrainingDto>>());
}

/// <summary>
/// Development route, do not use this in application.
/// </summary>
Expand Down Expand Up @@ -159,7 +249,7 @@ public async Task<ActionResult<SheriffDto>> UploadPhoto(Guid? id, string badgeNu

[HttpPut]
[Route("updateExcused")]
[PermissionClaimAuthorize(perm: Permission.GenerateReports)]
[PermissionClaimAuthorize(perm: Permission.GenerateReports)]
public async Task<ActionResult<SheriffDto>> UpdateExcused(Sheriff excusedSheriff)
{
var sheriff = await SheriffService.UpdateSheriffExcused(excusedSheriff);
Expand Down Expand Up @@ -296,7 +386,7 @@ public async Task<ActionResult<TrainingReportDto>> GetSheriffsTrainingReports(Tr
public async Task<ActionResult> TrainingExpiryAdjustment()
{
await TrainingService.TrainingExpiryAdjustment();
return Ok(new { result = "success"});
return Ok(new { result = "success" });
}

#endregion SheriffTrainingReports
Expand Down
63 changes: 61 additions & 2 deletions api/services/usermanagement/SheriffService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
using SS.Api.helpers.extensions;
using SS.Api.infrastructure.authorization;
using SS.Api.infrastructure.exceptions;
using SS.Api.models.dto.generated;
using SS.Api.Models.DB;
using SS.Api.services.scheduling;
using SS.Common.helpers.extensions;
using SS.Db.models;
using SS.Db.models.auth;
using SS.Db.models.scheduling;
using SS.Db.models.sheriff;

Expand Down Expand Up @@ -114,6 +116,63 @@ public async Task<Sheriff> GetFilteredSheriffForTeams(Guid id)
.SingleOrDefaultAsync(s => s.Id == id);
}

public async Task<Sheriff> GetSheriffIdentification(Guid id)
{
var daysPrevious = int.Parse(Configuration.GetNonEmptyValue("DaysInPastToIncludeAwayLocationAndTraining"));
var minDateForAwayAndTraining = DateTimeOffset.UtcNow.AddDays(-daysPrevious);
var sevenDaysFromNow = DateTimeOffset.UtcNow.AddDays(7);

return await Db.Sheriff.AsNoTracking()
.ApplyPermissionFilters(User, minDateForAwayAndTraining, sevenDaysFromNow, Db)
.Include(s => s.HomeLocation)
.SingleOrDefaultAsync(s => s.Id == id);
}

public async Task<List<SheriffLeave>> GetSheriffLeaves(Guid id)
{
var daysPrevious = int.Parse(Configuration.GetNonEmptyValue("DaysInPastToIncludeAwayLocationAndTraining"));
var minDateForAwayAndTraining = DateTimeOffset.UtcNow.AddDays(-daysPrevious);

return await Db.SheriffLeave.AsNoTracking()
.Where(l => l.SheriffId == id && l.EndDate >= minDateForAwayAndTraining && l.ExpiryDate == null)
.Include(l => l.LeaveType)
.ToListAsync();
}

public async Task<List<SheriffAwayLocation>> GetSheriffAwayLocations(Guid id)
{
var daysPrevious = int.Parse(Configuration.GetNonEmptyValue("DaysInPastToIncludeAwayLocationAndTraining"));
var minDateForAwayAndTraining = DateTimeOffset.UtcNow.AddDays(-daysPrevious);

return await Db.SheriffAwayLocation.AsNoTracking()
.Where(al => al.SheriffId == id && al.EndDate >= minDateForAwayAndTraining && al.ExpiryDate == null)
.Include(al => al.Location)
.ToListAsync();
}

public async Task<List<SheriffActingRank>> GetSheriffActingRanks(Guid id)
{
return await Db.SheriffActingRank.AsNoTracking()
.Where(ar => ar.SheriffId == id && ar.ExpiryDate == null)
.ToListAsync();
}

public async Task<List<UserRole>> GetSheriffRoles(Guid id)
{
return await Db.UserRole.AsNoTracking()
.Where(ur => ur.UserId == id)
.Include(ur => ur.Role)
.ToListAsync();
}

public async Task<List<SheriffTraining>> GetSheriffTrainings(Guid id)
{
return await Db.SheriffTraining.AsNoTracking()
.Where(t => t.SheriffId == id && t.ExpiryDate == null)
.Include(t => t.TrainingType)
.ToListAsync();
}

public async Task<Sheriff> UpdateSheriff(Sheriff sheriff, bool canEditIdir)
{
var savedSheriff = await Db.Sheriff.FindAsync(sheriff.Id);
Expand Down Expand Up @@ -168,7 +227,7 @@ public async Task<Sheriff> UpdateSheriffExcused(Sheriff sheriff)
{
var savedSheriff = await Db.Sheriff.FindAsync(sheriff.Id);
savedSheriff.ThrowBusinessExceptionIfNull($"No {nameof(Sheriff)} with Id: {sheriff.Id}");
savedSheriff.Excused = sheriff.Excused;
savedSheriff.Excused = sheriff.Excused;
await Db.SaveChangesAsync();
return savedSheriff;
}
Expand Down Expand Up @@ -344,7 +403,7 @@ public async Task<List<Sheriff>> GetSheriffsTraining()
var daysPrevious = int.Parse(Configuration.GetNonEmptyValue("DaysInPastToIncludeAwayLocationAndTraining"));
var minDateForAwayAndTraining = DateTimeOffset.UtcNow.AddDays(-daysPrevious);
var sevenDaysFromNow = DateTimeOffset.UtcNow.AddDays(7);

var sheriffQuery = Db.Sheriff.AsNoTracking()
.AsSplitQuery()
.ApplyPermissionFilters(User, minDateForAwayAndTraining, sevenDaysFromNow, Db)
Expand Down
6 changes: 3 additions & 3 deletions web/src/components/MyTeam/MyTeamMembers.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
</b-col>
<b-col cols="9">
<b-card no-body >
<b-tabs card v-model="tabIndex" @activate-tab="onTabChanged">
<b-tabs card v-model="tabIndex" @activate-tab="onTabChanged" lazy>
<b-tab title="Identification">
<identification-tab
:runMethod="identificationTabMethods"
Expand Down Expand Up @@ -468,7 +468,7 @@
public loadUserDetails(userId): void {
this.resetProfileWindowState();
this.editMode = true;
const url = 'api/sheriff/' + userId;
const url = `api/sheriff/${userId}/identification`;
this.$http.get(url)
.then(response => {
if(response.data){
Expand Down Expand Up @@ -503,7 +503,7 @@
if(userJson.awayLocation && userJson.awayLocation.length>0)
user.awayLocation = userJson.awayLocation;
user.actingRank = userJson.actingRank;
user.leave = userJson.leave;
user.training = userJson.training;
Expand Down
53 changes: 28 additions & 25 deletions web/src/components/MyTeam/Tabs/LeaveTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -215,33 +215,36 @@
}
public extractLeaves () {
const assignedLeavesJson = this.userToEdit.leave? this.userToEdit.leave: [];
for(const leaveJson of assignedLeavesJson){
const leave = {} as userLeaveInfoType;
leave.id = leaveJson.id;
leave.leaveType = leaveJson.leaveType;
leave.leaveTypeId = leaveJson.leaveTypeId;
leave.leaveName = leaveJson.leaveType?leaveJson.leaveType.description: '';
leave.comment = leaveJson.comment?leaveJson.comment:'';
if(Vue.filter('isDateFullday')(leaveJson.startDate,leaveJson.endDate)){
leave.isFullDay = true;
leave['_cellVariants'] = {isFullDay:'danger'}
} else{
leave.isFullDay = false;
leave['_cellVariants'] = {isFullDay:'success'}
const url = `api/sheriff/${this.userToEdit.id}/leaves`;
this.$http.get(url).then((response) => {
const assignedLeavesJson = response?.data ? response.data: [];
for(const leaveJson of assignedLeavesJson){
const leave = {} as userLeaveInfoType;
leave.id = leaveJson.id;
leave.leaveType = leaveJson.leaveType;
leave.leaveTypeId = leaveJson.leaveTypeId;
leave.leaveName = leaveJson.leaveType?leaveJson.leaveType.description: '';
leave.comment = leaveJson.comment?leaveJson.comment:'';
if(Vue.filter('isDateFullday')(leaveJson.startDate,leaveJson.endDate)){
leave.isFullDay = true;
leave['_cellVariants'] = {isFullDay:'danger'}
} else{
leave.isFullDay = false;
leave['_cellVariants'] = {isFullDay:'success'}
}
leave.startDate = moment(leaveJson.startDate).tz(this.timezone).format();
leave.endDate = moment(leaveJson.endDate).tz(this.timezone).format();
leave['_rowVariant'] = '';
if(leave.endDate < this.currentTime)
leave['_rowVariant'] = 'info';
this.assignedLeaves.push(leave);
}
leave.startDate = moment(leaveJson.startDate).tz(this.timezone).format();
leave.endDate = moment(leaveJson.endDate).tz(this.timezone).format();
leave['_rowVariant'] = '';
if(leave.endDate < this.currentTime)
leave['_rowVariant'] = 'info';
this.assignedLeaves.push(leave);
}
this.loadLeaveTypes();
this.loadLeaveTypes();
});
}
public loadLeaveTypes() {
Expand Down
46 changes: 25 additions & 21 deletions web/src/components/MyTeam/Tabs/LocationTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -200,28 +200,32 @@
public extractAwayLocations ()
{
this.assignedAwayLocations = this.userToEdit.awayLocation? this.userToEdit.awayLocation: [];
for(const inx in this.assignedAwayLocations)
{
const location = this.getLocation(this.assignedAwayLocations[inx].locationId)
this.assignedAwayLocations[inx]['locationNm'] = location? location.name : '';
if(Vue.filter('isDateFullday')(this.assignedAwayLocations[inx].startDate,this.assignedAwayLocations[inx].endDate)){
this.assignedAwayLocations[inx]['isFullDay'] = true;
this.assignedAwayLocations[inx]['_cellVariants'] = {isFullDay:'danger'}
}else{
this.assignedAwayLocations[inx]['isFullDay'] = false;
this.assignedAwayLocations[inx]['_cellVariants'] = {isFullDay:'success'}
const url = `api/sheriff/${this.userToEdit.id}/awaylocations`;
this.$http.get(url).then((response) => {
this.assignedAwayLocations = response.data? response.data: [];
for(const inx in this.assignedAwayLocations)
{
const location = this.getLocation(this.assignedAwayLocations[inx].locationId)
this.assignedAwayLocations[inx]['locationNm'] = location? location.name : '';
if(Vue.filter('isDateFullday')(this.assignedAwayLocations[inx].startDate,this.assignedAwayLocations[inx].endDate)){
this.assignedAwayLocations[inx]['isFullDay'] = true;
this.assignedAwayLocations[inx]['_cellVariants'] = {isFullDay:'danger'}
}else{
this.assignedAwayLocations[inx]['isFullDay'] = false;
this.assignedAwayLocations[inx]['_cellVariants'] = {isFullDay:'success'}
}
const timezone = location? location.timezone : 'UTC';
this.currentTime = moment(new Date()).tz(timezone).format();
this.assignedAwayLocations[inx].startDate = moment(this.assignedAwayLocations[inx].startDate).tz(timezone).format();
this.assignedAwayLocations[inx].endDate = moment(this.assignedAwayLocations[inx].endDate).tz(timezone).format();
this.currentTime = moment(new Date()).tz(timezone).format();
this.assignedAwayLocations[inx]['_rowVariant'] = '';
if(this.assignedAwayLocations[inx].endDate < this.currentTime)
this.assignedAwayLocations[inx]['_rowVariant'] = 'info';
}
const timezone = location? location.timezone : 'UTC';
this.currentTime = moment(new Date()).tz(timezone).format();
this.assignedAwayLocations[inx].startDate = moment(this.assignedAwayLocations[inx].startDate).tz(timezone).format();
this.assignedAwayLocations[inx].endDate = moment(this.assignedAwayLocations[inx].endDate).tz(timezone).format();
this.currentTime = moment(new Date()).tz(timezone).format();
this.assignedAwayLocations[inx]['_rowVariant'] = '';
if(this.assignedAwayLocations[inx].endDate < this.currentTime)
this.assignedAwayLocations[inx]['_rowVariant'] = 'info';
}
});
}
public confirmDeleteLocation(location) {
Expand Down
Loading

0 comments on commit 0dd7608

Please sign in to comment.