@@ -20,7 +20,7 @@ import {
2020import { ComplianceAndEnforcementService } from '../../../services/compliance-and-enforcement/compliance-and-enforcement.service' ;
2121import { OverviewComponent } from '../overview/overview.component' ;
2222import { ToastService } from '../../../services/toast/toast.service' ;
23- import { FormGroup } from '@angular/forms' ;
23+ import { FormArray , FormGroup } from '@angular/forms' ;
2424import { SubmitterComponent } from '../submitter/submitter.component' ;
2525import { ComplianceAndEnforcementSubmitterDto } from '../../../services/compliance-and-enforcement/submitter/submitter.dto' ;
2626import { ComplianceAndEnforcementSubmitterService } from '../../../services/compliance-and-enforcement/submitter/submitter.service' ;
@@ -257,7 +257,9 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
257257 // Load property data
258258 if ( this . file . uuid ) {
259259 try {
260- this . property = await this . complianceAndEnforcementPropertyService . fetchByFileUuid ( this . file . uuid ) ;
260+ const properties = await this . complianceAndEnforcementPropertyService . fetchParcels ( this . file . uuid ) ;
261+
262+ this . property = properties [ 0 ] ;
261263
262264 if ( this . propertyComponent && this . property ) {
263265 this . propertyComponent . property = this . property ;
@@ -354,6 +356,167 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
354356 }
355357 }
356358
359+ async onFinishCreateFileClicked ( ) {
360+ // Ensure child components and file exist
361+ if ( ! this . overviewComponent || ! this . submitterComponent || ! this . propertyComponent || ! this . file ?. uuid || ! this . responsiblePartiesComponent ) {
362+ this . toastService . showErrorToast ( 'Something went wrong, please refresh the page and try again' ) ;
363+ return ;
364+ }
365+
366+ // Trigger validation across all child forms
367+ const controlsToValidate : FormGroup [ ] = [
368+ this . overviewComponent . form ,
369+ this . submitterComponent . form ,
370+ this . propertyComponent . form ,
371+ ] ;
372+
373+ controlsToValidate . forEach ( ( fg ) => {
374+ fg . markAllAsTouched ( ) ;
375+ fg . updateValueAndValidity ( { onlySelf : false , emitEvent : false } ) ;
376+ } ) ;
377+
378+ // Ensure property local government display control syncs validation (may have to go back to the local gov validation)
379+ try {
380+ this . propertyComponent . onLocalGovernmentBlur ( ) ;
381+ } catch { }
382+ this . propertyComponent . localGovernmentControl . markAsTouched ( ) ;
383+ this . propertyComponent . localGovernmentControl . updateValueAndValidity ( { onlySelf : false , emitEvent : false } ) ;
384+
385+ // Trigger validation for Responsible Parties form array and nested director forms
386+ if ( this . responsiblePartiesComponent ?. form ) {
387+ this . responsiblePartiesComponent . form . controls . forEach ( ( group ) => {
388+ group . markAllAsTouched ( ) ;
389+ group . updateValueAndValidity ( { onlySelf : false , emitEvent : false } ) ;
390+ const directors = group . get ( 'directors' ) as FormArray | null ;
391+ directors ?. controls . forEach ( ( dg ) => {
392+ dg . markAllAsTouched ( ) ;
393+ dg . updateValueAndValidity ( { onlySelf : false , emitEvent : false } ) ;
394+ } ) ;
395+ } ) ;
396+ }
397+
398+ // Validate that at least one responsible party is added
399+ const hasValidResponsibleParties = this . responsiblePartiesComponent ?. validateRequiredParties ( ) ?? false ;
400+
401+ // If any form is invalid, show error toast and scroll to first error
402+ const hasInvalid =
403+ controlsToValidate . some ( ( fg ) => fg . invalid ) ||
404+ this . propertyComponent . localGovernmentControl . invalid ||
405+ ( this . responsiblePartiesComponent ?. form . controls . some ( ( g ) => g . invalid ) ?? false ) ||
406+ ! hasValidResponsibleParties ;
407+ if ( hasInvalid ) {
408+ this . toastService . showErrorToast ( 'Please correct all errors before submitting the form' ) ;
409+ // Attempt to scroll to first element with .ng-invalid within the form ( will check with SO if this is necessary)
410+
411+ const el = document . getElementsByClassName ( 'ng-invalid' ) ;
412+ if ( el && el . length > 0 ) {
413+ const target = Array . from ( el ) . find ( ( n ) => n . nodeName !== 'FORM' ) as HTMLElement | undefined ;
414+ target ?. scrollIntoView ( { behavior : 'smooth' , block : 'center' } ) ;
415+ }
416+
417+ return ;
418+ }
419+
420+ // Persist latest values before finalizing (same as save but without navigate)
421+ const overviewUpdate = this . overviewComponent . $changes . getValue ( ) ;
422+ const submitterUpdate = this . submitterComponent . $changes . getValue ( ) ;
423+ const propertyUpdate = this . propertyComponent . $changes . getValue ( ) ;
424+
425+ try {
426+ await firstValueFrom ( this . complianceAndEnforcementService . update ( this . file . uuid , overviewUpdate ) ) ;
427+
428+ if ( this . submitter ?. uuid ) {
429+ await firstValueFrom ( this . complianceAndEnforcementSubmitterService . update ( this . submitter . uuid , submitterUpdate ) ) ;
430+ } else {
431+ this . submitter = await firstValueFrom ( this . complianceAndEnforcementSubmitterService . create ( {
432+ ...submitterUpdate ,
433+ fileUuid : this . file . uuid ,
434+ } ) ) ;
435+ }
436+
437+ if ( this . property ?. uuid ) {
438+ await firstValueFrom ( this . complianceAndEnforcementPropertyService . update ( this . property . uuid , propertyUpdate ) ) ;
439+ } else {
440+ this . property = await firstValueFrom (
441+ this . complianceAndEnforcementPropertyService . create ( {
442+ fileUuid : this . file . uuid ,
443+ ...cleanPropertyUpdate ( propertyUpdate ) ,
444+ } ) ,
445+ ) ;
446+ }
447+
448+ // Mark file as submitted
449+ await firstValueFrom ( this . complianceAndEnforcementService . update ( this . file . uuid , {
450+ dateOpened : Date . now ( )
451+ } ) ) ;
452+ // Now submit the form - this will run backend validation
453+ await this . complianceAndEnforcementService . submit ( this . file . uuid ) ;
454+
455+ this . toastService . showSuccessToast ( 'C&E file created' ) ;
456+ await this . router . navigate ( [ '/home' ] ) ;
457+ } catch ( error : any ) {
458+ // Check if it's a validation error from the backend
459+ if ( error . status === 400 && error . error ?. message ?. includes ( 'Validation failed' ) ) {
460+ this . toastService . showErrorToast ( 'Please correct all errors before submitting the form' ) ;
461+
462+ // Trigger client-side validation to show field errors
463+ this . triggerClientSideValidation ( ) ;
464+
465+ // Scroll to first error
466+ setTimeout ( ( ) => {
467+ const el = document . getElementsByClassName ( 'ng-invalid' ) ;
468+ if ( el && el . length > 0 ) {
469+ const target = Array . from ( el ) . find ( ( n ) => n . nodeName !== 'FORM' ) as HTMLElement | undefined ;
470+ target ?. scrollIntoView ( { behavior : 'smooth' , block : 'center' } ) ;
471+ }
472+ } , 100 ) ;
473+ } else {
474+ this . toastService . showErrorToast ( 'Failed to create C&E file. Please try again.' ) ;
475+ }
476+ }
477+ }
478+
479+ private triggerClientSideValidation ( ) {
480+ // Trigger validation across all child forms to show field errors
481+ const controlsToValidate : FormGroup [ ] = [
482+ this . overviewComponent ?. form ,
483+ this . submitterComponent ?. form ,
484+ this . propertyComponent ?. form ,
485+ ] . filter ( Boolean ) as FormGroup [ ] ;
486+
487+ controlsToValidate . forEach ( ( fg ) => {
488+ fg . markAllAsTouched ( ) ;
489+ fg . updateValueAndValidity ( { onlySelf : false , emitEvent : false } ) ;
490+ } ) ;
491+
492+ // Ensure property local government display control syncs validation
493+ if ( this . propertyComponent ) {
494+ try {
495+ this . propertyComponent . onLocalGovernmentBlur ( ) ;
496+ } catch ( error ) {
497+ // Local government blur validation failed, continue
498+ }
499+ this . propertyComponent . localGovernmentControl . markAsTouched ( ) ;
500+ this . propertyComponent . localGovernmentControl . updateValueAndValidity ( { onlySelf : false , emitEvent : false } ) ;
501+ }
502+
503+ // Trigger validation for Responsible Parties form array and nested director forms
504+ if ( this . responsiblePartiesComponent ?. form ) {
505+ this . responsiblePartiesComponent . form . controls . forEach ( ( group ) => {
506+ group . markAllAsTouched ( ) ;
507+ group . updateValueAndValidity ( { onlySelf : false , emitEvent : false } ) ;
508+
509+ const directors = group . get ( 'directors' ) as FormArray | null ;
510+ if ( directors ) {
511+ directors . controls . forEach ( ( dg ) => {
512+ dg . markAllAsTouched ( ) ;
513+ dg . updateValueAndValidity ( { onlySelf : false , emitEvent : false } ) ;
514+ } ) ;
515+ }
516+ } ) ;
517+ }
518+ }
519+
357520 ngOnDestroy ( ) : void {
358521 this . $destroy . next ( ) ;
359522 this . $destroy . complete ( ) ;
0 commit comments