Skip to content

Commit fdb8b8f

Browse files
authored
WEB-38: fix view guarantors page not displaying data and update breadcrumb navigation to Loans (#3380)
1 parent fc579f5 commit fdb8b8f

13 files changed

Lines changed: 723 additions & 536 deletions

src/app/core/shell/breadcrumb/breadcrumb.component.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,23 @@ export class BreadcrumbComponent implements AfterViewInit, OnDestroy {
199199
} else {
200200
url = currentUrl;
201201
}
202+
203+
// For module root breadcrumbs (e.g. "Loans", "Savings") whose URL has no entity child,
204+
// extract the correct URL from the full router URL to build a navigable link.
205+
if (url && typeof url === 'string') {
206+
const accountPathMatch = url
207+
.replace(/\/+/g, '/')
208+
.match(/\/(loans-accounts|savings-accounts|shares-accounts)\/$/);
209+
if (accountPathMatch) {
210+
const fullUrl = this.router.url.replace(/\/+/g, '/');
211+
const entityUrlMatch = fullUrl.match(new RegExp(`(.*/${accountPathMatch[1]}/\\d+)`));
212+
if (entityUrlMatch) {
213+
url = entityUrlMatch[1];
214+
} else {
215+
url = false;
216+
}
217+
}
218+
}
202219
}
203220
if (url !== undefined) {
204221
if (url.length > 8 && url.search(`/clients/`) > 0) {

src/app/loans/common-resolvers/loan-action-button.resolver.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { Injectable, inject } from '@angular/core';
1111
import { ActivatedRouteSnapshot } from '@angular/router';
1212

1313
/** rxjs Imports */
14-
import { Observable } from 'rxjs';
14+
import { Observable, of } from 'rxjs';
15+
import { catchError } from 'rxjs/operators';
1516

1617
/** Custom Services */
1718
import { LoansService } from '../loans.service';
@@ -65,7 +66,7 @@ export class LoanActionButtonResolver {
6566
} else if (loanActionButton === 'Recovery Payment') {
6667
return this.loansService.getLoanActionTemplate(loanId, 'recoverypayment');
6768
} else if (loanActionButton === 'View Guarantors') {
68-
return this.loansService.getLoanAccountResource(loanId, 'guarantors');
69+
return this.loansService.getGuarantors(loanId).pipe(catchError(() => of([])));
6970
} else if (loanActionButton === 'Create Guarantor') {
7071
return this.loansService.getGuarantorTemplate(loanId);
7172
} else if (loanActionButton === 'Disburse') {
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!--
2+
Copyright since 2025 Mifos Initiative
3+
4+
This Source Code Form is subject to the terms of the Mozilla Public
5+
License, v. 2.0. If a copy of the MPL was not distributed with this
6+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
-->
8+
9+
<h1 mat-dialog-title>{{ 'labels.heading.Edit Guarantor' | translate }}</h1>
10+
11+
<div mat-dialog-content>
12+
<form [formGroup]="editGuarantorForm" class="layout-column">
13+
<mat-form-field>
14+
<mat-label>{{ 'labels.inputs.First Name' | translate }}</mat-label>
15+
<input matInput formControlName="firstname" />
16+
</mat-form-field>
17+
<mat-form-field>
18+
<mat-label>{{ 'labels.inputs.Last Name' | translate }}</mat-label>
19+
<input matInput formControlName="lastname" />
20+
</mat-form-field>
21+
<mat-form-field>
22+
<mat-label>{{ 'labels.inputs.Relationship' | translate }}</mat-label>
23+
<mat-select formControlName="clientRelationshipTypeId">
24+
@for (relationType of relationTypes; track relationType) {
25+
<mat-option [value]="relationType.id">
26+
{{ relationType.name }}
27+
</mat-option>
28+
}
29+
</mat-select>
30+
</mat-form-field>
31+
<mat-form-field>
32+
<mat-label>{{ 'labels.inputs.Address Line' | translate }} 1</mat-label>
33+
<input matInput formControlName="addressLine1" />
34+
</mat-form-field>
35+
<mat-form-field>
36+
<mat-label>{{ 'labels.inputs.Address Line' | translate }} 2</mat-label>
37+
<input matInput formControlName="addressLine2" />
38+
</mat-form-field>
39+
<mat-form-field>
40+
<mat-label>{{ 'labels.inputs.City' | translate }}</mat-label>
41+
<input matInput formControlName="city" />
42+
</mat-form-field>
43+
<mat-form-field>
44+
<mat-label>{{ 'labels.inputs.Zip' | translate }}</mat-label>
45+
<input matInput formControlName="zip" />
46+
</mat-form-field>
47+
<mat-form-field>
48+
<mat-label>{{ 'labels.inputs.Mobile' | translate }}</mat-label>
49+
<input type="tel" matInput formControlName="mobileNumber" />
50+
</mat-form-field>
51+
<mat-form-field>
52+
<mat-label>{{ 'labels.inputs.Residence Phone' | translate }} #</mat-label>
53+
<input type="tel" matInput formControlName="housePhoneNumber" />
54+
</mat-form-field>
55+
</form>
56+
</div>
57+
58+
<mat-dialog-actions class="layout-row layout-xs-column layout-align-center gap-2percent">
59+
<button mat-raised-button mat-dialog-close>{{ 'labels.buttons.Cancel' | translate }}</button>
60+
<button mat-raised-button color="primary" (click)="submit()">{{ 'labels.buttons.Submit' | translate }}</button>
61+
</mat-dialog-actions>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* Copyright since 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright since 2025 Mifos Initiative
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
import { Component, OnInit, inject } from '@angular/core';
10+
import {
11+
MatDialogRef,
12+
MAT_DIALOG_DATA,
13+
MatDialogTitle,
14+
MatDialogContent,
15+
MatDialogActions,
16+
MatDialogClose
17+
} from '@angular/material/dialog';
18+
import { UntypedFormGroup, UntypedFormBuilder, ReactiveFormsModule } from '@angular/forms';
19+
import { CdkScrollable } from '@angular/cdk/scrolling';
20+
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
21+
22+
@Component({
23+
selector: 'mifosx-edit-guarantor-dialog',
24+
templateUrl: './edit-guarantor-dialog.component.html',
25+
styleUrls: ['./edit-guarantor-dialog.component.scss'],
26+
imports: [
27+
...STANDALONE_SHARED_IMPORTS,
28+
MatDialogTitle,
29+
CdkScrollable,
30+
MatDialogContent,
31+
MatDialogActions,
32+
MatDialogClose,
33+
ReactiveFormsModule
34+
]
35+
})
36+
export class EditGuarantorDialogComponent implements OnInit {
37+
dialogRef = inject<MatDialogRef<EditGuarantorDialogComponent>>(MatDialogRef);
38+
data = inject(MAT_DIALOG_DATA);
39+
private formBuilder = inject(UntypedFormBuilder);
40+
41+
editGuarantorForm: UntypedFormGroup;
42+
relationTypes: any[] = [];
43+
44+
ngOnInit() {
45+
this.relationTypes = this.data.relationTypes || [];
46+
const guarantor = this.data.guarantorData;
47+
this.editGuarantorForm = this.formBuilder.group({
48+
firstname: [guarantor.firstname || ''],
49+
lastname: [guarantor.lastname || ''],
50+
clientRelationshipTypeId: [guarantor.clientRelationshipType?.id || ''],
51+
addressLine1: [guarantor.addressLine1 || ''],
52+
addressLine2: [guarantor.addressLine2 || ''],
53+
city: [guarantor.city || ''],
54+
zip: [guarantor.zip || ''],
55+
mobileNumber: [guarantor.mobileNumber || ''],
56+
housePhoneNumber: [guarantor.housePhoneNumber || '']
57+
});
58+
}
59+
60+
submit() {
61+
this.dialogRef.close(this.editGuarantorForm.value);
62+
}
63+
}

src/app/loans/custom-dialog/loans-account-view-guarantor-details-dialog/loans-account-view-guarantor-details-dialog.component.html

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,30 @@
99
<h1 mat-dialog-title>{{ 'labels.heading.Guarantor Detail' | translate }}</h1>
1010

1111
<div mat-dialog-content class="layout-column">
12-
<div class="layout-row-wrap responsive-column">
12+
<div class="layout-column">
1313
<div class="flex-fill">
14-
<span class="flex-50">{{ 'labels.inputs.First Name' | translate }}:</span>
15-
<span class="flex-50">{{ data.guarantorData.firstname }}</span>
14+
<span class="flex-40">{{ 'labels.inputs.First Name' | translate }}:</span>
15+
<span class="flex-60">{{ data.guarantorData.firstname }}</span>
1616
</div>
1717

1818
<div class="flex-fill">
19-
<span class="flex-50">{{ 'labels.inputs.Last Name' | translate }}:</span>
20-
<span class="flex-50">{{ data.guarantorData.lastname }}</span>
19+
<span class="flex-40">{{ 'labels.inputs.Last Name' | translate }}:</span>
20+
<span class="flex-60">{{ data.guarantorData.lastname }}</span>
2121
</div>
2222

2323
<div class="flex-fill">
24-
<span class="flex-50">{{ 'labels.inputs.Relationship' | translate }}:</span>
25-
<span class="flex-50">{{ data.guarantorData.clientRelationshipType.name }}</span>
24+
<span class="flex-40">{{ 'labels.inputs.Relationship' | translate }}:</span>
25+
<span class="flex-60">{{ data.guarantorData.clientRelationshipType?.name }}</span>
2626
</div>
2727

2828
<div class="flex-fill">
29-
<span class="flex-50">{{ 'labels.inputs.Guarantor Type' | translate }}:</span>
30-
<span class="flex-50">{{ data.guarantorData.guarantorType.value }}</span>
29+
<span class="flex-40">{{ 'labels.inputs.Guarantor Type' | translate }}:</span>
30+
<span class="flex-60">{{ data.guarantorData.guarantorType?.value }}</span>
3131
</div>
3232

3333
<div class="flex-fill">
34-
<span class="flex-50">{{ 'labels.inputs.Status' | translate }}:</span>
35-
<span class="flex-50">
34+
<span class="flex-40">{{ 'labels.inputs.Status' | translate }}:</span>
35+
<span class="flex-60">
3636
@if (data.guarantorData.status) {
3737
<span>
3838
{{ 'labels.inputs.Active' | translate }}
@@ -47,11 +47,11 @@ <h1 mat-dialog-title>{{ 'labels.heading.Guarantor Detail' | translate }}</h1>
4747
</div>
4848
@if (data.loanData?.delinquent?.availableDisbursementAmountWithOverApplied !== undefined) {
4949
<div class="flex-fill">
50-
<span class="flex-50"
50+
<span class="flex-40"
5151
>{{ 'labels.inputs.Available Disbursement Amount (with Over Applied)' | translate }}:</span
5252
>
53-
<span class="flex-50">{{
54-
data.loanData.delinquent.availableDisbursementAmountWithOverApplied | formatNumber
53+
<span class="flex-60">{{
54+
data.loanData?.delinquent?.availableDisbursementAmountWithOverApplied | formatNumber
5555
}}</span>
5656
</div>
5757
}

src/app/loans/custom-dialog/loans-account-view-guarantor-details-dialog/loans-account-view-guarantor-details-dialog.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from '@angular/material/dialog';
1818
import { CdkScrollable } from '@angular/cdk/scrolling';
1919
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
20+
import { FormatNumberPipe } from 'app/pipes/format-number.pipe';
2021

2122
@Component({
2223
selector: 'mifosx-loans-account-view-guarantor-details-dialog',
@@ -28,7 +29,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
2829
CdkScrollable,
2930
MatDialogContent,
3031
MatDialogActions,
31-
MatDialogClose
32+
MatDialogClose,
33+
FormatNumberPipe
3234
]
3335
})
3436
export class LoansAccountViewGuarantorDetailsDialogComponent implements OnInit {

src/app/loans/loans-view/loan-account-actions/create-guarantor/create-guarantor.component.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,13 @@ export class CreateGuarantorComponent implements OnInit, AfterViewInit {
202202
delete data.existingClient;
203203
delete data.name;
204204

205+
// Remove empty optional fields to avoid API validation errors
206+
Object.keys(data).forEach((key) => {
207+
if (data[key] === '' || data[key] === null || data[key] === undefined) {
208+
delete data[key];
209+
}
210+
});
211+
205212
this.loanService.createNewGuarantor(this.loanId, data).subscribe((response: any) => {
206213
this.router.navigate(['../../general'], { relativeTo: this.route });
207214
});

0 commit comments

Comments
 (0)