Skip to content

Commit 92e2b62

Browse files
authored
2394 Responsible Parties Part II UI Updates (#2480)
* fix: add party button - styling and saving issues resolved * fix: switching from CRWN to Fee, keeps parties * fix: updated tests * fix: responsible party header no longer dropdown * chore: updated tests * fix: updated the colour of add party to inherit
1 parent 22041d1 commit 92e2b62

File tree

6 files changed

+154
-185
lines changed

6 files changed

+154
-185
lines changed

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

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AfterViewInit, Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
22
import { ActivatedRoute } from '@angular/router';
3-
import { catchError, debounceTime, EMPTY, firstValueFrom, skip, Subject, switchMap, takeUntil, tap } from 'rxjs';
3+
import { catchError, debounceTime, EMPTY, filter, firstValueFrom, skip, Subject, switchMap, takeUntil, tap } from 'rxjs';
44
import {
55
ComplianceAndEnforcementDto,
66
InitialSubmissionType,
@@ -134,6 +134,7 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
134134
.pipe(
135135
skip(1), // Skip the initial emission to prevent save on load
136136
debounceTime(1000),
137+
filter(() => this.propertyComponent?.form.dirty ?? false),
137138
switchMap((property) => {
138139
// Only auto-save if there are meaningful changes (non-empty fields)
139140
const cleanedProperty = cleanPropertyUpdate(property);
@@ -172,6 +173,7 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
172173
if (!this.property) {
173174
this.property = property;
174175
}
176+
this.propertyComponent?.form.markAsPristine();
175177
}),
176178
catchError((error) => {
177179
console.error('Error saving C&E property draft', error);
@@ -198,15 +200,7 @@ export class DraftComponent implements OnInit, AfterViewInit, OnDestroy {
198200
if (wasCrown !== this.isPropertyCrown && this.responsiblePartiesComponent) {
199201
this.responsiblePartiesComponent.isPropertyCrown = this.isPropertyCrown;
200202

201-
if (this.isPropertyCrown) {
202-
// For Crown properties, clear all existing parties since they are not allowed in CRWN ownership
203-
await this.clearAllResponsibleParties();
204-
} else {
205-
// For non-Crown properties, reload parties from API and ensure form is rebuilt
206-
await this.responsiblePartiesComponent.loadResponsibleParties();
207-
// Refresh the form to ensure proper state (will add default party if none exist)
208-
this.responsiblePartiesComponent.refreshFormForPropertyChange();
209-
}
203+
await this.responsiblePartiesComponent.loadResponsibleParties();
210204
}
211205
}
212206
});

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

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ const PARCEL_OWNERSHIP_TYPE = {
1818
CROWN: 'CRWN',
1919
};
2020

21-
2221
// Custom validator for exactly 5 decimal places as per SO request
2322
function decimalPlacesValidator(decimals: number) {
2423
return (control: any) => {
@@ -42,7 +41,9 @@ function decimalPlacesValidator(decimals: number) {
4241
}
4342

4443
// Clean up the property update object to remove null, undefined, and empty strings, needed to save draft effectively
45-
export function cleanPropertyUpdate(update: UpdateComplianceAndEnforcementPropertyDto): UpdateComplianceAndEnforcementPropertyDto {
44+
export function cleanPropertyUpdate(
45+
update: UpdateComplianceAndEnforcementPropertyDto,
46+
): UpdateComplianceAndEnforcementPropertyDto {
4647
const cleaned: any = {};
4748
for (const [key, value] of Object.entries(update)) {
4849
if (
@@ -56,7 +57,11 @@ export function cleanPropertyUpdate(update: UpdateComplianceAndEnforcementProper
5657
}
5758
}
5859
// Always omit localGovernmentUuid if blank, null, or undefined
59-
if (cleaned.localGovernmentUuid === '' || cleaned.localGovernmentUuid === null || cleaned.localGovernmentUuid === undefined) {
60+
if (
61+
cleaned.localGovernmentUuid === '' ||
62+
cleaned.localGovernmentUuid === null ||
63+
cleaned.localGovernmentUuid === undefined
64+
) {
6065
delete cleaned.localGovernmentUuid;
6166
}
6267
return cleaned;
@@ -96,13 +101,13 @@ export class PropertyComponent implements OnInit, OnDestroy {
96101
Validators.required,
97102
Validators.min(48),
98103
Validators.max(61),
99-
decimalPlacesValidator(5)
104+
decimalPlacesValidator(5),
100105
]),
101106
longitude: new FormControl<number | null>(null, [
102107
Validators.required,
103108
Validators.min(-140),
104109
Validators.max(-113),
105-
decimalPlacesValidator(5)
110+
decimalPlacesValidator(5),
106111
]),
107112
ownershipTypeCode: new FormControl<string>(PARCEL_OWNERSHIP_TYPE.FEE_SIMPLE, [Validators.required]),
108113
pidOrPin: new FormControl<string>('PID'),
@@ -157,7 +162,6 @@ export class PropertyComponent implements OnInit, OnDestroy {
157162

158163
this.form.enable();
159164
this.isPatching = false;
160-
161165
}
162166

163167
// Prevent resubscription
@@ -167,41 +171,47 @@ export class PropertyComponent implements OnInit, OnDestroy {
167171
return;
168172
}
169173

170-
this.$changes.next(cleanPropertyUpdate({
171-
civicAddress: form.civicAddress ?? '',
172-
legalDescription: form.legalDescription ?? '',
173-
localGovernmentUuid: form.localGovernmentUuid && form.localGovernmentUuid.trim() !== '' ? form.localGovernmentUuid : undefined,
174-
regionCode: form.regionCode ?? '',
175-
latitude: toNumberOrUndefined(form.latitude),
176-
longitude: toNumberOrUndefined(form.longitude),
177-
ownershipTypeCode: form.ownershipTypeCode ?? PARCEL_OWNERSHIP_TYPE.FEE_SIMPLE,
178-
pid: form.pidOrPin === 'PID' ? (form.pid || null) : null,
179-
pin: form.pidOrPin === 'PIN' ? (form.pin || null) : null,
180-
areaHectares: toNumberOrUndefined(form.areaHectares),
181-
alrPercentage: toNumberOrUndefined(form.alrPercentage),
182-
alcHistory: form.alcHistory ?? '',
183-
}));
174+
this.$changes.next(
175+
cleanPropertyUpdate({
176+
civicAddress: form.civicAddress ?? '',
177+
legalDescription: form.legalDescription ?? '',
178+
localGovernmentUuid:
179+
form.localGovernmentUuid && form.localGovernmentUuid.trim() !== '' ? form.localGovernmentUuid : undefined,
180+
regionCode: form.regionCode ?? '',
181+
latitude: toNumberOrUndefined(form.latitude),
182+
longitude: toNumberOrUndefined(form.longitude),
183+
ownershipTypeCode: form.ownershipTypeCode ?? PARCEL_OWNERSHIP_TYPE.FEE_SIMPLE,
184+
pid: form.pidOrPin === 'PID' ? form.pid || null : null,
185+
pin: form.pidOrPin === 'PIN' ? form.pin || null : null,
186+
areaHectares: toNumberOrUndefined(form.areaHectares),
187+
alrPercentage: toNumberOrUndefined(form.alrPercentage),
188+
alcHistory: form.alcHistory ?? '',
189+
}),
190+
);
184191
});
185192

186193
// Watch for ownership type changes to update PID/PIN requirements
187-
this.form.get('ownershipTypeCode')?.valueChanges.pipe(takeUntil(this.$destroy)).subscribe((ownershipType) => {
188-
if (!this.isPatching && ownershipType) {
189-
this.updatePidPinRequirements(ownershipType);
190-
}
191-
});
194+
this.form
195+
.get('ownershipTypeCode')
196+
?.valueChanges.pipe(takeUntil(this.$destroy))
197+
.subscribe((ownershipType) => {
198+
if (!this.isPatching && ownershipType) {
199+
this.updatePidPinRequirements(ownershipType);
200+
}
201+
});
192202

193203
this.isSubscribed = true;
194204
}
195205
}
196206

197207
constructor(
198208
private localGovernmentService: ApplicationLocalGovernmentService,
199-
private applicationService: ApplicationService
209+
private applicationService: ApplicationService,
200210
) {
201211
// Initialize autocomplete filtering
202212
this.filteredLocalGovernments = this.localGovernmentControl.valueChanges.pipe(
203213
startWith(''),
204-
map(value => this.filterLocalGovernments(value || ''))
214+
map((value) => this.filterLocalGovernments(value || '')),
205215
);
206216
}
207217

@@ -214,9 +224,9 @@ export class PropertyComponent implements OnInit, OnDestroy {
214224
});
215225

216226
// If no property is set initially, set initialized to true after a delay to allow for any initial setup
217-
if (!this.isInitialized) {
218-
this.isInitialized = true;
219-
}
227+
if (!this.isInitialized) {
228+
this.isInitialized = true;
229+
}
220230
}
221231

222232
async loadLocalGovernments() {

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

Lines changed: 31 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,8 @@
11
<div class="responsible-parties-container">
22
<div class="header">
33
<h3>Allegedly Responsible Parties</h3>
4-
<button
5-
mat-flat-button
6-
color="primary"
7-
[matMenuTriggerFor]="addPartyMenu"
8-
[disabled]="isPropertyCrown"
9-
>
10-
+ ADD PARTY
11-
</button>
12-
13-
<mat-menu #addPartyMenu="matMenu">
14-
<button mat-menu-item *ngFor="let type of responsiblePartyTypes" (click)="addParty()">
15-
{{ type.value }}
16-
</button>
17-
</mat-menu>
184
</div>
195

20-
<div *ngIf="isPropertyCrown" class="crown-notice">
21-
<mat-icon>info</mat-icon>
22-
<span>Responsible parties are not required for Crown ownership properties.</span>
23-
</div>
246

257
<div *ngIf="isLoading" class="loading-container">
268
<mat-spinner diameter="40"></mat-spinner>
@@ -30,36 +12,13 @@ <h3>Allegedly Responsible Parties</h3>
3012
<!-- Responsible Parties Forms -->
3113
<div *ngFor="let partyForm of form.controls; let i = index" class="party-card">
3214
<form [formGroup]="partyForm">
33-
3415
<!-- Party Header -->
3516
<div class="party-header">
36-
<mat-form-field appearance="outline" class="party-type-field">
37-
<mat-label>Party Type</mat-label>
38-
<mat-select
39-
formControlName="partyType"
40-
(selectionChange)="onPartyTypeChange(i, $event)"
41-
>
42-
<mat-option *ngFor="let type of responsiblePartyTypes" [value]="type.value">
43-
{{ type.value }}
44-
</mat-option>
45-
</mat-select>
46-
</mat-form-field>
17+
<h4 class="party-type-title">{{ partyForm.get('partyType')?.value }}</h4>
4718

4819
<div class="party-actions">
49-
<mat-slide-toggle
50-
formControlName="isPrevious"
51-
class="previous-toggle"
52-
>
53-
Mark as Previous
54-
</mat-slide-toggle>
55-
<button
56-
mat-button
57-
color="warn"
58-
(click)="deleteParty(i)"
59-
class="delete-button"
60-
>
61-
DELETE
62-
</button>
20+
<mat-slide-toggle formControlName="isPrevious" class="previous-toggle"> Mark as Previous </mat-slide-toggle>
21+
<button mat-button color="warn" (click)="deleteParty(i)" class="delete-button">DELETE</button>
6322
</div>
6423
</div>
6524

@@ -86,16 +45,10 @@ <h3>Allegedly Responsible Parties</h3>
8645
<mat-form-field appearance="outline" class="full-width">
8746
<mat-label>Name</mat-label>
8847
<input matInput formControlName="individualName" />
89-
<mat-error *ngIf="partyForm.get('individualName')?.hasError('required')">
90-
Name is required
91-
</mat-error>
48+
<mat-error *ngIf="partyForm.get('individualName')?.hasError('required')"> Name is required </mat-error>
9249
</mat-form-field>
9350

94-
<mat-form-field
95-
*ngIf="isPropertyOwner(i)"
96-
appearance="outline"
97-
class="owner-since-field"
98-
>
51+
<mat-form-field *ngIf="isPropertyOwner(i)" appearance="outline" class="owner-since-field">
9952
<mat-label>Owner Since</mat-label>
10053
<input
10154
matInput
@@ -153,11 +106,7 @@ <h3>Allegedly Responsible Parties</h3>
153106
</mat-error>
154107
</mat-form-field>
155108

156-
<mat-form-field
157-
*ngIf="isPropertyOwner(i)"
158-
appearance="outline"
159-
class="owner-since-field"
160-
>
109+
<mat-form-field *ngIf="isPropertyOwner(i)" appearance="outline" class="owner-since-field">
161110
<mat-label>Owner Since</mat-label>
162111
<input
163112
matInput
@@ -187,7 +136,7 @@ <h3>Allegedly Responsible Parties</h3>
187136
Please enter a valid email address
188137
</mat-error>
189138
</mat-form-field>
190-
</div>
139+
</div>
191140

192141
<mat-form-field appearance="outline" class="full-width">
193142
<mat-label>Note</mat-label>
@@ -197,7 +146,7 @@ <h3>Allegedly Responsible Parties</h3>
197146
<!-- Directors Section -->
198147
<div class="directors-section">
199148
<div class="directors-header">
200-
<label class="directors-label" style="font-weight: bold; color: black;">Directors</label>
149+
<label class="directors-label" style="font-weight: bold; color: black">Directors</label>
201150
<div class="directors-column-headers">
202151
<div class="header-item header-number">#</div>
203152
<div class="header-item header-name">Director Name*</div>
@@ -207,7 +156,7 @@ <h3>Allegedly Responsible Parties</h3>
207156
<div class="header-item header-action">Action</div>
208157
</div>
209158
</div>
210-
159+
211160
<div class="directors-list">
212161
<div
213162
*ngFor="let director of getDirectorsArray(i).controls; let directorIdx = index"
@@ -227,7 +176,12 @@ <h3>Allegedly Responsible Parties</h3>
227176

228177
<div class="director-item director-address">
229178
<mat-form-field appearance="outline" class="director-field">
230-
<textarea matInput formControlName="directorMailingAddress" rows="1" placeholder="Enter Mailing Address"></textarea>
179+
<textarea
180+
matInput
181+
formControlName="directorMailingAddress"
182+
rows="1"
183+
placeholder="Enter Mailing Address"
184+
></textarea>
231185
<mat-error *ngIf="director.get('directorMailingAddress')?.hasError('required')">
232186
Mailing address is required
233187
</mat-error>
@@ -261,39 +215,35 @@ <h3>Allegedly Responsible Parties</h3>
261215
</div>
262216
</div>
263217

264-
<div *ngIf="getDirectorsArray(i).length === 0" class="no-directors-message">
265-
No directors added
266-
</div>
218+
<div *ngIf="getDirectorsArray(i).length === 0" class="no-directors-message">No directors added</div>
267219
</div>
268220

269-
<button
270-
mat-stroked-button
271-
color="primary"
272-
(click)="addDirector(i)"
273-
class="add-director-button"
274-
>
221+
<button mat-stroked-button color="primary" (click)="addDirector(i)" class="add-director-button">
275222
+ ADD ANOTHER DIRECTOR
276223
</button>
277224
</div>
278225
</div>
279-
280226
</form>
281227
</div>
282228

283-
<!-- Add Another Party Button -->
229+
<!-- Add Party Button -->
284230
<button
285-
mat-stroked-button
231+
mat-flat-button
286232
color="primary"
287-
(click)="addParty()"
233+
[matMenuTriggerFor]="addPartyMenu"
288234
class="add-party-button"
289-
[disabled]="isPropertyCrown"
290235
>
291-
+ ADD ANOTHER PARTY
236+
<span *ngIf="form.length === 0">+ ADD PARTY</span>
237+
<span *ngIf="form.length > 0">+ ADD ANOTHER PARTY</span>
238+
<mat-icon class="dropdown-arrow">arrow_drop_down</mat-icon>
292239
</button>
293240

294-
<!-- No parties message for Crown ownership -->
295-
<div *ngIf="isPropertyCrown && form.length === 0" class="no-parties-message">
296-
<mat-icon>info_outline</mat-icon>
297-
<span>No responsible parties information required for Crown ownership properties.</span>
298-
</div>
241+
242+
243+
<!-- Dropdown Menu for Adding Parties -->
244+
<mat-menu #addPartyMenu="matMenu">
245+
<button mat-menu-item *ngFor="let type of responsiblePartyTypes" (click)="addPartyOfType(type.value)">
246+
{{ type.value }}
247+
</button>
248+
</mat-menu>
299249
</div>

0 commit comments

Comments
 (0)