From 7e2e95fb32e20dafbc7f7d07d30254dcf53b8114 Mon Sep 17 00:00:00 2001 From: Felipe Barreta Date: Wed, 4 Dec 2024 12:00:16 -0800 Subject: [PATCH 01/10] ALCS-1858 Added string index --- .../conditions/condition/condition.component.html | 2 +- .../decision/conditions/condition/condition.component.ts | 4 ++++ .../decision/conditions/conditions.component.html | 5 +++-- alcs-frontend/src/app/shared/utils/count-to-string.ts | 8 ++++++++ 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 alcs-frontend/src/app/shared/utils/count-to-string.ts diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html index a7f9f54e8b..8ea06f4d1d 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html @@ -1,5 +1,5 @@
-

{{ condition.type.label }}

+

{{ stringIndex }}. {{ condition.type.label }}

diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts index 4e77b18c9c..a8a27a075e 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts @@ -18,6 +18,7 @@ import { CONDITION_STATUS, } from '../conditions.component'; import { environment } from '../../../../../../environments/environment'; +import { countToString } from '../../../../../shared/utils/count-to-string'; type Condition = ApplicationDecisionConditionWithStatus & { @@ -34,6 +35,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { @Input() condition!: Condition; @Input() isDraftDecision!: boolean; @Input() fileNumber!: string; + @Input() index!: number; incompleteLabel = DECISION_CONDITION_INCOMPLETE_LABEL; completeLabel = DECISION_CONDITION_COMPLETE_LABEL; @@ -43,6 +45,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { showAdmFeeField = false; showSecurityAmountField = false; singleDateFormated: string | undefined = undefined; + stringIndex: string = ''; CONDITION_STATUS = CONDITION_STATUS; @@ -60,6 +63,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { ngOnInit() { this.updateStatus(); + this.stringIndex = countToString(this.index); if (this.condition) { this.singleDateFormated = this.condition.singleDate ? moment(this.condition.singleDate).format(environment.dateFormat) : undefined; this.singleDateLabel = this.condition.type?.singleDateLabel ? this.condition.type?.singleDateLabel : 'End Date'; diff --git a/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.html b/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.html index f7179c26ad..40bc50ee5d 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.html +++ b/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.html @@ -9,7 +9,7 @@

View Conditions

[routerLink]="['../../']" [queryParams]="{ uuid: decision.uuid }" > - back to decision view + back to decision #{{ decision.index }}
@@ -28,7 +28,7 @@

View Conditions

-
Decision #{{ decision.index }}
+
Decision
calendar_month @@ -63,6 +63,7 @@
Decision #{{ decision.index }}
[condition]="condition" [isDraftDecision]="decision.isDraft" [fileNumber]="fileNumber" + [index]="j+1" >
diff --git a/alcs-frontend/src/app/shared/utils/count-to-string.ts b/alcs-frontend/src/app/shared/utils/count-to-string.ts new file mode 100644 index 0000000000..8ef2757e63 --- /dev/null +++ b/alcs-frontend/src/app/shared/utils/count-to-string.ts @@ -0,0 +1,8 @@ +export const countToString = (count: number) => { + var arr = []; + while(count >> 0 > 0){ + arr.unshift(String.fromCharCode(97 + --count % 26)); + count /= 26 + } + return arr.join("") +}; From c37f6ac23fdaad2dba2f89779318d0fd4aec1b4f Mon Sep 17 00:00:00 2001 From: Felipe Barreta Date: Thu, 5 Dec 2024 15:32:39 -0800 Subject: [PATCH 02/10] ALCS-1858 Pill status updates --- .../condition/condition.component.html | 7 +- .../condition/condition.component.ts | 93 ++++++++++++++-- .../conditions/conditions.component.ts | 9 +- .../decision-condition.component.ts | 6 - .../condition/condition.component.html | 9 +- .../condition/condition.component.ts | 103 ++++++++++++++++-- .../conditions/conditions.component.html | 5 +- .../conditions/conditions.component.ts | 9 +- .../decision-condition.component.ts | 6 - .../application-type-pill.constants.ts | 32 ++++++ 10 files changed, 228 insertions(+), 51 deletions(-) diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html index 9cedd2d16e..7195f99ac1 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html @@ -1,10 +1,7 @@

{{ stringIndex }}. {{ condition.type.label }}

- - - - - + +
diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts index bd49317ec2..c7fb2560bd 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts @@ -12,7 +12,10 @@ import { } from '../../../../../services/application/decision/application-decision-v2/application-decision-v2.dto'; import { DECISION_CONDITION_COMPLETE_LABEL, - DECISION_CONDITION_INCOMPLETE_LABEL, + DECISION_CONDITION_ONGOING_LABEL, + DECISION_CONDITION_PASTDUE_LABEL, + DECISION_CONDITION_PENDING_LABEL, + DECISION_CONDITION_EXPIRED_LABEL, } from '../../../../../shared/application-type-pill/application-type-pill.constants'; import { ApplicationDecisionConditionWithStatus, @@ -42,8 +45,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { dates: ApplicationDecisionConditionDateDto[] = []; - incompleteLabel = DECISION_CONDITION_INCOMPLETE_LABEL; - completeLabel = DECISION_CONDITION_COMPLETE_LABEL; + statusLabel = DECISION_CONDITION_ONGOING_LABEL; singleDateLabel = 'End Date'; showSingleDateField = false; @@ -51,6 +53,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { showSecurityAmountField = false; singleDateFormated: string | undefined = undefined; stringIndex: string = ''; + today!: number; CONDITION_STATUS = CONDITION_STATUS; @@ -63,13 +66,15 @@ export class ConditionComponent implements OnInit, AfterViewInit { constructor( private conditionService: ApplicationDecisionConditionService, private conditionLotService: ApplicationDecisionComponentToConditionLotService, - ) {} + ) { + this.today = moment().startOf('day').toDate().getTime(); + } - ngOnInit() { + async ngOnInit() { this.stringIndex = countToString(this.index); if (this.condition) { - this.fetchDates(this.condition.uuid); - + await this.fetchDates(this.condition.uuid); + this.calcStatus(); this.singleDateLabel = this.condition.type?.singleDateLabel ? this.condition.type?.singleDateLabel : 'End Date'; this.showSingleDateField = this.condition.type?.dateType === DateType.SINGLE; this.showAdmFeeField = this.condition.type?.isAdministrativeFeeAmountChecked @@ -192,7 +197,79 @@ export class ConditionComponent implements OnInit, AfterViewInit { return this.condition.conditionComponentsLabels?.find((e) => e.componentUuid === componentUuid)?.label; } - async fetchDates(uuid: string | undefined) { + calcStatus() { + if (this.dates && this.dates.length > 0) { + if (this.dates.every((date) => date.completedDate && date.completedDate <= this.today)) { + this.condition.status = CONDITION_STATUS.COMPLETE; + } else { + if (this.checkExpired()) { + this.condition.status = CONDITION_STATUS.EXPIRED; + } else if (this.checkPending()) { + this.condition.status = CONDITION_STATUS.PASTDUE; + } else if (this.checkPastDue()) { + this.condition.status = CONDITION_STATUS.PENDING; + } + } + } else { + this.condition.status = CONDITION_STATUS.ONGOING; + } + this.setPillLabel(); + } + + private checkExpired(): boolean { + const expiredDates = this.dates.filter((d) => { + if (d.date) { + return d.date <= this.today && !d.completedDate; + } + return false; + }); + return this.condition.type?.singleDateLabel === 'End Date' && expiredDates.length > 0; + } + + private checkPastDue(): boolean { + const expiredDates = this.dates.filter((d) => { + if (d.date) { + return d.date <= this.today && !d.completedDate; + } + return false; + }); + return this.condition.type?.singleDateLabel === 'Due Date' && expiredDates.length > 0; + } + + private checkPending(): boolean { + const dueDates = this.dates.filter((d) => { + if (d.date) { + return d.date >= this.today && !d.completedDate; + } + return false; + }); + return dueDates.length > 0; + } + + private setPillLabel() { + switch (this.condition.status) { + case CONDITION_STATUS.ONGOING: + this.statusLabel = DECISION_CONDITION_ONGOING_LABEL; + break; + case CONDITION_STATUS.COMPLETE: + this.statusLabel = DECISION_CONDITION_COMPLETE_LABEL; + break; + case CONDITION_STATUS.PASTDUE: + this.statusLabel = DECISION_CONDITION_PASTDUE_LABEL; + break; + case CONDITION_STATUS.PENDING: + this.statusLabel = DECISION_CONDITION_PENDING_LABEL; + break; + case CONDITION_STATUS.EXPIRED: + this.statusLabel = DECISION_CONDITION_EXPIRED_LABEL; + break; + default: + this.statusLabel = DECISION_CONDITION_ONGOING_LABEL; + break; + } + } + + private async fetchDates(uuid: string | undefined) { if (!uuid) { return; } diff --git a/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts b/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts index 14aa25ed25..4ba98c6d04 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts +++ b/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts @@ -36,8 +36,11 @@ export type ApplicationDecisionWithConditionComponentLabels = ApplicationDecisio }; export const CONDITION_STATUS = { - INCOMPLETE: 'incomplete', COMPLETE: 'complete', + ONGOING: 'ongoing', + PENDING: 'pending', + PASTDUE: 'pastdue', + EXPIRED: 'expired', }; @Component({ @@ -133,7 +136,7 @@ export class ConditionsComponent implements OnInit { conditions: ApplicationDecisionConditionWithStatus[], ) { decision.conditions = conditions.sort((a, b) => { - const order = [CONDITION_STATUS.INCOMPLETE, CONDITION_STATUS.COMPLETE]; + const order = [CONDITION_STATUS.ONGOING, CONDITION_STATUS.COMPLETE]; if (a.status === b.status) { if (a.type && b.type) { return a.type?.label.localeCompare(b.type.label); @@ -189,7 +192,7 @@ export class ConditionsComponent implements OnInit { if (dates.length > 0 && dates.every((date) => date.completedDate && date.completedDate <= this.today)) { status = CONDITION_STATUS.COMPLETE; } else if (decision.isDraft === false) { - status = CONDITION_STATUS.INCOMPLETE; + status = CONDITION_STATUS.ONGOING; } else { status = ''; } diff --git a/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts b/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts index 45e0c25b34..a14e69ec19 100644 --- a/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts +++ b/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts @@ -37,12 +37,10 @@ export class DecisionConditionComponent implements OnInit, OnChanges { securityAmount = new FormControl(null); administrativeFee = new FormControl(null); description = new FormControl(null, [Validators.required]); - singleDate = new FormControl(null, [Validators.required]); minDate = new Date(0); form = new FormGroup({ securityAmount: this.securityAmount, - singleDate: this.singleDate, administrativeFee: this.administrativeFee, description: this.description, componentsToCondition: this.componentsToCondition, @@ -57,10 +55,8 @@ export class DecisionConditionComponent implements OnInit, OnChanges { this.singleDateLabel = this.data.type?.singleDateLabel ? this.data.type?.singleDateLabel : 'End Date'; this.showSingleDateField = this.data.type?.dateType === DateType.SINGLE; if (this.data.type?.isDateRequired) { - this.singleDate.addValidators(Validators.required); this.isShowSingleDateRequired = true; } else { - this.singleDate.removeValidators(Validators.required); this.isShowSingleDateRequired = false; } @@ -112,7 +108,6 @@ export class DecisionConditionComponent implements OnInit, OnChanges { ? this.data.administrativeFee?.toString() : this.data.type?.administrativeFeeAmount?.toString(), description: this.data.description ?? null, - singleDate: this.data.singleDate ? new Date(this.data.singleDate) : undefined, }); } @@ -133,7 +128,6 @@ export class DecisionConditionComponent implements OnInit, OnChanges { administrativeFee: this.administrativeFee.value !== null ? parseFloat(this.administrativeFee.value) : undefined, description: this.description.value ?? undefined, componentsToCondition: selectedOptions, - singleDate: this.singleDate.value ? formatDateForApi(this.singleDate.value) : undefined, }); }); } diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html index 5cfa48d95c..c25333bc33 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html @@ -1,10 +1,7 @@
-

{{ condition.type.label }}

- - - - - +

{{ stringIndex }}. {{ condition.type.label }}

+ +
diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts index 6eb14498ac..d4d7b3049a 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts @@ -7,12 +7,15 @@ import { } from '../../../../../services/notice-of-intent/decision-v2/notice-of-intent-decision.dto'; import { DECISION_CONDITION_COMPLETE_LABEL, - DECISION_CONDITION_INCOMPLETE_LABEL, - DECISION_CONDITION_SUPERSEDED_LABEL, + DECISION_CONDITION_ONGOING_LABEL, + DECISION_CONDITION_PASTDUE_LABEL, + DECISION_CONDITION_PENDING_LABEL, + DECISION_CONDITION_EXPIRED_LABEL, } from '../../../../../shared/application-type-pill/application-type-pill.constants'; import { CONDITION_STATUS, ConditionComponentLabels, DecisionConditionWithStatus } from '../conditions.component'; import { environment } from '../../../../../../environments/environment'; -import { DateType } from 'src/app/services/application/decision/application-decision-v2/application-decision-v2.dto'; +import { DateType } from '../../../../../services/application/decision/application-decision-v2/application-decision-v2.dto'; +import { countToString } from '../../../../../shared/utils/count-to-string'; type Condition = DecisionConditionWithStatus & { componentLabelsStr?: string; @@ -28,14 +31,13 @@ export class ConditionComponent implements OnInit, AfterViewInit { @Input() condition!: Condition; @Input() isDraftDecision!: boolean; @Input() fileNumber!: string; + @Input() index!: number; DateType = DateType; dates: NoticeOfIntentDecisionConditionDateDto[] = []; - incompleteLabel = DECISION_CONDITION_INCOMPLETE_LABEL; - completeLabel = DECISION_CONDITION_COMPLETE_LABEL; - supersededLabel = DECISION_CONDITION_SUPERSEDED_LABEL; + statusLabel = DECISION_CONDITION_ONGOING_LABEL; singleDateLabel = 'End Date'; showSingleDateField = false; @@ -48,12 +50,18 @@ export class ConditionComponent implements OnInit, AfterViewInit { isReadMoreClicked = false; isReadMoreVisible = false; conditionStatus: string = ''; + stringIndex: string = ''; + today!: number; - constructor(private conditionService: NoticeOfIntentDecisionConditionService) {} + constructor(private conditionService: NoticeOfIntentDecisionConditionService) { + this.today = moment().startOf('day').toDate().getTime(); + } - ngOnInit() { + async ngOnInit() { + this.stringIndex = countToString(this.index); if (this.condition) { - this.fetchDates(this.condition.uuid); + await this.fetchDates(this.condition.uuid); + this.calcStatus(); this.singleDateLabel = this.condition.type?.singleDateLabel ? this.condition.type?.singleDateLabel : 'End Date'; this.showSingleDateField = this.condition.type?.dateType === DateType.SINGLE; @@ -104,6 +112,78 @@ export class ConditionComponent implements OnInit, AfterViewInit { return this.isReadMoreClicked || this.isEllipsisActive(this.condition.uuid + 'Description'); } + calcStatus() { + if (this.dates && this.dates.length > 0) { + if (this.dates.every((date) => date.completedDate && date.completedDate <= this.today)) { + this.condition.status = CONDITION_STATUS.COMPLETE; + } else { + if (this.checkExpired()) { + this.condition.status = CONDITION_STATUS.EXPIRED; + } else if (this.checkPending()) { + this.condition.status = CONDITION_STATUS.PASTDUE; + } else if (this.checkPastDue()) { + this.condition.status = CONDITION_STATUS.PENDING; + } + } + } else { + this.condition.status = CONDITION_STATUS.ONGOING; + } + this.setPillLabel(); + } + + private checkExpired(): boolean { + const expiredDates = this.dates.filter((d) => { + if (d.date) { + return d.date <= this.today && !d.completedDate; + } + return false; + }); + return this.condition.type?.singleDateLabel === 'End Date' && expiredDates.length > 0; + } + + private checkPastDue(): boolean { + const expiredDates = this.dates.filter((d) => { + if (d.date) { + return d.date <= this.today && !d.completedDate; + } + return false; + }); + return this.condition.type?.singleDateLabel === 'Due Date' && expiredDates.length > 0; + } + + private checkPending(): boolean { + const dueDates = this.dates.filter((d) => { + if (d.date) { + return d.date >= this.today && !d.completedDate; + } + return false; + }); + return dueDates.length > 0; + } + + private setPillLabel() { + switch (this.condition.status) { + case CONDITION_STATUS.ONGOING: + this.statusLabel = DECISION_CONDITION_ONGOING_LABEL; + break; + case CONDITION_STATUS.COMPLETE: + this.statusLabel = DECISION_CONDITION_COMPLETE_LABEL; + break; + case CONDITION_STATUS.PASTDUE: + this.statusLabel = DECISION_CONDITION_PASTDUE_LABEL; + break; + case CONDITION_STATUS.PENDING: + this.statusLabel = DECISION_CONDITION_PENDING_LABEL; + break; + case CONDITION_STATUS.EXPIRED: + this.statusLabel = DECISION_CONDITION_EXPIRED_LABEL; + break; + default: + this.statusLabel = DECISION_CONDITION_ONGOING_LABEL; + break; + } + } + async fetchDates(uuid: string | undefined) { if (!uuid) { return; @@ -111,8 +191,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { this.dates = await this.conditionService.getDates(uuid); - this.singleDateFormated = this.dates[0].date - ? moment(this.dates[0].date).format(environment.dateFormat) - : undefined; + this.singleDateFormated = + this.dates[0] && this.dates[0].date ? moment(this.dates[0].date).format(environment.dateFormat) : undefined; } } diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.html b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.html index f4927fbbe4..4c838a3241 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.html +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.html @@ -9,7 +9,7 @@

View Conditions

[routerLink]="['../../']" [queryParams]="{ uuid: decision.uuid }" > - back to decision view + back to decision #{{ decision.index }}
@@ -23,7 +23,7 @@

View Conditions

-
Decision #{{ decision.index }}
+
Decision
calendar_month @@ -55,6 +55,7 @@
Decision #{{ decision.index }}
[condition]="condition" [isDraftDecision]="decision.isDraft" [fileNumber]="fileNumber" + [index]="j+1" >
diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.ts index f4267178f4..cb596e005c 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.ts @@ -34,8 +34,11 @@ export type DecisionWithConditionComponentLabels = NoticeOfIntentDecisionWithLin }; export const CONDITION_STATUS = { - INCOMPLETE: 'incomplete', COMPLETE: 'complete', + ONGOING: 'ongoing', + PENDING: 'pending', + PASTDUE: 'pastdue', + EXPIRED: 'expired', }; @Component({ @@ -129,7 +132,7 @@ export class ConditionsComponent implements OnInit { conditions: DecisionConditionWithStatus[], ) { decision.conditions = conditions.sort((a, b) => { - const order = [CONDITION_STATUS.INCOMPLETE, CONDITION_STATUS.COMPLETE]; + const order = [CONDITION_STATUS.ONGOING, CONDITION_STATUS.COMPLETE]; if (a.status === b.status) { if (a.type && b.type) { return a.type?.label.localeCompare(b.type.label); @@ -185,7 +188,7 @@ export class ConditionsComponent implements OnInit { if (dates.length > 0 && dates.every((date) => date.completedDate && date.completedDate <= this.today)) { status = CONDITION_STATUS.COMPLETE; } else if (decision.isDraft === false) { - status = CONDITION_STATUS.INCOMPLETE; + status = CONDITION_STATUS.ONGOING; } else { status = ''; } diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts index a7bfb30996..d0a1447f90 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts @@ -31,12 +31,10 @@ export class DecisionConditionComponent implements OnInit, OnChanges { securityAmount = new FormControl(null); administrativeFee = new FormControl(null); description = new FormControl(null, [Validators.required]); - singleDate = new FormControl(null, [Validators.required]); minDate = new Date(0); form = new FormGroup({ securityAmount: this.securityAmount, - singleDate: this.singleDate, administrativeFee: this.administrativeFee, description: this.description, componentsToCondition: this.componentsToCondition, @@ -47,10 +45,8 @@ export class DecisionConditionComponent implements OnInit, OnChanges { this.singleDateLabel = this.data.type?.singleDateLabel ? this.data.type?.singleDateLabel : 'End Date'; this.showSingleDateField = this.data.type?.dateType === DateType.SINGLE; if (this.data.type?.isDateRequired) { - this.singleDate.addValidators(Validators.required); this.isShowSingleDateRequired = true; } else { - this.singleDate.removeValidators(Validators.required); this.isShowSingleDateRequired = false; } @@ -102,7 +98,6 @@ export class DecisionConditionComponent implements OnInit, OnChanges { ? this.data.administrativeFee?.toString() : this.data.type?.administrativeFeeAmount?.toString(), description: this.data.description ?? null, - singleDate: this.data.singleDate ? new Date(this.data.singleDate) : undefined, }); } @@ -124,7 +119,6 @@ export class DecisionConditionComponent implements OnInit, OnChanges { administrativeFee: this.administrativeFee.value !== null ? parseFloat(this.administrativeFee.value) : undefined, description: this.description.value ?? undefined, componentsToCondition: selectedOptions, - singleDate: this.singleDate.value ? formatDateForApi(this.singleDate.value) : undefined, }); }); } diff --git a/alcs-frontend/src/app/shared/application-type-pill/application-type-pill.constants.ts b/alcs-frontend/src/app/shared/application-type-pill/application-type-pill.constants.ts index f75fd2d12c..1f4ba804f6 100644 --- a/alcs-frontend/src/app/shared/application-type-pill/application-type-pill.constants.ts +++ b/alcs-frontend/src/app/shared/application-type-pill/application-type-pill.constants.ts @@ -54,6 +54,14 @@ export const DECISION_CONDITION_INCOMPLETE_LABEL = { textColor: '#000', }; +export const DECISION_CONDITION_ONGOING_LABEL = { + label: 'Ongoing', + shortLabel: 'ONGO', + backgroundColor: '#fff', + borderColor: '#003366', + textColor: '#000', +}; + export const DECISION_CONDITION_COMPLETE_LABEL = { label: 'Complete', shortLabel: 'COMD', @@ -62,6 +70,30 @@ export const DECISION_CONDITION_COMPLETE_LABEL = { textColor: '#000', }; +export const DECISION_CONDITION_PASTDUE_LABEL = { + label: 'Past Due', + shortLabel: 'PAST', + backgroundColor: '#fff', + borderColor: '#fcba19', + textColor: '#000', +}; + +export const DECISION_CONDITION_PENDING_LABEL = { + label: 'Pending', + shortLabel: 'PEND', + backgroundColor: '#fff', + borderColor: '#A7C7E8', + textColor: '#000', +}; + +export const DECISION_CONDITION_EXPIRED_LABEL = { + label: 'Expired', + shortLabel: 'EXPI', + backgroundColor: '#fff', + borderColor: '#c6242a', + textColor: '#000', +}; + export const DECISION_CONDITION_SUPERSEDED_LABEL = { label: 'Superseded', shortLabel: 'SUPD', From 754a3a85b9c0dcaf16a4f3dcecd1b0c2beefa739 Mon Sep 17 00:00:00 2001 From: Felipe Barreta Date: Mon, 9 Dec 2024 11:35:01 -0800 Subject: [PATCH 03/10] ALCS-1858 Call function to retrieve condition status --- .../condition/condition.component.html | 4 +- .../condition/condition.component.ts | 66 ++----- .../conditions/conditions.component.ts | 10 +- .../condition/condition.component.html | 4 +- .../condition/condition.component.ts | 67 ++----- .../application-condition-status.dto.ts | 4 + .../application-decision-v2.service.ts | 10 ++ .../notice-of-intent-decision-status.dto.ts | 4 + .../notice-of-intent-decision-v2.service.ts | 10 ++ .../application-condition-status.dto.ts | 4 + .../application-decision-v2.controller.ts | 11 ++ .../application-decision-v2.service.ts | 7 +- .../notice-of-intent-condition-status.dto.ts | 4 + ...notice-of-intent-decision-v2.controller.ts | 11 ++ .../notice-of-intent-decision-v2.service.ts | 7 +- ...433153-create_condition_status_function.ts | 163 ++++++++++++++++++ 16 files changed, 264 insertions(+), 122 deletions(-) create mode 100644 alcs-frontend/src/app/services/application/decision/application-decision-v2/application-condition-status.dto.ts create mode 100644 alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-status.dto.ts create mode 100644 services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-condition-status.dto.ts create mode 100644 services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-condition-status.dto.ts create mode 100644 services/apps/alcs/src/providers/typeorm/migrations/1733518433153-create_condition_status_function.ts diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html index 7195f99ac1..5b2532ad67 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html @@ -1,8 +1,6 @@

{{ stringIndex }}. {{ condition.type.label }}

- - - +
diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts index c7fb2560bd..69d5757aad 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts @@ -24,6 +24,7 @@ import { } from '../conditions.component'; import { environment } from '../../../../../../environments/environment'; import { countToString } from '../../../../../shared/utils/count-to-string'; +import { ApplicationDecisionV2Service } from '../../../../../services/application/decision/application-decision-v2/application-decision-v2.service'; type Condition = ApplicationDecisionConditionWithStatus & { componentLabelsStr?: string; @@ -66,6 +67,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { constructor( private conditionService: ApplicationDecisionConditionService, private conditionLotService: ApplicationDecisionComponentToConditionLotService, + private decisionService: ApplicationDecisionV2Service, ) { this.today = moment().startOf('day').toDate().getTime(); } @@ -197,70 +199,26 @@ export class ConditionComponent implements OnInit, AfterViewInit { return this.condition.conditionComponentsLabels?.find((e) => e.componentUuid === componentUuid)?.label; } - calcStatus() { - if (this.dates && this.dates.length > 0) { - if (this.dates.every((date) => date.completedDate && date.completedDate <= this.today)) { - this.condition.status = CONDITION_STATUS.COMPLETE; - } else { - if (this.checkExpired()) { - this.condition.status = CONDITION_STATUS.EXPIRED; - } else if (this.checkPending()) { - this.condition.status = CONDITION_STATUS.PASTDUE; - } else if (this.checkPastDue()) { - this.condition.status = CONDITION_STATUS.PENDING; - } - } - } else { - this.condition.status = CONDITION_STATUS.ONGOING; - } - this.setPillLabel(); - } - - private checkExpired(): boolean { - const expiredDates = this.dates.filter((d) => { - if (d.date) { - return d.date <= this.today && !d.completedDate; - } - return false; - }); - return this.condition.type?.singleDateLabel === 'End Date' && expiredDates.length > 0; - } - - private checkPastDue(): boolean { - const expiredDates = this.dates.filter((d) => { - if (d.date) { - return d.date <= this.today && !d.completedDate; - } - return false; - }); - return this.condition.type?.singleDateLabel === 'Due Date' && expiredDates.length > 0; - } - - private checkPending(): boolean { - const dueDates = this.dates.filter((d) => { - if (d.date) { - return d.date >= this.today && !d.completedDate; - } - return false; - }); - return dueDates.length > 0; + async calcStatus() { + const conditionStatus = await this.decisionService.getStatus(this.condition.uuid); + this.setPillLabel(conditionStatus.status); } - private setPillLabel() { - switch (this.condition.status) { - case CONDITION_STATUS.ONGOING: + private setPillLabel(status: string) { + switch (status) { + case 'ONGOING': this.statusLabel = DECISION_CONDITION_ONGOING_LABEL; break; - case CONDITION_STATUS.COMPLETE: + case 'COMPLETED': this.statusLabel = DECISION_CONDITION_COMPLETE_LABEL; break; - case CONDITION_STATUS.PASTDUE: + case 'PASTDUE': this.statusLabel = DECISION_CONDITION_PASTDUE_LABEL; break; - case CONDITION_STATUS.PENDING: + case 'PENDING': this.statusLabel = DECISION_CONDITION_PENDING_LABEL; break; - case CONDITION_STATUS.EXPIRED: + case 'EXPIRED': this.statusLabel = DECISION_CONDITION_EXPIRED_LABEL; break; default: diff --git a/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts b/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts index 4ba98c6d04..12c0116c9f 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts +++ b/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts @@ -36,11 +36,11 @@ export type ApplicationDecisionWithConditionComponentLabels = ApplicationDecisio }; export const CONDITION_STATUS = { - COMPLETE: 'complete', - ONGOING: 'ongoing', - PENDING: 'pending', - PASTDUE: 'pastdue', - EXPIRED: 'expired', + COMPLETE: 'COMPLETE', + ONGOING: 'ONGOING', + PENDING: 'PENDING', + PASTDUE: 'PASTDUE', + EXPIRED: 'EXPIRED', }; @Component({ diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html index c25333bc33..8d57b4258a 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html @@ -1,8 +1,6 @@

{{ stringIndex }}. {{ condition.type.label }}

- - - +
diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts index d4d7b3049a..a512f3d09c 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts @@ -16,6 +16,7 @@ import { CONDITION_STATUS, ConditionComponentLabels, DecisionConditionWithStatus import { environment } from '../../../../../../environments/environment'; import { DateType } from '../../../../../services/application/decision/application-decision-v2/application-decision-v2.dto'; import { countToString } from '../../../../../shared/utils/count-to-string'; +import { NoticeOfIntentDecisionV2Service } from '../../../../../services/notice-of-intent/decision-v2/notice-of-intent-decision-v2.service'; type Condition = DecisionConditionWithStatus & { componentLabelsStr?: string; @@ -53,7 +54,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { stringIndex: string = ''; today!: number; - constructor(private conditionService: NoticeOfIntentDecisionConditionService) { + constructor(private conditionService: NoticeOfIntentDecisionConditionService, private decisionService: NoticeOfIntentDecisionV2Service,) { this.today = moment().startOf('day').toDate().getTime(); } @@ -112,70 +113,26 @@ export class ConditionComponent implements OnInit, AfterViewInit { return this.isReadMoreClicked || this.isEllipsisActive(this.condition.uuid + 'Description'); } - calcStatus() { - if (this.dates && this.dates.length > 0) { - if (this.dates.every((date) => date.completedDate && date.completedDate <= this.today)) { - this.condition.status = CONDITION_STATUS.COMPLETE; - } else { - if (this.checkExpired()) { - this.condition.status = CONDITION_STATUS.EXPIRED; - } else if (this.checkPending()) { - this.condition.status = CONDITION_STATUS.PASTDUE; - } else if (this.checkPastDue()) { - this.condition.status = CONDITION_STATUS.PENDING; - } - } - } else { - this.condition.status = CONDITION_STATUS.ONGOING; - } - this.setPillLabel(); - } - - private checkExpired(): boolean { - const expiredDates = this.dates.filter((d) => { - if (d.date) { - return d.date <= this.today && !d.completedDate; - } - return false; - }); - return this.condition.type?.singleDateLabel === 'End Date' && expiredDates.length > 0; - } - - private checkPastDue(): boolean { - const expiredDates = this.dates.filter((d) => { - if (d.date) { - return d.date <= this.today && !d.completedDate; - } - return false; - }); - return this.condition.type?.singleDateLabel === 'Due Date' && expiredDates.length > 0; - } - - private checkPending(): boolean { - const dueDates = this.dates.filter((d) => { - if (d.date) { - return d.date >= this.today && !d.completedDate; - } - return false; - }); - return dueDates.length > 0; + async calcStatus() { + const conditionStatus = await this.decisionService.getStatus(this.condition.uuid); + this.setPillLabel(conditionStatus.status); } - private setPillLabel() { - switch (this.condition.status) { - case CONDITION_STATUS.ONGOING: + private setPillLabel(status: string) { + switch (status) { + case 'ONGOING': this.statusLabel = DECISION_CONDITION_ONGOING_LABEL; break; - case CONDITION_STATUS.COMPLETE: + case 'COMPLETED': this.statusLabel = DECISION_CONDITION_COMPLETE_LABEL; break; - case CONDITION_STATUS.PASTDUE: + case 'PASTDUE': this.statusLabel = DECISION_CONDITION_PASTDUE_LABEL; break; - case CONDITION_STATUS.PENDING: + case 'PENDING': this.statusLabel = DECISION_CONDITION_PENDING_LABEL; break; - case CONDITION_STATUS.EXPIRED: + case 'EXPIRED': this.statusLabel = DECISION_CONDITION_EXPIRED_LABEL; break; default: diff --git a/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-condition-status.dto.ts b/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-condition-status.dto.ts new file mode 100644 index 0000000000..b8398d0b05 --- /dev/null +++ b/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-condition-status.dto.ts @@ -0,0 +1,4 @@ +export interface ApplicationDecisionStatus { + uuid: string; + status: string; +} diff --git a/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-decision-v2.service.ts b/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-decision-v2.service.ts index 6f99fa2aae..5b3a1daece 100644 --- a/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-decision-v2.service.ts +++ b/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-decision-v2.service.ts @@ -12,6 +12,7 @@ import { CreateApplicationDecisionDto, UpdateApplicationDecisionDto, } from './application-decision-v2.dto'; +import { ApplicationDecisionStatus } from './application-condition-status.dto'; @Injectable({ providedIn: 'root', @@ -40,6 +41,15 @@ export class ApplicationDecisionV2Service { return decisions; } + async getStatus(conditionUuid: string): Promise { + try { + return await firstValueFrom(this.http.get(`${this.url}/condition/${conditionUuid}`)); + } catch (e: any) { + this.toastService.showErrorToast(e.error?.message ?? 'No status found'); + throw e; + } + } + async fetchCodes(): Promise { try { return await firstValueFrom(this.http.get(`${this.url}/codes`)); diff --git a/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-status.dto.ts b/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-status.dto.ts new file mode 100644 index 0000000000..2c4e707330 --- /dev/null +++ b/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-status.dto.ts @@ -0,0 +1,4 @@ +export interface NoticeOfIntentDecisionStatus { + uuid: string; + status: string; +} diff --git a/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-v2.service.ts b/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-v2.service.ts index 9d652007c7..fd2588fcfd 100644 --- a/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-v2.service.ts +++ b/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-v2.service.ts @@ -12,6 +12,7 @@ import { NoticeOfIntentDecisionWithLinkedResolutionDto, UpdateNoticeOfIntentDecisionDto, } from './notice-of-intent-decision.dto'; +import { NoticeOfIntentDecisionStatus } from './notice-of-intent-decision-status.dto'; @Injectable({ providedIn: 'root', @@ -40,6 +41,15 @@ export class NoticeOfIntentDecisionV2Service { return decisions; } + async getStatus(conditionUuid: string): Promise { + try { + return await firstValueFrom(this.http.get(`${this.url}/condition/${conditionUuid}`)); + } catch (e: any) { + this.toastService.showErrorToast(e.error?.message ?? 'No status found'); + throw e; + } + } + async fetchCodes(): Promise { try { return await firstValueFrom(this.http.get(`${this.url}/codes`)); diff --git a/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-condition-status.dto.ts b/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-condition-status.dto.ts new file mode 100644 index 0000000000..dff9c5fd1f --- /dev/null +++ b/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-condition-status.dto.ts @@ -0,0 +1,4 @@ +export class ApplicationConditionStatus { + uuid: string; + status: string; +} diff --git a/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.controller.ts b/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.controller.ts index c030043764..fb9df459d4 100644 --- a/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.controller.ts +++ b/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.controller.ts @@ -28,6 +28,7 @@ import { import { CeoCriterionCodeDto } from './ceo-criterion/ceo-criterion.dto'; import { ApplicationDecisionComponentType } from './component/application-decision-component-type.entity'; import { ApplicationDecisionComponentTypeDto } from './component/application-decision-component.dto'; +import { ApplicationConditionStatus } from './application-condition-status.dto'; @ApiOAuth2(config.get('KEYCLOAK.SCOPES')) @Controller('application-decision') @@ -48,6 +49,16 @@ export class ApplicationDecisionV2Controller { return await this.mapper.mapArrayAsync(decisions, ApplicationDecision, ApplicationDecisionDto); } + @Get('/condition/:uuid') + @UserRoles(...ANY_AUTH_ROLE) + async getConditionStatus(@Param('uuid') uuid): Promise { + const status = await this.appDecisionService.getDecisionConditionStatus(uuid); + return { + uuid: uuid, + status: status && status.length > 0 ? status[0]['get_current_status_for_application_condition'] : '', + }; + } + @Get('/codes') @UserRoles(...ANY_AUTH_ROLE) async getCodes() { diff --git a/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.service.ts b/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.service.ts index 99093c442d..41a8e49e5d 100644 --- a/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.service.ts +++ b/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.service.ts @@ -1,7 +1,7 @@ import { MultipartFile } from '@fastify/multipart'; import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { In, IsNull, LessThan, Repository } from 'typeorm'; +import { In, IsNull, LessThan, Repository, DataSource } from 'typeorm'; import { v4 } from 'uuid'; import { ServiceNotFoundException, @@ -56,6 +56,7 @@ export class ApplicationDecisionV2Service { private decisionComponentService: ApplicationDecisionComponentService, private decisionConditionService: ApplicationDecisionConditionService, private applicationSubmissionStatusService: ApplicationSubmissionStatusService, + private dataSource: DataSource, ) {} async getForPortal(fileNumber: string) { @@ -797,4 +798,8 @@ export class ApplicationDecisionV2Service { }, }); } + + async getDecisionConditionStatus(uuid: string) { + return await this.dataSource.query('SELECT alcs.get_current_status_for_application_condition($1)', [uuid]); + } } diff --git a/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-condition-status.dto.ts b/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-condition-status.dto.ts new file mode 100644 index 0000000000..db1e173a29 --- /dev/null +++ b/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-condition-status.dto.ts @@ -0,0 +1,4 @@ +export class NoticeOfIntentConditionStatus { + uuid: string; + status: string; +} diff --git a/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.controller.ts b/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.controller.ts index 239e70a3bf..d11730d5c0 100644 --- a/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.controller.ts +++ b/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.controller.ts @@ -32,6 +32,7 @@ import { import { NoticeOfIntentDecision } from '../notice-of-intent-decision.entity'; import { NoticeOfIntentModificationService } from '../notice-of-intent-modification/notice-of-intent-modification.service'; import { NoticeOfIntentDecisionV2Service } from './notice-of-intent-decision-v2.service'; +import { NoticeOfIntentConditionStatus } from './notice-of-intent-condition-status.dto'; @ApiOAuth2(config.get('KEYCLOAK.SCOPES')) @Controller('notice-of-intent-decision/v2') @@ -59,6 +60,16 @@ export class NoticeOfIntentDecisionV2Controller { ); } + @Get('/condition/:uuid') + @UserRoles(...ANY_AUTH_ROLE) + async getConditionStatus(@Param('uuid') uuid): Promise { + const status = await this.noticeOfIntentDecisionV2Service.getDecisionConditionStatus(uuid); + return { + uuid: uuid, + status: status && status.length > 0 ? status[0]['get_current_status_for_noi_condition'] : '', + }; + } + @Get('/codes') @UserRoles(...ANY_AUTH_ROLE) async getCodes() { diff --git a/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.service.ts b/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.service.ts index dc40e9aa67..277ac6bd46 100644 --- a/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.service.ts +++ b/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.service.ts @@ -5,7 +5,7 @@ import { import { MultipartFile } from '@fastify/multipart'; import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { In, IsNull, LessThan, Repository } from 'typeorm'; +import { In, IsNull, LessThan, Repository, DataSource } from 'typeorm'; import { v4 } from 'uuid'; import { DOCUMENT_SOURCE, @@ -51,6 +51,7 @@ export class NoticeOfIntentDecisionV2Service { private decisionComponentService: NoticeOfIntentDecisionComponentService, private decisionConditionService: NoticeOfIntentDecisionConditionService, private noticeOfIntentSubmissionStatusService: NoticeOfIntentSubmissionStatusService, + private dataSource: DataSource, ) {} async getForPortal(fileNumber: string) { @@ -712,4 +713,8 @@ export class NoticeOfIntentDecisionV2Service { }, }); } + + async getDecisionConditionStatus(uuid: string) { + return await this.dataSource.query('SELECT alcs.get_current_status_for_noi_condition($1)', [uuid]); + } } diff --git a/services/apps/alcs/src/providers/typeorm/migrations/1733518433153-create_condition_status_function.ts b/services/apps/alcs/src/providers/typeorm/migrations/1733518433153-create_condition_status_function.ts new file mode 100644 index 0000000000..a0f56ef8de --- /dev/null +++ b/services/apps/alcs/src/providers/typeorm/migrations/1733518433153-create_condition_status_function.ts @@ -0,0 +1,163 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class CreateConditionStatusFunction1733518433153 implements MigrationInterface { + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + CREATE OR REPLACE FUNCTION alcs.get_current_status_for_application_condition( + application_condition_uuid uuid, + OUT status VARCHAR(20) + ) + RETURNS VARCHAR(20) + LANGUAGE 'plpgsql' + COST 100 + VOLATILE PARALLEL UNSAFE + AS $BODY$ + DECLARE + dates_cursor CURSOR FOR + SELECT + t.single_date_label, d.uuid, d.date, d.comment, d.completed_date + FROM alcs.application_decision_condition_date d + INNER JOIN alcs.application_decision_condition c ON c.uuid = d.condition_uuid + INNER JOIN alcs.application_decision_condition_type t ON t.code = c.type_code + WHERE + d.condition_uuid = application_condition_uuid; + utc_timestamp_today timestamptz; + is_ongoing boolean; + is_completed boolean; + is_pastdue boolean; + is_pending boolean; + is_expired boolean; + date_record RECORD; + BEGIN + utc_timestamp_today = timezone('utc', (now())); + status = 'ONGOING'; + is_ongoing = true; + is_completed = false; + is_pastdue = false; + is_pending = false; + is_expired = false; + + OPEN dates_cursor; + + LOOP + FETCH NEXT FROM dates_cursor INTO date_record; + EXIT WHEN NOT FOUND; + is_ongoing = false; + IF (date_record.completed_date IS NOT NULL) THEN + is_completed = true; + ELSE + is_completed = false; + -- check if it's past due + IF (date_record.date <= utc_timestamp_today AND date_record.single_date_label = 'Due Date') THEN + is_pastdue = true; + END IF; + -- check if it's pending + IF (date_record.date >= utc_timestamp_today) THEN + is_pending = true; + END IF; + -- check if it's expired + IF (date_record.date <= utc_timestamp_today AND date_record.single_date_label = 'End Date') THEN + is_expired = true; + END IF; + END IF; + END LOOP; + CLOSE dates_cursor; + IF (is_completed) THEN + status = 'COMPLETED'; + ELSEIF (is_pending) THEN + status = 'PENDING'; + ELSEIF (is_expired) THEN + status = 'EXPIRED'; + ELSEIF (is_pastdue) THEN + status = 'PASTDUE'; + END IF; + END; + $BODY$; + + ALTER FUNCTION alcs.get_current_status_for_application_condition(uuid) + OWNER TO postgres; + `); + await queryRunner.query(` + CREATE OR REPLACE FUNCTION alcs.get_current_status_for_noi_condition( + noi_condition_uuid uuid, + OUT status VARCHAR(20) + ) + RETURNS VARCHAR(20) + LANGUAGE 'plpgsql' + COST 100 + VOLATILE PARALLEL UNSAFE + AS $BODY$ + DECLARE + dates_cursor CURSOR FOR + SELECT + t.single_date_label, d.uuid, d.date, d.comment, d.completed_date + FROM alcs.notice_of_intent_decision_condition_date d + INNER JOIN alcs.notice_of_intent_decision_condition c ON c.uuid = d.condition_uuid + INNER JOIN alcs.notice_of_intent_decision_condition_type t ON t.code = c.type_code + WHERE + d.condition_uuid = noi_condition_uuid; + utc_timestamp_today timestamptz; + is_ongoing boolean; + is_completed boolean; + is_pastdue boolean; + is_pending boolean; + is_expired boolean; + date_record RECORD; + BEGIN + utc_timestamp_today = timezone('utc', (now())); + status = 'ONGOING'; + is_ongoing = true; + is_completed = false; + is_pastdue = false; + is_pending = false; + is_expired = false; + + OPEN dates_cursor; + + LOOP + FETCH NEXT FROM dates_cursor INTO date_record; + EXIT WHEN NOT FOUND; + is_ongoing = false; + IF (date_record.completed_date IS NOT NULL) THEN + is_completed = true; + ELSE + is_completed = false; + -- check if it's past due + IF (date_record.date <= utc_timestamp_today AND date_record.single_date_label = 'Due Date') THEN + is_pastdue = true; + END IF; + -- check if it's pending + IF (date_record.date >= utc_timestamp_today) THEN + is_pending = true; + END IF; + -- check if it's expired + IF (date_record.date <= utc_timestamp_today AND date_record.single_date_label = 'End Date') THEN + is_expired = true; + END IF; + END IF; + END LOOP; + CLOSE dates_cursor; + IF (is_completed) THEN + status = 'COMPLETED'; + ELSEIF (is_pending) THEN + status = 'PENDING'; + ELSEIF (is_expired) THEN + status = 'EXPIRED'; + ELSEIF (is_pastdue) THEN + status = 'PASTDUE'; + END IF; + END; + $BODY$; + + ALTER FUNCTION alcs.get_current_status_for_noi_condition(uuid) + OWNER TO postgres; + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP FUNCTION IF EXISTS alcs.get_current_status_for_application_condition(uuid);`); + await queryRunner.query(`DROP FUNCTION IF EXISTS alcs.get_current_status_for_noi_condition(uuid);`); + } + +} From 4d6feb87c6fe18e7f1a86155f159fd8b024c09fc Mon Sep 17 00:00:00 2001 From: Felipe Barreta Date: Tue, 10 Dec 2024 14:32:15 -0800 Subject: [PATCH 04/10] ALCS-1858 End point naming change --- .../application-decision-v2/application-decision-v2.service.ts | 2 +- .../decision-v2/notice-of-intent-decision-v2.service.ts | 2 +- .../application-decision/application-decision-v2.controller.ts | 2 +- .../notice-of-intent-decision-v2.controller.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-decision-v2.service.ts b/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-decision-v2.service.ts index 5b3a1daece..500b7758fe 100644 --- a/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-decision-v2.service.ts +++ b/alcs-frontend/src/app/services/application/decision/application-decision-v2/application-decision-v2.service.ts @@ -43,7 +43,7 @@ export class ApplicationDecisionV2Service { async getStatus(conditionUuid: string): Promise { try { - return await firstValueFrom(this.http.get(`${this.url}/condition/${conditionUuid}`)); + return await firstValueFrom(this.http.get(`${this.url}/condition/${conditionUuid}/status`)); } catch (e: any) { this.toastService.showErrorToast(e.error?.message ?? 'No status found'); throw e; diff --git a/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-v2.service.ts b/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-v2.service.ts index fd2588fcfd..e36e13873a 100644 --- a/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-v2.service.ts +++ b/alcs-frontend/src/app/services/notice-of-intent/decision-v2/notice-of-intent-decision-v2.service.ts @@ -43,7 +43,7 @@ export class NoticeOfIntentDecisionV2Service { async getStatus(conditionUuid: string): Promise { try { - return await firstValueFrom(this.http.get(`${this.url}/condition/${conditionUuid}`)); + return await firstValueFrom(this.http.get(`${this.url}/condition/${conditionUuid}/status`)); } catch (e: any) { this.toastService.showErrorToast(e.error?.message ?? 'No status found'); throw e; diff --git a/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.controller.ts b/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.controller.ts index fb9df459d4..96602a9857 100644 --- a/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.controller.ts +++ b/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.controller.ts @@ -49,7 +49,7 @@ export class ApplicationDecisionV2Controller { return await this.mapper.mapArrayAsync(decisions, ApplicationDecision, ApplicationDecisionDto); } - @Get('/condition/:uuid') + @Get('/condition/:uuid/status') @UserRoles(...ANY_AUTH_ROLE) async getConditionStatus(@Param('uuid') uuid): Promise { const status = await this.appDecisionService.getDecisionConditionStatus(uuid); diff --git a/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.controller.ts b/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.controller.ts index d11730d5c0..a0b7b4d496 100644 --- a/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.controller.ts +++ b/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.controller.ts @@ -60,7 +60,7 @@ export class NoticeOfIntentDecisionV2Controller { ); } - @Get('/condition/:uuid') + @Get('/condition/:uuid/status') @UserRoles(...ANY_AUTH_ROLE) async getConditionStatus(@Param('uuid') uuid): Promise { const status = await this.noticeOfIntentDecisionV2Service.getDecisionConditionStatus(uuid); From e1bba2ecaa6443c4f185b245a3ea0454b79b432d Mon Sep 17 00:00:00 2001 From: Felipe Barreta Date: Wed, 11 Dec 2024 15:21:26 -0800 Subject: [PATCH 05/10] ALCS-1858 Remove single date patch --- .../decision-condition/decision-condition.component.ts | 4 ---- .../decision-condition/decision-condition.component.ts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts b/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts index 75efb8752a..1dd634cf28 100644 --- a/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts +++ b/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts @@ -82,10 +82,6 @@ export class DecisionConditionComponent implements OnInit, OnChanges { description: this.data.description ?? null, }); - if (this.showSingleDateField && this.dates.length > 0 && this.dates[0].date) { - this.form.patchValue({ singleDate: moment(this.dates[0].date) }); - } - this.form.valueChanges.subscribe(this.emitChanges.bind(this)); } diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts index 0737a444b5..c84b559924 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts @@ -82,10 +82,6 @@ export class DecisionConditionComponent implements OnInit, OnChanges { description: this.data.description ?? null, }); - if (this.showSingleDateField && this.dates.length > 0 && this.dates[0].date) { - this.form.patchValue({ singleDate: moment(this.dates[0].date) }); - } - this.form.valueChanges.subscribe(this.emitChanges.bind(this)); } From 8442fbb787062a773597769a9d2b2f73f9c384f0 Mon Sep 17 00:00:00 2001 From: Felipe Barreta Date: Wed, 11 Dec 2024 16:00:23 -0800 Subject: [PATCH 06/10] ALCS-1858 test fixes --- .../application-decision-v2.service.spec.ts | 9 ++++++++- .../notice-of-intent-decision-v2.service.spec.ts | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.service.spec.ts b/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.service.spec.ts index 40905df625..9e59866ceb 100644 --- a/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.service.spec.ts +++ b/services/apps/alcs/src/alcs/application-decision/application-decision-v2/application-decision/application-decision-v2.service.spec.ts @@ -7,7 +7,7 @@ import { AutomapperModule } from 'automapper-nestjs'; import { createMock, DeepMocked } from '@golevelup/nestjs-testing'; import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { DataSource, Repository } from 'typeorm'; import { initApplicationDecisionMock, initApplicationMockEntity, @@ -59,6 +59,7 @@ describe('ApplicationDecisionV2Service', () => { let mockDecisionConditionService: DeepMocked; let mockNaruSubtypeRepository: DeepMocked>; let mockApplicationSubmissionStatusService: DeepMocked; + let mockdataSource: DeepMocked; let mockApplication; let mockDecision; @@ -80,6 +81,8 @@ describe('ApplicationDecisionV2Service', () => { mockDecisionConditionService = createMock(); mockNaruSubtypeRepository = createMock(); mockApplicationSubmissionStatusService = createMock(); + mockdataSource = createMock(); + const module: TestingModule = await Test.createTestingModule({ imports: [ @@ -141,6 +144,10 @@ describe('ApplicationDecisionV2Service', () => { provide: ApplicationSubmissionStatusService, useValue: mockApplicationSubmissionStatusService, }, + { + provide: DataSource, + useValue: mockdataSource, + }, ], }).compile(); diff --git a/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.service.spec.ts b/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.service.spec.ts index 81ff57005f..6dc21dc9ce 100644 --- a/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.service.spec.ts +++ b/services/apps/alcs/src/alcs/notice-of-intent-decision/notice-of-intent-decision-v2/notice-of-intent-decision-v2.service.spec.ts @@ -7,7 +7,7 @@ import { AutomapperModule } from 'automapper-nestjs'; import { createMock, DeepMocked } from '@golevelup/nestjs-testing'; import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { Repository, DataSource } from 'typeorm'; import { DocumentService } from '../../../document/document.service'; import { NOI_SUBMISSION_STATUS } from '../../notice-of-intent/notice-of-intent-submission-status/notice-of-intent-status.dto'; import { NoticeOfIntentSubmissionStatusService } from '../../notice-of-intent/notice-of-intent-submission-status/notice-of-intent-submission-status.service'; @@ -46,6 +46,7 @@ describe('NoticeOfIntentDecisionV2Service', () => { let mockDecisionComponentService: DeepMocked; let mockDecisionConditionService: DeepMocked; let mockNoticeOfIntentSubmissionStatusService: DeepMocked; + let mockdataSource: DeepMocked; let mockNoticeOfIntent; let mockDecision; @@ -62,6 +63,7 @@ describe('NoticeOfIntentDecisionV2Service', () => { mockDecisionComponentService = createMock(); mockDecisionConditionService = createMock(); mockNoticeOfIntentSubmissionStatusService = createMock(); + mockdataSource = createMock(); const module: TestingModule = await Test.createTestingModule({ imports: [ @@ -111,6 +113,10 @@ describe('NoticeOfIntentDecisionV2Service', () => { provide: NoticeOfIntentSubmissionStatusService, useValue: mockNoticeOfIntentSubmissionStatusService, }, + { + provide: DataSource, + useValue: mockdataSource, + }, ], }).compile(); From 56d9f9f034e2d82e7446a1a8479d9de4e42b2943 Mon Sep 17 00:00:00 2001 From: Felipe Barreta Date: Wed, 11 Dec 2024 16:10:20 -0800 Subject: [PATCH 07/10] ALCS-1858 Cleanup --- .../decision/conditions/condition/condition.component.ts | 5 +---- .../decision/conditions/condition/condition.component.ts | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts index 69d5757aad..2de3702011 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts @@ -54,7 +54,6 @@ export class ConditionComponent implements OnInit, AfterViewInit { showSecurityAmountField = false; singleDateFormated: string | undefined = undefined; stringIndex: string = ''; - today!: number; CONDITION_STATUS = CONDITION_STATUS; @@ -68,9 +67,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { private conditionService: ApplicationDecisionConditionService, private conditionLotService: ApplicationDecisionComponentToConditionLotService, private decisionService: ApplicationDecisionV2Service, - ) { - this.today = moment().startOf('day').toDate().getTime(); - } + ) {} async ngOnInit() { this.stringIndex = countToString(this.index); diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts index a512f3d09c..deb7f347e4 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts @@ -52,11 +52,8 @@ export class ConditionComponent implements OnInit, AfterViewInit { isReadMoreVisible = false; conditionStatus: string = ''; stringIndex: string = ''; - today!: number; - constructor(private conditionService: NoticeOfIntentDecisionConditionService, private decisionService: NoticeOfIntentDecisionV2Service,) { - this.today = moment().startOf('day').toDate().getTime(); - } + constructor(private conditionService: NoticeOfIntentDecisionConditionService, private decisionService: NoticeOfIntentDecisionV2Service,) {} async ngOnInit() { this.stringIndex = countToString(this.index); From 1dcd77ca743f8bc5914948d23c5550767e6cdb9d Mon Sep 17 00:00:00 2001 From: Felipe Barreta Date: Thu, 12 Dec 2024 10:26:26 -0800 Subject: [PATCH 08/10] ALCS-1858 Remove fetchDate function --- .../conditions/condition/condition.component.html | 8 ++++++++ .../conditions/condition/condition.component.ts | 15 +++------------ .../conditions/condition/condition.component.html | 8 ++++++++ .../conditions/condition/condition.component.ts | 15 +++------------ 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html index 5b2532ad67..f8d0ae0f20 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.html @@ -17,6 +17,14 @@

{{ stringIndex }}. {{ condition.type.label }}

>
+
+
{{ singleDateLabel }}
+ {{ singleDateFormated }} + +
+
Security Amount
{{ condition.securityAmount }} diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts index 2de3702011..d41ca99f07 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts @@ -72,7 +72,9 @@ export class ConditionComponent implements OnInit, AfterViewInit { async ngOnInit() { this.stringIndex = countToString(this.index); if (this.condition) { - await this.fetchDates(this.condition.uuid); + this.dates = this.condition.dates ?? []; + this.singleDateFormated = + this.dates[0] && this.dates[0].date ? moment(this.dates[0].date).format(environment.dateFormat) : undefined; this.calcStatus(); this.singleDateLabel = this.condition.type?.singleDateLabel ? this.condition.type?.singleDateLabel : 'End Date'; this.showSingleDateField = this.condition.type?.dateType === DateType.SINGLE; @@ -223,15 +225,4 @@ export class ConditionComponent implements OnInit, AfterViewInit { break; } } - - private async fetchDates(uuid: string | undefined) { - if (!uuid) { - return; - } - - this.dates = await this.conditionService.getDates(uuid); - - this.singleDateFormated = - this.dates[0] && this.dates[0].date ? moment(this.dates[0].date).format(environment.dateFormat) : undefined; - } } diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html index 8d57b4258a..28fad8bcae 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.html @@ -17,6 +17,14 @@

{{ stringIndex }}. {{ condition.type.label }}

>
+
+
{{ singleDateLabel }}
+ {{ singleDateFormated }} + +
+
Security Amount
{{ condition.securityAmount }} diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts index deb7f347e4..fd182dea13 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts @@ -58,7 +58,9 @@ export class ConditionComponent implements OnInit, AfterViewInit { async ngOnInit() { this.stringIndex = countToString(this.index); if (this.condition) { - await this.fetchDates(this.condition.uuid); + this.dates = this.condition.dates ?? []; + this.singleDateFormated = + this.dates[0] && this.dates[0].date ? moment(this.dates[0].date).format(environment.dateFormat) : undefined; this.calcStatus(); this.singleDateLabel = this.condition.type?.singleDateLabel ? this.condition.type?.singleDateLabel : 'End Date'; @@ -137,15 +139,4 @@ export class ConditionComponent implements OnInit, AfterViewInit { break; } } - - async fetchDates(uuid: string | undefined) { - if (!uuid) { - return; - } - - this.dates = await this.conditionService.getDates(uuid); - - this.singleDateFormated = - this.dates[0] && this.dates[0].date ? moment(this.dates[0].date).format(environment.dateFormat) : undefined; - } } From cd457f46c21c3249d7d8e643c50f0083061c7853 Mon Sep 17 00:00:00 2001 From: Felipe Barreta Date: Thu, 12 Dec 2024 16:19:00 -0800 Subject: [PATCH 09/10] ALCS-1858 Move get status to parent component --- .../condition/condition.component.ts | 7 +--- .../conditions/conditions.component.ts | 40 +++++-------------- .../decision-condition.component.ts | 5 +++ .../condition/condition.component.ts | 7 +--- .../conditions/conditions.component.ts | 40 +++++-------------- .../decision-condition.component.ts | 5 +++ 6 files changed, 32 insertions(+), 72 deletions(-) diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts index d41ca99f07..e14ba1a88e 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts @@ -75,7 +75,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { this.dates = this.condition.dates ?? []; this.singleDateFormated = this.dates[0] && this.dates[0].date ? moment(this.dates[0].date).format(environment.dateFormat) : undefined; - this.calcStatus(); + this.setPillLabel(this.condition.status); this.singleDateLabel = this.condition.type?.singleDateLabel ? this.condition.type?.singleDateLabel : 'End Date'; this.showSingleDateField = this.condition.type?.dateType === DateType.SINGLE; this.showAdmFeeField = this.condition.type?.isAdministrativeFeeAmountChecked @@ -198,11 +198,6 @@ export class ConditionComponent implements OnInit, AfterViewInit { return this.condition.conditionComponentsLabels?.find((e) => e.componentUuid === componentUuid)?.label; } - async calcStatus() { - const conditionStatus = await this.decisionService.getStatus(this.condition.uuid); - this.setPillLabel(conditionStatus.status); - } - private setPillLabel(status: string) { switch (status) { case 'ONGOING': diff --git a/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts b/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts index 12c0116c9f..242914fc5d 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts +++ b/alcs-frontend/src/app/features/application/decision/conditions/conditions.component.ts @@ -100,18 +100,17 @@ export class ConditionsComponent implements OnInit { ), ), ) - .subscribe(({ decisions, datesByConditionUuid }) => { - this.decisions = decisions.map((decision) => { + .subscribe(async ({ decisions }) => { + this.decisions = await Promise.all(decisions.map(async (decision) => { if (decision.uuid === this.decisionUuid) { - const conditions = this.mapConditions(decision, datesByConditionUuid, decisions); - + const conditions = await this.mapConditions(decision, decisions); this.sortConditions(decision, conditions); this.decision = decision as ApplicationDecisionWithConditionComponentLabels; } return decision as ApplicationDecisionWithConditionComponentLabels; - }); + })); }); this.decisionService.loadDecisions(fileNumber); @@ -136,7 +135,7 @@ export class ConditionsComponent implements OnInit { conditions: ApplicationDecisionConditionWithStatus[], ) { decision.conditions = conditions.sort((a, b) => { - const order = [CONDITION_STATUS.ONGOING, CONDITION_STATUS.COMPLETE]; + const order = [CONDITION_STATUS.ONGOING, CONDITION_STATUS.COMPLETE, CONDITION_STATUS.PASTDUE, CONDITION_STATUS.EXPIRED]; if (a.status === b.status) { if (a.type && b.type) { return a.type?.label.localeCompare(b.type.label); @@ -149,18 +148,15 @@ export class ConditionsComponent implements OnInit { }); } - private mapConditions( + private async mapConditions( decision: ApplicationDecisionWithLinkedResolutionDto, - datesByConditionUuid: Map, decisions: ApplicationDecisionWithLinkedResolutionDto[], ) { - return decision.conditions.map((condition) => { - const dates = datesByConditionUuid.get(condition.uuid) ?? []; - const status = this.getStatus(dates, decision); - + return Promise.all(decision.conditions.map(async (condition) => { + const conditionStatus = await this.decisionService.getStatus(condition.uuid); return { ...condition, - status, + status: conditionStatus.status, conditionComponentsLabels: condition.components?.map((c) => { const matchingType = this.codes.decisionComponentTypes.find( (type) => type.code === c.applicationDecisionComponentTypeCode, @@ -180,22 +176,6 @@ export class ConditionsComponent implements OnInit { return { label, conditionUuid: condition.uuid, componentUuid: c.uuid }; }), } as ApplicationDecisionConditionWithStatus; - }); - } - - private getStatus( - dates: ApplicationDecisionConditionDateDto[], - decision: ApplicationDecisionWithLinkedResolutionDto, - ) { - let status = ''; - status = CONDITION_STATUS.COMPLETE; - if (dates.length > 0 && dates.every((date) => date.completedDate && date.completedDate <= this.today)) { - status = CONDITION_STATUS.COMPLETE; - } else if (decision.isDraft === false) { - status = CONDITION_STATUS.ONGOING; - } else { - status = ''; - } - return status; + })); } } diff --git a/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts b/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts index 1dd634cf28..74a637be6f 100644 --- a/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts +++ b/alcs-frontend/src/app/features/application/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts @@ -59,6 +59,7 @@ export class DecisionConditionComponent implements OnInit, OnChanges { administrativeFee: this.administrativeFee, description: this.description, componentsToCondition: this.componentsToCondition, + singleDate: this.singleDate, }); constructor(protected dialog: MatDialog) {} @@ -82,6 +83,10 @@ export class DecisionConditionComponent implements OnInit, OnChanges { description: this.data.description ?? null, }); + if (this.showSingleDateField && this.dates.length > 0 && this.dates[0].date) { + this.form.patchValue({ singleDate: moment(this.dates[0].date) }); + } + this.form.valueChanges.subscribe(this.emitChanges.bind(this)); } diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts index fd182dea13..939859c7f1 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts @@ -61,7 +61,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { this.dates = this.condition.dates ?? []; this.singleDateFormated = this.dates[0] && this.dates[0].date ? moment(this.dates[0].date).format(environment.dateFormat) : undefined; - this.calcStatus(); + this.setPillLabel(this.condition.status); this.singleDateLabel = this.condition.type?.singleDateLabel ? this.condition.type?.singleDateLabel : 'End Date'; this.showSingleDateField = this.condition.type?.dateType === DateType.SINGLE; @@ -112,11 +112,6 @@ export class ConditionComponent implements OnInit, AfterViewInit { return this.isReadMoreClicked || this.isEllipsisActive(this.condition.uuid + 'Description'); } - async calcStatus() { - const conditionStatus = await this.decisionService.getStatus(this.condition.uuid); - this.setPillLabel(conditionStatus.status); - } - private setPillLabel(status: string) { switch (status) { case 'ONGOING': diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.ts index c82994961c..e4bfbad900 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/conditions.component.ts @@ -96,18 +96,17 @@ export class ConditionsComponent implements OnInit { ), ), ) - .subscribe(({ decisions, datesByConditionUuid }) => { - this.decisions = decisions.map((decision) => { + .subscribe(async ({ decisions }) => { + this.decisions = await Promise.all(decisions.map(async (decision) => { if (decision.uuid === this.decisionUuid) { - const conditions = this.mapConditions(decision, datesByConditionUuid, decisions); - + const conditions = await this.mapConditions(decision, decisions); this.sortConditions(decision, conditions); this.decision = decision as DecisionWithConditionComponentLabels; } return decision as DecisionWithConditionComponentLabels; - }); + })); }); this.decisionService.loadDecisions(fileNumber); @@ -132,7 +131,7 @@ export class ConditionsComponent implements OnInit { conditions: DecisionConditionWithStatus[], ) { decision.conditions = conditions.sort((a, b) => { - const order = [CONDITION_STATUS.ONGOING, CONDITION_STATUS.COMPLETE]; + const order = [CONDITION_STATUS.ONGOING, CONDITION_STATUS.COMPLETE, CONDITION_STATUS.PASTDUE, CONDITION_STATUS.EXPIRED]; if (a.status === b.status) { if (a.type && b.type) { return a.type?.label.localeCompare(b.type.label); @@ -145,18 +144,15 @@ export class ConditionsComponent implements OnInit { }); } - private mapConditions( + private async mapConditions( decision: NoticeOfIntentDecisionWithLinkedResolutionDto, - datesByConditionUuid: Map, decisions: NoticeOfIntentDecisionWithLinkedResolutionDto[], ) { - return decision.conditions.map((condition) => { - const dates = datesByConditionUuid.get(condition.uuid) ?? []; - const status = this.getStatus(dates, decision); - + return Promise.all(decision.conditions.map(async (condition) => { + const conditionStatus = await this.decisionService.getStatus(condition.uuid); return { ...condition, - status, + status: conditionStatus.status, conditionComponentsLabels: condition.components?.map((c) => { const matchingType = this.codes.decisionComponentTypes.find( (type) => type.code === c.noticeOfIntentDecisionComponentTypeCode, @@ -176,22 +172,6 @@ export class ConditionsComponent implements OnInit { return { label, conditionUuid: condition.uuid, componentUuid: c.uuid }; }), } as DecisionConditionWithStatus; - }); - } - - private getStatus( - dates: NoticeOfIntentDecisionConditionDateDto[], - decision: NoticeOfIntentDecisionWithLinkedResolutionDto, - ) { - let status = ''; - status = CONDITION_STATUS.COMPLETE; - if (dates.length > 0 && dates.every((date) => date.completedDate && date.completedDate <= this.today)) { - status = CONDITION_STATUS.COMPLETE; - } else if (decision.isDraft === false) { - status = CONDITION_STATUS.ONGOING; - } else { - status = ''; - } - return status; + })); } } diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts index c84b559924..4f179f4b51 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/decision-v2/decision-input/decision-conditions/decision-condition/decision-condition.component.ts @@ -59,6 +59,7 @@ export class DecisionConditionComponent implements OnInit, OnChanges { administrativeFee: this.administrativeFee, description: this.description, componentsToCondition: this.componentsToCondition, + singleDate: this.singleDate, }); constructor(protected dialog: MatDialog) {} @@ -82,6 +83,10 @@ export class DecisionConditionComponent implements OnInit, OnChanges { description: this.data.description ?? null, }); + if (this.showSingleDateField && this.dates.length > 0 && this.dates[0].date) { + this.form.patchValue({ singleDate: moment(this.dates[0].date) }); + } + this.form.valueChanges.subscribe(this.emitChanges.bind(this)); } From 32c26632f569cb296df40aecf05852b64bb23fce Mon Sep 17 00:00:00 2001 From: Felipe Barreta Date: Thu, 12 Dec 2024 16:22:24 -0800 Subject: [PATCH 10/10] ALCS-1858 Cleanup --- .../decision/conditions/condition/condition.component.ts | 1 - .../decision/conditions/condition/condition.component.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts index e14ba1a88e..6e4d26c695 100644 --- a/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/application/decision/conditions/condition/condition.component.ts @@ -66,7 +66,6 @@ export class ConditionComponent implements OnInit, AfterViewInit { constructor( private conditionService: ApplicationDecisionConditionService, private conditionLotService: ApplicationDecisionComponentToConditionLotService, - private decisionService: ApplicationDecisionV2Service, ) {} async ngOnInit() { diff --git a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts index 939859c7f1..00d9e14e37 100644 --- a/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts +++ b/alcs-frontend/src/app/features/notice-of-intent/decision/conditions/condition/condition.component.ts @@ -53,7 +53,7 @@ export class ConditionComponent implements OnInit, AfterViewInit { conditionStatus: string = ''; stringIndex: string = ''; - constructor(private conditionService: NoticeOfIntentDecisionConditionService, private decisionService: NoticeOfIntentDecisionV2Service,) {} + constructor(private conditionService: NoticeOfIntentDecisionConditionService) {} async ngOnInit() { this.stringIndex = countToString(this.index);