Skip to content

Commit

Permalink
PRIME-2571 Allow multiple authorized users to update HA sites (#2452)
Browse files Browse the repository at this point in the history
* initial commit

* fix unit test

* bug fix

* update modal text as requested

* Update IHealthAuthoritySiteService.cs

* add disabled page for disabled authorized user

* add comment as requested

* update modal text

* initial commit

* Revert "initial commit"

This reverts commit 2fea9e5.

* update event log text

* more changes

* update text

* fix PR issues

* fix PR issues

* more change

* Minor corrections

* fix typo

* fix PR issues

---------

Co-authored-by: Alan Leung <[email protected]>
  • Loading branch information
bergomi02 and neophyte57 authored Apr 2, 2024
1 parent adb88bf commit a8c19aa
Show file tree
Hide file tree
Showing 39 changed files with 18,756 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ export class AuthorizedUserResource {
);
}

public getAuthorizedUserSiteCount(authorizedUserId: number): Observable<number> {
return this.apiResource.get<number>(`parties/authorized-users/${authorizedUserId}/site-count`)
.pipe(
map((response: ApiHttpResponse<number>) => response.result),
tap((siteCount: number) => this.logger.info('siteCount', siteCount)),
catchError((error: any) => {
this.toastService.openErrorToast('Authorized user site count could not be retrieved');
this.logger.error('[Core] AuthorizedUserResource::getAuthorizedUserSiteCount error has occurred: ', error);
throw error;
})
);
}

public createAuthorizedUser(authorizedUser: AuthorizedUser): Observable<AuthorizedUser> {
return this.apiResource.post<AuthorizedUser>('parties/authorized-users', authorizedUser)
.pipe(
Expand Down Expand Up @@ -141,4 +154,17 @@ export class AuthorizedUserResource {
);
}

public disableAuthorizedUser(authorizedUserId: number): NoContent {
return this.apiResource.put<NoContent>(`parties/authorized-users/${authorizedUserId}/disable`)
.pipe(
NoContentResponse,
tap(() => this.toastService.openSuccessToast('Authorized user has been disabled')),
catchError((error: any) => {
this.toastService.openErrorToast('Authorized user could not be disabled');
this.logger.error('[Core] AuthorizedUserResource::disableAuthorizedUser error has occurred: ', error);
throw error;
})
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,20 @@ export class HealthAuthoritySiteResource {
);
}


/**
* @description
* Transfer the health authority site(s) from one authorized user to another.
*/
public transferHealthAuthoritySite(healthAuthCode: number, currentAuthorizedUserId: number, newAuthorizedUserId: number): NoContent {
return this.apiResource.post<NoContent>(`health-authorities/${healthAuthCode}/sites/transfer/from/${currentAuthorizedUserId}/to/${newAuthorizedUserId}`)
.pipe(
NoContentResponse,
catchError((error: any) => {
this.toastService.openErrorToast('Health authority site could not be transfered');
this.logger.error('[Core] HealthAuthoritySiteResource::transferHealthAuthoritySite error has occurred: ', error);
throw error;
})
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ export class SiteEventsPageComponent implements OnInit {
return {
date: businessEvent.eventDate,
content: businessEvent.description,
name: businessEvent.adminIDIR
name: businessEvent.adminIDIR,
marginRight: businessEvent.partyName
};
})
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,20 @@
<div>
<button mat-flat-button
color="warn"
*ngIf="user?.status !== AccessStatusEnum.APPROVED"
*ngIf="user?.status === AccessStatusEnum.UNDER_REVIEW"
[disabled]="!(Role.SUPER_ADMIN | inRole)"
(click)="onDelete()">Delete Authorized User
</button>
<button mat-flat-button
color="warn"
*ngIf="user?.status === AccessStatusEnum.APPROVED || user?.status === AccessStatusEnum.ACTIVE"
[disabled]="!(Role.SUPER_ADMIN | inRole)"
(click)="onDisable()">Disable Authorized User
</button>
<button mat-flat-button
color="primary"
class="ml-1"
*ngIf="user?.status === AccessStatusEnum.UNDER_REVIEW"
[disabled]="!(Role.SUPER_ADMIN | inRole) || user?.status === AccessStatusEnum.APPROVED"
(click)="onApprove()">Approve Authorized User
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { AuthorizedUser } from '@shared/models/authorized-user.model';
import { Role } from '@auth/shared/enum/role.enum';
import { AdjudicationRoutes } from '@adjudication/adjudication.routes';
import { AccessStatusEnum } from '@health-auth/shared/enums/access-status.enum';
import { HealthAuthoritySiteService } from '@health-auth/shared/services/health-authority-site.service';
import { TransferHASiteComponent } from '@shared/components/dialogs/content/transfer-ha-site/transfer-ha-site.component';

@Component({
selector: 'app-authorized-user-review',
Expand All @@ -33,6 +35,7 @@ export class AuthorizedUserReviewComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private authorizedUserResource: AuthorizedUserResource,
private healthAuthoritySiteResource: HealthAuthoritySiteService,
private configService: ConfigService,
private dialog: MatDialog,
private router: Router
Expand Down Expand Up @@ -66,6 +69,52 @@ export class AuthorizedUserReviewComponent implements OnInit {
});
}

public onDisable() {

this.busy = this.authorizedUserResource.getAuthorizedUserSiteCount(this.route.snapshot.params.auid)
.subscribe((siteCount) => {
if (siteCount > 0) {

const data: DialogOptions = {
data: {
healthAuthorityId: this.user.healthAuthorityCode,
currentAuthorizedUserId: this.user.id,
currentAuthorizedUserName: `${this.user.firstName} ${this.user.lastName}`,
siteCount: siteCount,
}
};

this.dialog.open(TransferHASiteComponent, { data })
.afterClosed()
.subscribe((result: boolean) => {
if (result) {
this.authorizedUserResource
.disableAuthorizedUser(this.route.snapshot.params.auid)
.subscribe(() => this.routeUtils.routeRelativeTo(['../', AdjudicationRoutes.HEALTH_AUTH_AUTHORIZED_USERS]));
}
});
} else {
const data: DialogOptions = {
title: 'Disable Authorized User',
message: 'Are you sure you want to disable this authorized user?',
actionText: "Yes"
};

this.dialog.open(ConfirmDialogComponent, { data })
.afterClosed()
.subscribe((result: boolean) => {
if (result) {
this.authorizedUserResource
.disableAuthorizedUser(this.route.snapshot.params.auid)
.subscribe(() => this.routeUtils.routeRelativeTo(['../', AdjudicationRoutes.HEALTH_AUTH_AUTHORIZED_USERS]));
}
});
}
});


}

public onBack() {
this.routeUtils.routeRelativeTo(['../', AdjudicationRoutes.HEALTH_AUTH_AUTHORIZED_USERS]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@
}
}">
</ng-container>
<ng-container *ngIf="isDisabled(user)"
[ngTemplateOutlet]="notification"
[ngTemplateOutletContext]="{ props:
{
icon: 'no_accounts',
text: 'This Authorized User is disabled'
}
}">
</ng-container>
</app-summary-card>
</div>
</ng-container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export class HealthAuthAuthorizedUsersViewComponent implements OnInit {
return (user.status === AccessStatusEnum.UNDER_REVIEW);
}

public isDisabled(user: AuthorizedUser) {
return (user.status === AccessStatusEnum.DISABLED);
}

public getUserProperties(user: AuthorizedUser) {
return [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { AuthorizedUserPageComponent } from '@health-auth/pages/authorized-user-
import { AuthorizedUserNextStepsPageComponent } from '@health-auth/pages/authorized-user-next-steps-page/authorized-user-next-steps-page.component';
import { AuthorizedUserApprovedPageComponent } from '@health-auth/pages/authorized-user-approved-page/authorized-user-approved-page.component';
import { AuthorizedUserDeclinedPageComponent } from '@health-auth/pages/authorized-user-declined-page/authorized-user-declined-page.component';
import { AuthorizedUserDisabledPageComponent } from './pages/authorized-user-disabled-page/authorized-user-disabled-page.component';
import { SiteManagementPageComponent } from '@health-auth/pages/site-management-page/site-management-page.component';
import { HealthAuthCareTypePageComponent } from '@health-auth/pages/health-auth-care-type-page/health-auth-care-type-page.component';
import { SiteInformationPageComponent } from '@health-auth/pages/site-information-page/site-information-page.component';
Expand Down Expand Up @@ -61,6 +62,11 @@ const routes: Routes = [
component: AuthorizedUserDeclinedPageComponent,
data: { title: 'Access Declined' }
},
{
path: HealthAuthSiteRegRoutes.ACCESS_DISABLED,
component: AuthorizedUserDisabledPageComponent,
data: { title: 'Authorized user is disabled' }
},
{
path: '', // Equivalent to `/` and alias for default view
redirectTo: HealthAuthSiteRegRoutes.ACCESS_AUTHORIZED_USER,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { AuthorizedUserPageComponent } from './pages/authorized-user-page/author
import { AuthorizedUserNextStepsPageComponent } from './pages/authorized-user-next-steps-page/authorized-user-next-steps-page.component';
import { AuthorizedUserApprovedPageComponent } from './pages/authorized-user-approved-page/authorized-user-approved-page.component';
import { AuthorizedUserDeclinedPageComponent } from './pages/authorized-user-declined-page/authorized-user-declined-page.component';
import { AuthorizedUserDisabledPageComponent } from './pages/authorized-user-disabled-page/authorized-user-disabled-page.component';
import { SiteManagementPageComponent } from './pages/site-management-page/site-management-page.component';
import { HealthAuthCareTypePageComponent } from './pages/health-auth-care-type-page/health-auth-care-type-page.component';
import { SiteInformationPageComponent } from './pages/site-information-page/site-information-page.component';
Expand All @@ -28,6 +29,7 @@ import { OverviewPageComponent } from './pages/overview-page/overview-page.compo
AuthorizedUserNextStepsPageComponent,
AuthorizedUserApprovedPageComponent,
AuthorizedUserDeclinedPageComponent,
AuthorizedUserDisabledPageComponent,
SiteManagementPageComponent,
SiteInformationPageComponent,
HealthAuthCareTypePageComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class HealthAuthSiteRegRoutes {
public static ACCESS_REQUESTED = 'access-requested';
public static ACCESS_APPROVED = 'access-approved';
public static ACCESS_DECLINED = 'access-declined';
public static ACCESS_DISABLED = 'access-disabled';

public static AUTHORIZED_USER = 'authorized-user';
public static SITE_MANAGEMENT = 'site-management';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<app-page>

<app-page-header>PharmaNet Site Registration</app-page-header>

<app-progress-indicator message="Access Declined"></app-progress-indicator>

<div class="mb-5">
<app-alert type="danger"
icon="error_outline">
<ng-container #alertTitle
class="alert-title">
You have been disabled.
</ng-container>
</app-alert>
</div>

<app-page-subheader2>
<ng-container appPageSubheaderTitle>Next Steps</ng-container>
</app-page-subheader2>

<p>
If you have questions regarding your account, please contact us.
</p>

</app-page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.icon {
// Page refreshes cause mat-icon to take precendence
font-size: 3rem !important;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';

import { AuthorizedUserDisabledPageComponent } from './authorized-user-disabled-page.component';

describe('AuthorizedUserDisabledPageComponent', () => {
let component: AuthorizedUserDisabledPageComponent;
let fixture: ComponentFixture<AuthorizedUserDisabledPageComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [AuthorizedUserDisabledPageComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(AuthorizedUserDisabledPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';

@Component({
selector: 'app-authorized-user-disabled-page',
templateUrl: './authorized-user-disabled-page.component.html',
styleUrls: ['./authorized-user-disabled-page.component.scss']
})
export class AuthorizedUserDisabledPageComponent implements OnInit {
constructor() { }

public ngOnInit(): void { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@
[properties]="[
{ key: 'Site Name', value: healthAuthoritySite?.siteName },
{ key: 'Site ID', value: healthAuthoritySite?.pec },
{ key: 'Vendor', value: healthAuthoritySite?.healthAuthorityVendor?.vendorCode | configCode : 'vendors' }
{ key: 'Vendor', value: healthAuthoritySite?.healthAuthorityVendor?.vendorCode | configCode : 'vendors' },
{ key: healthAuthoritySite?.submittedDate ? 'Last Submitted by' : 'Last Updated by', value: getLastUpdatedUser(healthAuthoritySite?.authorizedUserName, healthAuthoritySite?.updatedTimeStamp)},
]">
<ng-container *ngIf="healthAuthoritySite.isIncomplete()"
[ngTemplateOutlet]="notification"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ import { HealthAuthSiteRegRoutes } from '@health-auth/health-auth-site-reg.route
import { HealthAuthoritySite } from '@health-auth/shared/models/health-authority-site.model';
import { HealthAuthoritySiteList } from '@health-auth/shared/models/health-authority-site-list.model';
import { AuthorizedUserService } from '@health-auth/shared/services/authorized-user.service';
import { FormatDatePipe } from '@shared/pipes/format-date.pipe';

@Component({
selector: 'app-site-management-page',
templateUrl: './site-management-page.component.html',
styleUrls: ['./site-management-page.component.scss']
styleUrls: ['./site-management-page.component.scss'],
providers: [FormatDatePipe]
})
export class SiteManagementPageComponent implements OnInit {
public busy: Subscription;
Expand All @@ -34,7 +36,8 @@ export class SiteManagementPageComponent implements OnInit {
private route: ActivatedRoute,
private router: Router,
private authorizedUserService: AuthorizedUserService,
private authorizedUserResource: AuthorizedUserResource
private authorizedUserResource: AuthorizedUserResource,
private formatDatePipe: FormatDatePipe
) {
this.title = this.route.snapshot.data.title;
this.routeUtils = new RouteUtils(route, router, HealthAuthSiteRegRoutes.MODULE_PATH);
Expand Down Expand Up @@ -95,4 +98,8 @@ export class SiteManagementPageComponent implements OnInit {
pagePath
]);
}

public getLastUpdatedUser(userName: string, updatedTimeStamp: string): string {
return `${userName} - ${this.formatDatePipe.transform(updatedTimeStamp, "DD MMM yyyy h:mm A")}`
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@ export enum AccessStatusEnum {
UNDER_REVIEW = 1,
APPROVED = 2,
ACTIVE = 3,
DECLINED = 4
//adding LOCKED to reserve the number value, it is not used in Site Registration
LOCKED = 4,
DECLINED = 5,
DISABLED = 6,
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ export class AuthorizedUserGuard extends BaseGuard {
case AccessStatusEnum.DECLINED: {
return this.manageDeclinedAuthorizedUser(routePath);
}
case AccessStatusEnum.DISABLED: {
return this.manageDisabledAuthorizedUser(routePath);
}
}

// Otherwise, no authorized user exists
Expand Down Expand Up @@ -107,6 +110,13 @@ export class AuthorizedUserGuard extends BaseGuard {
]);
}

private manageDisabledAuthorizedUser(routePath: string): boolean {
return this.navigate(routePath, [
HealthAuthSiteRegRoutes.ACCESS,
HealthAuthSiteRegRoutes.ACCESS_DISABLED
]);
}

private manageActiveAuthorizedUser(routePath: string): boolean {
// Remove query params as they aren't needed to determine
// the viability of the destination
Expand Down
Loading

0 comments on commit a8c19aa

Please sign in to comment.