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>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
ab01111424
commit
b890d24bb7
@@ -1,65 +1,808 @@
|
||||
<!--
|
||||
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="mat-typography" [formGroup]="kycForm">
|
||||
<div class="step-header">
|
||||
<div class="step-header__left">
|
||||
<fa-icon icon="id-card" class="step-header__icon"></fa-icon>
|
||||
<div>
|
||||
<h3 class="step-header__title">KYC & Documents</h3>
|
||||
<p class="step-header__sub">RBI-compliant KYC verification — PAN, Aadhaar & document upload.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ SECTION: KYC Verification ═══ -->
|
||||
<div class="section-block">
|
||||
<h4 class="section-title">
|
||||
<fa-icon icon="fingerprint" class="section-icon"></fa-icon>
|
||||
{{ 'labels.inputs.KYC Verification' | translate }}
|
||||
KYC Verification
|
||||
<span class="kyc-badge">KYC</span>
|
||||
</h4>
|
||||
|
||||
<div class="kyc-info-hint">
|
||||
<mat-icon class="kyc-info-hint-icon">info</mat-icon>
|
||||
<span>Verification (PAN, Aadhaar, GSTIN) will be available from the KYC tab after the client is created.</span>
|
||||
<div class="form-grid">
|
||||
|
||||
<!-- ══ INDIVIDUAL KYC ══ -->
|
||||
@if (!isNonIndividual) {
|
||||
<div class="form-field checkbox-field full-width">
|
||||
<mat-checkbox formControlName="hasPan">Client has PAN Card</mat-checkbox>
|
||||
</div>
|
||||
|
||||
<!-- Individual PAN with inline verify -->
|
||||
@if (kycForm.get('hasPan')?.value) {
|
||||
<div class="form-field verify-field">
|
||||
<mat-form-field appearance="outline" class="verify-input">
|
||||
<mat-label>PAN Number</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>credit_card</mat-icon></span>
|
||||
<input matInput formControlName="panNumber" placeholder="e.g. ABCDE1234F"
|
||||
maxlength="10" style="text-transform: uppercase"
|
||||
(input)="kycForm.get('panNumber')?.setValue($any($event.target).value.toUpperCase(), { emitEvent: false })" />
|
||||
@if (kycForm.get('panNumber')?.hasError('pattern') && kycForm.get('panNumber')?.touched) {
|
||||
<mat-error>Invalid PAN format</mat-error>
|
||||
}
|
||||
@if (panVerifyStatus === 'VERIFIED') {
|
||||
<mat-icon matSuffix class="verify-icon-ok">check_circle</mat-icon>
|
||||
}
|
||||
@if (panVerifyStatus === 'FAILED') {
|
||||
<mat-icon matSuffix class="verify-icon-fail">cancel</mat-icon>
|
||||
}
|
||||
<mat-hint>Format: ABCDE1234F</mat-hint>
|
||||
</mat-form-field>
|
||||
<div class="verify-action">
|
||||
@if (panVerifyStatus === 'NONE' || panVerifyStatus === 'FAILED') {
|
||||
<button mat-raised-button color="primary" type="button" class="verify-btn-inline"
|
||||
[disabled]="readOnly || !kycForm.get('panNumber')?.value || kycForm.get('panNumber')?.invalid || panVerifying"
|
||||
(click)="verifyPan()">
|
||||
@if (panVerifying) {
|
||||
<mat-progress-spinner diameter="16" mode="indeterminate"></mat-progress-spinner>
|
||||
} @else {
|
||||
<fa-icon icon="shield-alt"></fa-icon> Verify PAN
|
||||
}
|
||||
</button>
|
||||
}
|
||||
@if (panVerifyStatus === 'VERIFIED') {
|
||||
<span class="verify-chip verified"><fa-icon icon="check-circle"></fa-icon> Verified</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if (panVerifyStatus === 'VERIFIED') {
|
||||
<div class="pan-result-card full-width">
|
||||
<div class="pan-result-header">
|
||||
<fa-icon icon="check-circle" class="pan-result-ok-icon"></fa-icon>
|
||||
<span>PAN Verified Successfully</span>
|
||||
</div>
|
||||
<div class="pan-result-body">
|
||||
<div class="pan-result-row">
|
||||
<span class="pan-result-label">Full Name (as per PAN)</span>
|
||||
<span class="pan-result-value name">{{ panVerifyName }}</span>
|
||||
</div>
|
||||
<div class="pan-result-row">
|
||||
<span class="pan-result-label">PAN–Aadhaar Link Status</span>
|
||||
<span class="pan-result-value" [class.seeded]="panAadhaarSeedingStatus?.toUpperCase() === 'Y'" [class.not-seeded]="panAadhaarSeedingStatus?.toUpperCase() === 'N'">
|
||||
@if (panAadhaarSeedingStatus?.toUpperCase() === 'Y') { Linked }
|
||||
@else if (panAadhaarSeedingStatus?.toUpperCase() === 'N') { Not Linked }
|
||||
@else { — }
|
||||
</span>
|
||||
</div>
|
||||
<div class="pan-result-row">
|
||||
<span class="pan-result-label">Verified On</span>
|
||||
<span class="pan-result-value date">{{ panVerifiedOn }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<!-- Aadhaar with independent verify (Individual only) -->
|
||||
<div class="form-field verify-field">
|
||||
<mat-form-field appearance="outline" class="verify-input">
|
||||
<mat-label>Aadhaar Number</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>fingerprint</mat-icon></span>
|
||||
<input matInput formControlName="aadhaarNumber" placeholder="12-digit Aadhaar"
|
||||
maxlength="12" [type]="aadhaarVisible ? 'text' : 'password'" [readonly]="aadhaarOtpSent"
|
||||
(keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()" />
|
||||
<button mat-icon-button matSuffix type="button" (click)="aadhaarVisible = !aadhaarVisible" class="toggle-vis-btn">
|
||||
<fa-icon [icon]="aadhaarVisible ? 'eye-slash' : 'eye'"></fa-icon>
|
||||
</button>
|
||||
@if (kycForm.get('aadhaarNumber')?.hasError('pattern') && kycForm.get('aadhaarNumber')?.touched) {
|
||||
<mat-error>Must be 12 digits</mat-error>
|
||||
}
|
||||
@if (aadhaarVerifyStatus === 'VERIFIED') {
|
||||
<mat-icon matSuffix class="verify-icon-ok">check_circle</mat-icon>
|
||||
}
|
||||
</mat-form-field>
|
||||
<div class="verify-action">
|
||||
@if ((aadhaarVerifyStatus === 'NONE' || aadhaarVerifyStatus === 'FAILED') && !aadhaarOtpSent) {
|
||||
<button mat-raised-button color="primary" type="button" class="verify-btn-inline"
|
||||
[disabled]="readOnly || !kycForm.get('aadhaarNumber')?.value || kycForm.get('aadhaarNumber')?.invalid || aadhaarVerifying"
|
||||
(click)="sendAadhaarOtp()">
|
||||
@if (aadhaarVerifying) {
|
||||
<mat-progress-spinner diameter="16" mode="indeterminate"></mat-progress-spinner>
|
||||
} @else {
|
||||
<fa-icon icon="shield-alt"></fa-icon> Verify Aadhaar
|
||||
}
|
||||
</button>
|
||||
}
|
||||
@if (aadhaarVerifyStatus === 'VERIFIED') {
|
||||
<span class="verify-chip verified"><fa-icon icon="check-circle"></fa-icon> Verified</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Aadhaar OTP Section -->
|
||||
@if (aadhaarOtpSent && aadhaarVerifyStatus !== 'VERIFIED') {
|
||||
<div class="otp-card full-width">
|
||||
<div class="otp-card__header">
|
||||
<div class="otp-card__header-icon">
|
||||
<mat-icon>verified_user</mat-icon>
|
||||
</div>
|
||||
<div class="otp-card__header-text">
|
||||
<span class="otp-card__title">Aadhaar OTP Verification</span>
|
||||
<span class="otp-card__subtitle">A 6-digit OTP has been sent to the Aadhaar-linked mobile number</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="otp-card__body">
|
||||
<label class="otp-card__label">Enter OTP</label>
|
||||
<div class="otp-digits" (paste)="onOtpPaste($event)">
|
||||
@for (digit of otpDigits; track $index) {
|
||||
<input type="text" inputmode="numeric" maxlength="1"
|
||||
class="otp-digit-box" [class.filled]="digit"
|
||||
[value]="digit"
|
||||
(input)="onOtpDigitInput($index, $event)"
|
||||
(keydown)="onOtpKeydown($index, $event)"
|
||||
autocomplete="one-time-code" />
|
||||
}
|
||||
</div>
|
||||
<div class="otp-card__actions">
|
||||
<button mat-flat-button color="primary" type="button" class="otp-card__verify-btn"
|
||||
[disabled]="readOnly || aadhaarOtp.length < 6 || aadhaarOtpVerifying"
|
||||
(click)="verifyAadhaarOtp()">
|
||||
@if (aadhaarOtpVerifying) {
|
||||
<mat-progress-spinner diameter="16" mode="indeterminate" class="otp-spinner"></mat-progress-spinner>
|
||||
Verifying...
|
||||
} @else {
|
||||
<fa-icon icon="check-circle" class="otp-btn-icon"></fa-icon>
|
||||
Verify OTP
|
||||
}
|
||||
</button>
|
||||
<div class="otp-card__resend">
|
||||
@if (resendCountdown > 0) {
|
||||
<span class="otp-card__countdown">
|
||||
Resend in <strong>00:{{ resendCountdown.toString().padStart(2, '0') }}</strong>
|
||||
</span>
|
||||
} @else {
|
||||
<span class="otp-card__resend-text">Didn't receive the code?</span>
|
||||
<button mat-button type="button" class="otp-card__resend-btn"
|
||||
[disabled]="readOnly || aadhaarVerifying" (click)="sendAadhaarOtp()">
|
||||
Resend OTP
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (aadhaarVerifyError) {
|
||||
<div class="verify-error full-width"><fa-icon icon="exclamation-circle"></fa-icon> {{ aadhaarVerifyError }}</div>
|
||||
}
|
||||
@if (aadhaarVerifyStatus === 'VERIFIED' && aadhaarVerifyName) {
|
||||
<div class="aadhaar-result-card full-width">
|
||||
<div class="aadhaar-result-card__header">
|
||||
<fa-icon icon="check-circle" class="aadhaar-result-card__icon"></fa-icon>
|
||||
<span>Aadhaar Verified Successfully</span>
|
||||
</div>
|
||||
<div class="aadhaar-result-card__body">
|
||||
<span class="aadhaar-result-card__label">Name as per Aadhaar</span>
|
||||
<span class="aadhaar-result-card__name">{{ aadhaarVerifyName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- PAN-Aadhaar Seeding Status -->
|
||||
@if (kycForm.get('hasPan')?.value && panAadhaarSeedingStatus) {
|
||||
<div class="form-field pan-link-status full-width">
|
||||
<div class="pan-link-box" [class.linked]="panAadhaarSeedingStatus === 'Y'" [class.not-linked]="panAadhaarSeedingStatus === 'N'" [class.exempt]="panAadhaarSeedingStatus === 'E'">
|
||||
<fa-icon icon="link" class="pan-link-icon"></fa-icon>
|
||||
<div class="pan-link-info">
|
||||
<span class="pan-link-label">PAN–Aadhaar Link Status</span>
|
||||
<span class="pan-link-value">
|
||||
@if (panAadhaarSeedingStatus === 'Y') { Linked (Seeded) }
|
||||
@if (panAadhaarSeedingStatus === 'N') { Not Linked }
|
||||
@if (panAadhaarSeedingStatus === 'E') { Exempt }
|
||||
</span>
|
||||
</div>
|
||||
<span class="pan-link-hint">As per Income Tax Portal — Section 139AA</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<!-- ══ NON-INDIVIDUAL KYC ══ -->
|
||||
@if (isNonIndividual) {
|
||||
<!-- Incorporation Date -->
|
||||
<mat-form-field appearance="outline" class="form-field" (click)="incorpDatePicker.open()">
|
||||
<mat-label>Incorporation Date</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>calendar_today</mat-icon></span>
|
||||
<input matInput [max]="today" [matDatepicker]="incorpDatePicker" formControlName="incorporationDate" />
|
||||
<mat-datepicker-toggle matSuffix [for]="incorpDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #incorpDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<!-- GSTIN (all non-individual) -->
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>GSTIN Number</mat-label>
|
||||
<input matInput formControlName="gstinNumber" placeholder="15-character GSTIN"
|
||||
maxlength="15" style="text-transform: uppercase"
|
||||
(input)="kycForm.get('gstinNumber')?.setValue($any($event.target).value.toUpperCase(), { emitEvent: false })" />
|
||||
@if (kycForm.get('gstinNumber')?.hasError('pattern') && kycForm.get('gstinNumber')?.touched) {
|
||||
<mat-error>Invalid GSTIN format (e.g. 22AAAAA0000A1Z5)</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<!-- PAN — Proprietorship uses Individual PAN, others use Enterprise PAN -->
|
||||
@if (constitutionMeta?.value === 'PROPRIETORSHIP') {
|
||||
<!-- Proprietorship: Individual PAN (proprietor's personal PAN) -->
|
||||
<div class="form-field verify-field">
|
||||
<mat-form-field appearance="outline" class="verify-input">
|
||||
<mat-label>PAN of Proprietor</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>credit_card</mat-icon></span>
|
||||
<input matInput formControlName="panNumber" placeholder="e.g. ABCDE1234F"
|
||||
maxlength="10" style="text-transform: uppercase"
|
||||
(input)="kycForm.get('panNumber')?.setValue($any($event.target).value.toUpperCase(), { emitEvent: false })" />
|
||||
@if (kycForm.get('panNumber')?.hasError('pattern') && kycForm.get('panNumber')?.touched) {
|
||||
<mat-error>Invalid PAN format</mat-error>
|
||||
}
|
||||
@if (panVerifyStatus === 'VERIFIED') {
|
||||
<mat-icon matSuffix class="verify-icon-ok">check_circle</mat-icon>
|
||||
}
|
||||
@if (panVerifyStatus === 'FAILED') {
|
||||
<mat-icon matSuffix class="verify-icon-fail">cancel</mat-icon>
|
||||
}
|
||||
<mat-hint>PAN of Proprietor (no separate entity PAN needed)</mat-hint>
|
||||
</mat-form-field>
|
||||
<div class="verify-action">
|
||||
@if (panVerifyStatus === 'NONE' || panVerifyStatus === 'FAILED') {
|
||||
<button mat-raised-button color="primary" type="button" class="verify-btn-inline"
|
||||
[disabled]="readOnly || !kycForm.get('panNumber')?.value || kycForm.get('panNumber')?.invalid || panVerifying || !clientId"
|
||||
(click)="verifyPan()">
|
||||
@if (panVerifying) {
|
||||
<mat-progress-spinner diameter="16" mode="indeterminate"></mat-progress-spinner>
|
||||
} @else {
|
||||
<fa-icon icon="shield-alt"></fa-icon> Verify PAN
|
||||
}
|
||||
</button>
|
||||
}
|
||||
@if (panVerifyStatus === 'VERIFIED') {
|
||||
<span class="verify-chip verified"><fa-icon icon="check-circle"></fa-icon> Verified</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if (panVerifyStatus === 'VERIFIED') {
|
||||
<div class="pan-result-card full-width">
|
||||
<div class="pan-result-header">
|
||||
<fa-icon icon="check-circle" class="pan-result-ok-icon"></fa-icon>
|
||||
<span>PAN Verified Successfully</span>
|
||||
</div>
|
||||
<div class="pan-result-body">
|
||||
<div class="pan-result-row">
|
||||
<span class="pan-result-label">Proprietor Name (as per PAN)</span>
|
||||
<span class="pan-result-value name">{{ panVerifyName }}</span>
|
||||
</div>
|
||||
<div class="pan-result-row">
|
||||
<span class="pan-result-label">Verified On</span>
|
||||
<span class="pan-result-value date">{{ panVerifiedOn }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
} @else {
|
||||
<!-- Non-Proprietorship: Enterprise/Company PAN -->
|
||||
<div class="form-field verify-field">
|
||||
<mat-form-field appearance="outline" class="verify-input">
|
||||
<mat-label>{{ constitutionMeta?.panLabel || 'PAN of Entity' }}</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>credit_card</mat-icon></span>
|
||||
<input matInput formControlName="enterprisePan" placeholder="e.g. ABCDE1234F"
|
||||
maxlength="10" style="text-transform: uppercase"
|
||||
(input)="kycForm.get('enterprisePan')?.setValue($any($event.target).value.toUpperCase(), { emitEvent: false })" />
|
||||
@if (kycForm.get('enterprisePan')?.hasError('pattern') && kycForm.get('enterprisePan')?.touched) {
|
||||
<mat-error>Invalid PAN format</mat-error>
|
||||
}
|
||||
@if (entPanVerifyStatus === 'VERIFIED') {
|
||||
<mat-icon matSuffix class="verify-icon-ok">check_circle</mat-icon>
|
||||
}
|
||||
@if (entPanVerifyStatus === 'FAILED') {
|
||||
<mat-icon matSuffix class="verify-icon-fail">cancel</mat-icon>
|
||||
}
|
||||
<mat-hint>{{ constitutionMeta?.panHint || 'PAN mandatory' }}</mat-hint>
|
||||
</mat-form-field>
|
||||
<div class="verify-action">
|
||||
@if (entPanVerifyStatus === 'NONE' || entPanVerifyStatus === 'FAILED') {
|
||||
<button mat-raised-button color="primary" type="button" class="verify-btn-inline"
|
||||
[disabled]="readOnly || !kycForm.get('enterprisePan')?.value || kycForm.get('enterprisePan')?.invalid || entPanVerifying || !clientId"
|
||||
(click)="verifyEnterprisePan()">
|
||||
@if (entPanVerifying) {
|
||||
<mat-progress-spinner diameter="16" mode="indeterminate"></mat-progress-spinner>
|
||||
} @else {
|
||||
<fa-icon icon="shield-alt"></fa-icon> Verify PAN
|
||||
}
|
||||
</button>
|
||||
}
|
||||
@if (entPanVerifyStatus === 'VERIFIED') {
|
||||
<span class="verify-chip verified"><fa-icon icon="check-circle"></fa-icon> Verified</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if (entPanVerifyStatus === 'VERIFIED') {
|
||||
<div class="pan-result-card full-width">
|
||||
<div class="pan-result-header">
|
||||
<fa-icon icon="check-circle" class="pan-result-ok-icon"></fa-icon>
|
||||
<span>Enterprise PAN Verified</span>
|
||||
</div>
|
||||
<div class="pan-result-body">
|
||||
<div class="pan-result-row">
|
||||
<span class="pan-result-label">Entity Name (as per PAN)</span>
|
||||
<span class="pan-result-value name">{{ entPanVerifyName }}</span>
|
||||
</div>
|
||||
<div class="pan-result-row">
|
||||
<span class="pan-result-label">Verified On</span>
|
||||
<span class="pan-result-value date">{{ entPanVerifiedOn }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
<!-- CIN (Companies & LLP only) -->
|
||||
@if (constitutionMeta?.showCin) {
|
||||
<div class="form-field verify-field">
|
||||
<mat-form-field appearance="outline" class="verify-input">
|
||||
<mat-label>CIN (Corporate Identity Number)</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>business</mat-icon></span>
|
||||
<input matInput formControlName="cinNumber" maxlength="21" placeholder="From MCA"
|
||||
style="text-transform: uppercase"
|
||||
(input)="kycForm.get('cinNumber')?.setValue($any($event.target).value.toUpperCase(), { emitEvent: false })" />
|
||||
@if (kycForm.get('cinNumber')?.hasError('pattern') && kycForm.get('cinNumber')?.touched) {
|
||||
<mat-error>Invalid CIN format (21 characters)</mat-error>
|
||||
}
|
||||
@if (cinVerifyStatus === 'VERIFIED') {
|
||||
<mat-icon matSuffix class="verify-icon-ok">check_circle</mat-icon>
|
||||
}
|
||||
</mat-form-field>
|
||||
<div class="verify-action">
|
||||
@if (cinVerifyStatus === 'NONE' || cinVerifyStatus === 'FAILED') {
|
||||
<button mat-raised-button color="primary" type="button" class="verify-btn-inline"
|
||||
[disabled]="readOnly || !kycForm.get('cinNumber')?.value || cinVerifying"
|
||||
(click)="verifyCin()">
|
||||
@if (cinVerifying) {
|
||||
<mat-progress-spinner diameter="16" mode="indeterminate"></mat-progress-spinner>
|
||||
} @else {
|
||||
<fa-icon icon="shield-alt"></fa-icon> Verify CIN
|
||||
}
|
||||
</button>
|
||||
}
|
||||
@if (cinVerifyStatus === 'VERIFIED') {
|
||||
<span class="verify-chip verified"><fa-icon icon="check-circle"></fa-icon> Verified</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CIN verification progress -->
|
||||
@if (cinVerifying) {
|
||||
<div class="cin-progress full-width">
|
||||
<mat-progress-bar mode="determinate" [value]="cinProgressValue" color="primary"></mat-progress-bar>
|
||||
<div class="cin-progress-percent">{{ cinProgressValue }}%</div>
|
||||
<div class="cin-progress-steps">
|
||||
@for (step of cinProgressSteps; track step.label; let i = $index) {
|
||||
<div class="cin-step" [class.active]="i === cinCurrentStep" [class.done]="i < cinCurrentStep">
|
||||
<fa-icon [icon]="i < cinCurrentStep ? 'check-circle' : step.icon" [class.done-icon]="i < cinCurrentStep"></fa-icon>
|
||||
<span>{{ step.label }}</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (cinVerifyStatus === 'VERIFIED') {
|
||||
<div class="pan-result-card full-width">
|
||||
<div class="pan-result-header">
|
||||
<fa-icon icon="check-circle" class="pan-result-ok-icon"></fa-icon>
|
||||
<span>CIN Verified — {{ cinCompanyName }}</span>
|
||||
</div>
|
||||
<div class="pan-result-body">
|
||||
<div class="pan-result-row">
|
||||
<span class="pan-result-label">CIN</span>
|
||||
<span class="pan-result-value name">{{ kycForm.get('cinNumber')?.value }}</span>
|
||||
</div>
|
||||
<div class="pan-result-row">
|
||||
<span class="pan-result-label">Verified On</span>
|
||||
<span class="pan-result-value date">{{ cinVerifiedOn }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (!clientId) {
|
||||
<div class="verify-info-banner">
|
||||
<fa-icon icon="info-circle" class="verify-info-icon"></fa-icon>
|
||||
<span>PAN verification requires client to be created first. Complete General tab and click Next to save.</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- ═══ SECTION: KYC Document Management ═══ -->
|
||||
<div class="section-block">
|
||||
<h4 class="section-title">
|
||||
<fa-icon icon="file-alt" class="section-icon"></fa-icon>
|
||||
KYC Document
|
||||
<span class="kyc-badge doc-badge">OVD</span>
|
||||
@if (kycTemplate?.clientTypeName) {
|
||||
<span class="kyc-client-type-chip">{{ kycTemplate.clientTypeName }}</span>
|
||||
}
|
||||
</h4>
|
||||
|
||||
<div class="form-grid">
|
||||
<!-- PAN -->
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>{{ 'labels.inputs.PAN Number' | translate }}</mat-label>
|
||||
<input matInput formControlName="panNumber"
|
||||
placeholder="{{ 'labels.inputs.Enter 10-character PAN' | translate }}"
|
||||
maxlength="10" style="text-transform: uppercase"
|
||||
(input)="kycForm.get('panNumber')?.setValue($any($event.target).value.toUpperCase(), { emitEvent: false })" />
|
||||
@if (kycForm.get('panNumber')?.hasError('pattern') && kycForm.get('panNumber')?.touched) {
|
||||
<mat-error>{{ 'labels.inputs.Enter 10-character PAN' | translate }}</mat-error>
|
||||
<mat-label>KYC Document Type</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>description</mat-icon></span>
|
||||
<mat-select required formControlName="kycDocumentName">
|
||||
@for (doc of kycDocumentTypes; track doc) {
|
||||
<mat-option [value]="doc.name">
|
||||
{{ doc.name }}
|
||||
@if (doc.mandatory) {
|
||||
<span class="doc-mandatory-star"> *</span>
|
||||
}
|
||||
</mat-option>
|
||||
}
|
||||
</mat-select>
|
||||
@if (kycForm.get('kycDocumentName')?.hasError('required') && kycForm.get('kycDocumentName')?.touched) {
|
||||
<mat-error>KYC Document Type is <strong>required</strong></mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<!-- Aadhaar -->
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>{{ 'labels.inputs.Aadhaar Number' | translate }}</mat-label>
|
||||
<input matInput formControlName="aadhaarNumber"
|
||||
placeholder="{{ 'labels.inputs.Enter 12-digit Aadhaar' | translate }}"
|
||||
maxlength="12" type="password" />
|
||||
@if (kycForm.get('aadhaarNumber')?.hasError('pattern') && kycForm.get('aadhaarNumber')?.touched) {
|
||||
<mat-error>{{ 'labels.inputs.Enter 12-digit Aadhaar' | translate }}</mat-error>
|
||||
<mat-label>Document Number</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>tag</mat-icon></span>
|
||||
<input matInput required formControlName="kycDocumentNumber" placeholder="As printed on document" maxlength="30"
|
||||
style="text-transform: uppercase"
|
||||
(input)="kycForm.get('kycDocumentNumber')?.setValue($any($event.target).value.toUpperCase(), { emitEvent: false })" />
|
||||
@if (kycForm.get('kycDocumentNumber')?.hasError('required') && kycForm.get('kycDocumentNumber')?.touched) {
|
||||
<mat-error>Document number is <strong>required</strong></mat-error>
|
||||
} @else if (kycForm.get('kycDocumentNumber')?.hasError('pattern') && kycForm.get('kycDocumentNumber')?.touched) {
|
||||
<mat-error>Only letters, numbers, hyphens and slashes allowed</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<!-- GSTIN -->
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>{{ 'labels.inputs.GSTIN Number' | translate }}</mat-label>
|
||||
<input matInput formControlName="gstinNumber"
|
||||
placeholder="{{ 'labels.inputs.Enter 15-character GSTIN' | translate }}"
|
||||
maxlength="15" style="text-transform: uppercase"
|
||||
(input)="kycForm.get('gstinNumber')?.setValue($any($event.target).value.toUpperCase(), { emitEvent: false })" />
|
||||
@if (kycForm.get('gstinNumber')?.hasError('pattern') && kycForm.get('gstinNumber')?.touched) {
|
||||
<mat-error>{{ 'labels.inputs.Enter 15-character GSTIN' | translate }}</mat-error>
|
||||
}
|
||||
<mat-form-field appearance="outline" class="form-field" (click)="docIssueDatePicker.open()">
|
||||
<mat-label>Issue Date</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>calendar_today</mat-icon></span>
|
||||
<input matInput [max]="today" [matDatepicker]="docIssueDatePicker" formControlName="kycDocIssueDate" />
|
||||
<mat-datepicker-toggle matSuffix [for]="docIssueDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #docIssueDatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
|
||||
<!-- Remarks -->
|
||||
<mat-form-field appearance="outline" class="form-field full-width">
|
||||
<mat-label>{{ 'labels.inputs.Remarks' | translate }}</mat-label>
|
||||
<textarea matInput formControlName="remarks" rows="3"></textarea>
|
||||
<mat-form-field appearance="outline" class="form-field" (click)="docExpiryDatePicker.open()">
|
||||
<mat-label>Expiry Date</mat-label>
|
||||
<span matPrefix class="field-prefix-icon"><mat-icon>event_busy</mat-icon></span>
|
||||
<input matInput [matDatepicker]="docExpiryDatePicker" formControlName="kycDocExpiryDate" />
|
||||
<mat-datepicker-toggle matSuffix [for]="docExpiryDatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #docExpiryDatePicker></mat-datepicker>
|
||||
<mat-hint>If applicable (Passport, DL, Visa)</mat-hint>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ SECTION: Risk & KYC Review ═══ -->
|
||||
<div class="section-block">
|
||||
<h4 class="section-title">
|
||||
<fa-icon icon="shield-alt" class="section-icon"></fa-icon>
|
||||
Risk Category & KYC Review
|
||||
</h4>
|
||||
|
||||
<div class="form-grid">
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Risk Category</mat-label>
|
||||
<mat-select formControlName="riskCategory">
|
||||
<mat-select-trigger>
|
||||
@if (kycForm.get('riskCategory')?.value === 1) {
|
||||
<span class="risk-option"><span class="risk-meter risk-low"></span> Low Risk</span>
|
||||
} @else if (kycForm.get('riskCategory')?.value === 2) {
|
||||
<span class="risk-option"><span class="risk-meter risk-medium"></span> Medium Risk</span>
|
||||
} @else if (kycForm.get('riskCategory')?.value === 3) {
|
||||
<span class="risk-option"><span class="risk-meter risk-high"></span> High Risk</span>
|
||||
}
|
||||
</mat-select-trigger>
|
||||
<mat-option [value]="1">
|
||||
<span class="risk-option"><span class="risk-meter risk-low"></span> Low Risk <span class="risk-hint">10 yr renewal</span></span>
|
||||
</mat-option>
|
||||
<mat-option [value]="2">
|
||||
<span class="risk-option"><span class="risk-meter risk-medium"></span> Medium Risk <span class="risk-hint">8 yr renewal</span></span>
|
||||
</mat-option>
|
||||
<mat-option [value]="3">
|
||||
<span class="risk-option"><span class="risk-meter risk-high"></span> High Risk <span class="risk-hint">2 yr renewal</span></span>
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>KYC Review Due Date</mat-label>
|
||||
<input matInput [value]="getKycExpiryDate(getRenewalYears())" disabled />
|
||||
<mat-hint>Auto: Low 10yr, Medium 8yr, High 2yr</mat-hint>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="form-field full-width">
|
||||
<mat-label>Remarks</mat-label>
|
||||
<textarea matInput formControlName="remarks" rows="2"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<!-- KYC Expiry Alert Cards -->
|
||||
<div class="kyc-expiry-section">
|
||||
<div class="kyc-expiry-header">
|
||||
<fa-icon icon="bell" class="kyc-expiry-header-icon"></fa-icon>
|
||||
<span>KYC Expiry Alert</span>
|
||||
</div>
|
||||
<div class="kyc-expiry-body">
|
||||
<div class="kyc-expiry-cards">
|
||||
@for (rc of riskCards; track rc.value) {
|
||||
<div class="kyc-expiry-card" [class.active]="kycForm.get('riskCategory')?.value === rc.value">
|
||||
<span class="risk-gauge" [class]="'risk-gauge--' + rc.color">
|
||||
<svg viewBox="0 0 40 24" class="risk-gauge-svg">
|
||||
<path d="M4 22 A16 16 0 0 1 36 22" fill="none" stroke="#e2e8f0" stroke-width="4" stroke-linecap="round"/>
|
||||
<path d="M4 22 A16 16 0 0 1 36 22" fill="none"
|
||||
[attr.stroke]="rc.color === 'green' ? '#22c55e' : rc.color === 'amber' ? '#f59e0b' : '#ef4444'"
|
||||
stroke-width="4" stroke-linecap="round"
|
||||
[attr.stroke-dasharray]="rc.color === 'green' ? '16 50' : rc.color === 'amber' ? '33 50' : '50 50'"/>
|
||||
<line x1="20" y1="22"
|
||||
[attr.x2]="rc.color === 'green' ? '10' : rc.color === 'amber' ? '20' : '30'"
|
||||
[attr.y2]="rc.color === 'green' ? '10' : rc.color === 'amber' ? '6' : '10'"
|
||||
stroke="#334155" stroke-width="2" stroke-linecap="round"/>
|
||||
<circle cx="20" cy="22" r="2" fill="#334155"/>
|
||||
</svg>
|
||||
</span>
|
||||
<div class="kyc-expiry-card__info">
|
||||
<span class="kyc-expiry-card__label">{{ rc.label }}</span>
|
||||
<span class="kyc-expiry-card__cycle">Renewal every <strong>{{ rc.years }} years</strong></span>
|
||||
</div>
|
||||
@if (kycForm.get('riskCategory')?.value === rc.value) {
|
||||
<span class="kyc-expiry-card__next">Next: {{ getKycExpiryDate(rc.years) }}</span>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (kycForm.get('riskCategory')?.value) {
|
||||
<div class="kyc-expiry-alert"
|
||||
[class.kyc-expiry-alert--low]="kycForm.get('riskCategory')?.value === 1"
|
||||
[class.kyc-expiry-alert--medium]="kycForm.get('riskCategory')?.value === 2"
|
||||
[class.kyc-expiry-alert--high]="kycForm.get('riskCategory')?.value === 3">
|
||||
<fa-icon icon="exclamation-circle" class="kyc-expiry-alert__icon"></fa-icon>
|
||||
<span>System will auto-alert before KYC expiry on <strong>{{ getKycExpiryDate(getRenewalYears()) }}</strong>.</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ SECTION: AML & Compliance Checks ═══ -->
|
||||
<div class="section-block">
|
||||
<h4 class="section-title aml-title">
|
||||
<fa-icon icon="exclamation-triangle" class="section-icon"></fa-icon>
|
||||
AML & Compliance Checks
|
||||
</h4>
|
||||
|
||||
<div class="form-grid">
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>AML Check Status</mat-label>
|
||||
<mat-select required formControlName="amlCheckStatus">
|
||||
<mat-option value="CLEAR">Clear</mat-option>
|
||||
<mat-option value="FLAGGED">Flagged</mat-option>
|
||||
<mat-option value="BLOCKED">Blocked</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Banned Entity List Match</mat-label>
|
||||
<mat-select required formControlName="bannedEntityMatch">
|
||||
<mat-option value="NO">No</mat-option>
|
||||
<mat-option value="YES">Yes</mat-option>
|
||||
<mat-option value="PARTIAL_MATCH">Partial Match</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Willful Defaulter Flag</mat-label>
|
||||
<mat-select formControlName="willfulDefaulter">
|
||||
<mat-option value="NO">No</mat-option>
|
||||
<mat-option value="YES">Yes</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>ECGC SAL Flag</mat-label>
|
||||
<mat-select formControlName="ecgcSalFlag">
|
||||
<mat-option value="NO">No</mat-option>
|
||||
<mat-option value="YES">Yes</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Dormant / Inoperative Flag</mat-label>
|
||||
<mat-select formControlName="dormantFlag">
|
||||
<mat-option value="ACTIVE">Active</mat-option>
|
||||
<mat-option value="DORMANT">Dormant</mat-option>
|
||||
<mat-option value="INOPERATIVE">Inoperative</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
@if (kycForm.get('amlCheckStatus')?.value === 'BLOCKED' || kycForm.get('bannedEntityMatch')?.value === 'YES') {
|
||||
<div class="aml-block-alert">
|
||||
<fa-icon icon="ban" class="aml-block-icon"></fa-icon>
|
||||
<span><strong>BLOCKED</strong> — Account opening cannot proceed.</span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- ═══ SECTION: Freeze / Debit Controls ═══ -->
|
||||
<div class="section-block">
|
||||
<h4 class="section-title freeze-title">
|
||||
<fa-icon icon="lock" class="section-icon"></fa-icon>
|
||||
Freeze / Debit Controls
|
||||
</h4>
|
||||
|
||||
<div class="form-grid">
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Account Status</mat-label>
|
||||
<mat-select formControlName="accountStatus">
|
||||
<mat-option value="ACTIVE">Active</mat-option>
|
||||
<mat-option value="FROZEN">Frozen</mat-option>
|
||||
<mat-option value="UNFREEZE">Unfreeze</mat-option>
|
||||
<mat-option value="NO_DEBIT">No Debit</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
|
||||
@if (kycForm.get('accountStatus')?.value === 'FROZEN') {
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Reason for Freeze</mat-label>
|
||||
<mat-select required formControlName="freezeReason">
|
||||
<mat-option value="COURT_ORDER">Court Order</mat-option>
|
||||
<mat-option value="AML">AML</mat-option>
|
||||
<mat-option value="REGULATORY">Regulatory</mat-option>
|
||||
<mat-option value="INTERNAL">Internal</mat-option>
|
||||
<mat-option value="OTHER">Other</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Freeze Authority</mat-label>
|
||||
<mat-select required formControlName="freezeAuthority">
|
||||
<mat-option value="BRANCH_MANAGER">Branch Manager</mat-option>
|
||||
<mat-option value="HO">Head Office</mat-option>
|
||||
<mat-option value="REGULATORY">Regulatory Authority</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ SECTION: Form 60/61 ═══ -->
|
||||
<div class="section-block">
|
||||
<h4 class="section-title">
|
||||
<fa-icon icon="file-invoice" class="section-icon"></fa-icon>
|
||||
Form 60 / 61
|
||||
@if (!kycForm.get('hasPan')?.value) {
|
||||
<span class="kyc-badge required-badge">Required — No PAN</span>
|
||||
}
|
||||
</h4>
|
||||
@if (!kycForm.get('hasPan')?.value) {
|
||||
<div class="kyc-info-hint warning-hint">
|
||||
<mat-icon class="kyc-info-hint-icon">warning</mat-icon>
|
||||
<span>Client does not have PAN. Form 60/61 is mandatory.</span>
|
||||
</div>
|
||||
<div class="form-grid">
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Form Type</mat-label>
|
||||
<mat-select formControlName="form60Type">
|
||||
<mat-option value="FORM_60">Form 60</mat-option>
|
||||
<mat-option value="FORM_61">Form 61</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="form-field" (click)="form60DatePicker.open()">
|
||||
<mat-label>Declaration Date</mat-label>
|
||||
<input matInput [matDatepicker]="form60DatePicker" formControlName="form60Date" />
|
||||
<mat-datepicker-toggle matSuffix [for]="form60DatePicker"></mat-datepicker-toggle>
|
||||
<mat-datepicker #form60DatePicker></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
} @else {
|
||||
<p class="form60-not-needed">Client has PAN — Form 60/61 not required.</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- ═══ SECTION: Form 15G / 15H ═══ -->
|
||||
<div class="section-block">
|
||||
<h4 class="section-title">
|
||||
<fa-icon icon="receipt" class="section-icon"></fa-icon>
|
||||
Form 15G / 15H
|
||||
</h4>
|
||||
<div class="form-grid">
|
||||
<div class="form-field checkbox-field">
|
||||
<mat-checkbox formControlName="form15Applicable">Form 15G/15H Applicable</mat-checkbox>
|
||||
</div>
|
||||
@if (kycForm.get('form15Applicable')?.value) {
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Form Type</mat-label>
|
||||
<mat-select formControlName="form15Type">
|
||||
<mat-option value="15G">15G (Below 60 years)</mat-option>
|
||||
<mat-option value="15H">15H (Senior Citizen 60+)</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field appearance="outline" class="form-field">
|
||||
<mat-label>Financial Year</mat-label>
|
||||
<input matInput formControlName="form15FY" placeholder="e.g. 2025-26" />
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══ SECTION: Photograph & Signature ═══ -->
|
||||
<div class="section-block">
|
||||
<h4 class="section-title">
|
||||
<fa-icon icon="camera" class="section-icon"></fa-icon>
|
||||
Photograph & Signature
|
||||
</h4>
|
||||
<div class="photo-sig-grid">
|
||||
<div class="upload-box" [class.upload-box--has-preview]="photographPreview">
|
||||
@if (photographPreview) {
|
||||
<div class="upload-preview">
|
||||
<img [src]="photographPreview" alt="Photo" class="upload-preview__img upload-preview__img--photo" />
|
||||
<div class="upload-preview__actions">
|
||||
<label class="upload-preview__btn recrop">
|
||||
<input type="file" accept="image/*" hidden (change)="onPhotoSelected($event)" />
|
||||
<fa-icon icon="crop"></fa-icon> Re-crop
|
||||
</label>
|
||||
<button type="button" class="upload-preview__btn remove" (click)="removePhoto()">
|
||||
<fa-icon icon="trash"></fa-icon> Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="upload-box__icon"><fa-icon icon="user"></fa-icon></div>
|
||||
<span class="upload-box__label">Client Photograph</span>
|
||||
<span class="upload-box__hint">JPG / PNG, max 2MB</span>
|
||||
<label class="upload-box__btn">
|
||||
<input type="file" accept="image/*" hidden (change)="onPhotoSelected($event)" />
|
||||
<fa-icon icon="camera"></fa-icon> Choose & Crop
|
||||
</label>
|
||||
}
|
||||
</div>
|
||||
<div class="upload-box" [class.upload-box--has-preview]="signaturePreview">
|
||||
@if (signaturePreview) {
|
||||
<div class="upload-preview">
|
||||
<img [src]="signaturePreview" alt="Signature" class="upload-preview__img upload-preview__img--sig" />
|
||||
<div class="upload-preview__actions">
|
||||
<label class="upload-preview__btn recrop">
|
||||
<input type="file" accept="image/*" hidden (change)="onSignatureSelected($event)" />
|
||||
<fa-icon icon="crop"></fa-icon> Re-crop
|
||||
</label>
|
||||
<button type="button" class="upload-preview__btn remove" (click)="removeSignature()">
|
||||
<fa-icon icon="trash"></fa-icon> Remove
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<div class="upload-box__icon"><fa-icon icon="pen"></fa-icon></div>
|
||||
<span class="upload-box__label">Specimen Signature</span>
|
||||
<span class="upload-box__hint">Scanned image, max 1MB</span>
|
||||
<label class="upload-box__btn">
|
||||
<input type="file" accept="image/*" hidden (change)="onSignatureSelected($event)" />
|
||||
<fa-icon icon="crop"></fa-icon> Choose & Crop
|
||||
</label>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user