feat: add Current Account module, DIP verification, charges integration, and translations
- Current Account module with full CRUD, transactions, charges, hold/release, block/unblock - Current Products management under Products module - DIP mobile number verification with real API call to /lookup/check - Sanctions verification dialog - Charges support for Current Account (chargeAppliesTo=5) in create/edit - Fixed permissions from SAVINGSACCOUNT to CURRENTACCOUNT across all current account components - Added all missing translation keys for Current Account in en-US.json Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
73aa69ee02
commit
0a32d7135e
+34
-1
@@ -220,6 +220,39 @@
|
||||
</mat-checkbox>
|
||||
}
|
||||
|
||||
<div class="sanctions-verify-section flex-98">
|
||||
@if (!sanctionsVerified && !sanctionsBlocked) {
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
type="button"
|
||||
class="verify-client-btn"
|
||||
[disabled]="!createClientForm.get('firstname')?.value && !createClientForm.get('fullname')?.value"
|
||||
(click)="verifySanctions()"
|
||||
>
|
||||
<fa-icon icon="shield-alt" class="mr-05"></fa-icon>
|
||||
Verify Client
|
||||
</button>
|
||||
}
|
||||
@if (sanctionsVerified) {
|
||||
<div class="sanctions-result">
|
||||
<fa-icon icon="check-circle" class="text-success"></fa-icon>
|
||||
<span class="text-success">Client verified — No match found in sanctions list.</span>
|
||||
</div>
|
||||
}
|
||||
@if (sanctionsBlocked) {
|
||||
<div class="sanctions-blocked">
|
||||
<fa-icon icon="exclamation-triangle" class="text-danger"></fa-icon>
|
||||
<span class="text-danger">
|
||||
<strong>SANCTIONS MATCH FOUND</strong> — Client cannot be onboarded. Account opening is blocked.
|
||||
</span>
|
||||
<button mat-stroked-button color="warn" type="button" class="recheck-btn" (click)="verifySanctions()">
|
||||
Re-check
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<mat-divider class="flex-98"></mat-divider>
|
||||
|
||||
<div class="dip-info flex-98">
|
||||
@@ -356,7 +389,7 @@
|
||||
<fa-icon icon="arrow-left" class="m-r-10"></fa-icon>
|
||||
{{ 'labels.buttons.Previous' | translate }}
|
||||
</button>
|
||||
<button mat-raised-button matStepperNext>
|
||||
<button mat-raised-button matStepperNext [disabled]="sanctionsBlocked">
|
||||
{{ 'labels.buttons.Next' | translate }}
|
||||
<fa-icon icon="arrow-right" class="m-l-10"></fa-icon>
|
||||
</button>
|
||||
|
||||
@@ -172,6 +172,64 @@ mat-checkbox {
|
||||
}
|
||||
}
|
||||
|
||||
.sanctions-verify-section {
|
||||
margin: 12px 0;
|
||||
|
||||
.verify-client-btn {
|
||||
height: 40px;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sanctions-progress {
|
||||
mat-progress-bar {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.sanctions-msg {
|
||||
font-size: 13px;
|
||||
color: #555;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sanctions-result {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.text-success {
|
||||
color: #388e3c;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sanctions-blocked {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: #ffebee;
|
||||
border: 1px solid #ef9a9a;
|
||||
border-left: 4px solid #d32f2f;
|
||||
border-radius: 4px;
|
||||
padding: 10px 16px;
|
||||
margin: 12px 0;
|
||||
|
||||
.text-danger {
|
||||
color: #d32f2f;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.recheck-btn {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.external-id-status {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
|
||||
+57
-2
@@ -31,7 +31,10 @@ import { MatStepperPrevious, MatStepperNext } from '@angular/material/stepper';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
import { SanctionsVerifyDialogComponent } from './sanctions-verify-dialog.component';
|
||||
import { DipVerifyDialogComponent } from './dip-verify-dialog.component';
|
||||
|
||||
/**
|
||||
* Create Client Component
|
||||
@@ -59,6 +62,7 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy {
|
||||
private settingsService = inject(SettingsService);
|
||||
private clientService = inject(ClientsService);
|
||||
externalNationalIdService = inject(ExternalNationalIdService);
|
||||
private dialog = inject(MatDialog);
|
||||
|
||||
/** Subject to trigger unsubscription on destroy */
|
||||
private destroy$ = new Subject<void>();
|
||||
@@ -98,6 +102,10 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy {
|
||||
savingProductOptions: any;
|
||||
/** Mobile number DIP verification status */
|
||||
mobileVerified = false;
|
||||
/** Sanctions verification state */
|
||||
sanctionsVerified = false;
|
||||
/** Sanctions match was found — blocks account opening */
|
||||
sanctionsBlocked = false;
|
||||
|
||||
/**
|
||||
* @param {FormBuilder} formBuilder Form Builder
|
||||
@@ -258,10 +266,57 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify mobile number with DIP.
|
||||
* Verify mobile number with DIP via dialog.
|
||||
*/
|
||||
verifyWithDIP() {
|
||||
this.mobileVerified = true;
|
||||
const mobileNo = this.createClientForm.get('mobileNo')?.value || '';
|
||||
const dialogRef = this.dialog.open(DipVerifyDialogComponent, {
|
||||
width: '500px',
|
||||
data: { mobileNo }
|
||||
});
|
||||
dialogRef.afterClosed().subscribe((result: boolean) => {
|
||||
if (result) {
|
||||
this.mobileVerified = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify client name against sanctions lists via dialog.
|
||||
* Passes the client name to the dialog for real API screening.
|
||||
* If a match is found, sanctionsBlocked is set to true blocking further steps.
|
||||
*/
|
||||
verifySanctions() {
|
||||
const firstname = this.createClientForm.get('firstname')?.value || '';
|
||||
const middlename = this.createClientForm.get('middlename')?.value || '';
|
||||
const lastname = this.createClientForm.get('lastname')?.value || '';
|
||||
const fullname = this.createClientForm.get('fullname')?.value || '';
|
||||
|
||||
const name =
|
||||
fullname ||
|
||||
[
|
||||
firstname,
|
||||
middlename,
|
||||
lastname
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
|
||||
const dialogRef = this.dialog.open(SanctionsVerifyDialogComponent, {
|
||||
width: '550px',
|
||||
data: { name }
|
||||
});
|
||||
dialogRef.afterClosed().subscribe((verified: boolean) => {
|
||||
if (verified === true) {
|
||||
// CLEAR — no match found
|
||||
this.sanctionsVerified = true;
|
||||
this.sanctionsBlocked = false;
|
||||
} else if (verified === false) {
|
||||
// FLAGGED — match found, block account opening
|
||||
this.sanctionsVerified = false;
|
||||
this.sanctionsBlocked = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getDateLabel(legalFormId: number, values: string[]): string {
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
import { Component, OnInit, OnDestroy, inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { ClientsService } from 'app/clients/clients.service';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-dip-verify-dialog',
|
||||
template: `
|
||||
<h2 mat-dialog-title>
|
||||
<fa-icon icon="satellite-dish" class="dialog-title-icon"></fa-icon>
|
||||
DIP Verification
|
||||
</h2>
|
||||
<mat-dialog-content>
|
||||
@if (verifying) {
|
||||
<mat-progress-bar mode="determinate" [value]="progress" color="primary"></mat-progress-bar>
|
||||
<p class="progress-percent">{{ progress }}%</p>
|
||||
<div class="verify-info">
|
||||
<fa-icon icon="mobile-alt" class="info-icon"></fa-icon>
|
||||
<p class="verify-msg">
|
||||
Please wait while the system verifies the entered mobile number against the
|
||||
<strong>Mobile Number Revocation List (MNRL)</strong> available on the
|
||||
<strong>Digital Intelligence Platform (DIP)</strong> of the Department of Telecommunications (DoT),
|
||||
Government of India.
|
||||
</p>
|
||||
</div>
|
||||
<div class="warning-row">
|
||||
<fa-icon icon="exclamation-triangle" class="warning-icon"></fa-icon>
|
||||
<span class="warning-text">This may take a few seconds. Do not close or refresh this window.</span>
|
||||
</div>
|
||||
}
|
||||
@if (!verifying && serviceUnavailable) {
|
||||
<div class="verify-result unavailable">
|
||||
<fa-icon icon="info-circle" class="result-icon unavailable-icon"></fa-icon>
|
||||
<p class="result-msg unavailable-msg">{{ resultMessage }}</p>
|
||||
<div class="unavailable-detail">
|
||||
<fa-icon icon="exclamation-triangle" class="unavailable-detail-icon"></fa-icon>
|
||||
<span>Number treated as <strong>CLEAN</strong> by default. Proceed with caution.</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (!verifying && !suspected && !serviceUnavailable && !error) {
|
||||
<div class="verify-result">
|
||||
<fa-icon icon="check-circle" class="result-icon"></fa-icon>
|
||||
<p class="result-msg">Mobile number verified — Not found in MNRL revocation list.</p>
|
||||
</div>
|
||||
}
|
||||
@if (!verifying && suspected) {
|
||||
<div class="verify-result suspected">
|
||||
<fa-icon icon="exclamation-circle" class="result-icon suspected-icon"></fa-icon>
|
||||
<p class="result-msg suspected-msg">{{ resultMessage }}</p>
|
||||
@if (suspectedReason) {
|
||||
<div class="suspected-detail">
|
||||
<p><strong>Status:</strong> {{ suspectedStatus }}</p>
|
||||
<p><strong>Reason:</strong> {{ suspectedReason }}</p>
|
||||
<p><strong>Reported by:</strong> {{ suspectedReportedBy }}</p>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@if (!verifying && error) {
|
||||
<div class="verify-result error">
|
||||
<fa-icon icon="times-circle" class="result-icon error-icon"></fa-icon>
|
||||
<p class="result-msg error-msg">{{ error }}</p>
|
||||
</div>
|
||||
}
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
@if (!verifying) {
|
||||
<button mat-raised-button color="primary" (click)="close()">OK</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
display: block;
|
||||
min-width: 450px;
|
||||
}
|
||||
|
||||
h2[mat-dialog-title] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0;
|
||||
padding: 16px 24px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #1074b9;
|
||||
}
|
||||
|
||||
.dialog-title-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
mat-dialog-content {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
mat-progress-bar {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.progress-percent {
|
||||
text-align: center;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #1074b9;
|
||||
margin: 4px 0 12px;
|
||||
}
|
||||
|
||||
.verify-info {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
background: #e3f2fd;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.info-icon {
|
||||
font-size: 24px;
|
||||
color: #1074b9;
|
||||
margin-top: 2px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.verify-msg {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.warning-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
background: #fff3e0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.warning-icon {
|
||||
font-size: 16px;
|
||||
color: #e65100;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
font-size: 13px;
|
||||
color: #e65100;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.verify-result {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.result-icon {
|
||||
font-size: 48px;
|
||||
color: #388e3c;
|
||||
}
|
||||
|
||||
.result-msg {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #388e3c;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.suspected-icon {
|
||||
color: #e65100;
|
||||
}
|
||||
|
||||
.suspected-msg {
|
||||
color: #e65100;
|
||||
}
|
||||
|
||||
.suspected-detail {
|
||||
background: #fff3e0;
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.suspected-detail p {
|
||||
margin: 4px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.unavailable-icon {
|
||||
color: #1565c0;
|
||||
}
|
||||
|
||||
.unavailable-msg {
|
||||
color: #1565c0;
|
||||
}
|
||||
|
||||
.unavailable-detail {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: #e3f2fd;
|
||||
border-radius: 8px;
|
||||
padding: 10px 16px;
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.unavailable-detail-icon {
|
||||
font-size: 16px;
|
||||
color: #1565c0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
.error-msg {
|
||||
color: #d32f2f;
|
||||
}
|
||||
|
||||
mat-dialog-actions {
|
||||
padding: 8px 24px 16px;
|
||||
}
|
||||
`
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatDialogModule,
|
||||
MatProgressBarModule,
|
||||
MatButtonModule,
|
||||
FaIconComponent
|
||||
]
|
||||
})
|
||||
export class DipVerifyDialogComponent implements OnInit, OnDestroy {
|
||||
private dialogRef = inject(MatDialogRef<DipVerifyDialogComponent>);
|
||||
private data: { mobileNo: string } = inject(MAT_DIALOG_DATA);
|
||||
private clientsService = inject(ClientsService);
|
||||
|
||||
verifying = true;
|
||||
progress = 0;
|
||||
suspected = false;
|
||||
serviceUnavailable = false;
|
||||
error: string | null = null;
|
||||
resultMessage = '';
|
||||
suspectedStatus = '';
|
||||
suspectedReason = '';
|
||||
suspectedReportedBy = '';
|
||||
private subscription: any;
|
||||
private intervalId: any;
|
||||
private apiResponse: any = null;
|
||||
private apiError: any = null;
|
||||
private apiDone = false;
|
||||
|
||||
ngOnInit() {
|
||||
this.dialogRef.disableClose = true;
|
||||
|
||||
// Animate progress bar over ~3 seconds (0 → 100)
|
||||
this.intervalId = setInterval(() => {
|
||||
this.progress += 2;
|
||||
if (this.progress >= 100) {
|
||||
clearInterval(this.intervalId);
|
||||
this.intervalId = null;
|
||||
this.tryShowResult();
|
||||
}
|
||||
}, 60);
|
||||
|
||||
// Fire the API call in parallel
|
||||
this.subscription = this.clientsService.checkDipLookup(this.data.mobileNo).subscribe({
|
||||
next: (res: any) => {
|
||||
this.apiResponse = res;
|
||||
this.apiDone = true;
|
||||
this.tryShowResult();
|
||||
},
|
||||
error: (err: any) => {
|
||||
this.apiError = err;
|
||||
this.apiDone = true;
|
||||
this.tryShowResult();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Show result only after both progress bar completes and API responds */
|
||||
private tryShowResult() {
|
||||
if (!this.apiDone || this.progress < 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.verifying = false;
|
||||
this.dialogRef.disableClose = false;
|
||||
|
||||
if (this.apiError) {
|
||||
this.error =
|
||||
this.apiError?.error?.message || this.apiError?.message || 'DIP verification failed. Please try again.';
|
||||
return;
|
||||
}
|
||||
|
||||
const res = this.apiResponse;
|
||||
if (res?.data?.status === 'SERVICE_UNAVAILABLE') {
|
||||
this.serviceUnavailable = true;
|
||||
this.resultMessage = res.message || 'DIP/MNRL lookup service is unavailable. Number treated as CLEAN by default.';
|
||||
} else if (res?.data?.suspected) {
|
||||
this.suspected = true;
|
||||
this.resultMessage = res.message || `Mobile number is ${res.data.status} — found in DIP records`;
|
||||
const detail = res.data.suspectedDetail;
|
||||
if (detail) {
|
||||
this.suspectedStatus = detail.dipStatus || res.data.status;
|
||||
this.suspectedReason = detail.reason || '';
|
||||
this.suspectedReportedBy = detail.reportedBy || '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId);
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
// Return true if clear or service unavailable (treated as clean), false if suspected or error
|
||||
this.dialogRef.close(!this.suspected && !this.error);
|
||||
}
|
||||
}
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
import { Component, OnInit, OnDestroy, inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatDialogRef, MatDialogModule, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
||||
import { ClientsService } from 'app/clients/clients.service';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-sanctions-verify-dialog',
|
||||
template: `
|
||||
<h2 mat-dialog-title>
|
||||
<fa-icon icon="shield-alt" class="dialog-title-icon"></fa-icon>
|
||||
Sanctions Verification
|
||||
</h2>
|
||||
<mat-dialog-content>
|
||||
@if (verifying) {
|
||||
<mat-progress-bar mode="indeterminate" color="primary"></mat-progress-bar>
|
||||
<p class="sanctions-msg">
|
||||
Checking <strong>{{ clientName }}</strong> against sanctions list<br />
|
||||
<strong>UN Security Council</strong> &
|
||||
<strong>Financial Intelligence Unit - India (FIU-IND under UNSC Resolution 2231)</strong>
|
||||
</p>
|
||||
}
|
||||
@if (!verifying && !matchFound) {
|
||||
<div class="sanctions-result">
|
||||
<fa-icon icon="check-circle" class="result-icon success"></fa-icon>
|
||||
<p class="result-msg success">Client verified — No match found in sanctions list.</p>
|
||||
</div>
|
||||
}
|
||||
@if (!verifying && matchFound) {
|
||||
<div class="sanctions-result">
|
||||
<fa-icon icon="exclamation-triangle" class="result-icon danger"></fa-icon>
|
||||
<p class="result-msg danger">SANCTIONS MATCH FOUND</p>
|
||||
<p class="result-detail">
|
||||
<strong>{{ matchCount }}</strong> potential match(es) found in UN Security Council consolidated sanctions
|
||||
list for "<strong>{{ clientName }}</strong
|
||||
>".
|
||||
</p>
|
||||
<div class="match-list">
|
||||
@for (match of matches; track match.id) {
|
||||
<div class="match-item">
|
||||
<div class="match-name">{{ match.fullName }}</div>
|
||||
<div class="match-meta">
|
||||
<span class="match-type">{{ match.entryType }}</span>
|
||||
<span class="match-ref">{{ match.referenceNumber }}</span>
|
||||
@if (match.unListType) {
|
||||
<span class="match-list-type">{{ match.unListType }}</span>
|
||||
}
|
||||
</div>
|
||||
@if (match.aliases && match.aliases.length > 0) {
|
||||
<div class="match-aliases"><em>Also known as:</em> {{ match.aliases.join(', ') }}</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<p class="block-msg">This client cannot be onboarded. Account opening is blocked due to sanctions match.</p>
|
||||
</div>
|
||||
}
|
||||
@if (!verifying && errorOccurred) {
|
||||
<div class="sanctions-result">
|
||||
<fa-icon icon="exclamation-circle" class="result-icon warning"></fa-icon>
|
||||
<p class="result-msg warning">Verification failed. Please try again.</p>
|
||||
<p class="result-detail">{{ errorMessage }}</p>
|
||||
</div>
|
||||
}
|
||||
</mat-dialog-content>
|
||||
<mat-dialog-actions align="end">
|
||||
@if (!verifying && !matchFound && !errorOccurred) {
|
||||
<button mat-raised-button color="primary" (click)="close(true)">OK</button>
|
||||
}
|
||||
@if (!verifying && matchFound) {
|
||||
<button mat-raised-button color="warn" (click)="close(false)">Close</button>
|
||||
}
|
||||
@if (!verifying && errorOccurred) {
|
||||
<button mat-raised-button (click)="close(false)">Close</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
display: block;
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
h2[mat-dialog-title] {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0;
|
||||
padding: 16px 24px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #1074b9;
|
||||
}
|
||||
|
||||
.dialog-title-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
mat-dialog-content {
|
||||
padding: 16px 24px;
|
||||
max-height: 60vh;
|
||||
}
|
||||
|
||||
.sanctions-msg {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
line-height: 1.6;
|
||||
margin: 16px 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.sanctions-result {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.result-icon {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.result-icon.success {
|
||||
color: #388e3c;
|
||||
}
|
||||
.result-icon.danger {
|
||||
color: #d32f2f;
|
||||
}
|
||||
.result-icon.warning {
|
||||
color: #f57c00;
|
||||
}
|
||||
|
||||
.result-msg {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.result-msg.success {
|
||||
color: #388e3c;
|
||||
}
|
||||
.result-msg.danger {
|
||||
color: #d32f2f;
|
||||
}
|
||||
.result-msg.warning {
|
||||
color: #f57c00;
|
||||
}
|
||||
|
||||
.result-detail {
|
||||
font-size: 14px;
|
||||
color: #555;
|
||||
text-align: center;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.match-list {
|
||||
width: 100%;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.match-item {
|
||||
background: #fce4ec;
|
||||
border-left: 4px solid #d32f2f;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 6px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.match-name {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.match-meta {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-top: 4px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.match-type,
|
||||
.match-ref,
|
||||
.match-list-type {
|
||||
font-size: 11px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
background: #ffcdd2;
|
||||
color: #b71c1c;
|
||||
}
|
||||
|
||||
.match-aliases {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.block-msg {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #d32f2f;
|
||||
background: #ffebee;
|
||||
padding: 10px 16px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
mat-dialog-actions {
|
||||
padding: 8px 24px 16px;
|
||||
}
|
||||
`
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatDialogModule,
|
||||
MatProgressBarModule,
|
||||
MatButtonModule,
|
||||
FaIconComponent
|
||||
]
|
||||
})
|
||||
export class SanctionsVerifyDialogComponent implements OnInit, OnDestroy {
|
||||
private dialogRef = inject(MatDialogRef<SanctionsVerifyDialogComponent>);
|
||||
private data: { name: string } = inject(MAT_DIALOG_DATA);
|
||||
private clientsService = inject(ClientsService);
|
||||
|
||||
clientName = '';
|
||||
verifying = true;
|
||||
matchFound = false;
|
||||
matchCount = 0;
|
||||
matches: any[] = [];
|
||||
errorOccurred = false;
|
||||
errorMessage = '';
|
||||
|
||||
ngOnInit() {
|
||||
this.clientName = this.data?.name || '';
|
||||
this.dialogRef.disableClose = true;
|
||||
|
||||
this.clientsService.screenSanctions(this.clientName).subscribe({
|
||||
next: (result: any) => {
|
||||
this.verifying = false;
|
||||
this.dialogRef.disableClose = false;
|
||||
if (result.matchFound) {
|
||||
this.matchFound = true;
|
||||
this.matchCount = result.matchCount || 0;
|
||||
this.matches = result.matches || [];
|
||||
}
|
||||
},
|
||||
error: (err: any) => {
|
||||
this.verifying = false;
|
||||
this.dialogRef.disableClose = false;
|
||||
this.errorOccurred = true;
|
||||
this.errorMessage = err?.error?.message || err?.message || 'Unable to connect to sanctions screening service.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {}
|
||||
|
||||
close(verified: boolean) {
|
||||
this.dialogRef.close(verified);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user