Files
cbsnexora/src/app/clients/client-stepper/client-india-address-step/client-india-address-step.component.html
T
niteshandClaude Sonnet 5 31e99b1363 fix(clients): rebuild CKYC document upload as a popup with full proof details
The CKYC tab's document cards now open a dedicated dialog (with client-side
preview) instead of expanding inline, collecting document type, number, and
issue/expiry dates the same way client creation does. Fixes several bugs
found along the way:

- Uploading a document type that's valid for both identity and address
  (Aadhaar, Passport, etc.) no longer 404s or 500s — the "no such endpoint"
  and case-sensitive ClientIdentifierStatus.valueOf("Active") crashes are
  both resolved.
- Edit Client's Identity-proof and Address-proof steps no longer show each
  other's document when both are dual-purpose OVD types, and no longer show
  stale/mismatched front-back scans after a document type change.
- Address-proof's own document preview never showed a document at all when
  no m_client_kyc record existed yet.

Document number and issue/expiry dates are persisted on the identifier
(documentKey + a shared "Issue date: ..., Expiry date: ..." description
convention) and read back by both the CKYC tab and Edit Client so the same
details appear everywhere.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 11:39:14 +05:30

820 lines
38 KiB
HTML

<!--
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/.
-->
<div class="addr-container">
<div class="addr-header">
<div class="addr-header__left">
<fa-icon icon="map" class="addr-header__icon"></fa-icon>
<div>
<h3 class="addr-header__title">Address Details</h3>
<p class="addr-header__sub">All address fields are India-specific. PIN auto-lookup enabled.</p>
</div>
</div>
</div>
<mat-tab-group class="addr-tabs" [(selectedIndex)]="activeTab" animationDuration="150ms">
<!-- ═══ TAB 1: PERMANENT ═══ -->
<mat-tab>
<ng-template mat-tab-label> <span class="addr-tab-num">1</span> Permanent Address </ng-template>
<div class="addr-panel" [formGroup]="permanentForm">
<div class="addr-grid addr-grid--1">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Address Line 1</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>home</mat-icon></span>
<input
matInput
required
formControlName="addressLine1"
placeholder="House / Flat No., Floor, Building Name"
/>
</mat-form-field>
</div>
<div class="addr-grid addr-grid--1">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Address Line 2</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>signpost</mat-icon></span>
<input matInput formControlName="addressLine2" placeholder="Street, Road, Lane, Colony" />
</mat-form-field>
</div>
<div class="addr-divider"><span>Location</span></div>
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>PIN Code</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>pin_drop</mat-icon></span>
<input
matInput
required
formControlName="pinCode"
maxlength="6"
placeholder="6-digit PIN"
(input)="onPinInput('permanent')"
(keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()"
/>
@if (permanentForm.get('pinCode')?.hasError('pattern') && permanentForm.get('pinCode')?.touched) {
<mat-error>Enter a valid 6-digit PIN code</mat-error>
}
@if (pinStatus['permanent'].loading) {
<mat-hint class="pin-loading">Looking up...</mat-hint>
}
@if (pinStatus['permanent'].found) {
<mat-hint class="pin-ok">PIN verified</mat-hint>
}
@if (pinStatus['permanent'].error) {
<mat-error>{{ pinStatus['permanent'].message }}</mat-error>
}
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Locality / Area</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>location_on</mat-icon></span>
<mat-select required formControlName="locality">
@for (loc of localityOptions['permanent']; track loc) {
<mat-option [value]="loc">{{ loc }}</mat-option>
}
</mat-select>
</mat-form-field>
</div>
<div class="addr-grid addr-grid--3">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>City / Town</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>location_city</mat-icon></span>
<input matInput formControlName="city" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>District</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>map</mat-icon></span>
<input matInput formControlName="district" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>State</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>flag</mat-icon></span>
<input matInput required formControlName="state" />
</mat-form-field>
</div>
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Country</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>public</mat-icon></span>
<input matInput formControlName="country" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Landmark</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>place</mat-icon></span>
<input matInput formControlName="landmark" placeholder="Near / Opp. / Behind..." />
</mat-form-field>
</div>
<div class="addr-divider"><span>Proof of Address</span></div>
@if (loadingProofs) {
<div class="addr-proof-loading">
<fa-icon icon="spinner" [animation]="'spin'"></fa-icon>
Loading document types…
</div>
}
<div class="addr-proof-field">
<div
class="addr-proof-label-row"
style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px"
>
<label class="addr-proof-label" style="margin-bottom: 0"
><mat-icon>description</mat-icon> Document type</label
>
@if (isAddressProofLinkedToKyc && !readOnly) {
<div
class="addr-proof-linked-note"
style="
display: flex;
align-items: center;
gap: 8px;
color: #059669;
font-size: 0.85rem;
background: #ecfdf5;
padding: 4px 12px;
border-radius: 16px;
"
>
<mat-icon style="font-size: 16px; width: 16px; height: 16px">link</mat-icon>
Automatically linked from KYC step
<button
mat-icon-button
(click)="unlinkAddressProof()"
matTooltip="Provide a different address proof"
style="width: 24px; height: 24px; padding: 0; line-height: 24px"
>
<mat-icon style="font-size: 16px; color: #047857">edit</mat-icon>
</button>
</div>
}
</div>
<div class="addr-chip-wrap" [class.addr-chip-wrap--locked]="readOnly || isAddressProofLinkedToKyc">
@for (group of addressGroups; track group.label; let last = $last) {
<div class="addr-chip-group-label"><mat-icon>folder_open</mat-icon> {{ group.label }}</div>
<div class="addr-chip-grid">
@for (p of group.items; track p.id) {
<span
class="addr-chip"
[class.addr-chip--sel]="isAddrProofSelected(p)"
[class.addr-chip--locked]="(readOnly || isAddressProofLinkedToKyc) && !isAddrProofSelected(p)"
[attr.aria-disabled]="readOnly || isAddressProofLinkedToKyc ? 'true' : null"
(click)="readOnly || isAddressProofLinkedToKyc ? null : selectAddrProof(p)"
(keydown.enter)="readOnly || isAddressProofLinkedToKyc ? null : selectAddrProof(p)"
[attr.tabindex]="readOnly || isAddressProofLinkedToKyc ? -1 : 0"
role="button"
>
<mat-icon class="addr-chip__icon">
{{ isAddrProofSelected(p) ? 'check_circle' : 'radio_button_unchecked' }}
</mat-icon>
{{ p.name }}
</span>
}
</div>
@if (!last) {
<div class="addr-chip-divider"></div>
}
}
</div>
</div>
@if (selectedAddrProof?.info) {
<div class="addr-proof-banner">
<mat-icon>check_circle</mat-icon>
<span>{{ selectedAddrProof.info }}</span>
</div>
}
@if (selectedAddrProof) {
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>{{ selectedAddrProof.documentKeyLabel || 'Document number' }}</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>tag</mat-icon></span>
<input
matInput
formControlName="documentKey"
[readonly]="readOnly || isAddressProofLinkedToKyc"
[placeholder]="selectedAddrProof.documentKeyPlaceholder || ''"
/>
</mat-form-field>
@if (selectedAddrProof.requiresDate) {
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Issue / bill date</mat-label>
<input
matInput
[matDatepicker]="addrIssueDatePicker"
formControlName="issueDate"
[readonly]="readOnly || isAddressProofLinkedToKyc"
/>
<mat-datepicker-toggle
matSuffix
[for]="addrIssueDatePicker"
[disabled]="readOnly || isAddressProofLinkedToKyc"
></mat-datepicker-toggle>
<mat-datepicker #addrIssueDatePicker></mat-datepicker>
</mat-form-field>
}
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Expiry date (optional)</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>event_busy</mat-icon></span>
<input
matInput
[matDatepicker]="addrExpiryDatePicker"
formControlName="expiryDate"
[readonly]="readOnly || isAddressProofLinkedToKyc"
/>
<mat-datepicker-toggle
matSuffix
[for]="addrExpiryDatePicker"
[disabled]="readOnly || isAddressProofLinkedToKyc"
></mat-datepicker-toggle>
<mat-datepicker #addrExpiryDatePicker></mat-datepicker>
</mat-form-field>
</div>
<div class="addr-upload-grid" [class.addr-upload-grid--two]="selectedAddrProof.sides === 2">
<div class="addr-upload-slot">
<div class="addr-upload-head">
<span class="addr-upload-title">
<mat-icon>image</mat-icon>
{{ selectedAddrProof.sides === 2 ? 'Front side' : selectedAddrProof.name + ' (single copy)' }}
</span>
<span class="addr-upload-req">Required</span>
</div>
<!-- Existing saved document — locked by default. Replace flow
unlocks automatically when the section's top-level Edit
button toggles `readOnly` off. -->
@if (existingAddressProofDocuments.length > 0 && !addrFiles.front && readOnly) {
<div class="addr-dropzone addr-dropzone--has addr-dropzone--locked">
<div class="addr-dropzone__existing">
@if (existingAddrFrontSlotPreview) {
<img
[src]="existingAddrFrontSlotPreview"
alt="Saved address proof"
class="addr-dropzone__thumb"
/>
} @else {
<mat-icon class="addr-dropzone__doc-ic">description</mat-icon>
}
<div class="addr-dropzone__existing-meta">
<span class="addr-dropzone__existing-name">
{{ existingAddrFrontSlotName }}
</span>
<button type="button" class="addr-dropzone__view" (click)="viewExistingAddressProof()">
<mat-icon>visibility</mat-icon>
Click here to view
</button>
</div>
</div>
</div>
} @else {
<label
class="addr-dropzone"
[class.addr-dropzone--has]="addrFiles.front || existingAddressProofDocuments.length > 0"
[class.addr-dropzone--req]="!addrFiles.front && existingAddressProofDocuments.length === 0"
>
<input type="file" accept=".jpg,.jpeg,.png,.pdf" hidden (change)="onAddrFile($event, 'front')" />
@if (addrFiles.front) {
<div class="addr-dropzone__file">
<mat-icon>insert_drive_file</mat-icon>
<span class="addr-dropzone__name">{{ addrFiles.front.name }}</span>
<button
type="button"
mat-icon-button
class="addr-dropzone__remove"
(click)="$event.preventDefault(); removeAddrFile('front')"
>
<mat-icon>close</mat-icon>
</button>
</div>
} @else if (existingAddressProofDocuments.length > 0) {
<!-- Edit mode WITH existing doc — let the user view it OR
click anywhere to replace. -->
<div class="addr-dropzone__existing">
@if (existingAddrFrontSlotPreview) {
<img
[src]="existingAddrFrontSlotPreview"
alt="Saved address proof"
class="addr-dropzone__thumb"
/>
} @else {
<mat-icon class="addr-dropzone__doc-ic">description</mat-icon>
}
<div class="addr-dropzone__existing-meta">
<span class="addr-dropzone__existing-name">
{{ existingAddrFrontSlotName }}
</span>
<div class="addr-dropzone__existing-actions">
<button
type="button"
class="addr-dropzone__view"
(click)="$event.preventDefault(); $event.stopPropagation(); viewExistingAddressProof()"
>
<mat-icon>visibility</mat-icon>
View
</button>
<span class="addr-dropzone__sep">·</span>
<span class="addr-dropzone__existing-hint">
<mat-icon>cloud_upload</mat-icon>
Click here to replace
</span>
</div>
</div>
</div>
} @else {
<div class="addr-dropzone__hint">
<mat-icon>cloud_upload</mat-icon>
<span>Click to upload (JPG / PNG / PDF · max 5 MB)</span>
</div>
}
</label>
}
</div>
@if (selectedAddrProof.sides === 2) {
<div class="addr-upload-slot">
<div class="addr-upload-head">
<span class="addr-upload-title"> <mat-icon>image</mat-icon> Back side </span>
<!-- "Required" pill is hidden when a saved combined doc covers both sides -->
@if (!(existingAddressProofDocuments.length > 0 && !addrFiles.back)) {
<span class="addr-upload-req">Required</span>
}
</div>
<!-- Locked view: saved combined doc covers the back side too. -->
@if (existingAddressProofDocuments.length > 0 && !addrFiles.back && readOnly) {
<div class="addr-dropzone addr-dropzone--has addr-dropzone--locked">
<div class="addr-dropzone__existing">
@if (existingAddrBackSlotPreview) {
<img
[src]="existingAddrBackSlotPreview"
alt="Saved address proof"
class="addr-dropzone__thumb"
/>
} @else {
<mat-icon class="addr-dropzone__doc-ic">description</mat-icon>
}
<div class="addr-dropzone__existing-meta">
<span class="addr-dropzone__existing-name">
{{ existingAddrBackSlotName }}
</span>
<button type="button" class="addr-dropzone__view" (click)="viewExistingAddressProof()">
<mat-icon>visibility</mat-icon>
Click here to view
</button>
</div>
</div>
</div>
} @else {
<label
class="addr-dropzone"
[class.addr-dropzone--has]="addrFiles.back || existingAddressProofDocuments.length > 0"
[class.addr-dropzone--req]="!addrFiles.back && existingAddressProofDocuments.length === 0"
>
<input type="file" accept=".jpg,.jpeg,.png,.pdf" hidden (change)="onAddrFile($event, 'back')" />
@if (addrFiles.back) {
<div class="addr-dropzone__file">
<mat-icon>insert_drive_file</mat-icon>
<span class="addr-dropzone__name">{{ addrFiles.back.name }}</span>
<button
type="button"
mat-icon-button
class="addr-dropzone__remove"
(click)="$event.preventDefault(); removeAddrFile('back')"
>
<mat-icon>close</mat-icon>
</button>
</div>
} @else if (existingAddressProofDocuments.length > 0) {
<!-- Edit mode WITH existing combined doc — same view + replace
pattern as the front slot, anchored to the saved file. -->
<div class="addr-dropzone__existing">
@if (existingAddrBackSlotPreview) {
<img
[src]="existingAddrBackSlotPreview"
alt="Saved address proof"
class="addr-dropzone__thumb"
/>
} @else {
<mat-icon class="addr-dropzone__doc-ic">description</mat-icon>
}
<div class="addr-dropzone__existing-meta">
<span class="addr-dropzone__existing-name">
{{ existingAddrBackSlotName }}
</span>
<div class="addr-dropzone__existing-actions">
<button
type="button"
class="addr-dropzone__view"
(click)="$event.preventDefault(); $event.stopPropagation(); viewExistingAddressProof()"
>
<mat-icon>visibility</mat-icon>
View
</button>
<span class="addr-dropzone__sep">·</span>
<span class="addr-dropzone__existing-hint">
<mat-icon>cloud_upload</mat-icon>
Click here to replace
</span>
</div>
</div>
</div>
} @else {
<div class="addr-dropzone__hint">
<mat-icon>cloud_upload</mat-icon>
<span>Click to upload (JPG / PNG / PDF · max 5 MB)</span>
</div>
}
</label>
}
</div>
}
</div>
<!-- Combined preview + re-crop buttons are an edit-mode tool; hide in read-only.
Also hide when the saved combined PNG is already visible as the thumbnail
inside both slot tiles (no value in repeating the same image below). -->
@if (selectedAddrProof.sides === 2 && addrMergedPreview && !readOnly && (addrFiles.front || addrFiles.back)) {
<div class="addr-merged">
<div class="addr-merged__head">
<span class="addr-merged__title">
<mat-icon>filter_none</mat-icon>
Combined preview (front + back)
</span>
<span class="addr-merged__hint">Saved as a single PNG. Re-crop a side to update the merged file.</span>
</div>
<div class="addr-merged__preview">
<img [src]="addrMergedPreview" alt="Merged address proof" />
</div>
<div class="addr-merged__actions">
<button type="button" mat-stroked-button class="addr-merged__btn" (click)="reCropAddrFile('front')">
<mat-icon>crop</mat-icon> Re-crop front
</button>
<button type="button" mat-stroked-button class="addr-merged__btn" (click)="reCropAddrFile('back')">
<mat-icon>crop</mat-icon> Re-crop back
</button>
</div>
</div>
}
}
</div>
</mat-tab>
<!-- ═══ TAB 2: CURRENT / RESIDENTIAL ═══ -->
<mat-tab>
<ng-template mat-tab-label> <span class="addr-tab-num">2</span> Current / Residential </ng-template>
<div class="addr-panel" [formGroup]="currentForm">
<div class="addr-same-row">
<mat-checkbox [checked]="sameAsPermanent" (change)="toggleSameAsPermanent($event.checked)">
Same as Permanent Address
</mat-checkbox>
</div>
@if (!sameAsPermanent) {
<div class="addr-grid addr-grid--1">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Address Line 1</mat-label>
<input
matInput
required
formControlName="addressLine1"
placeholder="House / Flat No., Floor, Building Name"
/>
</mat-form-field>
</div>
<div class="addr-grid addr-grid--1">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Address Line 2</mat-label>
<input matInput formControlName="addressLine2" placeholder="Street, Road, Lane, Colony" />
</mat-form-field>
</div>
<div class="addr-divider"><span>Location</span></div>
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>PIN Code</mat-label>
<input
matInput
required
formControlName="pinCode"
maxlength="6"
placeholder="6-digit PIN"
(input)="onPinInput('current')"
(keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()"
/>
@if (currentForm.get('pinCode')?.hasError('pattern') && currentForm.get('pinCode')?.touched) {
<mat-error>Enter a valid 6-digit PIN code</mat-error>
}
@if (pinStatus['current'].found) {
<mat-hint class="pin-ok">PIN verified</mat-hint>
}
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Locality / Area</mat-label>
<mat-select required formControlName="locality">
@for (loc of localityOptions['current']; track loc) {
<mat-option [value]="loc">{{ loc }}</mat-option>
}
</mat-select>
</mat-form-field>
</div>
<div class="addr-grid addr-grid--3">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>City / Town</mat-label>
<input matInput formControlName="city" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>District</mat-label>
<input matInput formControlName="district" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>State</mat-label>
<input matInput required formControlName="state" />
</mat-form-field>
</div>
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Country</mat-label>
<input matInput formControlName="country" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Landmark</mat-label>
<input matInput formControlName="landmark" placeholder="Near / Opp. / Behind..." />
</mat-form-field>
</div>
<div class="addr-divider"><span>Residency</span></div>
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Residence Type</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>cottage</mat-icon></span>
<mat-select formControlName="residenceType">
@for (rt of residenceTypes; track rt) {
<mat-option [value]="rt">{{ rt }}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Years at This Address</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>schedule</mat-icon></span>
<input matInput type="number" formControlName="yearsAtAddress" placeholder="e.g. 3" />
</mat-form-field>
</div>
} @else {
<div class="addr-same-info">
<fa-icon icon="check-circle" class="addr-same-icon"></fa-icon>
Current address is same as Permanent address.
</div>
}
</div>
</mat-tab>
<!-- ═══ TAB 3: OFFICE / WORK ═══ -->
<mat-tab>
<ng-template mat-tab-label> <span class="addr-tab-num">3</span> Office / Work </ng-template>
<div class="addr-panel" [formGroup]="officeForm">
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Organisation / Company Name</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>business</mat-icon></span>
<input matInput formControlName="orgName" placeholder="Legal name of employer / business" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Department / Unit</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>corporate_fare</mat-icon></span>
<input matInput formControlName="department" placeholder="e.g. Finance, Operations" />
</mat-form-field>
</div>
<div class="addr-grid addr-grid--1">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Office Address Line 1</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>home_work</mat-icon></span>
<input matInput formControlName="addressLine1" placeholder="Office No., Floor, Building Name" />
</mat-form-field>
</div>
<div class="addr-grid addr-grid--1">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Office Address Line 2</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>signpost</mat-icon></span>
<input matInput formControlName="addressLine2" placeholder="Street, Road, Industrial Area" />
</mat-form-field>
</div>
<div class="addr-divider"><span>Location</span></div>
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>PIN Code</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>pin_drop</mat-icon></span>
<input
matInput
formControlName="pinCode"
maxlength="6"
placeholder="6-digit PIN"
(input)="onPinInput('office')"
(keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()"
/>
@if (officeForm.get('pinCode')?.hasError('pattern') && officeForm.get('pinCode')?.touched) {
<mat-error>Enter a valid 6-digit PIN code</mat-error>
}
@if (pinStatus['office'].found) {
<mat-hint class="pin-ok">PIN verified</mat-hint>
}
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Locality / Area</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>location_on</mat-icon></span>
<mat-select formControlName="locality">
@for (loc of localityOptions['office']; track loc) {
<mat-option [value]="loc">{{ loc }}</mat-option>
}
</mat-select>
</mat-form-field>
</div>
<div class="addr-grid addr-grid--3">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>City / Town</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>location_city</mat-icon></span>
<input matInput formControlName="city" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>District</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>map</mat-icon></span>
<input matInput formControlName="district" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>State</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>flag</mat-icon></span>
<input matInput formControlName="state" />
</mat-form-field>
</div>
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Country</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>public</mat-icon></span>
<input matInput formControlName="country" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Office Phone</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>phone</mat-icon></span>
<input matInput formControlName="officePhone" placeholder="+91 XXXXX XXXXX" />
</mat-form-field>
</div>
<div class="addr-divider"><span>Employment Details</span></div>
<div class="addr-grid addr-grid--3">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Employment Type</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>work</mat-icon></span>
<mat-select formControlName="employmentType">
@for (et of employmentTypes; track et) {
<mat-option [value]="et">{{ et }}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Employee / Staff ID</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>badge</mat-icon></span>
<input matInput formControlName="employeeId" placeholder="Optional" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Years with Organisation</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>schedule</mat-icon></span>
<input matInput type="number" formControlName="yearsWithOrg" placeholder="e.g. 5" />
</mat-form-field>
</div>
</div>
</mat-tab>
<!-- ═══ TAB 4: CORRESPONDENCE ═══ -->
<mat-tab>
<ng-template mat-tab-label> <span class="addr-tab-num">4</span> Correspondence </ng-template>
<div class="addr-panel" [formGroup]="correspondenceForm">
<div class="addr-copy-row">
<span class="addr-copy-label">Copy address from:</span>
<button mat-stroked-button type="button" class="addr-copy-btn" (click)="copyFrom('permanent')">
Permanent
</button>
<button mat-stroked-button type="button" class="addr-copy-btn" (click)="copyFrom('current')">Current</button>
<button mat-stroked-button type="button" class="addr-copy-btn" (click)="copyFrom('office')">Office</button>
</div>
<div class="addr-grid addr-grid--1">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Address Line 1</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>home</mat-icon></span>
<input
matInput
required
formControlName="addressLine1"
placeholder="House / Flat No., Floor, Building Name"
/>
</mat-form-field>
</div>
<div class="addr-grid addr-grid--1">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Address Line 2</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>signpost</mat-icon></span>
<input matInput formControlName="addressLine2" placeholder="Street, Road, Lane, Colony" />
</mat-form-field>
</div>
<div class="addr-divider"><span>Location</span></div>
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>PIN Code</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>pin_drop</mat-icon></span>
<input
matInput
required
formControlName="pinCode"
maxlength="6"
placeholder="6-digit PIN"
(input)="onPinInput('correspondence')"
(keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()"
/>
@if (correspondenceForm.get('pinCode')?.hasError('pattern') && correspondenceForm.get('pinCode')?.touched) {
<mat-error>Enter a valid 6-digit PIN code</mat-error>
}
@if (pinStatus['correspondence'].found) {
<mat-hint class="pin-ok">PIN verified</mat-hint>
}
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Locality / Area</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>location_on</mat-icon></span>
<mat-select required formControlName="locality">
@for (loc of localityOptions['correspondence']; track loc) {
<mat-option [value]="loc">{{ loc }}</mat-option>
}
</mat-select>
</mat-form-field>
</div>
<div class="addr-grid addr-grid--3">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>City / Town</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>location_city</mat-icon></span>
<input matInput formControlName="city" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>District</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>map</mat-icon></span>
<input matInput formControlName="district" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>State</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>flag</mat-icon></span>
<input matInput required formControlName="state" />
</mat-form-field>
</div>
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Country</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>public</mat-icon></span>
<input matInput formControlName="country" />
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Landmark</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>place</mat-icon></span>
<input matInput formControlName="landmark" placeholder="Near / Opp. / Behind..." />
</mat-form-field>
</div>
<div class="addr-grid addr-grid--2">
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Preferred Communication Mode</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>mail</mat-icon></span>
<mat-select formControlName="commMode">
@for (cm of commModes; track cm) {
<mat-option [value]="cm">{{ cm }}</mat-option>
}
</mat-select>
</mat-form-field>
<mat-form-field appearance="outline" class="addr-ff">
<mat-label>Contact Number for Correspondence</mat-label>
<span matPrefix class="addr-prefix"><mat-icon>phone_android</mat-icon></span>
<input matInput formControlName="contactNumber" placeholder="+91 XXXXX XXXXX" />
</mat-form-field>
</div>
</div>
</mat-tab>
</mat-tab-group>
</div>