Merge branch 'feature/merge-updates' from cbsnexora into feature/ui-reports-product

Conflict resolutions:
- src/main.scss: kept both new dialog backdrops (CIC/PSL/ALM/CIMS/CKYC/Acct)
  and incoming dialog panel styles (.abd-dialog-panel, .arc-dialog-panel)
- client-general-step.component.html/.ts: kept HEAD's agriculture/non-agri
  income fields, education/degree/religion/social-category dropdowns,
  and inject() refactor matching the incoming pattern
- kyc-management.component.html/.scss/.ts: kept HEAD's Home Visit
  terminology, takeAction() method, hv-* and vkyc-* dialog styles;
  accepted incoming .km-warn-d style addition

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-04-29 19:22:00 +05:30
co-authored by Claude Opus 4.7
470 changed files with 46834 additions and 11014 deletions
@@ -2020,11 +2020,12 @@ export class ClientGeneralStepComponent implements OnInit, OnDestroy, OnChanges
export class SignatoryDetailDialogComponent {
private clientService = inject(ClientsService);
private sanitizer = inject(DomSanitizer);
private inputData = inject<any>(MAT_DIALOG_DATA);
data: any;
loading = false;
constructor() {
const inputData = inject<any>(MAT_DIALOG_DATA);
const inputData = this.inputData;
this.data = { ...inputData };
const cid = inputData.id?.toString();
if (!cid) return;
@@ -1,3 +1,11 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Component, OnInit, OnDestroy, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@@ -39,13 +47,23 @@ import { ClientsService } from 'app/clients/clients.service';
}
<div class="din-info-bar">
<fa-icon icon="info-circle" class="info-icon"></fa-icon>
<span>Enter the <strong>Director Identification Number (DIN)</strong> issued by the Ministry of Corporate Affairs (MCA) for this authorized signatory. The system will verify the DIN and match the entity name against the director's associated companies.</span>
<span
>Enter the <strong>Director Identification Number (DIN)</strong> issued by the Ministry of Corporate
Affairs (MCA) for this authorized signatory. The system will verify the DIN and match the entity name
against the director's associated companies.</span
>
</div>
<mat-form-field appearance="outline" class="din-input-field">
<mat-label>DIN Number</mat-label>
<mat-icon matPrefix class="din-prefix-icon">badge</mat-icon>
<input matInput [(ngModel)]="dinNumber" placeholder="e.g. 01234567" maxlength="8"
(keyup.enter)="onVerify()" autocomplete="off" />
<input
matInput
[(ngModel)]="dinNumber"
placeholder="e.g. 01234567"
maxlength="8"
(keyup.enter)="onVerify()"
autocomplete="off"
/>
<mat-hint>8-digit DIN number from MCA</mat-hint>
</mat-form-field>
</div>
@@ -59,14 +77,17 @@ import { ClientsService } from 'app/clients/clients.service';
<div class="progress-steps">
@for (step of progressSteps; track step.label; let i = $index) {
<div class="progress-step" [class.active]="i === currentStep" [class.done]="i < currentStep">
<fa-icon [icon]="i < currentStep ? 'check-circle' : step.icon" [class.done-icon]="i < currentStep"></fa-icon>
<fa-icon
[icon]="i < currentStep ? 'check-circle' : step.icon"
[class.done-icon]="i < currentStep"
></fa-icon>
<span>{{ step.label }}</span>
</div>
}
</div>
<p class="verify-msg">
Verifying DIN <strong>{{ dinNumber }}</strong> for <strong>{{ signatoryName }}</strong>
against <strong>MCA (Ministry of Corporate Affairs)</strong> records.
Verifying DIN <strong>{{ dinNumber }}</strong> for <strong>{{ signatoryName }}</strong> against
<strong>MCA (Ministry of Corporate Affairs)</strong> records.
</p>
</div>
}
@@ -80,9 +101,8 @@ import { ClientsService } from 'app/clients/clients.service';
<div class="result-badge success-badge">VERIFIED</div>
<h3 class="result-title">DIN Verified Successfully</h3>
<p class="result-detail">
DIN <strong class="din-highlight">{{ dinNumber }}</strong> for
<strong>{{ signatoryName }}</strong> has been verified against
<strong>MCA</strong> records.
DIN <strong class="din-highlight">{{ dinNumber }}</strong> for <strong>{{ signatoryName }}</strong> has been
verified against <strong>MCA</strong> records.
</p>
<div class="result-meta">
<div class="meta-row">
@@ -124,7 +144,9 @@ import { ClientsService } from 'app/clients/clients.service';
<fa-icon icon="check-circle" class="match-banner-icon"></fa-icon>
<div>
<strong>Entity Match Found</strong>
<p><strong>{{ entityName }}</strong> matches <strong>{{ matchedCompany }}</strong></p>
<p>
<strong>{{ entityName }}</strong> matches <strong>{{ matchedCompany }}</strong>
</p>
</div>
</div>
} @else {
@@ -132,7 +154,9 @@ import { ClientsService } from 'app/clients/clients.service';
<fa-icon icon="exclamation-triangle" class="match-banner-icon"></fa-icon>
<div>
<strong>No Entity Match</strong>
<p><strong>{{ entityName }}</strong> does not match any of the director's associated companies.</p>
<p>
<strong>{{ entityName }}</strong> does not match any of the director's associated companies.
</p>
</div>
</div>
}
@@ -148,11 +172,21 @@ import { ClientsService } from 'app/clients/clients.service';
<div class="company-info">
<span class="company-name">{{ company.companyName }}</span>
<span class="company-meta">
@if (company.cin) { CIN: {{ company.cin }} }
@if (company.cin && company.designation) { &middot; }
@if (company.designation) { {{ company.designation }} }
@if ((company.cin || company.designation) && company.appointmentDate) { &middot; }
@if (company.appointmentDate) { Since: {{ company.appointmentDate }} }
@if (company.cin) {
CIN: {{ company.cin }}
}
@if (company.cin && company.designation) {
&middot;
}
@if (company.designation) {
{{ company.designation }}
}
@if ((company.cin || company.designation) && company.appointmentDate) {
&middot;
}
@if (company.appointmentDate) {
Since: {{ company.appointmentDate }}
}
</span>
</div>
</div>
@@ -201,15 +235,11 @@ import { ClientsService } from 'app/clients/clients.service';
</button>
}
@if (!verifying && verified) {
<button mat-raised-button color="primary" (click)="onClose(true)">
<fa-icon icon="check"></fa-icon> Done
</button>
<button mat-raised-button color="primary" (click)="onClose(true)"><fa-icon icon="check"></fa-icon> Done</button>
}
@if (!verifying && failed) {
<button mat-stroked-button (click)="onRetry()">Retry</button>
<button mat-raised-button color="warn" (click)="onClose(false)">
<fa-icon icon="times"></fa-icon> Close
</button>
<button mat-raised-button color="warn" (click)="onClose(false)"><fa-icon icon="times"></fa-icon> Close</button>
}
@if (!verifying && errorOccurred) {
<button mat-stroked-button (click)="onRetry()">Retry</button>
@@ -235,7 +265,9 @@ import { ClientsService } from 'app/clients/clients.service';
color: #1074b9;
}
.dialog-title-icon { font-size: 20px; }
.dialog-title-icon {
font-size: 20px;
}
mat-dialog-content {
padding: 16px 24px;
@@ -255,10 +287,24 @@ import { ClientsService } from 'app/clients/clients.service';
border: 1px solid #e2e8f0;
}
.sig-icon { font-size: 22px; color: #1074b9; }
.sig-detail { display: flex; flex-direction: column; }
.sig-name { font-size: 15px; font-weight: 700; color: #1a2332; }
.sig-meta { font-size: 12px; color: #64748b; margin-top: 2px; }
.sig-icon {
font-size: 22px;
color: #1074b9;
}
.sig-detail {
display: flex;
flex-direction: column;
}
.sig-name {
font-size: 15px;
font-weight: 700;
color: #1a2332;
}
.sig-meta {
font-size: 12px;
color: #64748b;
margin-top: 2px;
}
/* Entity info */
.entity-info {
@@ -272,10 +318,26 @@ import { ClientsService } from 'app/clients/clients.service';
border: 1px solid #fde68a;
}
.entity-icon { font-size: 18px; color: #b45309; }
.entity-detail { display: flex; flex-direction: column; }
.entity-label { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; color: #92400e; }
.entity-name { font-size: 14px; font-weight: 700; color: #78350f; }
.entity-icon {
font-size: 18px;
color: #b45309;
}
.entity-detail {
display: flex;
flex-direction: column;
}
.entity-label {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #92400e;
}
.entity-name {
font-size: 14px;
font-weight: 700;
color: #78350f;
}
/* DIN info bar */
.din-info-bar {
@@ -291,49 +353,180 @@ import { ClientsService } from 'app/clients/clients.service';
line-height: 1.5;
}
.info-icon { font-size: 16px; color: #1074b9; flex-shrink: 0; margin-top: 2px; }
.info-icon {
font-size: 16px;
color: #1074b9;
flex-shrink: 0;
margin-top: 2px;
}
/* DIN input */
.din-input-field { width: 100%; }
.din-prefix-icon { color: #64748b; margin-right: 4px; }
.din-input-field {
width: 100%;
}
.din-prefix-icon {
color: #64748b;
margin-right: 4px;
}
/* Progress */
.progress-section { display: flex; flex-direction: column; align-items: center; gap: 12px; }
.progress-percent { font-size: 22px; font-weight: 700; color: #1074b9; margin-top: 4px; }
.progress-section {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.progress-percent {
font-size: 22px;
font-weight: 700;
color: #1074b9;
margin-top: 4px;
}
.progress-steps { display: flex; flex-direction: column; gap: 10px; width: 100%; padding: 8px 16px; }
.progress-step { display: flex; align-items: center; gap: 8px; font-size: 13px; color: #9ca3af; transition: color 0.3s ease; }
.progress-step.active { color: #1074b9; font-weight: 600; }
.progress-step.done { color: #388e3c; }
.done-icon { color: #388e3c; }
.progress-steps {
display: flex;
flex-direction: column;
gap: 10px;
width: 100%;
padding: 8px 16px;
}
.progress-step {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: #9ca3af;
transition: color 0.3s ease;
}
.progress-step.active {
color: #1074b9;
font-weight: 600;
}
.progress-step.done {
color: #388e3c;
}
.done-icon {
color: #388e3c;
}
.verify-msg { font-size: 14px; color: #555; line-height: 1.6; text-align: center; margin: 4px 0 0; }
.verify-msg {
font-size: 14px;
color: #555;
line-height: 1.6;
text-align: center;
margin: 4px 0 0;
}
/* Result cards */
.result-card { display: flex; flex-direction: column; align-items: center; padding: 20px 16px; gap: 12px; }
.result-icon-ring { width: 64px; height: 64px; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
.success-ring { background: linear-gradient(135deg, #ecfdf5, #d1fae5); border: 3px solid #a7f3d0; }
.fail-ring { background: linear-gradient(135deg, #fef2f2, #fecaca); border: 3px solid #fca5a5; }
.result-shield { font-size: 28px; }
.success-shield { color: #059669; }
.fail-shield { color: #dc2626; }
.result-card {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px 16px;
gap: 12px;
}
.result-icon-ring {
width: 64px;
height: 64px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.success-ring {
background: linear-gradient(135deg, #ecfdf5, #d1fae5);
border: 3px solid #a7f3d0;
}
.fail-ring {
background: linear-gradient(135deg, #fef2f2, #fecaca);
border: 3px solid #fca5a5;
}
.result-shield {
font-size: 28px;
}
.success-shield {
color: #059669;
}
.fail-shield {
color: #dc2626;
}
.result-badge { display: inline-block; font-size: 11px; font-weight: 800; letter-spacing: 1.5px; padding: 3px 14px; border-radius: 20px; }
.success-badge { color: #059669; background: #ecfdf5; border: 1px solid #a7f3d0; }
.fail-badge { color: #dc2626; background: #fef2f2; border: 1px solid #fecaca; }
.result-badge {
display: inline-block;
font-size: 11px;
font-weight: 800;
letter-spacing: 1.5px;
padding: 3px 14px;
border-radius: 20px;
}
.success-badge {
color: #059669;
background: #ecfdf5;
border: 1px solid #a7f3d0;
}
.fail-badge {
color: #dc2626;
background: #fef2f2;
border: 1px solid #fecaca;
}
.result-title { font-size: 17px; font-weight: 700; color: #111827; margin: 0; }
.result-detail { font-size: 13px; color: #6b7280; text-align: center; line-height: 1.6; margin: 0; max-width: 400px; }
.din-highlight { color: #059669; font-family: 'JetBrains Mono', 'Fira Code', monospace; }
.din-highlight-fail { color: #dc2626; font-family: 'JetBrains Mono', 'Fira Code', monospace; }
.result-title {
font-size: 17px;
font-weight: 700;
color: #111827;
margin: 0;
}
.result-detail {
font-size: 13px;
color: #6b7280;
text-align: center;
line-height: 1.6;
margin: 0;
max-width: 400px;
}
.din-highlight {
color: #059669;
font-family: 'JetBrains Mono', 'Fira Code', monospace;
}
.din-highlight-fail {
color: #dc2626;
font-family: 'JetBrains Mono', 'Fira Code', monospace;
}
.result-meta { width: 100%; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 12px 16px; margin-top: 4px; }
.meta-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #f3f4f6; }
.meta-row:last-child { border-bottom: none; }
.meta-label { font-size: 12px; color: #9ca3af; font-weight: 500; }
.meta-value { font-size: 12px; color: #374151; font-weight: 600; }
.meta-mono { font-family: 'JetBrains Mono', 'Fira Code', monospace; letter-spacing: 0.5px; }
.meta-success { color: #059669; }
.result-meta {
width: 100%;
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 8px;
padding: 12px 16px;
margin-top: 4px;
}
.meta-row {
display: flex;
justify-content: space-between;
padding: 6px 0;
border-bottom: 1px solid #f3f4f6;
}
.meta-row:last-child {
border-bottom: none;
}
.meta-label {
font-size: 12px;
color: #9ca3af;
font-weight: 500;
}
.meta-value {
font-size: 12px;
color: #374151;
font-weight: 600;
}
.meta-mono {
font-family: 'JetBrains Mono', 'Fira Code', monospace;
letter-spacing: 0.5px;
}
.meta-success {
color: #059669;
}
/* ═══ Company Match Section ═══ */
.company-match-section {
@@ -351,7 +544,10 @@ import { ClientsService } from 'app/clients/clients.service';
margin: 0 0 10px;
}
.company-title-icon { font-size: 14px; color: #64748b; }
.company-title-icon {
font-size: 14px;
color: #64748b;
}
/* Match banner */
.match-banner {
@@ -365,7 +561,10 @@ import { ClientsService } from 'app/clients/clients.service';
line-height: 1.5;
}
.match-banner p { margin: 2px 0 0; font-size: 12px; }
.match-banner p {
margin: 2px 0 0;
font-size: 12px;
}
.match-success {
background: #ecfdf5;
@@ -373,7 +572,12 @@ import { ClientsService } from 'app/clients/clients.service';
color: #065f46;
}
.match-success .match-banner-icon { color: #059669; font-size: 18px; margin-top: 1px; flex-shrink: 0; }
.match-success .match-banner-icon {
color: #059669;
font-size: 18px;
margin-top: 1px;
flex-shrink: 0;
}
.match-warning {
background: #fffbeb;
@@ -381,7 +585,12 @@ import { ClientsService } from 'app/clients/clients.service';
color: #92400e;
}
.match-warning .match-banner-icon { color: #d97706; font-size: 18px; margin-top: 1px; flex-shrink: 0; }
.match-warning .match-banner-icon {
color: #d97706;
font-size: 18px;
margin-top: 1px;
flex-shrink: 0;
}
/* Company list */
.company-list {
@@ -399,9 +608,13 @@ import { ClientsService } from 'app/clients/clients.service';
transition: background 0.15s;
}
.company-item:last-child { border-bottom: none; }
.company-item:last-child {
border-bottom: none;
}
.company-item:hover { background: #f8fafc; }
.company-item:hover {
background: #f8fafc;
}
.company-matched {
background: #f0fdf4 !important;
@@ -415,28 +628,78 @@ import { ClientsService } from 'app/clients/clients.service';
flex-shrink: 0;
}
.company-match-icon.match { color: #22c55e; }
.company-match-icon.no-match { color: #d1d5db; }
.company-match-icon.match {
color: #22c55e;
}
.company-match-icon.no-match {
color: #d1d5db;
}
.company-info { display: flex; flex-direction: column; min-width: 0; }
.company-name { font-size: 13px; font-weight: 600; color: #1e293b; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.company-meta { font-size: 11px; color: #94a3b8; margin-top: 2px; }
.company-info {
display: flex;
flex-direction: column;
min-width: 0;
}
.company-name {
font-size: 13px;
font-weight: 600;
color: #1e293b;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.company-meta {
font-size: 11px;
color: #94a3b8;
margin-top: 2px;
}
/* Fail reason */
.fail-reason {
display: flex; align-items: center; gap: 10px; width: 100%;
background: #fef2f2; border: 1px solid #fecaca; padding: 10px 14px;
border-radius: 6px; font-size: 13px; color: #991b1b; line-height: 1.5;
display: flex;
align-items: center;
gap: 10px;
width: 100%;
background: #fef2f2;
border: 1px solid #fecaca;
padding: 10px 14px;
border-radius: 6px;
font-size: 13px;
color: #991b1b;
line-height: 1.5;
}
.fail-reason-icon {
font-size: 16px;
color: #dc2626;
flex-shrink: 0;
}
.fail-reason-icon { font-size: 16px; color: #dc2626; flex-shrink: 0; }
/* Error */
.error-icon-large { font-size: 48px; color: #f57c00; }
.error-msg { font-size: 16px; font-weight: 600; color: #f57c00; margin: 0; text-align: center; }
.error-detail { font-size: 14px; color: #555; text-align: center; }
.error-icon-large {
font-size: 48px;
color: #f57c00;
}
.error-msg {
font-size: 16px;
font-weight: 600;
color: #f57c00;
margin: 0;
text-align: center;
}
.error-detail {
font-size: 14px;
color: #555;
text-align: center;
}
mat-dialog-actions { padding: 8px 24px 16px; }
mat-dialog-actions button { display: flex; align-items: center; gap: 6px; }
mat-dialog-actions {
padding: 8px 24px 16px;
}
mat-dialog-actions button {
display: flex;
align-items: center;
gap: 6px;
}
`
],
imports: [
@@ -561,7 +824,11 @@ export class DinVerifyDialogComponent implements OnInit, OnDestroy {
if (this.apiError) {
this.errorOccurred = true;
this.errorMessage = this.apiError?.error?.defaultUserMessage || this.apiError?.error?.message || this.apiError?.message || 'Unable to verify DIN. Please try again.';
this.errorMessage =
this.apiError?.error?.defaultUserMessage ||
this.apiError?.error?.message ||
this.apiError?.message ||
'Unable to verify DIN. Please try again.';
return;
}
@@ -590,7 +857,11 @@ export class DinVerifyDialogComponent implements OnInit, OnDestroy {
this.matchCompanyName();
} else {
this.failed = true;
this.failReason = changes?.dinFailReason || res?.failReason || res?.defaultUserMessage || 'DIN could not be verified against MCA records.';
this.failReason =
changes?.dinFailReason ||
res?.failReason ||
res?.defaultUserMessage ||
'DIN could not be verified against MCA records.';
}
}
@@ -1,3 +1,11 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
import { Component, OnInit, OnDestroy, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatDialogRef, MatDialogModule, MAT_DIALOG_DATA } from '@angular/material/dialog';
@@ -21,7 +29,10 @@ import { ClientsService } from 'app/clients/clients.service';
<div class="progress-steps">
@for (step of progressSteps; track step.label; let i = $index) {
<div class="progress-step" [class.active]="i === currentStep" [class.done]="i < currentStep">
<fa-icon [icon]="i < currentStep ? 'check-circle' : step.icon" [class.done-icon]="i < currentStep"></fa-icon>
<fa-icon
[icon]="i < currentStep ? 'check-circle' : step.icon"
[class.done-icon]="i < currentStep"
></fa-icon>
<span>{{ step.label }}</span>
</div>
}
@@ -42,8 +53,8 @@ import { ClientsService } from 'app/clients/clients.service';
<h3 class="approve-title">No Sanctions Match Found</h3>
<p class="approve-detail">
<strong>{{ clientName }}</strong> has been screened against the
<strong>UN Security Council Consolidated List</strong> and
<strong>FIU-IND</strong> records. No confirmed match was found.
<strong>UN Security Council Consolidated List</strong> and <strong>FIU-IND</strong> records. No confirmed
match was found.
</p>
<div class="approve-meta">
<div class="meta-row">
@@ -550,7 +561,12 @@ export class SanctionsVerifyDialogComponent implements OnInit, OnDestroy {
}
private runProgress() {
const stepDurations = [1200, 1500, 1800, 1000]; // ms per step
const stepDurations = [
1200,
1500,
1800,
1000
]; // ms per step
const totalDuration = stepDurations.reduce((a, b) => a + b, 0);
let elapsed = 0;
const interval = 50;
@@ -602,7 +618,10 @@ export class SanctionsVerifyDialogComponent implements OnInit, OnDestroy {
if (this.apiError) {
this.errorOccurred = true;
this.errorMessage = this.apiError?.error?.message || this.apiError?.message || 'Unable to connect to sanctions screening service.';
this.errorMessage =
this.apiError?.error?.message ||
this.apiError?.message ||
'Unable to connect to sanctions screening service.';
} else {
this.screeningLogId = this.apiResult?.screeningLogId || null;
if (this.apiResult?.matchFound) {