Skip to content

Commit 641a192

Browse files
authored
Merge pull request #2535 from bcgov/2416-create-complaint-referral-tab-from-submission-qa-2
2415 QA 2: Add form validation to overview
2 parents 6a757b7 + 7fd0cc2 commit 641a192

File tree

10 files changed

+84
-64
lines changed

10 files changed

+84
-64
lines changed

alcs-frontend/src/app/features/compliance-and-enforcement/details/complaint-referral/complaint-referral.component.html

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ <h3>Submitters</h3>
2525
<section class="file-section">
2626
<app-compliance-and-enforcement-documents
2727
#submissionDocumentsComponent
28-
[title]="'Complaint/Referral Submission'"
29-
[noDocumentsText]="'No Complaint/Referral'"
28+
title="Complaint/Referral Submission"
29+
noDocumentsText="No Complaint/Referral"
3030
[fileNumber]="fileNumber"
3131
[options]="submissionDocumentOptions"
3232
[section]="Section.SUBMISSION"
@@ -38,12 +38,16 @@ <h3>Submitters</h3>
3838
<form [formGroup]="form">
3939
<section class="form-section">
4040
<h3>Complaint / Referral > Edit Overview</h3>
41-
<app-compliance-and-enforcement-overview #overviewComponent [parentForm]="form" [file]="file" />
41+
<app-compliance-and-enforcement-overview
42+
#overviewComponent
43+
[file]="file"
44+
(formReady)="registerFormGroup('overview', $event)"
45+
/>
4246
</section>
4347

4448
<div class="button-container">
4549
<a mat-stroked-button color="warn" [routerLink]="'../..'">Cancel</a>
46-
<button mat-flat-button color="primary" (click)="saveOverview()">Save</button>
50+
<button mat-flat-button color="primary" [disabled]="form.invalid" (click)="saveOverview()">Save</button>
4751
</div>
4852
</form>
4953
</ng-container>
@@ -53,15 +57,15 @@ <h3>Complaint / Referral > Edit Overview</h3>
5357
<section class="form-section">
5458
<h3>Complaint / Referral > Edit Submitters</h3>
5559

56-
<div class="submitter-container" *ngFor="let submitter of file?.submitters; let i = index">
60+
<div class="submitter-container" *ngFor="let submitter of file?.submitters; let i = index; trackBy: trackByUuid">
5761
<app-compliance-and-enforcement-submitter
5862
#submitterComponents
5963
[title]="'#' + (i + 1)"
6064
[showDeleteButton]="true"
6165
[submitter]="submitter"
6266
[initialSubmissionType]="file?.initialSubmissionType ?? undefined"
6367
(deleteButtonClicked)="deleteSubmitter(submitter.uuid)"
64-
(formReady)="registerFormGroup('submitter-' + i, $event.formGroup)"
68+
(formReady)="registerFormGroup('submitter-' + submitter.uuid, $event)"
6569
/>
6670
</div>
6771
</section>

alcs-frontend/src/app/features/compliance-and-enforcement/details/complaint-referral/complaint-referral.component.spec.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,12 @@ describe('ComplaintReferralComponent', () => {
6060
});
6161

6262
it('should set editing from route data on ngOnInit', () => {
63-
const dataSubject = new Subject<any>();
64-
mockActivatedRoute.data = dataSubject as any;
63+
const editing = 'overview';
64+
mockActivatedRoute.snapshot.data = { editing };
65+
6566
component.ngOnInit();
66-
dataSubject.next({ editing: 'overview' });
67-
expect(component.editing).toBe('overview');
67+
68+
expect(component.editing).toBe(editing);
6869
});
6970

7071
it('should set file, fileNumber, and submissionDocumentOptions.fileId from service.$file', () => {

alcs-frontend/src/app/features/compliance-and-enforcement/details/complaint-referral/complaint-referral.component.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import { submissionDocumentOptions } from '../../draft/draft.component';
1212
import { OverviewComponent } from '../../overview/overview.component';
1313
import { ToastService } from '../../../../services/toast/toast.service';
1414
import { SubmitterComponent } from '../../submitter/submitter.component';
15-
import { UpdateComplianceAndEnforcementSubmitterDto } from '../../../../services/compliance-and-enforcement/submitter/submitter.dto';
15+
import {
16+
ComplianceAndEnforcementSubmitterDto,
17+
UpdateComplianceAndEnforcementSubmitterDto,
18+
} from '../../../../services/compliance-and-enforcement/submitter/submitter.dto';
1619
import { ComplianceAndEnforcementSubmitterService } from '../../../../services/compliance-and-enforcement/submitter/submitter.service';
1720
import { AddSubmitterDialogComponent } from './submitters/add-submitter-dialog/add-submitter-dialog.component';
1821
import { MatDialog } from '@angular/material/dialog';
@@ -51,9 +54,7 @@ export class ComplaintReferralComponent implements OnInit, OnDestroy {
5154
) {}
5255

5356
ngOnInit(): void {
54-
this.route.data.pipe(takeUntil(this.$destroy)).subscribe(async (data) => {
55-
this.editing = data['editing'];
56-
});
57+
this.editing = this.route.snapshot.data['editing'];
5758

5859
this.service.$file.pipe(takeUntil(this.$destroy)).subscribe((file) => {
5960
if (file) {
@@ -152,10 +153,14 @@ export class ComplaintReferralComponent implements OnInit, OnDestroy {
152153

153154
registerFormGroup(name: string, formGroup: FormGroup) {
154155
setTimeout(() => {
155-
this.form.addControl(name, formGroup);
156+
this.form.setControl(name, formGroup);
156157
});
157158
}
158159

160+
trackByUuid(_: number, item: ComplianceAndEnforcementSubmitterDto) {
161+
return item.uuid;
162+
}
163+
159164
ngOnDestroy() {
160165
this.$destroy.next();
161166
this.$destroy.complete();

alcs-frontend/src/app/features/compliance-and-enforcement/details/complaint-referral/submitters/add-submitter-dialog/add-submitter-dialog.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
<form [formGroup]="form">
22
<app-compliance-and-enforcement-submitter
33
#submitterComponent
4-
[title]="'Add Submitter'"
4+
title="Add Submitter"
55
[submitter]="undefined"
66
[initialSubmissionType]="data?.initialSubmissionType"
7-
(formReady)="registerFormGroup($event)"
7+
(formReady)="registerFormGroup('submitter', $event)"
88
/>
99

1010
<div class="button-container">

alcs-frontend/src/app/features/compliance-and-enforcement/details/complaint-referral/submitters/add-submitter-dialog/add-submitter-dialog.component.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ export class AddSubmitterDialogComponent {
5050
this.isSaving = false;
5151
}
5252

53-
registerFormGroup({ name, formGroup }: { name: string; formGroup: FormGroup }) {
54-
this.form.addControl(name, formGroup);
53+
registerFormGroup(name: string, formGroup: FormGroup) {
54+
setTimeout(() => {
55+
this.form.setControl(name, formGroup);
56+
});
5557
}
5658
}

alcs-frontend/src/app/features/compliance-and-enforcement/draft/draft.component.html

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,24 @@ <h2>C&E File ID: {{ file?.fileNumber }}</h2>
55
<section class="form-section">
66
<div>
77
<h3>Overview</h3>
8-
<app-compliance-and-enforcement-overview #overviewComponent [parentForm]="form" [file]="file" />
8+
<app-compliance-and-enforcement-overview
9+
#overviewComponent
10+
[file]="file"
11+
(formReady)="registerFormGroup('overview', $event)"
12+
/>
913
</div>
1014
<app-compliance-and-enforcement-submitter
1115
#submitterComponent
16+
title="Submitter"
1217
[submitter]="submitter"
1318
[initialSubmissionType]="initialSubmissionType"
14-
(formReady)="registerFormGroup($event)"
19+
(formReady)="registerFormGroup('submitter', $event)"
1520
/>
1621

1722
<app-compliance-and-enforcement-documents
1823
#submissionDocumentsComponent
19-
[title]="'Complaint/Referral Submission'"
20-
[noDocumentsText]="'No Complaint/Referral'"
24+
title="Complaint/Referral Submission"
25+
noDocumentsText="No Complaint/Referral"
2126
[fileNumber]="fileNumber"
2227
[options]="submissionDocumentOptions"
2328
[section]="Section.SUBMISSION"
@@ -36,8 +41,8 @@ <h3>Overview</h3>
3641
/>
3742
<app-compliance-and-enforcement-documents
3843
#ownershipDocumentsComponent
39-
[title]="'Property Ownership Documents'"
40-
[noDocumentsText]="'No owner information'"
44+
title="Property Ownership Documents"
45+
noDocumentsText="No owner information"
4146
[fileNumber]="fileNumber"
4247
[options]="ownershipDocumentOptions"
4348
[section]="Section.OWNERSHIP"

alcs-frontend/src/app/features/compliance-and-enforcement/draft/draft.component.ts

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,13 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
373373

374374
async onFinishCreateFileClicked() {
375375
// Ensure child components and file exist
376-
if (!this.overviewComponent || !this.submitterComponent || !this.propertyComponent || !this.file?.uuid || !this.responsiblePartiesComponent) {
376+
if (
377+
!this.overviewComponent ||
378+
!this.submitterComponent ||
379+
!this.propertyComponent ||
380+
!this.file?.uuid ||
381+
!this.responsiblePartiesComponent
382+
) {
377383
this.toastService.showErrorToast('Something went wrong, please refresh the page and try again');
378384
return;
379385
}
@@ -422,13 +428,13 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
422428
if (hasInvalid) {
423429
this.toastService.showErrorToast('Please correct all errors before submitting the form');
424430
// Attempt to scroll to first element with .ng-invalid within the form ( will check with SO if this is necessary)
425-
426-
const el = document.getElementsByClassName('ng-invalid');
427-
if (el && el.length > 0) {
428-
const target = Array.from(el).find((n) => n.nodeName !== 'FORM') as HTMLElement | undefined;
429-
target?.scrollIntoView({ behavior: 'smooth', block: 'center' });
430-
}
431-
431+
432+
const el = document.getElementsByClassName('ng-invalid');
433+
if (el && el.length > 0) {
434+
const target = Array.from(el).find((n) => n.nodeName !== 'FORM') as HTMLElement | undefined;
435+
target?.scrollIntoView({ behavior: 'smooth', block: 'center' });
436+
}
437+
432438
return;
433439
}
434440

@@ -441,12 +447,16 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
441447
await firstValueFrom(this.complianceAndEnforcementService.update(this.file.uuid, overviewUpdate));
442448

443449
if (this.submitter?.uuid) {
444-
await firstValueFrom(this.complianceAndEnforcementSubmitterService.update(this.submitter.uuid, submitterUpdate));
450+
await firstValueFrom(
451+
this.complianceAndEnforcementSubmitterService.update(this.submitter.uuid, submitterUpdate),
452+
);
445453
} else {
446-
this.submitter = await firstValueFrom(this.complianceAndEnforcementSubmitterService.create({
447-
...submitterUpdate,
448-
fileUuid: this.file.uuid,
449-
}));
454+
this.submitter = await firstValueFrom(
455+
this.complianceAndEnforcementSubmitterService.create({
456+
...submitterUpdate,
457+
fileUuid: this.file.uuid,
458+
}),
459+
);
450460
}
451461

452462
if (this.property?.uuid) {
@@ -461,10 +471,12 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
461471
}
462472

463473
// Mark file as submitted
464-
await firstValueFrom(this.complianceAndEnforcementService.update(this.file.uuid, {
465-
dateOpened: Date.now()
466-
}));
467-
// Now submit the form - this will run backend validation
474+
await firstValueFrom(
475+
this.complianceAndEnforcementService.update(this.file.uuid, {
476+
dateOpened: Date.now(),
477+
}),
478+
);
479+
// Now submit the form - this will run backend validation
468480
await this.complianceAndEnforcementService.submit(this.file.uuid);
469481

470482
this.toastService.showSuccessToast('C&E file created');
@@ -473,10 +485,10 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
473485
// Check if it's a validation error from the backend
474486
if (error.status === 400 && error.error?.message?.includes('Validation failed')) {
475487
this.toastService.showErrorToast('Please correct all errors before submitting the form');
476-
488+
477489
// Trigger client-side validation to show field errors
478490
this.triggerClientSideValidation();
479-
491+
480492
// Scroll to first error
481493
setTimeout(() => {
482494
const el = document.getElementsByClassName('ng-invalid');
@@ -520,7 +532,7 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
520532
this.responsiblePartiesComponent.form.controls.forEach((group) => {
521533
group.markAllAsTouched();
522534
group.updateValueAndValidity({ onlySelf: false, emitEvent: false });
523-
535+
524536
const directors = group.get('directors') as FormArray | null;
525537
if (directors) {
526538
directors.controls.forEach((dg) => {
@@ -532,8 +544,10 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
532544
}
533545
}
534546

535-
registerFormGroup({ name, formGroup }: { name: string; formGroup: FormGroup }) {
536-
this.form.addControl(name, formGroup);
547+
registerFormGroup(name: string, formGroup: FormGroup) {
548+
setTimeout(() => {
549+
this.form.setControl(name, formGroup);
550+
});
537551
}
538552

539553
ngOnDestroy(): void {

alcs-frontend/src/app/features/compliance-and-enforcement/overview/overview.component.spec.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,6 @@ describe('OverviewComponent', () => {
7070
expect(component).toBeTruthy();
7171
});
7272

73-
it('should add overview control to parent form', () => {
74-
const parentForm = new FormGroup({ overview: new FormGroup({}) });
75-
component.parentForm = parentForm;
76-
77-
expect(parentForm.contains('overview')).toBe(true);
78-
});
79-
8073
it('should patch form values and enable form when file input is set', () => {
8174
component.file = mockFile;
8275

alcs-frontend/src/app/features/compliance-and-enforcement/overview/overview.component.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Input, OnDestroy } from '@angular/core';
1+
import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core';
22
import { BehaviorSubject, Subject, takeUntil } from 'rxjs';
33
import {
44
AllegedActivity,
@@ -37,13 +37,7 @@ export class OverviewComponent implements OnDestroy {
3737
intakeNotes: new FormControl<string>({ value: '', disabled: true }),
3838
});
3939

40-
@Input() set parentForm(parentForm: FormGroup) {
41-
if (!parentForm || parentForm.contains('overview')) {
42-
return;
43-
}
44-
45-
parentForm.addControl('overview', this.form);
46-
}
40+
@Output() formReady = new EventEmitter<FormGroup>();
4741

4842
$changes: BehaviorSubject<UpdateComplianceAndEnforcementDto> = new BehaviorSubject<UpdateComplianceAndEnforcementDto>(
4943
{},
@@ -81,6 +75,8 @@ export class OverviewComponent implements OnDestroy {
8175
});
8276
});
8377

78+
this.formReady.emit(this.form);
79+
8480
this.isSubscribed = true;
8581
}
8682
}

alcs-frontend/src/app/features/compliance-and-enforcement/submitter/submitter.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class SubmitterComponent implements OnDestroy {
3535
additionalContactInformation: new FormControl<string>(''),
3636
});
3737

38-
@Output() formReady = new EventEmitter<{ name: string; formGroup: FormGroup }>();
38+
@Output() formReady = new EventEmitter<FormGroup>();
3939

4040
$changes: BehaviorSubject<[string | undefined, UpdateComplianceAndEnforcementSubmitterDto]> = new BehaviorSubject<
4141
[string | undefined, UpdateComplianceAndEnforcementSubmitterDto]
@@ -82,7 +82,7 @@ export class SubmitterComponent implements OnDestroy {
8282
]);
8383
});
8484

85-
this.formReady.emit({ name: 'submitter', formGroup: this.form });
85+
this.formReady.emit(this.form);
8686

8787
this.isSubscribed = true;
8888
}

0 commit comments

Comments
 (0)