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
@@ -20,7 +20,10 @@ import { SettingsService } from 'app/settings/settings.service';
/** Http request (default) options headers. */
const httpOptions: { headers: { [key: string]: string } } = {
headers: {
'Fineract-Platform-TenantId': environment.fineractPlatformTenantId
'Fineract-Platform-TenantId': environment.fineractPlatformTenantId,
// Suppresses Spring's WWW-Authenticate: Basic header on 401, which prevents
// the browser's native sign-in dialog from hijacking expired-session responses.
'X-Requested-With': 'XMLHttpRequest'
}
};
@@ -45,11 +45,10 @@ export class ErrorHandlerInterceptor implements HttpInterceptor {
*/
private handleError(response: HttpErrorResponse, request: HttpRequest<any>): Observable<HttpEvent<any>> {
const status = response.status;
let errorMessage = response.error.developerMessage || response.message;
if (response.error.errors) {
if (response.error.errors[0]) {
errorMessage = response.error.errors[0].defaultUserMessage || response.error.errors[0].developerMessage;
}
const errorBody = response.error;
let errorMessage = errorBody?.developerMessage || response.message;
if (errorBody?.errors?.[0]) {
errorMessage = errorBody.errors[0].defaultUserMessage || errorBody.errors[0].developerMessage;
}
const isClientImageRequest = request.url.includes('/clients/') && request.url.includes('/images');