feat: DSC enrollment + MFA controls, user CRUD UX, auth error hardening

- DSC module: list/enroll/revoke dialogs and dsc.service (m_bank_user_dsc API)
- Access control: DSC + MFA toggles via disable-confirm-dialog
- Two-factor auth: expanded UX (DSC + OTP flows)
- Users: create/edit form polish, success snackbar on edit, DSC widget
  retained only inside dsc/ (removed from view-user)
- Auth: X-Requested-With added globally; error handler null-safe on
  empty 500 bodies

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-05-12 11:26:22 +05:30
co-authored by Claude Opus 4.7
parent 3b752dc6ae
commit 09fab88451
29 changed files with 2482 additions and 79 deletions
+30 -10
View File
@@ -10,6 +10,7 @@
import { Component, OnInit, inject } from '@angular/core';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
/** Custom Services */
import { UsersService } from '../users.service';
@@ -27,7 +28,8 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
imports: [
...STANDALONE_SHARED_IMPORTS,
MatCheckbox,
MatIcon
MatIcon,
MatSnackBarModule
]
})
export class EditUserComponent implements OnInit {
@@ -35,6 +37,7 @@ export class EditUserComponent implements OnInit {
private usersService = inject(UsersService);
private route = inject(ActivatedRoute);
private router = inject(Router);
private snackBar = inject(MatSnackBar);
/** User Data */
userData: any;
@@ -81,7 +84,8 @@ export class EditUserComponent implements OnInit {
this.userData.email,
[
Validators.required,
Validators.email
Validators.email,
Validators.pattern(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/)
]
],
mobileNo: [
@@ -135,14 +139,30 @@ export class EditUserComponent implements OnInit {
*/
submit() {
const editedUser = this.editUserForm.value;
this.usersService.editUser(this.userData.id, editedUser).subscribe((response: any) => {
this.router.navigate(
[
'../../',
response.resourceId
],
{ relativeTo: this.route }
);
this.usersService.editUser(this.userData.id, editedUser).subscribe({
next: (response: any) => {
// Fineract sometimes omits resourceId in PUT responses — fall back to
// the user id we're editing so navigation never silently no-ops.
const targetId = response?.resourceId ?? this.userData.id;
this.snackBar.open('User updated successfully', 'OK', {
duration: 3000,
panelClass: ['success-snackbar']
});
this.router.navigate(
[
'../../',
targetId
],
{ relativeTo: this.route }
);
},
error: (err) => {
console.error('[EditUser] update failed', err);
this.snackBar.open('Failed to update user', 'Close', {
duration: 4000,
panelClass: ['error-snackbar']
});
}
});
}
}