feat: edit client — save per tab, DOB re-KYC alert, audit logs, KYC improvements
- Edit button per tab with Save & Next confirmation dialog - DOB change triggers re-KYC alert, resets PAN verification, auto-fills remarks - Mobile/email changes logged as audit notes via client notes API - Redesigned confirmation dialog with icon + title, type-based theming - KYC re-edit flow: remarks mandatory for VERIFIED records, re-edit API command - Load existing photo & signature in edit KYC, immediate upload on change - Populate all KYC fields (document, AML, form60/15) from API on edit - Non-individual PAN verification uses same flow as individual - Fix table overflow when sidebar panel is open - Add fr unit to stylelint allowed-list 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
b890d24bb7
commit
79e455c4ed
@@ -6,13 +6,42 @@
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
|
||||
<h1 mat-dialog-title>{{ data.heading }}</h1>
|
||||
<div mat-dialog-content>
|
||||
<p>{{ data.dialogContext }} ?</p>
|
||||
<div class="confirm-dialog" [class]="'confirm-dialog confirm-dialog--' + (data.type || 'basic')">
|
||||
<!-- Icon + Title -->
|
||||
<div class="confirm-header">
|
||||
<div class="confirm-header__icon-wrap">
|
||||
@switch (data.type) {
|
||||
@case ('warn') {
|
||||
<i class="fa fa-exclamation-triangle"></i>
|
||||
}
|
||||
@case ('Strong') {
|
||||
<i class="fa fa-trash-alt"></i>
|
||||
}
|
||||
@default {
|
||||
<i class="fa fa-pen-to-square"></i>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<h2 class="confirm-header__title">{{ data.heading }}</h2>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<mat-dialog-content class="confirm-body">
|
||||
<p class="confirm-body__text">{{ data.dialogContext }}</p>
|
||||
</mat-dialog-content>
|
||||
|
||||
<!-- Actions -->
|
||||
<mat-dialog-actions class="confirm-actions">
|
||||
<button mat-stroked-button class="confirm-actions__cancel" mat-dialog-close>
|
||||
{{ 'labels.buttons.Cancel' | translate }}
|
||||
</button>
|
||||
<button
|
||||
mat-flat-button
|
||||
class="confirm-actions__confirm confirm-actions__confirm--{{ data.type || 'basic' }}"
|
||||
[mat-dialog-close]="{ confirm: true }"
|
||||
>
|
||||
<i class="fa fa-check confirm-actions__btn-icon"></i>
|
||||
{{ 'labels.buttons.Confirm' | translate }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
</div>
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-raised-button mat-dialog-close>{{ 'labels.buttons.Cancel' | translate }}</button>
|
||||
<button mat-raised-button color="{{ color }}" [mat-dialog-close]="{ confirm: true }">
|
||||
{{ 'labels.buttons.Confirm' | translate }}
|
||||
</button>
|
||||
</mat-dialog-actions>
|
||||
|
||||
@@ -5,3 +5,145 @@
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
$primary: #1a73e8;
|
||||
$primary-light: #e8f0fe;
|
||||
$amber: #f59e0b;
|
||||
$amber-light: #fef7e0;
|
||||
$red: #ef4444;
|
||||
$red-light: #fef2f2;
|
||||
$gray-100: #f1f3f4;
|
||||
$gray-200: #e8eaed;
|
||||
$gray-500: #9aa0a6;
|
||||
$gray-700: #5f6368;
|
||||
$gray-900: #202124;
|
||||
$white: #fff;
|
||||
|
||||
.confirm-dialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 460px;
|
||||
min-width: 380px;
|
||||
}
|
||||
|
||||
// ─── Header ───
|
||||
.confirm-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 22px 24px 16px;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
|
||||
&__icon-wrap {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
.confirm-dialog--basic & {
|
||||
background: $primary-light;
|
||||
|
||||
i {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-dialog--warn & {
|
||||
background: $amber-light;
|
||||
|
||||
i {
|
||||
color: $amber;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-dialog--Strong & {
|
||||
background: $red-light;
|
||||
|
||||
i {
|
||||
color: $red;
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
color: $gray-900;
|
||||
letter-spacing: -0.2px;
|
||||
line-height: 1.25;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Body ───
|
||||
.confirm-body {
|
||||
padding: 18px 24px !important;
|
||||
overflow: visible !important;
|
||||
max-height: none !important;
|
||||
|
||||
&__text {
|
||||
font-size: 13.5px;
|
||||
line-height: 1.65;
|
||||
color: $gray-700;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Actions ───
|
||||
.confirm-actions {
|
||||
padding: 14px 24px 20px !important;
|
||||
border-top: 1px solid $gray-200;
|
||||
margin: 0 !important;
|
||||
display: flex !important;
|
||||
justify-content: flex-end !important;
|
||||
align-items: center !important;
|
||||
gap: 10px !important;
|
||||
|
||||
&__cancel {
|
||||
border-radius: 8px !important;
|
||||
font-size: 13px !important;
|
||||
font-weight: 600 !important;
|
||||
padding: 0 20px !important;
|
||||
height: 38px !important;
|
||||
color: $gray-700 !important;
|
||||
border-color: $gray-200 !important;
|
||||
}
|
||||
|
||||
&__confirm {
|
||||
border-radius: 8px !important;
|
||||
font-size: 13px !important;
|
||||
font-weight: 600 !important;
|
||||
padding: 0 24px !important;
|
||||
height: 38px !important;
|
||||
letter-spacing: 0.2px;
|
||||
color: $white !important;
|
||||
|
||||
&--basic {
|
||||
background: $primary !important;
|
||||
box-shadow: 0 1px 3px rgba($primary, 0.3) !important;
|
||||
}
|
||||
|
||||
&--warn {
|
||||
background: $amber !important;
|
||||
box-shadow: 0 1px 3px rgba($amber, 0.3) !important;
|
||||
}
|
||||
|
||||
&--Strong {
|
||||
background: $red !important;
|
||||
box-shadow: 0 1px 3px rgba($red, 0.3) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__btn-icon {
|
||||
margin-right: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,22 +7,21 @@
|
||||
*/
|
||||
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import {
|
||||
MatDialogRef,
|
||||
MAT_DIALOG_DATA,
|
||||
MatDialogTitle,
|
||||
MatDialogContent,
|
||||
MatDialogActions,
|
||||
MatDialogClose
|
||||
} from '@angular/material/dialog';
|
||||
import { DialogData } from 'app/core/dialogs/dialog-data.model';
|
||||
import { Dialogs } from 'app/core/dialogs/dialogs';
|
||||
import { CdkScrollable } from '@angular/cdk/scrolling';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
/**
|
||||
* Delete dialog component.
|
||||
* Confirmation dialog component.
|
||||
* Supports types: 'basic' (blue), 'warn' (amber), 'Strong' (red/destructive).
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-confirmation-dialog',
|
||||
@@ -30,25 +29,13 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
styleUrls: ['./confirmation-dialog.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatDialogTitle,
|
||||
CdkScrollable,
|
||||
MatDialogContent,
|
||||
MatDialogActions,
|
||||
MatDialogClose
|
||||
]
|
||||
})
|
||||
export class ConfirmationDialogComponent implements OnInit {
|
||||
export class ConfirmationDialogComponent {
|
||||
dialogRef = inject<MatDialogRef<ConfirmationDialogComponent>>(MatDialogRef);
|
||||
private dialogs = inject(Dialogs);
|
||||
data = inject<DialogData>(MAT_DIALOG_DATA);
|
||||
|
||||
color: string;
|
||||
|
||||
ngOnInit() {
|
||||
this.setColor();
|
||||
}
|
||||
|
||||
setColor() {
|
||||
this.color = this.dialogs.setColor(this.data.type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,9 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
border-radius: 12px;
|
||||
border: 1px solid $border;
|
||||
box-shadow: 0 1px 3px rgb(0 0 0 / 5%);
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
// Legacy layout support
|
||||
.layout-column,
|
||||
@@ -45,14 +48,21 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
&.m-r-30 { margin-right: 12px; }
|
||||
&.m-r-30 {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
mat-form-field {
|
||||
min-width: 200px;
|
||||
flex: 1;
|
||||
|
||||
&.search-box { width: auto; }
|
||||
+ mat-form-field { margin-left: 12px; }
|
||||
&.search-box {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
+ mat-form-field {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
mat-checkbox {
|
||||
@@ -60,7 +70,9 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
flex-shrink: 0;
|
||||
margin-left: 8px;
|
||||
|
||||
&.m-r-10 { margin-right: 8px; }
|
||||
&.m-r-10 {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,14 +82,18 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
|
||||
&.gap-25px { gap: 10px; }
|
||||
&.gap-25px {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
white-space: nowrap;
|
||||
|
||||
fa-icon {
|
||||
&.m-r-10,
|
||||
&.m-r-20 { margin-right: 6px; }
|
||||
&.m-r-20 {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,11 +108,16 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
|
||||
.select-row {
|
||||
cursor: pointer;
|
||||
&:hover { background-color: $hover-bg; }
|
||||
|
||||
&:hover {
|
||||
background-color: $hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td { padding: 10px 16px; }
|
||||
td {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: 600;
|
||||
@@ -110,7 +131,10 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
|
||||
tr {
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
&:last-child { border-bottom: none; }
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
@@ -125,7 +149,9 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
mat-progress-bar { margin: 0; }
|
||||
mat-progress-bar {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Modern Table Header =====
|
||||
@@ -167,10 +193,21 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
span {
|
||||
border-radius: 2px;
|
||||
|
||||
&:nth-child(1) { background: $brand; }
|
||||
&:nth-child(2) { background: #0ea5e9; }
|
||||
&:nth-child(3) { background: #10b981; }
|
||||
&:nth-child(4) { background: #cbd5e1; }
|
||||
&:nth-child(1) {
|
||||
background: $brand;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
background: #0ea5e9;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
background: #10b981;
|
||||
}
|
||||
|
||||
&:nth-child(4) {
|
||||
background: #cbd5e1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,7 +235,9 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
max-width: 360px;
|
||||
|
||||
::ng-deep {
|
||||
.mat-mdc-form-field-flex { height: 38px; }
|
||||
.mat-mdc-form-field-flex {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.mat-mdc-form-field-infix {
|
||||
padding: 6px 0;
|
||||
@@ -206,8 +245,13 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
}
|
||||
|
||||
.mdc-notched-outline {
|
||||
.mdc-notched-outline__leading { border-radius: 8px 0 0 8px; }
|
||||
.mdc-notched-outline__trailing { border-radius: 0 8px 8px 0; }
|
||||
.mdc-notched-outline__leading {
|
||||
border-radius: 8px 0 0 8px;
|
||||
}
|
||||
|
||||
.mdc-notched-outline__trailing {
|
||||
border-radius: 0 8px 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-mdc-form-field-icon-prefix {
|
||||
@@ -215,7 +259,9 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
color: $text-muted;
|
||||
}
|
||||
|
||||
input { font-size: 13px; }
|
||||
input {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,7 +283,10 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
height: 36px;
|
||||
padding: 0 16px;
|
||||
|
||||
fa-icon { margin-right: 6px; font-size: 12px; }
|
||||
fa-icon {
|
||||
margin-right: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-import {
|
||||
@@ -259,7 +308,7 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
.table-container {
|
||||
border: 1px solid $border;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
overflow: hidden auto;
|
||||
}
|
||||
|
||||
.table-loader {
|
||||
@@ -294,7 +343,9 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
transition: background 0.1s;
|
||||
|
||||
&:last-child { border-bottom: none; }
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
&:nth-child(even) {
|
||||
background: #fafbfc;
|
||||
@@ -314,7 +365,11 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
font-weight: 400;
|
||||
color: $text;
|
||||
padding: 0 12px;
|
||||
font-family: 'Inter', 'Roboto', -apple-system, sans-serif;
|
||||
font-family:
|
||||
Inter,
|
||||
Roboto,
|
||||
-apple-system,
|
||||
sans-serif;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -324,9 +379,15 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
font-family: 'Inter', 'Roboto', -apple-system, sans-serif;
|
||||
font-family:
|
||||
Inter,
|
||||
Roboto,
|
||||
-apple-system,
|
||||
sans-serif;
|
||||
|
||||
&:hover { text-decoration: underline; }
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.cell-mono {
|
||||
@@ -350,7 +411,11 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: capitalize;
|
||||
white-space: nowrap;
|
||||
font-family: 'Inter', 'Roboto', -apple-system, sans-serif;
|
||||
font-family:
|
||||
Inter,
|
||||
Roboto,
|
||||
-apple-system,
|
||||
sans-serif;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
@@ -367,7 +432,9 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
background: #ecfdf5 !important;
|
||||
color: #059669 !important;
|
||||
|
||||
&::before { background: #059669; }
|
||||
&::before {
|
||||
background: #059669;
|
||||
}
|
||||
}
|
||||
|
||||
.status-active-overdue,
|
||||
@@ -375,7 +442,9 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
background: #fef2f2 !important;
|
||||
color: #dc2626 !important;
|
||||
|
||||
&::before { background: #dc2626; }
|
||||
&::before {
|
||||
background: #dc2626;
|
||||
}
|
||||
}
|
||||
|
||||
.status-submitted-and-pending-approval,
|
||||
@@ -383,14 +452,18 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
background: #fffbeb !important;
|
||||
color: #d97706 !important;
|
||||
|
||||
&::before { background: #d97706; }
|
||||
&::before {
|
||||
background: #d97706;
|
||||
}
|
||||
}
|
||||
|
||||
.status-overpaid {
|
||||
background: #eff6ff !important;
|
||||
color: #2563eb !important;
|
||||
|
||||
&::before { background: #2563eb; }
|
||||
&::before {
|
||||
background: #2563eb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,8 +475,13 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
vertical-align: middle;
|
||||
margin-right: 6px;
|
||||
|
||||
&.person { color: #2563eb; }
|
||||
&.entity { color: #7c3aed; }
|
||||
&.person {
|
||||
color: #2563eb;
|
||||
}
|
||||
|
||||
&.entity {
|
||||
color: #7c3aed;
|
||||
}
|
||||
}
|
||||
|
||||
.legal-form-badge {
|
||||
@@ -550,10 +628,15 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
min-width: 150px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
+ mat-form-field { margin-left: 0; }
|
||||
+ mat-form-field {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
mat-checkbox { margin-left: 0; margin-top: 8px; }
|
||||
mat-checkbox {
|
||||
margin-left: 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-button {
|
||||
@@ -561,25 +644,50 @@ $mono: 'Roboto Mono', 'DM Mono', monospace;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
|
||||
&.gap-25px { gap: 10px; }
|
||||
&.gap-25px {
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.table-actions { flex-direction: column; align-items: stretch; }
|
||||
.table-search { max-width: 100%; }
|
||||
.table-buttons { margin-left: 0; justify-content: stretch; }
|
||||
.table-buttons button { flex: 1; }
|
||||
.table-actions {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.table-search {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.table-buttons {
|
||||
margin-left: 0;
|
||||
justify-content: stretch;
|
||||
}
|
||||
|
||||
.table-buttons button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy table styles
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
tr.select-row:hover { cursor: pointer; }
|
||||
tr.select-row:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
// Utility classes
|
||||
.m-r-10 { margin-right: 10px !important; }
|
||||
.m-r-20 { margin-right: 20px !important; }
|
||||
.m-r-30 { margin-right: 30px !important; }
|
||||
.m-r-10 {
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
|
||||
.m-r-20 {
|
||||
margin-right: 20px !important;
|
||||
}
|
||||
|
||||
.m-r-30 {
|
||||
margin-right: 30px !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user