Skip to content

Commit

Permalink
Merge pull request #824 from bcgov/feature/ALCS-931
Browse files Browse the repository at this point in the history
Add inclusion fields when user is LFNG
  • Loading branch information
dhaselhan authored Jul 27, 2023
2 parents 2a8df3f + 429b6ab commit fbd8030
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ describe('ApplicationDetailsComponent', () => {
component = fixture.componentInstance;
component.submission = {
exclShareGovernmentBorders: null,
inclGovernmentOwnsAllParcels: null,
exclWhyLand: null,
inclAgricultureSupport: null,
inclExclHectares: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ describe('ApplicationSubmissionService', () => {
inclAgricultureSupport: null,
inclImprovements: null,
exclShareGovernmentBorders: null,
inclGovernmentOwnsAllParcels: null,
exclWhyLand: null,
inclExclHectares: null,
prescribedBody: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ export interface ApplicationSubmissionDto {
inclAgricultureSupport: string | null;
inclImprovements: string | null;
exclShareGovernmentBorders: boolean | null;
inclGovernmentOwnsAllParcels: boolean | null;
}

export interface ApplicationDto {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,15 @@ <h2>Proposal</h2>
<mat-form-field appearance="outline">
<textarea
formControlName="improvements"
#outsideLandsText
#improvementsText
maxlength="4000"
id="improvements"
matInput
placeholder="Type comment"
></textarea>
</mat-form-field>
<div class="subtext">Example: 40 ha of grazing land fenced in 2010.</div>
<span class="subtext">Characters left: {{ 4000 - outsideLandsText.textLength }}</span>
<span class="subtext">Characters left: {{ 4000 - improvementsText.textLength }}</span>
<div *ngIf="improvements.invalid && (improvements.dirty || improvements.touched)" class="field-error">
<mat-icon>warning</mat-icon>
<div *ngIf="improvements.errors?.['required']">This field is required</div>
Expand All @@ -118,9 +118,119 @@ <h2>Proposal</h2>
[isRequired]="true"
></app-file-drag-drop>
</div>
<div class="full-row" *ngIf="isGovernmentUser">
<label for="isFarm"
>Is the {{ governmentName }} the registered land owner of all parcels under this inclusion application?</label
>
<div class="subtext">
The ALR General Regulation does not require the {{ governmentName }} to complete a public hearing if all
inclusion application parcel(s) are owned by the {{ governmentName }}.
</div>
<app-info-banner> Please refer to Inclusion page on the ALC website for more information. </app-info-banner>
<mat-button-toggle-group
(change)="onSelectLocalGovernmentParcelOwner($event)"
class="input"
id="isFarm"
formControlName="isLFNGOwnerOfAllParcels"
>
<mat-button-toggle
value="true"
[ngClass]="{
'error-outline':
governmentOwnsAllParcels.invalid && (governmentOwnsAllParcels.dirty || governmentOwnsAllParcels.touched)
}"
>Yes</mat-button-toggle
>
<mat-button-toggle
value="false"
[ngClass]="{
'error-outline':
governmentOwnsAllParcels.invalid && (governmentOwnsAllParcels.dirty || governmentOwnsAllParcels.touched)
}"
>No</mat-button-toggle
>
</mat-button-toggle-group>
<div
*ngIf="
showErrors &&
governmentOwnsAllParcels.invalid &&
(governmentOwnsAllParcels.dirty || governmentOwnsAllParcels.touched)
"
class="field-error"
>
<mat-icon>warning</mat-icon>
<div *ngIf="governmentOwnsAllParcels.errors?.['required']">This field is required</div>
</div>
</div>
</div>
</form>

<section *ngIf="isGovernmentUser">
<h3>Notification and Public Hearing Requirements</h3>
<p class="requirement-description">
A printed copy of the application will need to be used for notification. Please ensure all prior fields are complete
and correct before downloading the PDF of the application (Step 8 of this application form will flag outstanding
fields).
</p>

<button [disabled]="disableNotificationFileUploads" mat-flat-button color="accent">Download PDF</button>
<app-warning-banner>
You will not be able to complete the remaining portion of the application until the notification and public hearing
process is complete.
</app-warning-banner>
<form>
<div class="form-row">
<div class="full-row">
<label class="subheading2" for="advertising">Notice of Public Hearing (Advertisement)</label>
<div class="subtext">
Proof that notice of the application was provided in a form and manner acceptable to the Commission
</div>
<app-file-drag-drop
id="advertising"
[uploadedFiles]="noticeOfPublicHearing"
(uploadFiles)="attachFile($event, DOCUMENT.PROOF_OF_ADVERTISING)"
(deleteFile)="onDeleteFile($event)"
(openFile)="openFile($event)"
[showErrors]="showErrors"
[isRequired]="!disableNotificationFileUploads"
[disabled]="disableNotificationFileUploads"
></app-file-drag-drop>
</div>
<div class="full-row">
<label class="subheading2" for="signage">Proof of Signage</label>
<div class="subtext">
Proof that a sign, in a form and manner acceptable to the Commission, was posted on the land that is the
subject of the application
</div>
<app-file-drag-drop
id="signage"
[uploadedFiles]="proofOfSignage"
(uploadFiles)="attachFile($event, DOCUMENT.PROOF_OF_SIGNAGE)"
(deleteFile)="onDeleteFile($event)"
(openFile)="openFile($event)"
[showErrors]="showErrors"
[isRequired]="!disableNotificationFileUploads"
[disabled]="disableNotificationFileUploads"
></app-file-drag-drop>
</div>
<div class="full-row">
<label class="subheading2" for="report">Report of Public Hearing</label>
<div class="subtext">Public hearing report and any other public comments received</div>
<app-file-drag-drop
id="report"
[uploadedFiles]="reportOfPublicHearing"
(uploadFiles)="attachFile($event, DOCUMENT.REPORT_OF_PUBLIC_HEARING)"
(deleteFile)="onDeleteFile($event)"
(openFile)="openFile($event)"
[showErrors]="showErrors"
[isRequired]="!disableNotificationFileUploads"
[disabled]="disableNotificationFileUploads"
></app-file-drag-drop>
</div>
</div>
</form>
</section>

<div class="button-container">
<button *ngIf="!draftMode" (click)="onSaveExit()" mat-stroked-button color="accent">Save and Exit</button>
<button *ngIf="draftMode" (click)="onSaveExit()" mat-stroked-button color="accent">Discard all Changes</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@use '../../../../../styles/functions' as *;

section {
margin-top: rem(32);
}

.requirement-description {
margin: rem(8) 0 !important;
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialog } from '@angular/material/dialog';
import { DeepMocked } from '@golevelup/ts-jest';
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { BehaviorSubject } from 'rxjs';
import { UserDto } from '../../../../services/authentication/authentication.dto';
import { AuthenticationService } from '../../../../services/authentication/authentication.service';
import { InclProposalComponent } from './incl-proposal.component';
import { ApplicationSubmissionService } from '../../../../services/application-submission/application-submission.service';
import { ApplicationDocumentService } from '../../../../services/application-document/application-document.service';
Expand All @@ -14,8 +16,14 @@ describe('InclProposalComponent', () => {
let fixture: ComponentFixture<InclProposalComponent>;
let mockApplicationService: DeepMocked<ApplicationSubmissionService>;
let mockAppDocumentService: DeepMocked<ApplicationDocumentService>;
let mockAuthService: DeepMocked<AuthenticationService>;

beforeEach(async () => {
mockApplicationService = createMock();
mockAppDocumentService = createMock();
mockAuthService = createMock();
mockAuthService.$currentProfile = new BehaviorSubject<UserDto | undefined>(undefined);

await TestBed.configureTestingModule({
providers: [
{
Expand All @@ -26,6 +34,10 @@ describe('InclProposalComponent', () => {
provide: ApplicationDocumentService,
useValue: mockAppDocumentService,
},
{
provide: AuthenticationService,
useValue: mockAuthService,
},
{
provide: MatDialog,
useValue: {},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { AuthenticationService } from '../../../../services/authentication/authentication.service';
import { formatBooleanToString } from '../../../../shared/utils/boolean-helper';
import { parseStringToBoolean } from '../../../../shared/utils/string-helper';
import { FilesStepComponent } from '../../files-step.partial';
import { ApplicationSubmissionService } from '../../../../services/application-submission/application-submission.service';
import { ApplicationDocumentService } from '../../../../services/application-document/application-document.service';
Expand All @@ -19,25 +23,33 @@ import { ApplicationSubmissionUpdateDto } from '../../../../services/application
})
export class InclProposalComponent extends FilesStepComponent implements OnInit, OnDestroy {
DOCUMENT = DOCUMENT_TYPE;

currentStep = EditApplicationSteps.Proposal;
private submissionUuid = '';
isGovernmentUser = false;
governmentName? = '';
disableNotificationFileUploads = false;

hectares = new FormControl<string | null>(null, [Validators.required]);
purpose = new FormControl<string | null>(null, [Validators.required]);
agSupport = new FormControl<string | null>(null, [Validators.required]);
improvements = new FormControl<string | null>(null, [Validators.required]);
governmentOwnsAllParcels = new FormControl<string | undefined>(undefined, [Validators.required]);

form = new FormGroup({
hectares: this.hectares,
purpose: this.purpose,
agSupport: this.agSupport,
improvements: this.improvements,
});
private submissionUuid = '';

proposalMap: ApplicationDocumentDto[] = [];
noticeOfPublicHearing: ApplicationDocumentDto[] = [];
proofOfSignage: ApplicationDocumentDto[] = [];
reportOfPublicHearing: ApplicationDocumentDto[] = [];

constructor(
private applicationSubmissionService: ApplicationSubmissionService,
private authenticationService: AuthenticationService,
applicationDocumentService: ApplicationDocumentService,
dialog: MatDialog
) {
Expand All @@ -57,6 +69,13 @@ export class InclProposalComponent extends FilesStepComponent implements OnInit,
improvements: applicationSubmission.inclImprovements,
});

if (applicationSubmission.inclGovernmentOwnsAllParcels !== null) {
this.governmentOwnsAllParcels.setValue(
formatBooleanToString(applicationSubmission.inclGovernmentOwnsAllParcels)
);
this.disableNotificationFileUploads = applicationSubmission.inclGovernmentOwnsAllParcels;
}

if (this.showErrors) {
this.form.markAllAsTouched();
}
Expand All @@ -65,6 +84,23 @@ export class InclProposalComponent extends FilesStepComponent implements OnInit,

this.$applicationDocuments.pipe(takeUntil(this.$destroy)).subscribe((documents) => {
this.proposalMap = documents.filter((document) => document.type?.code === DOCUMENT_TYPE.PROPOSAL_MAP);
this.noticeOfPublicHearing = documents.filter(
(document) => document.type?.code === DOCUMENT_TYPE.PROOF_OF_ADVERTISING
);
this.proofOfSignage = documents.filter((document) => document.type?.code === DOCUMENT_TYPE.PROOF_OF_SIGNAGE);
this.reportOfPublicHearing = documents.filter(
(document) => document.type?.code === DOCUMENT_TYPE.REPORT_OF_PUBLIC_HEARING
);
});

this.authenticationService.$currentProfile.pipe(takeUntil(this.$destroy)).subscribe((userProfile) => {
if (userProfile) {
this.isGovernmentUser = userProfile?.isLocalGovernment || userProfile?.isFirstNationGovernment;
this.governmentName = userProfile.government;

// @ts-ignore Angular / Typescript hate dynamic controls
this.form.addControl('isLFNGOwnerOfAllParcels', this.governmentOwnsAllParcels);
}
});
}

Expand All @@ -78,16 +114,22 @@ export class InclProposalComponent extends FilesStepComponent implements OnInit,
const purpose = this.purpose.value;
const inclAgricultureSupport = this.agSupport.value;
const inclImprovements = this.improvements.value;
const inclGovernmentOwnsAllParcels = this.governmentOwnsAllParcels.value;

const updateDto: ApplicationSubmissionUpdateDto = {
inclExclHectares: inclExclHectares ? parseFloat(inclExclHectares) : null,
purpose,
inclAgricultureSupport,
inclImprovements,
inclGovernmentOwnsAllParcels: parseStringToBoolean(inclGovernmentOwnsAllParcels),
};

const updatedApp = await this.applicationSubmissionService.updatePending(this.submissionUuid, updateDto);
this.$applicationSubmission.next(updatedApp);
}
}

onSelectLocalGovernmentParcelOwner($event: MatButtonToggleChange) {
this.disableNotificationFileUploads = $event.value === 'true';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ export interface ApplicationSubmissionDetailedDto extends ApplicationSubmissionD
inclAgricultureSupport: string | null;
inclImprovements: string | null;
exclShareGovernmentBorders: boolean | null;
inclGovernmentOwnsAllParcels: boolean | null;
}

export interface ApplicationSubmissionUpdateDto {
Expand Down Expand Up @@ -259,4 +260,5 @@ export interface ApplicationSubmissionUpdateDto {
inclAgricultureSupport?: string | null;
inclImprovements?: string | null;
exclShareGovernmentBorders?: boolean | null;
inclGovernmentOwnsAllParcels?: boolean | null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ export class ApplicationSubmissionDetailedDto extends ApplicationSubmissionDto {

@AutoMap(() => Boolean)
exclShareGovernmentBorders: boolean | null;

@AutoMap(() => Boolean)
inclGovernmentOwnsAllParcels?: boolean | null;
}

export class ApplicationSubmissionCreateDto {
Expand Down Expand Up @@ -695,4 +698,8 @@ export class ApplicationSubmissionUpdateDto {
@IsBoolean()
@IsOptional()
exclShareGovernmentBorders?: boolean | null;

@IsBoolean()
@IsOptional()
inclGovernmentOwnsAllParcels?: boolean | null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,10 @@ export class ApplicationSubmission extends Base {
@Column({ type: 'boolean', nullable: true })
exclShareGovernmentBorders: boolean | null;

@AutoMap(() => Boolean)
@Column({ type: 'boolean', nullable: true })
inclGovernmentOwnsAllParcels: boolean | null;

//END SUBMISSION FIELDS

@AutoMap(() => Application)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,10 @@ export class ApplicationSubmissionService {
updateDto.exclShareGovernmentBorders,
applicationSubmission.exclShareGovernmentBorders,
);
applicationSubmission.inclGovernmentOwnsAllParcels = filterUndefined(
updateDto.inclGovernmentOwnsAllParcels,
applicationSubmission.inclGovernmentOwnsAllParcels,
);
}

async listNaruSubtypes() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class addGovernmentInclFieldsToSubmission1690414066995
implements MigrationInterface
{
name = 'addGovernmentInclFieldsToSubmission1690414066995';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "alcs"."application_submission" ADD "incl_government_owns_all_parcels" boolean`,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "alcs"."application_submission" DROP COLUMN "incl_government_owns_all_parcels"`,
);
}
}

0 comments on commit fbd8030

Please sign in to comment.