feat: MFA/2FA, OTP audit logs, KYC + client/user enhancements
- Access Control module: SMS/Email/TOTP/Hardware-token MFA factor management - OTP Audit Logs module under Admin menu (list + detail) with 401/403 handling - Two-factor authentication login flow updates - Client KYC, India address, family-members, ckyc, nominee enhancements - New shared KYC helpers (kyc-proof-types, kyc-upload-helper.service) - User create/edit/view enhancements - Shell navigation: OTP Audit Logs entry under real Admin menu
This commit is contained in:
@@ -27,7 +27,8 @@
|
||||
<div class="cu-grid" #userFormRef>
|
||||
<mat-form-field appearance="outline" class="cu-field">
|
||||
<mat-label>{{ 'labels.inputs.Username' | translate }}</mat-label>
|
||||
<input matInput required formControlName="username" />
|
||||
<span matPrefix class="cu-prefix-icon"><mat-icon>person</mat-icon></span>
|
||||
<input matInput required formControlName="username" placeholder="Login username" />
|
||||
@if (userForm.controls.username.hasError('required')) {
|
||||
<mat-error
|
||||
>{{ 'labels.inputs.Username' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
@@ -36,26 +37,10 @@
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="cu-field">
|
||||
<mat-label>{{ 'labels.inputs.Email' | translate }}</mat-label>
|
||||
<input matInput [required]="userForm.controls.sendPasswordToEmail.value" formControlName="email" />
|
||||
@if (userForm.controls.email.hasError('email')) {
|
||||
<mat-error
|
||||
>{{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.invalid' | translate }}</strong></mat-error
|
||||
>
|
||||
}
|
||||
@if (userForm.controls.email.hasError('required')) {
|
||||
<mat-error
|
||||
>{{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong></mat-error
|
||||
>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="cu-field">
|
||||
<mat-label>{{ 'labels.inputs.First Name' | translate }}</mat-label>
|
||||
<input matInput required formControlName="firstname" />
|
||||
<span matPrefix class="cu-prefix-icon"><mat-icon>badge</mat-icon></span>
|
||||
<input matInput required formControlName="firstname" placeholder="Given name" />
|
||||
@if (userForm.controls.firstname.hasError('pattern')) {
|
||||
<mat-error
|
||||
>{{ 'labels.inputs.First Name' | translate }}
|
||||
@@ -74,7 +59,8 @@
|
||||
|
||||
<mat-form-field appearance="outline" class="cu-field">
|
||||
<mat-label>{{ 'labels.inputs.Last Name' | translate }}</mat-label>
|
||||
<input matInput required formControlName="lastname" />
|
||||
<span matPrefix class="cu-prefix-icon"><mat-icon>person_outline</mat-icon></span>
|
||||
<input matInput required formControlName="lastname" placeholder="Family name" />
|
||||
@if (userForm.controls.lastname.hasError('pattern')) {
|
||||
<mat-error
|
||||
>{{ 'labels.inputs.Last Name' | translate }}
|
||||
@@ -90,6 +76,54 @@
|
||||
>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="cu-field">
|
||||
<mat-label>{{ 'labels.inputs.Email' | translate }}</mat-label>
|
||||
<span matPrefix class="cu-prefix-icon"><mat-icon>email</mat-icon></span>
|
||||
<input
|
||||
matInput
|
||||
[required]="userForm.controls.sendPasswordToEmail.value"
|
||||
formControlName="email"
|
||||
placeholder="name@example.com"
|
||||
/>
|
||||
@if (userForm.controls.email.hasError('email')) {
|
||||
<mat-error
|
||||
>{{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.invalid' | translate }}</strong></mat-error
|
||||
>
|
||||
}
|
||||
@if (userForm.controls.email.hasError('required')) {
|
||||
<mat-error
|
||||
>{{ 'labels.inputs.Email' | translate }} {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong></mat-error
|
||||
>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field appearance="outline" class="cu-field">
|
||||
<mat-label>Mobile Number</mat-label>
|
||||
<span matPrefix class="cu-mobile-prefix">
|
||||
<mat-icon>phone_android</mat-icon>
|
||||
<span class="cu-mobile-prefix__code">+91</span>
|
||||
</span>
|
||||
<input
|
||||
matInput
|
||||
required
|
||||
formControlName="mobileNo"
|
||||
maxlength="10"
|
||||
placeholder="10-digit mobile number"
|
||||
(keypress)="$event.charCode >= 48 && $event.charCode <= 57 ? true : $event.preventDefault()"
|
||||
/>
|
||||
@if (userForm.controls.mobileNo.hasError('required')) {
|
||||
<mat-error
|
||||
>Mobile number {{ 'labels.commons.is' | translate }}
|
||||
<strong>{{ 'labels.commons.required' | translate }}</strong>
|
||||
</mat-error>
|
||||
}
|
||||
@if (userForm.controls.mobileNo.hasError('pattern')) {
|
||||
<mat-error>Enter a valid <strong>10-digit mobile number</strong></mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -179,6 +213,7 @@
|
||||
<div class="cu-grid">
|
||||
<mat-form-field appearance="outline" class="cu-field">
|
||||
<mat-label>{{ 'labels.inputs.Office' | translate }}</mat-label>
|
||||
<span matPrefix class="cu-prefix-icon"><mat-icon>business</mat-icon></span>
|
||||
<mat-select required formControlName="officeId">
|
||||
@for (office of officesData; track office) {
|
||||
<mat-option [value]="office.id">{{ office.name }}</mat-option>
|
||||
@@ -194,6 +229,7 @@
|
||||
|
||||
<mat-form-field appearance="outline" class="cu-field">
|
||||
<mat-label>{{ 'labels.inputs.Staff' | translate }}</mat-label>
|
||||
<span matPrefix class="cu-prefix-icon"><mat-icon>work</mat-icon></span>
|
||||
<mat-select formControlName="staffId">
|
||||
@for (staff of staffData; track staff) {
|
||||
<mat-option [value]="staff.id">{{ staff.displayName }}</mat-option>
|
||||
@@ -203,6 +239,7 @@
|
||||
|
||||
<mat-form-field appearance="outline" class="cu-field cu-field--full">
|
||||
<mat-label>{{ 'labels.inputs.Roles' | translate }}</mat-label>
|
||||
<span matPrefix class="cu-prefix-icon"><mat-icon>verified_user</mat-icon></span>
|
||||
<mat-select required formControlName="roles" multiple>
|
||||
@for (role of rolesData; track role) {
|
||||
<mat-option [value]="role.id">{{ role.name }}</mat-option>
|
||||
|
||||
@@ -110,6 +110,50 @@ $gray-900: #202124;
|
||||
}
|
||||
}
|
||||
|
||||
// Prefix icon (matches client-creation field-prefix-icon)
|
||||
.cu-prefix-icon {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding-left: 4px;
|
||||
padding-right: 8px;
|
||||
margin-right: 8px;
|
||||
border-right: 1.5px solid #cbd5e1;
|
||||
line-height: 1;
|
||||
|
||||
mat-icon {
|
||||
font-size: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
// Combined icon + country-code prefix (mobile field)
|
||||
.cu-mobile-prefix {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-left: 4px;
|
||||
padding-right: 10px;
|
||||
margin-right: 10px;
|
||||
border-right: 1.5px solid #cbd5e1;
|
||||
line-height: 1;
|
||||
|
||||
mat-icon {
|
||||
font-size: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
color: $primary;
|
||||
}
|
||||
|
||||
&__code {
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
color: $gray-900;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
}
|
||||
|
||||
.cu-checkbox-row {
|
||||
grid-column: 1 / -1;
|
||||
display: flex;
|
||||
|
||||
@@ -28,6 +28,7 @@ import { confirmPasswordValidator } from 'app/login/reset-password/confirm-passw
|
||||
import { ConfigurationWizardService } from 'app/configuration-wizard/configuration-wizard.service';
|
||||
import { ContinueSetupDialogComponent } from 'app/configuration-wizard/continue-setup-dialog/continue-setup-dialog.component';
|
||||
import { MatCheckbox } from '@angular/material/checkbox';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
|
||||
/**
|
||||
@@ -39,7 +40,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
|
||||
styleUrls: ['./create-user.component.scss'],
|
||||
imports: [
|
||||
...STANDALONE_SHARED_IMPORTS,
|
||||
MatCheckbox
|
||||
MatCheckbox,
|
||||
MatIcon
|
||||
]
|
||||
})
|
||||
export class CreateUserComponent implements OnInit, AfterViewInit {
|
||||
@@ -108,6 +110,13 @@ export class CreateUserComponent implements OnInit, AfterViewInit {
|
||||
Validators.email
|
||||
]
|
||||
],
|
||||
mobileNo: [
|
||||
'',
|
||||
[
|
||||
Validators.required,
|
||||
Validators.pattern(/^\d{10}$/)
|
||||
]
|
||||
],
|
||||
firstname: [
|
||||
'',
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user