Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SS-788: fix performance issue loading profile details #234

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading