Skip to content
Open
Show file tree
Hide file tree
Changes from 13 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
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { HealthAuthoritySiteAdminList } from '@health-auth/shared/models/health-
import { HealthAuthoritySiteAdmin } from '@health-auth/shared/models/health-authority-admin-site.model';
import { ApiResourceUtilsService } from './api-resource-utils.service';
import { BusinessDay } from '@lib/models/business-day.model';
import { HaCareTypeVendor } from '@lib/models/ha-care-type-vendor.model';

@Injectable({
providedIn: 'root'
Expand Down Expand Up @@ -136,6 +137,18 @@ export class HealthAuthorityResource {
);
}

public updateHealthAuthorityCareTypeVendor(healthAuthorityId: number, careTypeVendor: HaCareTypeVendor[]): NoContent {
return this.apiResource.put<NoContent>(`health-authorities/${healthAuthorityId}/care-type-vendor`, careTypeVendor)
.pipe(
NoContentResponse,
catchError((error: any) => {
this.toastService.openErrorToast('Health authority care type vendor could not be updated');
this.logger.error('[Core] HealthAuthorityResource::updateHealthAuthorityCareTypeVendor error has occurred: ', error);
throw error;
})
);
}

public getHealthAuthorityVendorSiteIds(healthAuthorityId: number, vendorId: number): Observable<number[]> {
return this.apiResource.get<number[]>(`health-authorities/${healthAuthorityId}/vendors/${vendorId}/sites`)
.pipe(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface HaCareTypeVendor {
careType: string;
vendorCode: number;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AbstractControl, FormArray, ValidationErrors, ValidatorFn } from '@angular/forms';
import { AbstractControl, FormArray, FormGroup, ValidationErrors, ValidatorFn } from '@angular/forms';

export class FormArrayValidators {
/**
Expand All @@ -19,4 +19,30 @@ export class FormArrayValidators {
return (atLeast) ? null : { atleast: true };
};
}

public static noDuplicateValue(
keyString: string
): ValidatorFn {
return (array: FormArray): ValidationErrors | null => {

let setSize = (array: FormArray) => {
const values = array.controls.filter((form: FormGroup) => {
return form.get(keyString) && form.get(keyString).value !== null
}).map((form: FormGroup) => {
return form.get(keyString) ?
form.get(keyString).value : null;
});
const newSet = new Set(values);
return newSet.size;
};

if (array && array.controls?.length && array.controls?.length > 1) {
const valid = setSize(array) === array.controls.filter(c => c.get(keyString) && c.get(keyString).value !== null).length;
return (valid) ? null : { duplicate: true };
} else {
return null;
}
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import { SiteOverviewPageComponent } from './pages/health-authorities/site-overv
import { SiteEventsPageComponent } from './pages/health-authorities/site-events-page/site-events-page.component';
import { SiteNotesPageComponent } from './pages/health-authorities/site-notes-page/site-notes-page.component';
import { SiteDocumentsPageComponent } from './pages/health-authorities/site-documents-page/site-documents-page.component';
import { CareTypeVendorPageComponent } from './pages/health-authorities/care-type-vendor-page/care-type-vendor-page.component';
import { HaSiteSubmissionListPageComponent } from './pages/health-authorities/ha-site-submission-list-page/ha-site-submission-list-page.component';
import { HaSiteSubmissionPageComponent } from './pages/health-authorities/ha-site-submission-page/ha-site-submission-page.component';
import { CommunitySiteSubmissionPageComponent } from './pages/community-site-submission-page/community-site-submission-page.component';
Expand Down Expand Up @@ -355,6 +356,11 @@ const routes: Routes = [
component: VendorsPageComponent,
data: { title: 'Vendors' }
},
{
path: AdjudicationRoutes.HEALTH_AUTH_CARE_TYPE_VENDOR,
component: CareTypeVendorPageComponent,
data: { title: 'Health Authority Care Type Vendor' }
},
{
path: AdjudicationRoutes.HEALTH_AUTH_PRIVACY_OFFICE,
component: PrivacyOfficePageComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import { SiteOverviewPageComponent } from './pages/health-authorities/site-overv
import { SiteEventsPageComponent } from './pages/health-authorities/site-events-page/site-events-page.component';
import { SiteNotesPageComponent } from './pages/health-authorities/site-notes-page/site-notes-page.component';
import { SiteDocumentsPageComponent } from './pages/health-authorities/site-documents-page/site-documents-page.component';
import { CareTypeVendorPageComponent } from './pages/health-authorities/care-type-vendor-page/care-type-vendor-page.component';
import { CommunitySiteSubmissionPageComponent } from './pages/community-site-submission-page/community-site-submission-page.component';
import { CommunitySiteSubmissionListPageComponent } from './pages/community-site-submission-list-page/community-site-submission-list-page.component';
import { HaSiteSubmissionListPageComponent } from './pages/health-authorities/ha-site-submission-list-page/ha-site-submission-list-page.component';
Expand Down Expand Up @@ -150,6 +151,7 @@ import { OrganizationSitesComponent } from './pages/organization-sites/organizat
EnrolleeToaMaintenanceListPageComponent,
EnrolleeToaMaintenanceViewPageComponent,
HealthAuthCareTypesPageComponent,
CareTypeVendorPageComponent,
VendorsPageComponent,
PrivacyOfficePageComponent,
TechnicalSupportsPageComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class AdjudicationRoutes {
public static HEALTH_AUTHORITIES = 'health-authorities';
public static HEALTH_AUTH_CARE_TYPES = 'health-auth-care-types';
public static HEALTH_AUTH_VENDORS = 'vendors';
public static HEALTH_AUTH_CARE_TYPE_VENDOR = 'health-auth-care-type-vendor';
public static HEALTH_AUTH_PRIVACY_OFFICE = 'privacy-office';
public static HEALTH_AUTH_TECHNICAL_SUPPORTS = 'technical-supports';
public static HEALTH_AUTH_ADMINISTRATORS = 'administrators';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<app-page [busy]="busy">
<app-page-header>PharmaNet Health Authority Information</app-page-header>
<app-progress-indicator [noContent]="true"></app-progress-indicator>

<form (ngSubmit)="onSubmit()"
[formGroup]="form"
novalidate>

<app-page-section>
<app-page-subheader>
<ng-container appPageSubheaderTitle>{{ title }}</ng-container>
<ng-container appPageSubheaderSummary>
Identify the Care Type Vendor associated with this Health Authority.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest changing to "Identify the Vendor(s) associated with each Care Type."

</ng-container>
</app-page-subheader>

<div class="row">
<div class="col col-sm-10 py-3">

<ng-container *ngFor="let careTypeVendor of careTypeVendors.controls; let careTypeIndex = index;">

<div class="mb-3"
[ngClass]="{'mt-3':careTypeIndex > 0}">
<strong>Care Type: </strong><span>{{careTypeVendor.get('careType').value}}</span>
</div>
<div class="text-danger mb-2"
*ngIf="hasDuplicate(careTypeIndex)">
Duplicate vendors detected. Please remove duplicate vendor.
</div>
<ng-container *ngIf="careTypeVendor.get('vendorCodes').controls">
<ng-container
*ngFor="let careTypeVendorCode of careTypeVendor.get('vendorCodes').controls; let vendorIndex = index;">
<ng-container [formGroup]="careTypeVendorCode">
<app-form-icon-group [show]="careTypeVendor.get('vendorCodes').length > 1"
(event)="removeVendor(careTypeIndex, vendorIndex)">
<mat-form-field class="w-100">
<mat-label>Vendor</mat-label>
<mat-select formControlName="vendorCode">
<mat-option *ngFor="let vendor of availableVendors"
[value]="vendor.code">
{{ vendor.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="isRequired(careTypeIndex, vendorIndex)">
Required
</mat-error>
</mat-form-field>
</app-form-icon-group>
</ng-container>
</ng-container>
<button mat-button
*ngIf="careTypeVendor.get('vendorCodes').length < availableVendors.length"
type="button"
color="primary"
(click)="addVendor(careTypeIndex)">
<mat-icon>add</mat-icon>
Add Vendor
</button>
</ng-container>

</ng-container>
</div>
</div>
</app-page-section>
</form>

<app-page-footer [hasSecondaryAction]="true"
primaryActionLabel="Save and Continue"
(save)="onSubmit()"
(back)="onBack()"></app-page-footer>
</app-page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { MatSnackBarModule } from '@angular/material/snack-bar';

import { MockConfigService } from 'test/mocks/mock-config.service';

import { APP_CONFIG, APP_DI_CONFIG } from 'app/app-config.module';
import { ConfigService } from '@config/config.service';
import { CapitalizePipe } from '@shared/pipes/capitalize.pipe';
import { CareTypeVendorPageComponent } from './care-type-vendor-page.component';

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

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule,
HttpClientTestingModule,
ReactiveFormsModule,
MatSnackBarModule
],
declarations: [
CareTypeVendorPageComponent
],
providers: [
{
provide: APP_CONFIG,
useValue: APP_DI_CONFIG
},
{
provide: ConfigService,
useClass: MockConfigService
},
CapitalizePipe
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
.compileComponents();
}));

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

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@

import { AdjudicationRoutes } from '@adjudication/adjudication.routes';
import { Component, OnInit } from '@angular/core';
import { FormArray, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { VendorConfig } from '@config/config.model';
import { ConfigService } from '@config/config.service';
import { HealthAuthorityResource } from '@core/resources/health-authority-resource.service';
import { FormUtilsService } from '@core/services/form-utils.service';
import { ToastService } from '@core/services/toast.service';
import { HaCareTypeVendor } from '@lib/models/ha-care-type-vendor.model';
import { RouteUtils } from '@lib/utils/route-utils.class';
import { FormArrayValidators } from '@lib/validators/form-array.validators';
import { HealthAuthority } from '@shared/models/health-authority.model';
import { Subscription } from 'rxjs/internal/Subscription';

@Component({
selector: 'app-care-type-vendor-page',
templateUrl: './care-type-vendor-page.component.html',
styleUrls: ['./care-type-vendor-page.component.scss']
})
export class CareTypeVendorPageComponent implements OnInit {
public busy: Subscription;
public title: string;
public form: FormGroup;
public isInitialEntry: boolean;
public availableVendors: VendorConfig[];
public healthAuthority: HealthAuthority;

private routeUtils: RouteUtils;

constructor(
private fb: FormBuilder,
private healthAuthResource: HealthAuthorityResource,
private formUtilsService: FormUtilsService,
private configService: ConfigService,
private route: ActivatedRoute,
private toastService: ToastService,
router: Router
) {
this.title = route.snapshot.data.title;
this.isInitialEntry = !!this.route.snapshot.queryParams.initial;
this.routeUtils = new RouteUtils(route, router, [
AdjudicationRoutes.routePath(AdjudicationRoutes.SITE_REGISTRATIONS),
AdjudicationRoutes.SITE_REGISTRATIONS,
AdjudicationRoutes.HEALTH_AUTHORITIES,
this.route.snapshot.params.haid
]);
}

public get careTypeVendors(): FormArray {
return this.form.get('careTypeVendors') as FormArray;
}

public onSubmit() {
if (this.formUtilsService.checkValidity(this.form) || this.form.disabled) {

let haCareTypeVendors: HaCareTypeVendor[];

this.careTypeVendors.controls.forEach((c: FormControl) => {
let vendorCodeArray = c.get("vendorCodes") as FormArray;
let vendorCodes = vendorCodeArray.controls.map((v) => {
let haCareTypeVendor: HaCareTypeVendor = {
careType: c.get("careType").value,
vendorCode: v.get("vendorCode").value
}
return haCareTypeVendor;
});
if (!haCareTypeVendors) {
haCareTypeVendors = vendorCodes;
} else {
haCareTypeVendors = haCareTypeVendors.concat(vendorCodes);
}
});
this.busy = this.healthAuthResource.updateHealthAuthorityCareTypeVendor(this.route.snapshot.params.haid, haCareTypeVendors)
.subscribe(() => this.nextRouteAfterSubmit());
}
}

public ngOnInit(): void {
this.createFormInstance();
this.initForm();
}

private createFormInstance() {
this.form = this.fb.group({
careTypeVendors: this.fb.array([])
});
}

private createCareTypeFormControl() {
return this.fb.group({
careType: [],
vendorCodes: this.fb.array([]),
});
}

private initForm() {
this.busy = this.healthAuthResource.getHealthAuthorityById(this.route.snapshot.params.haid)
.subscribe((healthAuthority) => {
this.healthAuthority = healthAuthority;
this.availableVendors = this.configService.vendors.filter(v => healthAuthority.vendors.some(hav => hav.vendorCode === v.code));

if (healthAuthority.careTypes?.length) {
healthAuthority.careTypes.forEach((careType) => {
let careTypeVendor = this.createCareTypeFormControl();
careTypeVendor.get('careType').patchValue(careType.careType);
careType.vendors.forEach((vendor) => {
let vendorArray = careTypeVendor.get('vendorCodes') as FormArray;
vendorArray.addValidators(FormArrayValidators.noDuplicateValue('vendorCode'));
vendorArray.push(this.fb.group({ vendorCode: [vendor.vendorCode, [Validators.required]] }));
});
this.careTypeVendors.push(careTypeVendor);
})
}
}
);
}

public addVendor(caretypeIndex: number) {
var vendorArray = this.careTypeVendors.controls[caretypeIndex].get('vendorCodes') as FormArray;
vendorArray.push(this.fb.group({ vendorCode: [null, [Validators.required]] }));
}

public removeVendor(caretypeIndex: number, vendorIndex: number) {
var vendorArray = this.careTypeVendors.controls[caretypeIndex].get('vendorCodes') as FormArray;
vendorArray.removeAt(vendorIndex);
}

public onBack() {
this.routeTo();
}

private nextRouteAfterSubmit() {
this.routeTo(AdjudicationRoutes.HEALTH_AUTH_CARE_TYPE_VENDOR);
}

private routeTo(routeSegment?: string) {
const routePath = (this.isInitialEntry && routeSegment)
? routeSegment
: AdjudicationRoutes.ORGANIZATION_INFORMATION;
this.routeUtils.routeRelativeTo(routePath, { queryParamsHandling: 'preserve' });
}

public isRequired(careTypeIndex: number, vendorIndex: number) {
var careTypeControl = this.careTypeVendors.controls[careTypeIndex];
var vendorCodes = careTypeControl.get("vendorCodes") as FormArray;
return vendorCodes.controls[vendorIndex].get("vendorCode").hasError('required');
}

public hasDuplicate(careTypeIndex: number) {
var careTypeControl = this.careTypeVendors.controls[careTypeIndex];
var vendorCodes = careTypeControl.get("vendorCodes") as FormArray;
return vendorCodes.hasError('duplicate');
}
}
Loading