Files
cbsnexora/src/app/clients/client-stepper/client-family-members-step/client-family-members-step.component.scss
T
AdityaandClaude Opus 4.7 83ea57ccf0 Client-stepper: pre-fill existing KYC/address proofs in edit, fix identifier saves
Edit-mode now shows the previously selected KYC document type + uploaded file thumbnail
(loads from /clients/{id}/identifiers + /client_identifiers/{id}/documents). Same for
address proof on the India address step. Closes the gap where editing a client showed
an empty file picker even when proof had been uploaded on create.

Identifier-save hardened across all three callsites (create-client, edit-client,
identities-tab.createIdentifierWithDocs) - the KYC chip picker proofId is a CODE
string ('AADHAAR'), but backend ClientIdentifierRequest.documentTypeId is Long, which
Spring rejects with HttpMessageNotReadableException -> 400. resolveDocumentTypeId
helper looks up the code against /kyc/proof-types?type=id and substitutes the numeric
CodeValue id before posting. Skips silently for constitution-specific non-individual
docs that have no CodeValue mapping.

KYC payload normalises panNumber / enterprisePan / gstinNumber / cinNumber via
toUpperCase + whitespace strip so user-typed lowercase or trailing spaces don't trip
the backend regex (validation.msg.CLIENTKYC.panNumber.does.not.match.regexp).

State name match in client-address save now normalises both sides (lowercase, collapse
underscores + spaces) so legacy MADHYA_PRADESH resolves to 'Madhya Pradesh' returned
by api.postalpincode.in. Backed by tenant migration 0297.

Identities-tab resolver flattened - was returning identifiers before the per-row
forkJoin(getClientIdentificationDocuments) completed, so the view rendered empty
documents arrays. Now uses switchMap + forkJoin so the resolver awaits completion.

Also bundles concurrent work-in-progress on letter-mgmt, organization, users,
bank-calendar, shell, nominee/notes/identifier dialogs, and theming.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 12:40:58 +05:30

931 lines
14 KiB
SCSS

/**
* 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/.
*/
@use '../stepper-readonly-overrides' as readonly;
:host {
@include readonly.readonly-form-overrides;
}
.zoomable {
cursor: zoom-in;
transition:
transform 0.15s ease,
box-shadow 0.15s ease;
&:hover {
transform: scale(1.08);
box-shadow: 0 4px 16px rgb(0 0 0 / 18%);
}
}
::ng-deep .image-zoom-panel {
.mat-mdc-dialog-container {
padding: 0 !important;
border-radius: 10px !important;
overflow: hidden;
}
}
.search-no-results-hint {
font-size: 12px;
color: #94a3b8;
text-align: center;
padding: 4px 0 0;
margin-top: -8px;
}
.step-header {
background: #0f172a;
padding: 16px 24px;
border-radius: 10px;
margin-bottom: 20px;
&__left {
display: flex;
align-items: center;
gap: 12px;
}
&__icon {
font-size: 18px;
color: #60a5fa;
}
&__title {
font-size: 16px;
font-weight: 600;
color: #fff;
margin: 0;
}
&__sub {
font-size: 11px;
color: rgb(255 255 255 / 50%);
margin: 2px 0 0;
}
}
.fm-step__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.fm-step__title {
display: flex;
align-items: center;
gap: 8px;
font-size: 15px;
font-weight: 600;
color: #1e293b;
}
.fm-step__icon {
color: #1074b9;
font-size: 14px;
}
.fm-step__count {
background: #1074b9;
color: #fff;
font-size: 11px;
font-weight: 700;
width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.fm-step__add-btn {
font-size: 13px !important;
height: 36px !important;
border-radius: 8px !important;
}
.fm-step__empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 32px 16px;
color: #94a3b8;
font-size: 14px;
background: #f8fafc;
border-radius: 8px;
border: 1px dashed #e2e8f0;
}
.fm-step__empty-icon {
font-size: 36px !important;
width: 36px !important;
height: 36px !important;
color: #cbd5e1;
}
.fm-step__empty-sub {
font-size: 12px;
color: #cbd5e1;
}
.fm-step__empty--required {
border-color: #fca5a5;
background: #fef2f2;
}
.fm-step__empty-required {
font-size: 12px;
font-weight: 600;
color: #dc2626;
}
.fm-spouse-hint {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
background: #fff7ed;
border: 1px solid #fed7aa;
border-radius: 8px;
font-size: 13px;
color: #92400e;
margin-top: 12px;
margin-bottom: 4px;
}
.fm-spouse-hint__icon {
font-size: 18px !important;
width: 18px !important;
height: 18px !important;
color: #f59e0b;
}
/* Table */
.fm-table {
border: 1px solid #e2e8f0;
border-radius: 10px;
overflow: hidden;
}
.fm-table__header {
display: flex;
align-items: center;
background: #f8fafc;
border-bottom: 1px solid #e2e8f0;
.fm-table__col {
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #64748b;
padding: 10px 12px;
}
}
.fm-table__row {
display: flex;
align-items: center;
border-bottom: 1px solid #f1f5f9;
transition: background 0.1s;
&:last-child {
border-bottom: none;
}
&:hover {
background: #f8fafc;
}
}
.fm-table__col {
padding: 10px 12px;
font-size: 13px;
color: #1e293b;
&--name {
flex: 1;
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
&--rel {
flex: 0 0 130px;
}
&--dob {
flex: 0 0 120px;
color: #64748b;
font-size: 12px;
}
&--id {
flex: 0 0 140px;
}
&--status {
flex: 0 0 60px;
text-align: center;
}
&--actions {
flex: 0 0 80px;
display: flex;
gap: 2px;
}
}
.fm-table__avatar {
font-size: 18px !important;
width: 18px !important;
height: 18px !important;
color: #1074b9;
flex-shrink: 0;
}
.fm-table__name-info {
display: flex;
flex-direction: column;
min-width: 0;
}
.fm-table__name-text {
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fm-table__linked-tag {
font-size: 10px;
color: #1074b9;
font-weight: 500;
}
.fm-mono {
font-family: 'Roboto Mono', monospace;
font-size: 12px;
letter-spacing: 0.3px;
}
.fm-alive-badge {
font-size: 11px;
font-weight: 600;
padding: 2px 8px;
border-radius: 10px;
&--yes {
background: #ecfdf5;
color: #059669;
}
&--no {
background: #fef2f2;
color: #dc2626;
}
}
.fm-action-btn {
width: 30px !important;
height: 30px !important;
line-height: 30px !important;
mat-icon {
font-size: 16px !important;
width: 16px !important;
height: 16px !important;
color: #64748b;
}
&:hover mat-icon {
color: #1074b9;
}
&--delete:hover mat-icon {
color: #ef4444;
}
}
@media (width <= 768px) {
.fm-step__header {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.fm-table__col {
&--type,
&--id {
display: none;
}
}
}
/* ═══ NOMINEES ═══ */
.nom-section {
margin-top: 28px;
padding-top: 24px;
border-top: 2px solid #e2e8f0;
}
.nom-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.nom-header__left {
display: flex;
align-items: center;
gap: 10px;
}
.nom-header__icon {
font-size: 16px;
color: #1565c0;
}
.nom-header__title {
font-size: 16px;
font-weight: 600;
color: #1e293b;
display: block;
}
.nom-header__sub {
font-size: 11px;
color: #94a3b8;
display: block;
}
.nom-add-btn {
font-size: 13px;
}
/* Share bar */
.nom-share-bar {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 14px;
padding: 8px 12px;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 6px;
}
.nom-share-bar__label {
font-size: 12px;
color: #64748b;
white-space: nowrap;
}
.nom-share-bar__track {
flex: 1;
height: 8px;
background: #e2e8f0;
border-radius: 4px;
overflow: hidden;
}
.nom-share-bar__fill {
height: 100%;
background: #1565c0;
border-radius: 4px;
transition: width 0.3s;
&.complete {
background: #059669;
}
&.over {
background: #dc2626;
}
}
.nom-share-bar__value {
font-size: 13px;
font-weight: 700;
color: #1565c0;
min-width: 36px;
text-align: right;
&.over {
color: #dc2626;
}
}
/* Nominee card */
.nom-card {
border: 1px solid #e2e8f0;
border-radius: 8px;
margin-bottom: 8px;
overflow: hidden;
}
.nom-card__main {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
}
.nom-card__avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: #dbeafe;
display: flex;
align-items: center;
justify-content: center;
color: #1565c0;
mat-icon {
font-size: 20px;
}
}
.nom-card__photo {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
}
.nom-card__info {
flex: 1;
}
.nom-card__name {
font-weight: 600;
font-size: 14px;
color: #1e293b;
display: block;
}
.nom-card__meta {
font-size: 12px;
color: #64748b;
display: block;
margin-top: 2px;
}
.nom-card__sig {
flex-shrink: 0;
img {
height: 24px;
max-width: 60px;
object-fit: contain;
border: 1px solid #e2e8f0;
border-radius: 4px;
padding: 2px;
}
}
.nom-card__actions {
display: flex;
gap: 2px;
}
.nom-card__edit {
padding: 12px 16px;
border-top: 1px solid #e2e8f0;
background: #f8fafc;
}
.nom-card__edit-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
margin-top: 4px;
}
.nom-minor-tag {
font-size: 10px;
font-weight: 700;
color: #dc2626;
background: #fef2f2;
padding: 1px 6px;
border-radius: 3px;
}
.nom-card__share {
text-align: center;
min-width: 50px;
}
.nom-card__share-value {
font-size: 18px;
font-weight: 700;
color: #1565c0;
display: block;
}
.nom-card__share-label {
font-size: 10px;
color: #94a3b8;
text-transform: uppercase;
}
.nom-card__guardian {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
background: #f0f9ff;
border-top: 1px solid #e0f2fe;
font-size: 12px;
color: #0c4a6e;
mat-icon {
font-size: 16px;
width: 16px;
height: 16px;
color: #0ea5e9;
}
}
/* Empty */
.nom-empty {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
padding: 32px;
background: #f8fafc;
border: 2px dashed #e2e8f0;
border-radius: 10px;
color: #94a3b8;
font-size: 14px;
}
.nom-empty__icon {
font-size: 36px;
width: 36px;
height: 36px;
color: #cbd5e1;
}
.nom-empty__sub {
font-size: 12px;
}
/* Nominee form */
.nom-form {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 16px;
margin-top: 12px;
}
.nom-form__title {
font-size: 14px;
font-weight: 600;
color: #1e293b;
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 12px;
}
/* Search */
.nom-search-wrapper {
position: relative;
}
.nom-search-field {
width: 100%;
}
.nom-search-prefix {
display: inline-flex;
align-items: center;
padding-right: 8px;
mat-icon {
font-size: 20px;
color: #94a3b8;
}
}
.nom-search-results {
position: absolute;
top: 100%;
left: 0;
right: 0;
z-index: 10;
background: #fff;
border: 1px solid #e2e8f0;
border-radius: 8px;
box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
max-height: 240px;
overflow-y: auto;
margin-top: -12px;
}
.nom-search-loading {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
font-size: 13px;
color: #64748b;
}
.nom-search-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 16px;
cursor: pointer;
&:hover {
background: #f1f5f9;
}
}
.nom-search-item__icon {
color: #94a3b8;
font-size: 20px;
}
.nom-search-item__info {
flex: 1;
}
.nom-search-item__name {
font-weight: 500;
font-size: 13px;
color: #1e293b;
display: block;
}
.nom-search-item__meta {
font-size: 11px;
color: #94a3b8;
display: block;
}
.nom-search-item__status {
font-size: 11px;
font-weight: 600;
color: #94a3b8;
&.active {
color: #22c55e;
}
}
/* Selected nominee */
.nom-selected {
border: 1px solid #93c5fd;
border-radius: 10px;
overflow: hidden;
background: #fff;
}
.nom-selected__header {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
background: #eff6ff;
border-bottom: 1px solid #dbeafe;
}
.nom-selected__avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: #dbeafe;
display: flex;
align-items: center;
justify-content: center;
color: #1565c0;
overflow: hidden;
}
.nom-selected__photo {
width: 100%;
height: 100%;
object-fit: cover;
}
.nom-selected__info {
flex: 1;
}
.nom-selected__name {
font-weight: 600;
font-size: 14px;
color: #1e293b;
display: block;
}
.nom-selected__meta {
font-size: 12px;
color: #64748b;
display: block;
}
.nom-selected__sig {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
flex-shrink: 0;
img {
height: 28px;
max-width: 70px;
object-fit: contain;
border: 1px solid #e2e8f0;
border-radius: 4px;
background: #fff;
padding: 2px 4px;
}
span {
font-size: 9px;
color: #94a3b8;
text-transform: uppercase;
letter-spacing: 0.04em;
}
}
.nom-selected__remove {
color: #94a3b8;
}
.nom-selected__loading {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
font-size: 12px;
color: #64748b;
}
.nom-kyc-badge {
display: inline-flex;
align-items: center;
margin-left: 4px;
mat-icon {
font-size: 14px;
width: 14px;
height: 14px;
}
&.verified {
color: #16a34a;
}
}
.nom-selected__details {
display: flex;
gap: 16px;
padding: 10px 16px;
border-bottom: 1px solid #f1f5f9;
flex-wrap: wrap;
}
.nom-detail {
display: flex;
flex-direction: column;
gap: 2px;
}
.nom-detail__label {
font-size: 10px;
text-transform: uppercase;
color: #94a3b8;
letter-spacing: 0.5px;
}
.nom-detail__value {
font-size: 13px;
font-weight: 500;
color: #1e293b;
}
.nom-detail__value.mono {
font-family: 'Roboto Mono', monospace;
font-size: 12px;
}
/* Form fields */
.nom-form-fields {
display: grid;
grid-template-columns: 1fr 1fr auto;
gap: 8px 16px;
padding: 12px 16px;
align-items: center;
}
.nom-form-field {
width: 100%;
}
.nom-minor-toggle {
padding-top: 8px;
}
/* Guardian */
.nom-guardian-section {
padding: 12px 16px;
border-top: 1px dashed #e2e8f0;
}
.nom-guardian-title {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
font-weight: 600;
color: #0c4a6e;
margin-bottom: 10px;
mat-icon {
font-size: 18px;
color: #0ea5e9;
}
}
.nom-guardian-card {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
background: #f0f9ff;
border: 1px solid #bae6fd;
border-radius: 8px;
mat-icon {
color: #0ea5e9;
}
}
.nom-guardian-card__info {
flex: 1;
}
.nom-guardian-card__name {
font-weight: 600;
font-size: 13px;
color: #1e293b;
display: block;
}
.nom-guardian-card__meta {
font-size: 11px;
color: #64748b;
display: block;
}
.nom-guardian-rel {
width: 160px;
}
/* Actions */
.nom-form-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 12px 16px;
border-top: 1px solid #f1f5f9;
button {
display: flex;
align-items: center;
gap: 6px;
}
}
@media (width <= 768px) {
.nom-form-fields {
grid-template-columns: 1fr;
}
.nom-guardian-card {
flex-wrap: wrap;
}
.nom-guardian-rel {
width: 100%;
}
}