diff --git a/src/app/pages/user-registration/user-registration-form/user-registration-form.component.ts b/src/app/pages/user-registration/user-registration-form/user-registration-form.component.ts new file mode 100644 index 00000000..850f6fe4 --- /dev/null +++ b/src/app/pages/user-registration/user-registration-form/user-registration-form.component.ts @@ -0,0 +1,228 @@ +/* +Copyright 2022-2023 University of Oxford +and Health and Social Care Information Centre, also known as NHS Digital + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +SPDX-License-Identifier: Apache-2.0 +*/ +/* eslint-disable @typescript-eslint/unbound-method */ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { MatFormFieldAppearance } from '@angular/material/form-field'; +import { DisplayValuePair } from '@maurodatamapper/sde-resources'; +import { ToastrService } from 'ngx-toastr'; +import { StateRouterService } from 'src/app/core/state-router.service'; +import { DialogService } from 'src/app/data-explorer/dialog.service'; + +export interface UserRegistrationFormData { + firstName: string; + lastName: string; + email: string; + phoneNumber: string; + jobTitle: string; + orcidId: string; + gmcNumber: string; + nmcNumber: string; + hcpcNumber: string; + arNumber: string; + organisationId: string; + organisationName: string; + organisationLegalName: string; + organisationWebsite: string; + organisationCountryOfOrigin: string; + organisationType: OrganisationType; + organisationIsSmb: boolean; + departmentId: string; + departmentName: string; +} + +export type OrganisationType = 'NOT_SELECTED' | 'UNIVERSITY' | 'HEALTHTECH'; +export interface OrganisationTypeOption { + value: OrganisationType; + displayName: 'University' | 'Healthcare technology company'; +} + +export const ORGANISATION_TYPE_OPTIONS: OrganisationTypeOption[] = [ + { value: 'UNIVERSITY', displayName: 'University' }, + { value: 'HEALTHTECH', displayName: 'Healthcare technology company' }, +]; + +@Component({ + selector: 'mdm-user-registration-form', + templateUrl: './user-registration-form.component.html', + styleUrls: ['./user-registration-form.component.scss'], +}) +export class UserRegistrationFormComponent { + @Input() organisationOptions: DisplayValuePair[] = []; + @Output() formSubmitted = new EventEmitter(); + + formFieldAppearance: MatFormFieldAppearance = 'outline'; + isCreatingNewOrganisation = false; + + organisationTypeOptions = ORGANISATION_TYPE_OPTIONS; + + registrationForm: FormGroup = this.formBuilder.group({ + personalDetails: this.formBuilder.group({ + firstName: ['', Validators.required], + lastName: ['', Validators.required], + email: ['', Validators.compose([Validators.required, Validators.email])], + phoneNumber: ['', Validators.pattern('^[- +()0-9]+$')], + jobTitle: ['', Validators.required], + orcidId: null, + gmcNumber: null, + nmcNumber: null, + hcpcNumber: null, + arNumber: null, + + // TODO: Unconfirmed field. + // confirmations: false, + }), + organisationDetails: this.formBuilder.group({ + organisation: ['', Validators.required], + organisationName: null, + organisationLegalName: null, + organisationWebsite: null, + organisationCountryOfOrigin: null, + organisationType: null, + organisationIsSmb: null, + }), + departmentDetails: this.formBuilder.group({ + department: ['', Validators.required], + departmentName: null, + }), + }); + + mockedDepartments: DisplayValuePair[] = [ + { displayValue: 'HR', value: 'hr' }, + { displayValue: 'Engineering', value: 'engineering' }, + { displayValue: 'Marketing', value: 'marketing' }, + ]; + + constructor( + private formBuilder: FormBuilder, + private toastr: ToastrService, + private dialogService: DialogService, + private stateRouter: StateRouterService + ) {} + + get personalDetails() { + return (this.registrationForm.controls.personalDetails as FormGroup).controls; + } + + get organisationDetails() { + return (this.registrationForm.controls.organisationDetails as FormGroup).controls; + } + + get departmentDetails() { + return (this.registrationForm.controls.departmentDetails as FormGroup).controls; + } + + submit(): void { + if (this.registrationForm.invalid) { + this.toastr.info('Please fill in all required fields'); + this.logValidationErrors(this.registrationForm); + return; + } + + // Gather form data and emit. + const formData = { + ...this.registrationForm.get('personalDetails')?.value, + ...this.registrationForm.get('organisationDetails')?.value, + ...this.registrationForm.get('departmentDetails')?.value, + } as UserRegistrationFormData; + + this.formSubmitted.emit(formData); + + // Show success dialog and navigate to home page. + this.dialogService + .openSuccess({ + heading: 'Form submission successful', + message: 'User registration complete. Please check your email for further instructions.', + }) + .afterClosed() + .subscribe(() => { + this.stateRouter.navigateTo(['/']); + }); + } + + logValidationErrors(group: FormGroup): void { + Object.keys(group.controls).forEach((key: string) => { + const control = group.get(key); + if (control instanceof FormGroup) { + this.logValidationErrors(control); + } else { + if (control && control.invalid) { + console.log(`Control: ${key}, Errors:`, control.errors); + } + } + }); + } + + toggleOrganisationCreation(value: boolean) { + this.isCreatingNewOrganisation = value; + if (this.isCreatingNewOrganisation) { + this.registrationForm.get('organisationDetails.organisation')?.clearValidators(); + this.registrationForm.get('departmentDetails.department')?.clearValidators(); + this.registrationForm + .get('organisationDetails.organisationName') + ?.setValidators(Validators.required); + this.registrationForm + .get('organisationDetails.organisationLegalName') + ?.setValidators(Validators.required); + this.registrationForm + .get('organisationDetails.organisationWebsite') + ?.setValidators(Validators.required); + this.registrationForm + .get('organisationDetails.organisationCountryOfOrigin') + ?.setValidators(Validators.required); + this.registrationForm + .get('organisationDetails.organisationType') + ?.setValidators(Validators.required); + this.registrationForm + .get('organisationDetails.organisationIsSmb') + ?.setValidators(Validators.required); + this.registrationForm + .get('departmentDetails.departmentName') + ?.setValidators(Validators.required); + } else { + this.registrationForm + .get('organisationDetails.organisation') + ?.setValidators(Validators.required); + this.registrationForm.get('organisationDetails.organisationName')?.clearValidators(); + this.registrationForm.get('organisationDetails.organisationLegalName')?.clearValidators(); + this.registrationForm.get('organisationDetails.organisationWebsite')?.clearValidators(); + this.registrationForm + .get('organisationDetails.organisationCountryOfOrigin') + ?.clearValidators(); + this.registrationForm.get('organisationDetails.organisationType')?.clearValidators(); + this.registrationForm.get('organisationDetails.organisationIsSmb')?.clearValidators(); + this.registrationForm.get('departmentDetails.department')?.setValidators(Validators.required); + this.registrationForm.get('departmentDetails.departmentName')?.clearValidators(); + } + + this.registrationForm.get('organisationDetails.organisation')?.updateValueAndValidity(); + this.registrationForm.get('organisationDetails.organisationName')?.updateValueAndValidity(); + this.registrationForm + .get('organisationDetails.organisationLegalName') + ?.updateValueAndValidity(); + this.registrationForm.get('organisationDetails.organisationWebsite')?.updateValueAndValidity(); + this.registrationForm + .get('organisationDetails.organisationCountryOfOrigin') + ?.updateValueAndValidity(); + this.registrationForm.get('organisationDetails.organisationType')?.updateValueAndValidity(); + this.registrationForm.get('organisationDetails.organisationIsSmb')?.updateValueAndValidity(); + this.registrationForm.get('departmentDetails.department')?.updateValueAndValidity(); + this.registrationForm.get('departmentDetails.departmentName')?.updateValueAndValidity(); + } +} diff --git a/src/app/secure-data-environment/pages/departments/departments.component.html b/src/app/secure-data-environment/pages/departments/departments.component.html index c394f532..c10f42e2 100644 --- a/src/app/secure-data-environment/pages/departments/departments.component.html +++ b/src/app/secure-data-environment/pages/departments/departments.component.html @@ -26,6 +26,10 @@

+ +
diff --git a/src/app/secure-data-environment/pages/departments/departments.component.ts b/src/app/secure-data-environment/pages/departments/departments.component.ts index 3ef19701..951b9d5c 100644 --- a/src/app/secure-data-environment/pages/departments/departments.component.ts +++ b/src/app/secure-data-environment/pages/departments/departments.component.ts @@ -53,7 +53,12 @@ export class DepartmentsComponent implements OnInit { this.sdeDepartmentService .getUsersDepartments() .pipe( + // NOTE: Use a forkJoin to retrieve the users organisation information too. Alternatively, + // create a simple organisation card component. switchMap((userDepts: UserDepartmentDTO[]) => { + // NOTE: It is no longer necessary that a user have a department membership. However, + // they will always have an organisation membership. + // // Theoretically, a user should always have an department. If they don't, trigger // a flag to show a message to the user. if (userDepts.length === 0) {