Files
cbsnexora/src/app/clients/client-stepper/client-general-step/dip-verify-dialog.component.ts
T
AdityaandClaude Opus 4.6 b890d24bb7 feat: non-individual entity UI improvements — hide individual-only sections, fix legal form display, send extended fields to API
- Hide Family Members tab/section for non-individual entities in client view, create flow, and preview
- Hide Financial & Classification section for non-individual entities in personal data and preview
- Fix legal form column in clients list — fetch legalForm from client API for correct display
- Add person/business icon before client name in list rows
- Remove unsupportedNpd filter — send authorizedSignatory, additionalInfo, dateOfRegistration to API
- Add registration number format hint (mat-hint) based on constitution type
- Add spacing between verification row and constitution type dropdowns
- Reduce table cell padding and prevent action buttons from being clipped
- Add missing MPL-2.0 license headers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 15:10:51 +05:30

341 lines
9.1 KiB
TypeScript

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">
<fa-icon icon="check-circle" class="result-icon"></fa-icon>
<p class="result-msg">DIP & MNRL verification completed successfully.</p>
<div class="success-detail">
<fa-icon icon="shield-alt" class="success-detail-icon"></fa-icon>
<span>Mobile number is <strong>CLEAN</strong> — not found in the revocation list.</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;
}
.success-detail {
display: flex;
align-items: center;
gap: 8px;
background: #e8f5e9;
border: 1px solid #a5d6a7;
border-radius: 8px;
padding: 10px 16px;
width: 100%;
font-size: 13px;
color: #2e7d32;
}
.success-detail-icon {
font-size: 16px;
color: #388e3c;
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() {
const verified = !this.suspected && !this.error;
this.dialogRef.close({
verified,
lookupLogId: this.apiResponse?.lookupLogId || null,
timestamp: this.apiResponse?.timestamp || null
});
}
}