diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/angular.json b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/angular.json index ffbd386457..d3bf4a0f83 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/angular.json +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/angular.json @@ -1,118 +1,110 @@ { - "$schema": "./node_modules/@angular/cli/lib/config/schema.json", - "version": 1, - "newProjectRoot": "projects", - "projects": { - "ClientApp": { - "projectType": "application", - "schematics": { - "@schematics/angular:component": { - "style": "scss", - "inlineStyle": true, - "inlineTemplate": true, - "flat": true, - "skipTests": true - } - }, - "root": "", - "sourceRoot": "src", - "prefix": "app", - "architect": { - "build": { - "builder": "@angular-devkit/build-angular:application", - "options": { + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "ClientApp": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss", + "inlineStyle": true, + "inlineTemplate": true, + "flat": true, + "skipTests": true + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:application", + "options": { "outputPath": { "base": "dist", "browser": "" }, - "index": "src/index.html", - "browser": "src/main.ts", - "polyfills": ["zone.js"], - "tsConfig": "tsconfig.app.json", - "inlineStyleLanguage": "scss", + "index": "src/index.html", + "browser": "src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", "assets": ["src/favicon.ico", "src/assets", "src/maintenance"], - "styles": [ - "@angular/material/prebuilt-themes/azure-blue.css", - "node_modules/bootstrap/dist/css/bootstrap.min.css", - "node_modules/ngx-spinner/animations/square-jelly-box.css", - "node_modules/@ngxpert/hot-toast/src/styles/styles.css", - "src/scss/app.scss" - ], - "scripts": [ - "node_modules/jquery/dist/jquery.min.js", - "node_modules/bootstrap/dist/js/bootstrap.min.js" - ], - "allowedCommonJsDependencies": ["moment"] - }, - "configurations": { - "production": { - "budgets": [ - { - "type": "initial", - "maximumWarning": "500kB", - "maximumError": "5mb" - }, - { - "type": "anyComponentStyle", - "maximumWarning": "4kB", - "maximumError": "8kB" - } - ], - "outputHashing": "all", - "baseHref": "/guide-dog-service-dog/" - }, - "development": { - "optimization": false, - "extractLicenses": false, - "sourceMap": true - } - }, - "defaultConfiguration": "production" - }, - "serve": { - "builder": "@angular-devkit/build-angular:dev-server", - "configurations": { - "production": { - "buildTarget": "ClientApp:build:production" - }, - "development": { - "buildTarget": "ClientApp:build:development" - } - }, - "defaultConfiguration": "development" - }, - "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n" - }, - "test": { - "builder": "@angular-devkit/build-angular:karma", - "options": { - "polyfills": ["zone.js", "zone.js/testing"], - "tsConfig": "tsconfig.spec.json", + "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", + "node_modules/bootstrap/dist/css/bootstrap.min.css", + "node_modules/ngx-spinner/animations/square-jelly-box.css", + "src/scss/app.scss" + ], + "scripts": ["node_modules/jquery/dist/jquery.min.js", "node_modules/bootstrap/dist/js/bootstrap.min.js"], + "allowedCommonJsDependencies": ["moment"] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kB", + "maximumError": "5mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "4kB", + "maximumError": "8kB" + } + ], + "outputHashing": "all", + "baseHref": "/guide-dog-service-dog/" + }, + "development": { + "optimization": false, + "extractLicenses": false, + "sourceMap": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "buildTarget": "ClientApp:build:production" + }, + "development": { + "buildTarget": "ClientApp:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": ["zone.js", "zone.js/testing"], + "tsConfig": "tsconfig.spec.json", "assets": ["src/favicon.ico", "src/assets", "src/maintenance"], - "styles": [ - "@angular/material/prebuilt-themes/azure-blue.css", - "node_modules/bootstrap/dist/css/bootstrap.min.css", - "node_modules/ngx-spinner/animations/square-jelly-box.css", - "node_modules/@ngxpert/hot-toast/src/styles/styles.css", - "src/scss/app.scss" - ], - "scripts": [ - "node_modules/jquery/dist/jquery.min.js", - "node_modules/bootstrap/dist/js/bootstrap.min.js" - ] - } - }, - "lint": { - "builder": "@angular-eslint/builder:lint", - "options": { - "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"] - } - } - } - } - }, - "cli": { - "analytics": false - } + "styles": [ + "@angular/material/prebuilt-themes/azure-blue.css", + "node_modules/bootstrap/dist/css/bootstrap.min.css", + "node_modules/ngx-spinner/animations/square-jelly-box.css", + "src/scss/app.scss" + ], + "scripts": ["node_modules/jquery/dist/jquery.min.js", "node_modules/bootstrap/dist/js/bootstrap.min.js"] + } + }, + "lint": { + "builder": "@angular-eslint/builder:lint", + "options": { + "lintFilePatterns": ["src/**/*.ts", "src/**/*.html"] + } + } + } + } + }, + "cli": { + "analytics": false + } } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/package-lock.json b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/package-lock.json index 9c0a83d8b7..b129a1493a 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/package-lock.json +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/package-lock.json @@ -22,7 +22,6 @@ "@bcgov/bc-sans": "^2.1.0", "@ngneat/overview": "^6.1.1", "@ngneat/until-destroy": "^10.0.0", - "@ngxpert/hot-toast": "^4.1.2", "angular-oauth2-oidc": "^19.0.0", "bootstrap": "^5.3.5", "jquery": "^3.7.0", @@ -4440,20 +4439,6 @@ "webpack": "^5.54.0" } }, - "node_modules/@ngxpert/hot-toast": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@ngxpert/hot-toast/-/hot-toast-4.2.0.tgz", - "integrity": "sha512-sFWJedVAS0OIDAF58DVMuE2z9JF0rN6cJuRGeGKEBRJtDtnd2vtFLIIlTtte7HJeFI8UBvbYqIMWA7UELi1/Mw==", - "license": "MIT", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": ">= 19.0.0", - "@angular/core": ">= 19.0.0", - "@ngneat/overview": "6.1.1" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/package.json b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/package.json index a4dc1dd07b..e35e0ae7e6 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/package.json +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/package.json @@ -29,7 +29,6 @@ "@bcgov/bc-sans": "^2.1.0", "@ngneat/overview": "^6.1.1", "@ngneat/until-destroy": "^10.0.0", - "@ngxpert/hot-toast": "^4.1.2", "angular-oauth2-oidc": "^19.0.0", "bootstrap": "^5.3.5", "jquery": "^3.7.0", diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/app.module.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/app.module.ts index 790ca64a79..12f31b70e8 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/app.module.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/app.module.ts @@ -4,7 +4,6 @@ import { NgModule } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { provideHotToastConfig } from '@ngxpert/hot-toast'; import { OAuthModule } from 'angular-oauth2-oidc'; import { NgxSpinnerModule } from 'ngx-spinner'; import { AppRoutingModule } from './app-routing.module'; @@ -246,7 +245,6 @@ import { SharedModule } from './shared/shared.module'; SharedModule, ], providers: [ - provideHotToastConfig(), { provide: APP_BASE_HREF, useFactory: (location: PlatformLocation) => location.getBaseHrefFromDOM(), diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/dog-trainer/step-dt-dog-trainer-info.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/dog-trainer/step-dt-dog-trainer-info.component.ts index 86f602048c..4d2668af0a 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/dog-trainer/step-dt-dog-trainer-info.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/dog-trainer/step-dt-dog-trainer-info.component.ts @@ -38,26 +38,22 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m Date of Birth - - Date format YYYY-MM-DD Date format YYYY-MM-DD This is required - - Invalid date of birth - - - This must be on or before {{ maxBirthDate | formatDate }} - + This date is invalid + This date cannot be in the future
@@ -104,8 +100,7 @@ export class StepDtDogTrainerInfoComponent implements OnInit, LicenceChildSteppe applicationTypeCodes = ApplicationTypeCode; matcher = new FormErrorStateMatcher(); - maxBirthDate = this.utilService.getBirthDateMax(); - minDate = this.utilService.getDateMin(); + dateMask = SPD_CONSTANTS.date.dateMask; phoneMask = SPD_CONSTANTS.phone.displayMask; form: FormGroup = this.dogTrainerApplicationService.dogTrainerFormGroup; @@ -127,13 +122,20 @@ export class StepDtDogTrainerInfoComponent implements OnInit, LicenceChildSteppe return this.form.valid; } + onValidateDate(): void { + const errorKey = this.utilService.getIsInputValidDate(this.trainerDateOfBirth.value); + if (errorKey) { + this.trainerDateOfBirth.setErrors({ [errorKey]: true }); + } + } + get isRenewal(): boolean { return this.applicationTypeCode === ApplicationTypeCode.Renewal; } get showHintError(): boolean { return (this.trainerDateOfBirth?.dirty || this.trainerDateOfBirth?.touched) && this.trainerDateOfBirth?.invalid; } - public get trainerDateOfBirth(): FormControl { + get trainerDateOfBirth(): FormControl { return this.form.get('trainerDateOfBirth') as FormControl; } } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/dog-trainer/step-dt-training-school-info.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/dog-trainer/step-dt-training-school-info.component.ts index 79c4276b17..4cfe3b2010 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/dog-trainer/step-dt-training-school-info.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/dog-trainer/step-dt-training-school-info.component.ts @@ -12,7 +12,7 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m
-
+
- + - + - + - + { + const matchingIndex = this.getMatchingIndex(this.STEP_REVIEW_AND_CONFIRM); setTimeout(() => { // hack... does not navigate without the timeout - this.stepper.selectedIndex = this.getMatchingIndex(this.STEP_REVIEW_AND_CONFIRM); + this.stepper.selectedIndex = matchingIndex; }, 250); }, error: (error: HttpErrorResponse) => { diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-other-trainings.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-other-trainings.component.ts index 0a393bcc78..597bb333fb 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-other-trainings.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-other-trainings.component.ts @@ -300,10 +300,10 @@ export class StepTeamOtherTrainingsComponent implements LicenceChildStepperStepC get otherTrainingsArray(): FormArray { return this.form.get('otherTrainings'); } - public get attachments(): FormControl { + get attachments(): FormControl { return this.form.get('attachments') as FormControl; } - public get practiceLogAttachments(): FormControl { + get practiceLogAttachments(): FormControl { return this.form.get('practiceLogAttachments') as FormControl; } } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-school-trainings.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-school-trainings.component.ts index 412981debb..b84657e991 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-school-trainings.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-school-trainings.component.ts @@ -10,7 +10,6 @@ import { LicenceChildStepperStepComponent, UtilService } from '@app/core/service import { DialogComponent, DialogOptions } from '@app/shared/components/dialog.component'; import { FileUploadComponent } from '@app/shared/components/file-upload.component'; import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-matcher.directive'; -import moment from 'moment'; @Component({ selector: 'app-step-team-school-trainings', @@ -40,10 +39,7 @@ import moment from 'moment';
Training School Contact Information
-
+
Training Start Date must be on or before the Training End Date @@ -94,29 +90,28 @@ import moment from 'moment';
-
Training Start Date - - + + Date format YYYY-MM-DD This is required - - Invalid date of birth - - - This must be on or before {{ maxDate | formatDate }} - + This date is invalid + This date cannot be in the future
@@ -124,21 +119,21 @@ import moment from 'moment'; Training End Date - - + + Date format YYYY-MM-DD This is required - - Invalid date of birth - - - This must be on or before {{ maxDate | formatDate }} - + This date is invalid + This date cannot be in the future
@@ -250,10 +245,11 @@ import moment from 'moment'; export class StepTeamSchoolTrainingsComponent implements LicenceChildStepperStepComponent { form: FormGroup = this.gdsdTeamApplicationService.schoolTrainingHistoryFormGroup; + invalidDateRange = false; + booleanTypeCodes = BooleanTypeCode; matcher = new FormErrorStateMatcher(); - maxDate = moment(); - minDate = this.utilService.getDateMin(); + dateMask = SPD_CONSTANTS.date.dateMask; phoneMask = SPD_CONSTANTS.phone.displayMask; @ViewChild(FileUploadComponent) fileUploadComponent!: FileUploadComponent; @@ -264,6 +260,46 @@ export class StepTeamSchoolTrainingsComponent implements LicenceChildStepperStep private gdsdTeamApplicationService: GdsdTeamApplicationService ) {} + onValidateStartDate(index: number): void { + const schoolTrainingsArray = this.form.get('schoolTrainings') as FormArray; + const form = schoolTrainingsArray.at(index); + + const dateFormControl = form.get('trainingStartDate'); + const errorKey = this.utilService.getIsInputValidDate(dateFormControl?.value); + if (errorKey) { + dateFormControl?.setErrors({ [errorKey]: true }); + return; + } + + const dateEndFormControl = form.get('trainingEndDate'); + if (dateFormControl?.value && dateEndFormControl?.value) { + this.invalidDateRange = !this.utilService.getIsInputValidDateRange( + dateFormControl?.value, + dateEndFormControl?.value + ); + } + } + + onValidateEndDate(index: number): void { + const schoolTrainingsArray = this.form.get('schoolTrainings') as FormArray; + const form = schoolTrainingsArray.at(index); + + const dateFormControl = form.get('trainingEndDate'); + const errorKey = this.utilService.getIsInputValidDate(dateFormControl?.value); + if (errorKey) { + dateFormControl?.setErrors({ [errorKey]: true }); + return; + } + + const dateStartFormControl = form.get('trainingStartDate'); + if (dateStartFormControl?.value && dateFormControl?.value) { + this.invalidDateRange = !this.utilService.getIsInputValidDateRange( + dateStartFormControl?.value, + dateFormControl?.value + ); + } + } + onRemoveSchoolTrainingRow(index: number) { const data: DialogOptions = { icon: 'warning', @@ -316,7 +352,7 @@ export class StepTeamSchoolTrainingsComponent implements LicenceChildStepperStep get schoolTrainingsArray(): FormArray { return this.form.get('schoolTrainings'); } - public get attachments(): FormControl { + get attachments(): FormControl { return this.form.get('attachments') as FormControl; } } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-training-history.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-training-history.component.ts index 7c385a515f..8512d0ee9c 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-training-history.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/gdsd-team/step-team-training-history.component.ts @@ -68,15 +68,13 @@ export class StepTeamTrainingHistoryComponent implements LicenceChildStepperStep this.logattachments.setValue([]); } - public get schoolattachments(): FormControl { + get schoolattachments(): FormControl { return this.gdsdTeamApplicationService.schoolTrainingHistoryFormGroup.get('attachments') as FormControl; } - - public get otherattachments(): FormControl { + get otherattachments(): FormControl { return this.gdsdTeamApplicationService.otherTrainingHistoryFormGroup.get('attachments') as FormControl; } - - public get logattachments(): FormControl { + get logattachments(): FormControl { return this.gdsdTeamApplicationService.otherTrainingHistoryFormGroup.get('practiceLogAttachments') as FormControl; } } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/retired-dog/step-rd-dog-living-info.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/retired-dog/step-rd-dog-living-info.component.ts index 3a0fc886fa..2ea814053b 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/retired-dog/step-rd-dog-living-info.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/retired-dog/step-rd-dog-living-info.component.ts @@ -3,8 +3,7 @@ import { FormGroup } from '@angular/forms'; import { ApplicationTypeCode } from '@app/api/models'; import { BooleanTypeCode } from '@app/core/code-types/model-desc.models'; import { RetiredDogApplicationService } from '@app/core/services/retired-dog-application.service'; -import { LicenceChildStepperStepComponent, UtilService } from '@app/core/services/util.service'; -import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-matcher.directive'; +import { LicenceChildStepperStepComponent } from '@app/core/services/util.service'; @Component({ selector: 'app-step-rd-dog-living-info', @@ -40,19 +39,11 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m export class StepRdDogLivingInfoComponent implements LicenceChildStepperStepComponent { booleanTypeCodes = BooleanTypeCode; - matcher = new FormErrorStateMatcher(); - form: FormGroup = this.retiredDogApplicationService.dogLivingForm; - maxToday = this.utilService.getToday(); - minDate = this.utilService.getDogDateMin(); - @Input() applicationTypeCode!: ApplicationTypeCode; - constructor( - private utilService: UtilService, - private retiredDogApplicationService: RetiredDogApplicationService - ) {} + constructor(private retiredDogApplicationService: RetiredDogApplicationService) {} isFormValid(): boolean { this.form.markAllAsTouched(); diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/retired-dog/step-rd-dog-retired-info.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/retired-dog/step-rd-dog-retired-info.component.ts index 74b59955e7..356261336e 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/retired-dog/step-rd-dog-retired-info.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/retired-dog/step-rd-dog-retired-info.component.ts @@ -2,6 +2,7 @@ import { Component, Input } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { ApplicationTypeCode } from '@app/api/models'; import { BooleanTypeCode } from '@app/core/code-types/model-desc.models'; +import { SPD_CONSTANTS } from '@app/core/constants/constants'; import { RetiredDogApplicationService } from '@app/core/services/retired-dog-application.service'; import { LicenceChildStepperStepComponent, UtilService } from '@app/core/services/util.service'; import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-matcher.directive'; @@ -17,24 +18,20 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m Date of Retirement - - Date format YYYY-MM-DD Date format YYYY-MM-DD This is required - Invalid date of retirement - - This must be on or before {{ maxToday | formatDate }} - + This date is invalid + This date cannot be in the future
@@ -58,8 +55,7 @@ export class StepRdDogRetiredInfoComponent implements LicenceChildStepperStepCom form: FormGroup = this.retiredDogApplicationService.dogRetiredForm; - maxToday = this.utilService.getToday(); - minDate = this.utilService.getDogDateMin(); + dateMask = SPD_CONSTANTS.date.dateMask; @Input() applicationTypeCode!: ApplicationTypeCode; @@ -68,6 +64,13 @@ export class StepRdDogRetiredInfoComponent implements LicenceChildStepperStepCom private retiredDogApplicationService: RetiredDogApplicationService ) {} + onValidateDate(): void { + const errorKey = this.utilService.getIsInputValidDate(this.dogRetiredDate.value); + if (errorKey) { + this.dogRetiredDate.setErrors({ [errorKey]: true }); + } + } + isFormValid(): boolean { this.form.markAllAsTouched(); return this.form.valid; @@ -79,7 +82,7 @@ export class StepRdDogRetiredInfoComponent implements LicenceChildStepperStepCom get showHintError(): boolean { return (this.dogRetiredDate?.dirty || this.dogRetiredDate?.touched) && this.dogRetiredDate?.invalid; } - public get dogRetiredDate(): FormControl { + get dogRetiredDate(): FormControl { return this.form.get('dogRetiredDate') as FormControl; } } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-accredited-school.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-accredited-school.component.ts index 88aa7a0cff..97db70e2a6 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-accredited-school.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-accredited-school.component.ts @@ -1,42 +1,35 @@ import { Component, Input, OnInit } from '@angular/core'; import { FormControl } from '@angular/forms'; -import { MatSelectChange } from '@angular/material/select'; import { ApplicationTypeCode } from '@app/api/models'; import { SPD_CONSTANTS } from '@app/core/constants/constants'; import { ConfigService, DogSchoolResponseExt } from '@app/core/services/config.service'; import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-matcher.directive'; +import { map, Observable, startWith } from 'rxjs'; @Component({ selector: 'app-form-gdsd-accredited-school', template: `
{{ schoolLabel }}
- School Name - - - {{ selectedSchool?.schoolName }} - - - -
-
{{ item.schoolName }}
-
{{ item.schoolAddress }}
-
+ School Name + + + +
{{ field.schoolName }}
+
{{ field.schoolAddress }}
-
- This is required + + search + Start typing name of school or address + This is required
- +
If your school is not in the list, please contact the Security Licencing Unit at {{ spdPhoneNumber }} during regular office hours. - +
`, styles: [ ` @@ -61,6 +54,7 @@ export class FormGdsdAccreditedSchoolComponent implements OnInit { @Input() applicationTypeCode!: ApplicationTypeCode; accreditedDogSchools = this.configService.accreditedDogSchools; + filteredOptions!: Observable; constructor(private configService: ConfigService) {} @@ -70,16 +64,55 @@ export class FormGdsdAccreditedSchoolComponent implements OnInit { } else { this.accreditedSchoolIdControl.enable({ emitEvent: false }); } + + this.filteredOptions = this.accreditedSchoolIdControl.valueChanges.pipe( + startWith(''), + map((value) => { + const searchValue = + typeof value === 'number' || typeof value === 'string' ? this.getName(value) || value : value; + return this._filterAccreditedSchool(searchValue?.toString()); + }) + ); + } + + onSchoolIdBlur(): void { + const inputText = this.displayFn(this.accreditedSchoolIdControl.value)?.trim(); + const matchedSchool = this.accreditedDogSchools?.find( + (s) => s.schoolName?.toLowerCase() === inputText.toLowerCase() + ); + if (matchedSchool) { + this.accreditedSchoolIdControl.setValue(matchedSchool.schoolId); + this.accreditedSchoolNameControl.setValue(matchedSchool.schoolName); + } else { + this.accreditedSchoolIdControl.setValue(null); + this.accreditedSchoolNameControl.setValue(null); + } } - get selectedSchool(): DogSchoolResponseExt | null | undefined { - return this.accreditedSchoolIdControl.value - ? this.accreditedDogSchools?.find((school) => school.schoolId === this.accreditedSchoolIdControl.value) - : null; + getName(id: number | string): string { + if (typeof id !== 'number' && typeof id !== 'string') return ''; + + const match = this.accreditedDogSchools?.find((o) => o.schoolId == id); + return match ? match.schoolName! : ''; } - onSchoolChange(_event: MatSelectChange): void { - this.accreditedSchoolNameControl.setValue(this.selectedSchool?.schoolName); + displayFn = (id: number): string => { + return this.getName(id); + }; + + private _filterAccreditedSchool(value: string): DogSchoolResponseExt[] { + if (!value) { + return this.accreditedDogSchools!; + } + + const filterValue = value.toLowerCase(); + return ( + this.accreditedDogSchools?.filter( + (option: DogSchoolResponseExt) => + option.schoolName?.toLowerCase().includes(filterValue) || + option.schoolAddress?.toLowerCase().includes(filterValue) + ) ?? [] + ); } get isRenewal(): boolean { diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-dog-info.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-dog-info.component.ts index 29a0f462f1..af56e6679d 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-dog-info.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-dog-info.component.ts @@ -2,6 +2,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { ApplicationTypeCode } from '@app/api/models'; import { DogGenderTypes, SelectOptions } from '@app/core/code-types/model-desc.models'; +import { SPD_CONSTANTS } from '@app/core/constants/constants'; import { UtilService } from '@app/core/services/util.service'; import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-matcher.directive'; @@ -24,24 +25,20 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m Date of Birth - - Date format YYYY-MM-DD Date format YYYY-MM-DD This is required - Invalid date of birth - - This must be on or before {{ maxBirthDate | formatDate }} - + This date is invalid + This date cannot be in the future
@@ -87,8 +84,7 @@ export class FormGdsdDogInfoComponent implements OnInit { genderMfTypes: SelectOptions[] = DogGenderTypes; matcher = new FormErrorStateMatcher(); - maxBirthDate = this.utilService.getDogBirthDateMax(); - minDate = this.utilService.getDogDateMin(); + dateMask = SPD_CONSTANTS.date.dateMask; @Input() form!: FormGroup; @Input() applicationTypeCode!: ApplicationTypeCode; @@ -103,13 +99,20 @@ export class FormGdsdDogInfoComponent implements OnInit { } } + onValidateDate(): void { + const errorKey = this.utilService.getIsInputValidDate(this.dogDateOfBirth.value); + if (errorKey) { + this.dogDateOfBirth.setErrors({ [errorKey]: true }); + } + } + get isNew(): boolean { return this.applicationTypeCode === ApplicationTypeCode.New; } get showHintError(): boolean { return (this.dogDateOfBirth?.dirty || this.dogDateOfBirth?.touched) && this.dogDateOfBirth?.invalid; } - public get dogDateOfBirth(): FormControl { + get dogDateOfBirth(): FormControl { return this.form.get('dogDateOfBirth') as FormControl; } } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-goverment-photo-id.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-goverment-photo-id.component.ts index 078faec293..d91ad0b28c 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-goverment-photo-id.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-goverment-photo-id.component.ts @@ -3,6 +3,7 @@ import { FormControl, FormGroup } from '@angular/forms'; import { MatSelectChange } from '@angular/material/select'; import { showHideTriggerSlideAnimation } from '@app/core/animations'; import { GovernmentIssuedPhotoIdTypes } from '@app/core/code-types/model-desc.models'; +import { SPD_CONSTANTS } from '@app/core/constants/constants'; import { UtilService } from '@app/core/services/util.service'; import { FileUploadComponent } from '@app/shared/components/file-upload.component'; import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-matcher.directive'; @@ -31,25 +32,25 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m
-
+
Document Expiry Date - - Date format YYYY-MM-DD Date format YYYY-MM-DD - This is required - Invalid expiry date + This is required + This date is invalid + This date cannot be in the future
@@ -85,8 +86,8 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m export class FormGdsdGovermentPhotoIdComponent { governmentIssuedPhotoIdTypes = GovernmentIssuedPhotoIdTypes; - minDate = this.utilService.getDateMin(); matcher = new FormErrorStateMatcher(); + dateMask = SPD_CONSTANTS.date.dateMask; @Input() form!: FormGroup; @@ -97,6 +98,13 @@ export class FormGdsdGovermentPhotoIdComponent { constructor(private utilService: UtilService) {} + onValidateDate(): void { + const errorKey = this.utilService.getIsInputValidDate(this.expiryDate.value); + if (errorKey) { + this.expiryDate.setErrors({ [errorKey]: true }); + } + } + onFileUploaded(file: File): void { this.fileUploaded.emit(file); } @@ -118,7 +126,7 @@ export class FormGdsdGovermentPhotoIdComponent { get showHintError(): boolean { return (this.expiryDate?.dirty || this.expiryDate?.touched) && this.expiryDate?.invalid; } - public get expiryDate(): FormControl { + get expiryDate(): FormControl { return this.form.get('expiryDate') as FormControl; } } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-personal-info-anonymous.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-personal-info-anonymous.component.ts index 6a39f2d360..5ce967a166 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-personal-info-anonymous.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-personal-info-anonymous.component.ts @@ -36,24 +36,20 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m Date of Birth - - Date format YYYY-MM-DD Date format YYYY-MM-DD This is required - Invalid date of birth - - This must be on or before {{ maxBirthDate | formatDate }} - + This date is invalid + This date cannot be in the future
@@ -96,22 +92,28 @@ export class FormGdsdPersonalInfoAnonymousComponent { applicationTypeCodes = ApplicationTypeCode; matcher = new FormErrorStateMatcher(); - maxBirthDate = this.utilService.getBirthDateMax(); - minDate = this.utilService.getDateMin(); phoneMask = SPD_CONSTANTS.phone.displayMask; + dateMask = SPD_CONSTANTS.date.dateMask; @Input() form!: FormGroup; @Input() applicationTypeCode!: ApplicationTypeCode; constructor(private utilService: UtilService) {} + onValidateDate(): void { + const errorKey = this.utilService.getIsInputValidDate(this.dateOfBirth.value); + if (errorKey) { + this.dateOfBirth.setErrors({ [errorKey]: true }); + } + } + get isRenewal(): boolean { return this.applicationTypeCode === ApplicationTypeCode.Renewal; } get showHintError(): boolean { return (this.dateOfBirth?.dirty || this.dateOfBirth?.touched) && this.dateOfBirth?.invalid; } - public get dateOfBirth(): FormControl { + get dateOfBirth(): FormControl { return this.form.get('dateOfBirth') as FormControl; } } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-terms-of-use.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-terms-of-use.component.ts index 99dfc4d36f..75182a8055 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-terms-of-use.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/components/shared/form-gdsd-terms-of-use.component.ts @@ -13,7 +13,7 @@ import { LicenceChildStepperStepComponent, UtilService } from '@app/core/service
-
+
Terms of Use for Submitting an Application for Guide Dog or Service Dog Certification Online
diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/constants/constants.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/constants/constants.ts index 8bc3bcbc47..b58ecb8b54 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/constants/constants.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/constants/constants.ts @@ -7,6 +7,7 @@ export const SPD_CONSTANTS = { monthYearFormat: 'MMM yyyy', dateTimeFormat: 'YYYY-MM-DD HH:mm', backendDateFormat: 'YYYY-MM-DD', + dateMask: '0000-00-00', }, periods: { applicationNotSubmittedWarningDays: 14, // show warning 14 days before the 30 day mark diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/config.service.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/config.service.ts index a20b405859..8bf10048b2 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/config.service.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/config.service.ts @@ -61,6 +61,10 @@ export class ConfigService { }); console.debug('[ConfigService] getAccreditedDogSchools', schools); + schools.sort((a: DogSchoolResponse, b: DogSchoolResponse) => + this.utilService.compareByStringUpper(a.schoolName, b.schoolName) + ); + this.accreditedDogSchools = schools; return resp; }) diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/gdsd-team-application.service.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/gdsd-team-application.service.ts index c4d5d5e2d7..2fe15c9c50 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/gdsd-team-application.service.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/gdsd-team-application.service.ts @@ -336,12 +336,6 @@ export class GdsdTeamApplicationService extends GdsdTeamApplicationHelper { tap((res: StrictHttpResponse) => { this.hasValueChanged = false; - let msg = 'Your application has been saved'; - if (isSaveAndExit) { - msg = 'Your application has been saved. Please note that inactive applications will expire in 30 days'; - } - this.utilService.toasterSuccess(msg); - if (!teamModelFormValue.licenceAppId) { this.gdsdTeamModelFormGroup.patchValue({ licenceAppId: res.body.licenceAppId! }, { emitEvent: false }); } @@ -1492,36 +1486,29 @@ export class GdsdTeamApplicationService extends GdsdTeamApplicationHelper { 'schoolTrainingHistoryData.schoolTrainings' ) as FormArray; schoolTrainingsArray.push( - new FormGroup( - { - trainingId: new FormControl(train?.trainingId ?? null), // placeholder for ID - trainingBizName: new FormControl(train?.trainingBizName ?? null, [FormControlValidators.required]), - contactGivenName: new FormControl(train?.contactGivenName ?? null), - contactSurname: new FormControl(train?.contactSurname ?? null, [FormControlValidators.required]), - contactPhoneNumber: new FormControl(train?.contactPhoneNumber ?? null, [FormControlValidators.required]), - contactEmailAddress: new FormControl(train?.contactEmailAddress ?? null, [FormControlValidators.email]), - trainingStartDate: new FormControl(train?.trainingStartDate ?? null, [Validators.required]), - trainingEndDate: new FormControl(train?.trainingEndDate ?? null, [Validators.required]), - trainingName: new FormControl(train?.trainingName ?? null, [FormControlValidators.required]), - totalTrainingHours: new FormControl(train?.totalTrainingHours ?? null, [Validators.required]), - whatLearned: new FormControl(train?.whatLearned ?? null, [FormControlValidators.required]), - addressLine1: new FormControl(train?.trainingBizMailingAddress?.addressLine1 ?? null, [ - FormControlValidators.required, - ]), - addressLine2: new FormControl(train?.trainingBizMailingAddress?.addressLine2 ?? null), - city: new FormControl(train?.trainingBizMailingAddress?.city ?? null, [FormControlValidators.required]), - postalCode: new FormControl(train?.trainingBizMailingAddress?.postalCode ?? null, [ - FormControlValidators.required, - ]), - province: new FormControl(train?.trainingBizMailingAddress?.province ?? null, [ - FormControlValidators.required, - ]), - country: new FormControl(train?.trainingBizMailingAddress?.country ?? null, [FormControlValidators.required]), - }, - { - validators: [FormGroupValidators.daterangeValidator('trainingStartDate', 'trainingEndDate')], - } - ) + new FormGroup({ + trainingId: new FormControl(train?.trainingId ?? null), // placeholder for ID + trainingBizName: new FormControl(train?.trainingBizName ?? null, [FormControlValidators.required]), + contactGivenName: new FormControl(train?.contactGivenName ?? null), + contactSurname: new FormControl(train?.contactSurname ?? null, [FormControlValidators.required]), + contactPhoneNumber: new FormControl(train?.contactPhoneNumber ?? null, [FormControlValidators.required]), + contactEmailAddress: new FormControl(train?.contactEmailAddress ?? null, [FormControlValidators.email]), + trainingStartDate: new FormControl(train?.trainingStartDate ?? null, [Validators.required]), + trainingEndDate: new FormControl(train?.trainingEndDate ?? null, [Validators.required]), + trainingName: new FormControl(train?.trainingName ?? null, [FormControlValidators.required]), + totalTrainingHours: new FormControl(train?.totalTrainingHours ?? null, [Validators.required]), + whatLearned: new FormControl(train?.whatLearned ?? null, [FormControlValidators.required]), + addressLine1: new FormControl(train?.trainingBizMailingAddress?.addressLine1 ?? null, [ + FormControlValidators.required, + ]), + addressLine2: new FormControl(train?.trainingBizMailingAddress?.addressLine2 ?? null), + city: new FormControl(train?.trainingBizMailingAddress?.city ?? null, [FormControlValidators.required]), + postalCode: new FormControl(train?.trainingBizMailingAddress?.postalCode ?? null, [ + FormControlValidators.required, + ]), + province: new FormControl(train?.trainingBizMailingAddress?.province ?? null, [FormControlValidators.required]), + country: new FormControl(train?.trainingBizMailingAddress?.country ?? null, [FormControlValidators.required]), + }) ); } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/retired-dog-application.service.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/retired-dog-application.service.ts index 437cb87c6a..3aa17bc557 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/retired-dog-application.service.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/retired-dog-application.service.ts @@ -343,12 +343,6 @@ export class RetiredDogApplicationService extends RetiredDogApplicationHelper { tap((res: StrictHttpResponse) => { this.hasValueChanged = false; - let msg = 'Your application has been saved'; - if (isSaveAndExit) { - msg = 'Your application has been saved. Please note that inactive applications will expire in 30 days'; - } - this.utilService.toasterSuccess(msg); - if (!rdModelFormValue.licenceAppId) { this.retiredDogModelFormGroup.patchValue({ licenceAppId: res.body.licenceAppId! }, { emitEvent: false }); } diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/util.service.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/util.service.ts index 13f95efca0..e087e4b4b6 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/util.service.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/core/services/util.service.ts @@ -9,7 +9,6 @@ import { BooleanTypeCode } from '@app/core/code-types/model-desc.models'; import { SPD_CONSTANTS } from '@app/core/constants/constants'; import { DialogComponent, DialogOptions } from '@app/shared/components/dialog.component'; import { FormatDatePipe } from '@app/shared/pipes/format-date.pipe'; -import { HotToastService } from '@ngxpert/hot-toast'; import { jwtDecode } from 'jwt-decode'; import moment from 'moment'; import * as CodeDescTypes from 'src/app/core/code-types/code-desc-types.models'; @@ -42,8 +41,7 @@ export class UtilService { constructor( @Inject(DOCUMENT) private document: Document, private dialog: MatDialog, - private formatDatePipe: FormatDatePipe, - private hotToastService: HotToastService + private formatDatePipe: FormatDatePipe ) {} //------------------------------------ @@ -117,22 +115,6 @@ export class UtilService { return moment().startOf('day'); } - getBirthDateMax(): moment.Moment { - return moment().startOf('day').subtract(SPD_CONSTANTS.date.birthDateMinAgeYears, 'years'); - } - - getDateMin(): moment.Moment { - return moment('1800-01-01'); - } - - getDogBirthDateMax(): moment.Moment { - return moment().startOf('day').subtract(6, 'months'); - } - - getDogDateMin(): moment.Moment { - return moment().startOf('day').subtract(50, 'years'); - } - getIsFutureDate(aDate: string | null | undefined): boolean { if (!aDate) return false; return moment(aDate).startOf('day').isAfter(moment().startOf('day'), 'day'); @@ -184,6 +166,52 @@ export class UtilService { } } + // Used by the date fields + // Validates the date and returns the errorKey + getIsInputValidDate(input: string): string | null { + const date = this.getInputDate(input); + if (!date) { + return 'invalidDate'; + } + + const now = new Date(); + return date.getTime() > now.getTime() ? 'futureDate' : null; + } + + getIsInputValidDateRange(input1: string, input2: string): boolean { + const date1 = this.getInputDate(input1); + if (!date1) { + return true; + } + + const date2 = this.getInputDate(input2); + if (!date2) { + return true; + } + + return moment(date1).startOf('day').isSameOrBefore(moment(date2).startOf('day')); + } + + private getInputDate(input: string): Date | null { + if (!/^\d{8}$/.test(input)) return null; + + const year = +input.slice(0, 4); + const month = +input.slice(4, 6); + const day = +input.slice(6, 8); + + // Basic range checks + if (month < 1 || month > 12 || day < 1 || day > 31 || year < 1800) return null; + + // Create a Date object (note: JS months are 0-based) + const date = new Date(year, month - 1, day); + + // Check if Date object matches input + const isDateValid = date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day; + if (!isDateValid) return null; + + return date; + } + getAddressString(params: { addressLine1: string | null | undefined; addressLine2?: string | null | undefined; @@ -228,7 +256,7 @@ export class UtilService { getCodeDescSorted(codeTableName: keyof typeof CodeDescTypes): SelectOptions[] { const codeDescs = this.getCodeDescByType(codeTableName); - codeDescs.sort((a: SelectOptions, b: SelectOptions) => this.compareByStringUpper(a.desc ?? '', b.desc)); + codeDescs.sort((a: SelectOptions, b: SelectOptions) => this.compareByStringUpper(a.desc, b.desc)); return codeDescs; } @@ -482,10 +510,6 @@ export class UtilService { }); } - toasterSuccess(msg: string): void { - this.hotToastService.success(msg, { ariaLive: 'polite' }); - } - dialogSuccess(msg: string): void { const data: DialogOptions = { icon: 'info', diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/material.module.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/material.module.ts index 20b2f86282..397bd85f2e 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/material.module.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/material.module.ts @@ -8,17 +8,16 @@ import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatChipsModule } from '@angular/material/chips'; -import { DateAdapter, MatNativeDateModule, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; -import { MatDatepickerModule } from '@angular/material/datepicker'; -import { MatDialogModule, MAT_DIALOG_DEFAULT_OPTIONS } from '@angular/material/dialog'; +import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE, MatNativeDateModule } from '@angular/material/core'; +import { MAT_DIALOG_DEFAULT_OPTIONS, MatDialogModule } from '@angular/material/dialog'; import { MatExpansionModule } from '@angular/material/expansion'; -import { MatFormFieldDefaultOptions, MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field'; +import { MAT_FORM_FIELD_DEFAULT_OPTIONS, MatFormFieldDefaultOptions } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatListModule } from '@angular/material/list'; import { MatMenuModule } from '@angular/material/menu'; import { MatPaginatorModule } from '@angular/material/paginator'; -import { MatRadioModule, MAT_RADIO_DEFAULT_OPTIONS } from '@angular/material/radio'; +import { MAT_RADIO_DEFAULT_OPTIONS, MatRadioModule } from '@angular/material/radio'; import { MatSelectModule } from '@angular/material/select'; import { MatSidenavModule } from '@angular/material/sidenav'; import { MatSlideToggleModule } from '@angular/material/slide-toggle'; @@ -38,7 +37,6 @@ const AngularMaterialModules = [ MatIconModule, MatListModule, MatInputModule, - MatDatepickerModule, MatCheckboxModule, MatSelectModule, MatExpansionModule, diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/file-upload.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/file-upload.component.ts index 19b253f329..3bbdb7367e 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/file-upload.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/file-upload.component.ts @@ -102,7 +102,6 @@ export class FileUploadHelper {
-
+
+ {{ uploadStatusMessage }} +
`, styles: [ ` + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; + } + .file-preview { background-image: linear-gradient(to top, #ededed, #efefef, #f1f1f1, #f4f4f4, #f6f6f6); align-items: center; @@ -213,6 +226,8 @@ export class FileUploadComponent implements OnInit { imagePreviews: Array = []; + uploadStatusMessage = ''; + constructor( private utilService: UtilService, private dialog: MatDialog, @@ -277,6 +292,11 @@ export class FileUploadComponent implements OnInit { this.files.push(newFile); this.filesUpdated(); + this.uploadStatusMessage = ''; // Clear first to trigger screen reader reliably + setTimeout(() => { + this.uploadStatusMessage = 'File uploaded successfully'; + }, 100); // Small delay helps ensure the change is recognized + this.fileUploaded.emit(newFile); } } @@ -346,6 +366,11 @@ export class FileUploadComponent implements OnInit { this.imagePreviews.splice(removeFileIndex, 1); this.files.splice(removeFileIndex, 1); this.filesUpdated(); + + this.uploadStatusMessage = ''; // Clear first to trigger screen reader reliably + setTimeout(() => { + this.uploadStatusMessage = 'File successfully removed'; + }, 100); // Small delay helps ensure the change is recognized } getFileIcon(file: File): IconType { diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/form-access-code-anonymous.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/form-access-code-anonymous.component.ts index 322ba9a1cf..410f349b32 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/form-access-code-anonymous.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/form-access-code-anonymous.component.ts @@ -210,9 +210,6 @@ export class FormAccessCodeAnonymousComponent implements OnInit { linkedLicenceHolderName: resp.licenceHolderName, }); this.linkSuccess.emit(resp); - - const serviceTypeCodeDesc = this.optionsPipe.transform(this.serviceTypeCode, 'ServiceTypes'); - this.utilService.toasterSuccess(`The ${serviceTypeCodeDesc} has been found.`); } if (this.errorMessage) { diff --git a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/form-address.component.ts b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/form-address.component.ts index 87872471a5..3eb4a46e1f 100644 --- a/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/form-address.component.ts +++ b/src/Spd.Presentation.GuideDogServiceDog/ClientApp/src/app/shared/components/form-address.component.ts @@ -19,13 +19,13 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m
Street Address 2 (optional) - +
City - + This is required
@@ -36,6 +36,7 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m matInput formControlName="postalCode" oninput="this.value = this.value.toUpperCase()" + [errorStateMatcher]="matcher" maxlength="20" /> This is required @@ -44,7 +45,7 @@ import { FormErrorStateMatcher } from '@app/shared/directives/form-error-state-m
Province/State - + This is required This must be '{{ provinceOfBC }}' or '{{ provinceBritishColumbia }}' Country - + This is required This must be '{{ countryCA }}' or '{{ countryCanada }}'