Skip to content

Commit

Permalink
Merge pull request #2004 from bcgov/feature/ALCS-2312
Browse files Browse the repository at this point in the history
Expand Admin Conditions to Add Multiple Due Dates
  • Loading branch information
trslater authored Dec 11, 2024
2 parents 6f904c8 + 4490e2e commit 76f4812
Show file tree
Hide file tree
Showing 79 changed files with 1,923 additions and 641 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
<span class="tab-label">Application</span>
</ng-template>
<br />
<app-decision-condition-types [service]="applicationService" />
<app-decision-condition-types [service]="applicationService" [conditionService]="applicationConditionService" />
</mat-tab>

<mat-tab>
<ng-template mat-tab-label>
<span class="tab-label">Notice of Intent</span>
</ng-template>
<br />
<app-decision-condition-types [service]="noiService" />
<app-decision-condition-types [service]="noiService" [conditionService]="noiConditionService" />
</mat-tab>
</mat-tab-group>
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import { Component } from '@angular/core';
import { ApplicationDecisionConditionTypesService } from '../../../services/application/application-decision-condition-types/application-decision-condition-types.service';
import { NoticeofIntentDecisionConditionTypesService } from '../../../services/notice-of-intent/notice-of-intent-decision-condition-types/notice-of-intent-decision-condition-types.service';
import { NoticeOfIntentDecisionConditionService } from '../../../services/notice-of-intent/decision-v2/notice-of-intent-decision-condition/notice-of-intent-decision-condition.service';
import { ApplicationDecisionConditionService } from '../../../services/application/decision/application-decision-v2/application-decision-condition/application-decision-condition.service';

@Component({
selector: 'app-decision-condition-container',
templateUrl: './decision-condition-container.component.html',
styleUrls: ['./decision-condition-container.component.scss'],
})
export class DecisionConditionContainerComponent {

applicationService: ApplicationDecisionConditionTypesService;
applicationConditionService: ApplicationDecisionConditionService;
noiService: NoticeofIntentDecisionConditionTypesService;
noiConditionService: NoticeOfIntentDecisionConditionService;

constructor(
private aplicationDecisionConditionTypesService: ApplicationDecisionConditionTypesService,
private aplicationDecisionConditionService: ApplicationDecisionConditionService,
private noiDecisionConditionTypesService: NoticeofIntentDecisionConditionTypesService,
private noiDecisionConditionService: NoticeOfIntentDecisionConditionService,
) {
this.applicationService = this.aplicationDecisionConditionTypesService;
this.applicationConditionService = this.aplicationDecisionConditionService;
this.noiService = this.noiDecisionConditionTypesService;
this.noiConditionService = this.noiDecisionConditionService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,41 @@ <h4>{{ isEdit ? 'Edit' : 'Create' }} Decision Condition Type</h4>

<div class="condition-field">
<div class="condition-field-header">
<mat-checkbox formControlName="isSingleDateChecked">Single Date - Select date label</mat-checkbox>
<div>
<mat-checkbox formControlName="isDateChecked"> Date </mat-checkbox>
<app-error-message
*ngIf="!conditionTypeForm.controls['isDateChecked'].valid"
[message]="'Field contains data, cannot be removed'"
></app-error-message>
</div>

<div class="toggle" *ngIf="conditionTypeForm.get('isSingleDateChecked')?.value">
<mat-label [ngClass]="{ inactive: conditionTypeForm.get('isSingleDateRequired')?.value ?? false }"
<div class="toggle" *ngIf="conditionTypeForm.get('isDateChecked')?.value">
<mat-label [ngClass]="{ inactive: conditionTypeForm.get('isDateRequired')?.value ?? false }"
>Mark Field as Required (*)
</mat-label>
<mat-slide-toggle
formControlName="isSingleDateRequired"
aria-labelledby="isSingleDateRequiredLabel"
></mat-slide-toggle>
<mat-slide-toggle formControlName="isDateRequired" aria-labelledby="isDateRequiredLabel"></mat-slide-toggle>
</div>
</div>
<div *ngIf="conditionTypeForm.get('isSingleDateChecked')?.value">
<mat-button-toggle-group id="singleDateLabelId" formControlName="singleDateLabel" name="singleDateLabel">
<mat-button-toggle value="Due Date">Due Date</mat-button-toggle>
<mat-button-toggle value="End Date">End Date</mat-button-toggle>
</mat-button-toggle-group>
<div>
<mat-radio-group
formControlName="dateType"
*ngIf="conditionTypeForm.get('isDateChecked')?.value"
class="condition-date-types"
>
<mat-radio-button [value]="DateType.SINGLE">Single - Select date label </mat-radio-button>
<div class="condition-fields-container" *ngIf="conditionTypeForm.value.dateType === 'single'">
<mat-button-toggle-group
id="singleDateLabelId"
formControlName="singleDateLabel"
name="singleDateLabel"
class="single-date-label-toggle"
>
<mat-button-toggle value="Due Date">Due Date</mat-button-toggle>
<mat-button-toggle value="End Date">End Date</mat-button-toggle>
</mat-button-toggle-group>
</div>
<mat-radio-button [value]="DateType.MULTIPLE">Multiple - Date label will be 'Due Date'</mat-radio-button>
</mat-radio-group>
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,33 @@
flex-direction: row;
justify-content: space-between;
align-items: center;

app-error-message {
position: relative;
left: 11px;
}
}

.single-date-label-toggle {
width: 90%;
margin-left: 45px;
}

.condition-field-input {
width: 95%;
margin-left: 25px;
}

.condition-date-types {
display: flex;
flex-direction: column;
padding-left: 20px;
}

:host::ng-deep & .mat-mdc-checkbox.ng-invalid .mdc-checkbox__background {
border-color: colors.$error-color !important;
}

::ng-deep .mdc-checkbox {
--mdc-checkbox-selected-icon-color: #{colors.$primary-color};
--mdc-checkbox-selected-focus-icon-color: #{colors.$primary-color};
Expand All @@ -63,6 +83,21 @@
--mdc-switch-selected-pressed-track-color: #94c6ac61;
--mdc-switch-selected-track-color: #94c6ac61;
}

::ng-deep .mat-mdc-radio-button.mat-accent {
--mdc-radio-disabled-selected-icon-color: black;
--mdc-radio-disabled-unselected-icon-color: black;
--mdc-radio-unselected-hover-icon-color: #212121;
--mdc-radio-unselected-icon-color: rgba(0, 0, 0, 0.54);
--mdc-radio-unselected-pressed-icon-color: rgba(0, 0, 0, 0.54);
--mdc-radio-selected-focus-icon-color: #{colors.$primary-color};
--mdc-radio-selected-hover-icon-color: #{colors.$primary-color};
--mdc-radio-selected-icon-color: #{colors.$primary-color};
--mdc-radio-selected-pressed-icon-color: #{colors.$primary-color};
--mat-radio-ripple-color: black;
--mat-radio-checked-ripple-color: #{colors.$primary-color};
--mat-radio-disabled-label-color: rgba(0, 0, 0, 0.38);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import {
ApplicationDecisionConditionDto,
ApplicationDecisionConditionTypeDto,
DateLabel,
DateType,
} from '../../../../services/application/decision/application-decision-v2/application-decision-v2.dto';
import { ApplicationDecisionConditionTypesService } from '../../../../services/application/application-decision-condition-types/application-decision-condition-types.service';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { AbstractControl, AsyncValidatorFn, FormControl, FormGroup, Validators } from '@angular/forms';
import { DecisionDialogDataInterface } from '../decision-dialog-data.interface';
import { NoticeofIntentDecisionConditionTypesService } from '../../../../services/notice-of-intent/notice-of-intent-decision-condition-types/notice-of-intent-decision-condition-types.service';
import {
NoticeOfIntentDecisionConditionDto,
NoticeOfIntentDecisionConditionTypeDto,
} from '../../../../services/notice-of-intent/decision-v2/notice-of-intent-decision.dto';
import { ApplicationDecisionConditionService } from '../../../../services/application/decision/application-decision-v2/application-decision-condition/application-decision-condition.service';
import { NoticeOfIntentDecisionConditionService } from '../../../../services/notice-of-intent/decision-v2/notice-of-intent-decision-condition/notice-of-intent-decision-condition.service';
import { catchError, debounceTime, map, Observable, of, switchMap } from 'rxjs';
import { codeExistsValidator } from '../../../../shared/validators/code-exists-validator';

@Component({
Expand All @@ -16,19 +25,24 @@ import { codeExistsValidator } from '../../../../shared/validators/code-exists-v
styleUrls: ['./decision-condition-types-dialog.component.scss'],
})
export class DecisionConditionTypesDialogComponent {
// Reference for use in templates
DateType = DateType;

conditionTypeForm: FormGroup;

isLoading = false;
isEdit = false;
showWarning = false;

service: ApplicationDecisionConditionTypesService | NoticeofIntentDecisionConditionTypesService | undefined;
conditionService: ApplicationDecisionConditionService | NoticeOfIntentDecisionConditionService | undefined;

constructor(
@Inject(MAT_DIALOG_DATA) public data: DecisionDialogDataInterface | undefined,
private dialogRef: MatDialogRef<DecisionConditionTypesDialogComponent>,
) {
this.service = data?.service;
this.conditionService = data?.conditionService;
this.isEdit = !!data?.content;
this.conditionTypeForm = new FormGroup({
description: new FormControl(this.data?.content?.description ? this.data.content.description : '', [
Expand Down Expand Up @@ -61,12 +75,13 @@ export class DecisionConditionTypesDialogComponent {
administrativeFeeAmount: new FormControl(
this.data?.content?.administrativeFeeAmount ? this.data.content.administrativeFeeAmount : '',
),
isSingleDateChecked: new FormControl(
this.data?.content?.isSingleDateChecked ? this.data.content.isSingleDateChecked : false,
),
isSingleDateRequired: new FormControl(
this.data?.content?.isSingleDateRequired ? this.data.content.isSingleDateRequired : false,
isDateChecked: new FormControl(
this.data?.content?.isDateChecked ? this.data.content.isDateChecked : false,
[],
[this.conditionAsyncValidator()],
),
isDateRequired: new FormControl(this.data?.content?.isDateRequired ? this.data.content.isDateRequired : false),
dateType: new FormControl(this.data?.content?.dateType),
singleDateLabel: new FormControl(
this.data?.content?.singleDateLabel ? this.data.content.singleDateLabel : DateLabel.DUE_DATE,
),
Expand Down Expand Up @@ -95,24 +110,28 @@ export class DecisionConditionTypesDialogComponent {
async onSubmit() {
this.isLoading = true;

const dto: ApplicationDecisionConditionTypeDto | NoticeofIntentDecisionConditionTypesService = {
const dto: ApplicationDecisionConditionTypeDto | NoticeOfIntentDecisionConditionTypeDto = {
code: this.conditionTypeForm.get('code')?.value,
label: this.conditionTypeForm.get('label')?.value,
description: this.conditionTypeForm.get('description')?.value,
isActive: this.conditionTypeForm.get('isActive')?.value,
isAdministrativeFeeAmountChecked: this.conditionTypeForm.get('isAdministrativeFeeAmountChecked')?.value,
isAdministrativeFeeAmountRequired: this.conditionTypeForm.get('isAdministrativeFeeAmountRequired')?.value,
isSingleDateChecked: this.conditionTypeForm.get('isSingleDateChecked')?.value,
isSingleDateRequired: this.conditionTypeForm.get('isSingleDateRequired')?.value,
singleDateLabel: this.conditionTypeForm.get('singleDateLabel')?.value,
administrativeFeeAmount: this.conditionTypeForm.get('administrativeFeeAmount')?.value,
isDateChecked: this.conditionTypeForm.get('isDateChecked')?.value,
isDateRequired: this.conditionTypeForm.get('isDateRequired')?.value,
dateType: this.conditionTypeForm.get('dateType')?.value,
singleDateLabel:
this.conditionTypeForm.get('dateType')?.value === DateType.SINGLE
? this.conditionTypeForm.get('singleDateLabel')?.value
: null,
isSecurityAmountChecked: this.conditionTypeForm.get('isSecurityAmountChecked')?.value,
isSecurityAmountRequired: this.conditionTypeForm.get('isSecurityAmountRequired')?.value,
};

if (this.conditionTypeForm.get('administrativeFeeAmount')?.value !== '') {
dto.administrativeFeeAmount = this.conditionTypeForm.get('administrativeFeeAmount')?.value;
}

if (!this.service) return;
if (this.isEdit) {
await this.service.update(dto.code, dto);
Expand All @@ -122,4 +141,30 @@ export class DecisionConditionTypesDialogComponent {
this.isLoading = false;
this.dialogRef.close(true);
}

conditionAsyncValidator(): AsyncValidatorFn {
return (control: AbstractControl): Observable<{ [key: string]: any } | null> => {
return of(control.value).pipe(
debounceTime(300),
switchMap((isDateChecked) => {
if (!this.conditionService) {
throw Error('Condition service not found');
}
return this.conditionService.fetchByTypeCode(this.conditionTypeForm.controls['code'].value);
}),
map((conditions) =>
!control.value && conditions && this.hasAnyDates(conditions) ? { hasConditions: true } : null,
),
catchError((e) => {
return of({ hasConditions: true });
}),
);
};
}

hasAnyDates(
conditions: Partial<ApplicationDecisionConditionDto>[] | Partial<NoticeOfIntentDecisionConditionDto>[],
): boolean {
return conditions.some((condition) => condition.dates && condition.dates.length > 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { DecisionConditionTypesDialogComponent } from './decision-condition-type
import { ApplicationDecisionConditionTypesService } from '../../../services/application/application-decision-condition-types/application-decision-condition-types.service';
import { NoticeofIntentDecisionConditionTypesService } from '../../../services/notice-of-intent/notice-of-intent-decision-condition-types/notice-of-intent-decision-condition-types.service';
import { NoticeOfIntentDecisionConditionTypeDto } from '../../../services/notice-of-intent/decision-v2/notice-of-intent-decision.dto';
import { ApplicationDecisionConditionService } from '../../../services/application/decision/application-decision-v2/application-decision-condition/application-decision-condition.service';
import { NoticeOfIntentDecisionConditionService } from '../../../services/notice-of-intent/decision-v2/notice-of-intent-decision-condition/notice-of-intent-decision-condition.service';

@Component({
selector: 'app-decision-condition-types',
Expand All @@ -19,6 +21,11 @@ export class DecisionConditionTypesComponent implements OnInit {
| NoticeofIntentDecisionConditionTypesService
| undefined;

@Input() public conditionService:
| ApplicationDecisionConditionService
| NoticeOfIntentDecisionConditionService
| undefined;

destroy = new Subject<void>();

decisionConditionTypeDtos: ApplicationDecisionConditionTypeDto[] | NoticeOfIntentDecisionConditionTypeDto[] = [];
Expand All @@ -45,6 +52,7 @@ export class DecisionConditionTypesComponent implements OnInit {
width: '70%',
data: {
service: this.service,
conditionService: this.conditionService,
existingCodes: this.decisionConditionTypeDtos.map((dct) => dct.code),
},
});
Expand All @@ -62,6 +70,7 @@ export class DecisionConditionTypesComponent implements OnInit {
width: '70%',
data: {
service: this.service,
conditionService: this.conditionService,
content: dto,
existingCodes: this.decisionConditionTypeDtos.map((dct) => dct.code),
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { NoticeofIntentDecisionConditionTypesService } from '../../../services/notice-of-intent/notice-of-intent-decision-condition-types/notice-of-intent-decision-condition-types.service';
import { ApplicationDecisionConditionTypesService } from '../../../services/application/application-decision-condition-types/application-decision-condition-types.service';
import { ApplicationDecisionConditionTypeDto } from '../../../services/application/decision/application-decision-v2/application-decision-v2.dto';
import { ApplicationDecisionConditionService } from '../../../services/application/decision/application-decision-v2/application-decision-condition/application-decision-condition.service';
import { NoticeOfIntentDecisionConditionService } from '../../../services/notice-of-intent/decision-v2/notice-of-intent-decision-condition/notice-of-intent-decision-condition.service';

export interface DecisionDialogDataInterface {
service: ApplicationDecisionConditionTypesService | NoticeofIntentDecisionConditionTypesService;
conditionService: ApplicationDecisionConditionService | NoticeOfIntentDecisionConditionService;
content: ApplicationDecisionConditionTypeDto;
existingCodes: string[];
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<div class="header">
<h4 *ngIf="condition.type">{{ condition.type.label }}</h4>
<ng-container *ngIf="conditionStatus === CONDITION_STATUS.INCOMPLETE">
<ng-container *ngIf="condition.status === CONDITION_STATUS.INCOMPLETE">
<app-application-type-pill [type]="incompleteLabel"></app-application-type-pill>
</ng-container>
<ng-container *ngIf="conditionStatus === CONDITION_STATUS.COMPLETE">
<ng-container *ngIf="condition.status === CONDITION_STATUS.COMPLETE">
<app-application-type-pill [type]="completeLabel"></app-application-type-pill>
</ng-container>
</div>
Expand All @@ -28,20 +28,6 @@ <h4 *ngIf="condition.type">{{ condition.type.label }}</h4>
<app-no-data *ngIf="condition.securityAmount === null || condition.securityAmount === undefined"></app-no-data>
</div>

<div *ngIf="showSingleDateField">
<div class="subheading2">{{ singleDateLabel }}</div>
{{ singleDateFormated }}
<app-no-data *ngIf="condition.singleDate === null || condition.singleDate === undefined"></app-no-data>
</div>

<div>
<div class="subheading2">Completion Date</div>
<app-inline-datepicker
[value]="condition.completionDate ?? undefined"
(save)="onUpdateCondition('completionDate', $event)"
></app-inline-datepicker>
</div>

<div *ngIf="isRequireSurveyPlan && planNumbers.length > 0" class="full-width grid-3">
<ng-container *ngFor="let plan of planNumbers">
<div class="full-width">
Expand Down
Loading

0 comments on commit 76f4812

Please sign in to comment.