Merge pull request #2810 from AnvayKharb/WEB-429-implement-centralized-error-handler-service-for-user-friendly-api-error-messaging
Web 429 implement centralized error handler service for user friendly api error messaging
This commit is contained in:
@@ -0,0 +1,384 @@
|
|||||||
|
# Error Handler Service
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `ErrorHandlerService` is a centralized error-handling service for the Mifos X Web App. It provides consistent, user-friendly error messaging across the application by converting HTTP errors into meaningful messages and displaying them through Material Design snackbars.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Trigger Mechanism
|
||||||
|
|
||||||
|
The `ErrorHandlerService` is **manually triggered** by components and services using RxJS's `catchError` operator. It works **alongside** (not replacing) the existing `ErrorHandlerInterceptor`:
|
||||||
|
|
||||||
|
#### Existing Interceptor (`error-handler.interceptor.ts`)
|
||||||
|
|
||||||
|
- Automatically intercepts **all** HTTP errors globally
|
||||||
|
- Shows generic error dialog for unhandled errors
|
||||||
|
- Cannot provide context-specific messages
|
||||||
|
- Located at: `src/app/core/http/error-handler.interceptor.ts`
|
||||||
|
|
||||||
|
#### ErrorHandlerService (This Service)
|
||||||
|
|
||||||
|
- **Manually invoked** per API call
|
||||||
|
- Provides **context-specific** error messages
|
||||||
|
- Displays user-friendly snackbar notifications
|
||||||
|
- Extracts Fineract-specific error details
|
||||||
|
- Gives developers fine-grained control
|
||||||
|
|
||||||
|
### When to Use
|
||||||
|
|
||||||
|
Use `ErrorHandlerService` when you need:
|
||||||
|
|
||||||
|
- Custom error messages for specific operations
|
||||||
|
- Context-aware error handling (e.g., "User not found", "Loan approval failed")
|
||||||
|
- Snackbar notifications instead of modal dialogs
|
||||||
|
- To extract and display Fineract API error messages
|
||||||
|
|
||||||
|
The interceptor still catches errors you don't explicitly handle, providing a safety net.
|
||||||
|
|
||||||
|
## Problem Solved
|
||||||
|
|
||||||
|
**Before:** The app handled API errors inconsistently:
|
||||||
|
|
||||||
|
- Some errors were shown directly from the server
|
||||||
|
- Others were not displayed clearly to users
|
||||||
|
- Error handling code was duplicated across components
|
||||||
|
- Poor user experience with technical error messages
|
||||||
|
|
||||||
|
**After:** Centralized error handling with:
|
||||||
|
|
||||||
|
- Consistent error messaging
|
||||||
|
- User-friendly error descriptions
|
||||||
|
- Reusable service across all components
|
||||||
|
- Better UX with appropriate snackbar notifications
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
### 1. HTTP Error Handling
|
||||||
|
|
||||||
|
- Automatically converts HTTP status codes into user-friendly messages
|
||||||
|
- Extracts Fineract-specific error messages
|
||||||
|
- Handles network/connection errors
|
||||||
|
- Supports contextual error messages
|
||||||
|
|
||||||
|
### 2. Supported Error Codes
|
||||||
|
|
||||||
|
| Status Code | Title | Behavior |
|
||||||
|
| ------------- | ------------------- | ----------------------- |
|
||||||
|
| 400 | Invalid Request | Shows validation errors |
|
||||||
|
| 401 | Unauthorized | Session expired message |
|
||||||
|
| 403 | Access Denied | Permission error |
|
||||||
|
| 404 | Not Found | Resource not found |
|
||||||
|
| 409 | Conflict | Duplicate resource |
|
||||||
|
| 500 | Server Error | Generic server error |
|
||||||
|
| 503 | Service Unavailable | Service down message |
|
||||||
|
| Network Error | Connection Error | Connection issues |
|
||||||
|
|
||||||
|
### 3. Notification Types
|
||||||
|
|
||||||
|
- **Error Notifications**: Red snackbar, top-center, 5 seconds
|
||||||
|
- **Success Notifications**: Green snackbar, bottom-center, 3 seconds
|
||||||
|
- **Info Notifications**: Blue snackbar, bottom-center, 4 seconds
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Basic Error Handling
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ErrorHandlerService } from '@core/error-handler/error-handler.service';
|
||||||
|
import { catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
export class MyComponent {
|
||||||
|
constructor(private errorHandler: ErrorHandlerService) {}
|
||||||
|
|
||||||
|
loadData() {
|
||||||
|
this.dataService
|
||||||
|
.getData()
|
||||||
|
.pipe(catchError((error) => this.errorHandler.handleError(error)))
|
||||||
|
.subscribe((data) => {
|
||||||
|
// Handle success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Error Handling with Context
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
loadUser(userId: string) {
|
||||||
|
this.userService.getUser(userId).pipe(
|
||||||
|
catchError(error => this.errorHandler.handleError(error, 'User'))
|
||||||
|
).subscribe(user => {
|
||||||
|
// Handle success
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Shows: "Not Found: User not found." for 404 errors
|
||||||
|
```
|
||||||
|
|
||||||
|
### Success Messages
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
saveData() {
|
||||||
|
this.dataService.save(data).subscribe(
|
||||||
|
response => {
|
||||||
|
this.errorHandler.showSuccess('Data saved successfully!');
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.errorHandler.handleError(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Info Messages
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
loadData() {
|
||||||
|
this.errorHandler.showInfo('Loading data, please wait...');
|
||||||
|
this.dataService.getData().subscribe(/* ... */);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Custom Error Handling
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
|
||||||
|
processData() {
|
||||||
|
this.dataService.process().subscribe(
|
||||||
|
response => {
|
||||||
|
this.errorHandler.showSuccess('Processing complete!');
|
||||||
|
},
|
||||||
|
(error: HttpErrorResponse) => {
|
||||||
|
if (error.status === 409) {
|
||||||
|
// Custom handling for conflicts
|
||||||
|
this.handleConflict(error);
|
||||||
|
} else {
|
||||||
|
this.errorHandler.handleError(error, 'Data processing');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration with Form Validation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
submitForm() {
|
||||||
|
if (this.form.invalid) {
|
||||||
|
this.errorHandler.showInfo('Please fill all required fields');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.formService.submit(this.form.value).pipe(
|
||||||
|
catchError(error => this.errorHandler.handleError(error, 'Form submission'))
|
||||||
|
).subscribe(response => {
|
||||||
|
this.errorHandler.showSuccess('Form submitted successfully!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Styling
|
||||||
|
|
||||||
|
The service uses three CSS classes defined in `error-handler.component.scss`:
|
||||||
|
|
||||||
|
```scss
|
||||||
|
.error-snackbar // Red background (#f44336) for errors
|
||||||
|
.success-snackbar // Green background (#4caf50) for success
|
||||||
|
.info-snackbar // Blue background (#2196f3) for info
|
||||||
|
```
|
||||||
|
|
||||||
|
These styles are automatically applied based on the notification type. The styles are imported globally in `main.scss` so they work across the entire application.
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
#### `handleError(error: HttpErrorResponse, context?: string): Observable<never>`
|
||||||
|
|
||||||
|
Handles HTTP errors and shows user-friendly messages.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `error`: The HTTP error response
|
||||||
|
- `context` (optional): Additional context for more specific messages
|
||||||
|
|
||||||
|
**Returns:** Observable that throws the original error
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
this.errorHandler.handleError(error, 'Client');
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `showSuccess(message: string, action?: string): void`
|
||||||
|
|
||||||
|
Shows a success message to the user.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `message`: The success message to display
|
||||||
|
- `action` (optional): Button text (defaults to 'OK')
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
this.errorHandler.showSuccess('Client created successfully!', 'View');
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### `showInfo(message: string, action?: string): void`
|
||||||
|
|
||||||
|
Shows an informational message to the user.
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `message`: The info message to display
|
||||||
|
- `action` (optional): Button text (defaults to 'OK')
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
this.errorHandler.showInfo('Loading data...', 'Dismiss');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fineract Integration
|
||||||
|
|
||||||
|
The service automatically extracts Fineract-specific error messages:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Fineract error structure
|
||||||
|
{
|
||||||
|
"errors": [{
|
||||||
|
"defaultUserMessage": "Client with same name already exists"
|
||||||
|
}],
|
||||||
|
"defaultUserMessage": "Validation error"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The service prioritizes:
|
||||||
|
|
||||||
|
1. `errors[0].defaultUserMessage`
|
||||||
|
2. `defaultUserMessage`
|
||||||
|
3. Generic fallback message
|
||||||
|
|
||||||
|
## Migration Guide
|
||||||
|
|
||||||
|
### Before (Old Approach)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Inconsistent error handling
|
||||||
|
this.clientService.getClient(id).subscribe(
|
||||||
|
(data) => {
|
||||||
|
/* success */
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
console.error(error);
|
||||||
|
alert('Error loading client'); // Poor UX
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### After (Centralized Approach)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Consistent error handling
|
||||||
|
this.clientService
|
||||||
|
.getClient(id)
|
||||||
|
.pipe(catchError((error) => this.errorHandler.handleError(error, 'Client')))
|
||||||
|
.subscribe((data) => {
|
||||||
|
// Handle success
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Always provide context** for better error messages:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
catchError((error) => this.errorHandler.handleError(error, 'Loan Application'));
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Use appropriate notification types**:
|
||||||
|
- Errors: Use `handleError()` for API failures
|
||||||
|
- Success: Use `showSuccess()` for successful operations
|
||||||
|
- Info: Use `showInfo()` for informational messages
|
||||||
|
|
||||||
|
3. **Don't duplicate error handling**: Let the service handle standard errors
|
||||||
|
|
||||||
|
4. **Custom handling when needed**: Handle specific error cases before using the service
|
||||||
|
|
||||||
|
5. **Combine with loading states**:
|
||||||
|
```typescript
|
||||||
|
this.isLoading = true;
|
||||||
|
this.service
|
||||||
|
.getData()
|
||||||
|
.pipe(
|
||||||
|
finalize(() => (this.isLoading = false)),
|
||||||
|
catchError((error) => this.errorHandler.handleError(error))
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { ErrorHandlerService } from './error-handler.service';
|
||||||
|
|
||||||
|
describe('ErrorHandlerService', () => {
|
||||||
|
let service: ErrorHandlerService;
|
||||||
|
let snackBar: jasmine.SpyObj<MatSnackBar>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const snackBarSpy = jasmine.createSpyObj('MatSnackBar', ['open']);
|
||||||
|
const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
ErrorHandlerService,
|
||||||
|
{ provide: MatSnackBar, useValue: snackBarSpy },
|
||||||
|
{ provide: Router, useValue: routerSpy }]
|
||||||
|
});
|
||||||
|
|
||||||
|
service = TestBed.inject(ErrorHandlerService);
|
||||||
|
snackBar = TestBed.inject(MatSnackBar) as jasmine.SpyObj<MatSnackBar>;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should show success message', () => {
|
||||||
|
service.showSuccess('Test success');
|
||||||
|
expect(snackBar.open).toHaveBeenCalledWith(
|
||||||
|
'Test success',
|
||||||
|
'OK',
|
||||||
|
jasmine.objectContaining({
|
||||||
|
panelClass: ['success-snackbar']
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related Issues
|
||||||
|
|
||||||
|
- **WEB-429**: Implement centralized error handler service for user-friendly API error messaging
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
When modifying this service:
|
||||||
|
|
||||||
|
1. Ensure all HTTP status codes are handled appropriately
|
||||||
|
2. Update the documentation with new features
|
||||||
|
3. Maintain consistent snackbar positioning and duration
|
||||||
|
4. Test with actual Fineract API responses
|
||||||
|
5. Follow Angular and TypeScript best practices
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0. See the LICENSE file for details.
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* === Snackbar Styles for Error Handler Service === */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error notification snackbar styling
|
||||||
|
* - Red background for errors
|
||||||
|
* - White text for contrast
|
||||||
|
* - Displayed at top-center for 5 seconds
|
||||||
|
*/
|
||||||
|
.error-snackbar {
|
||||||
|
background-color: #f44336 !important;
|
||||||
|
color: white !important;
|
||||||
|
|
||||||
|
.mat-mdc-snack-bar-label,
|
||||||
|
.mdc-snackbar__label,
|
||||||
|
.mat-simple-snackbar-action {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-button {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Success notification snackbar styling
|
||||||
|
* - Green background for success messages
|
||||||
|
* - White text for contrast
|
||||||
|
* - Displayed at bottom-center for 3 seconds
|
||||||
|
*/
|
||||||
|
.success-snackbar {
|
||||||
|
background-color: #4caf50 !important;
|
||||||
|
color: white !important;
|
||||||
|
|
||||||
|
.mat-mdc-snack-bar-label,
|
||||||
|
.mdc-snackbar__label,
|
||||||
|
.mat-simple-snackbar-action {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-button {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Info notification snackbar styling
|
||||||
|
* - Blue background for informational messages
|
||||||
|
* - White text for contrast
|
||||||
|
* - Displayed at bottom-center for 4 seconds
|
||||||
|
*/
|
||||||
|
.info-snackbar {
|
||||||
|
background-color: #2196f3 !important;
|
||||||
|
color: white !important;
|
||||||
|
|
||||||
|
.mat-mdc-snack-bar-label,
|
||||||
|
.mdc-snackbar__label,
|
||||||
|
.mat-simple-snackbar-action {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-mdc-button {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
import { HttpErrorResponse } from '@angular/common/http';
|
||||||
|
import { Observable, throwError } from 'rxjs';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
|
export interface ErrorMessage {
|
||||||
|
title: string;
|
||||||
|
message: string;
|
||||||
|
action?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Centralized error handler service for consistent error messaging
|
||||||
|
* across the application. Converts HTTP errors into user-friendly
|
||||||
|
* messages and displays them through snackbar notifications.
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ErrorHandlerService {
|
||||||
|
constructor(
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
private router: Router
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle HTTP errors and show user-friendly messages
|
||||||
|
* @param error The HTTP error response
|
||||||
|
* @param context Optional context to provide more specific error messages
|
||||||
|
* @returns Observable that throws the original error
|
||||||
|
*/
|
||||||
|
handleError(error: HttpErrorResponse, context?: string): Observable<never> {
|
||||||
|
const errorMessage = this.getErrorMessage(error, context);
|
||||||
|
this.showError(errorMessage);
|
||||||
|
return throwError(() => error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user-friendly error message based on HTTP status
|
||||||
|
* @param error The HTTP error response
|
||||||
|
* @param context Optional context for more specific messages
|
||||||
|
* @returns ErrorMessage object with title, message, and action
|
||||||
|
*/
|
||||||
|
private getErrorMessage(error: HttpErrorResponse, context?: string): ErrorMessage {
|
||||||
|
if (error.error instanceof ErrorEvent) {
|
||||||
|
// Client-side or network error
|
||||||
|
return {
|
||||||
|
title: 'Connection Error',
|
||||||
|
message: 'Unable to connect to the server. Please check your internet connection.',
|
||||||
|
action: 'OK'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server-side error - Extract Fineract-specific error messages
|
||||||
|
const fineractError = error.error?.errors?.[0]?.defaultUserMessage;
|
||||||
|
const defaultMessage = error.error?.defaultUserMessage;
|
||||||
|
|
||||||
|
switch (error.status) {
|
||||||
|
case 400:
|
||||||
|
return {
|
||||||
|
title: 'Invalid Request',
|
||||||
|
message: fineractError || defaultMessage || 'Please check your input and try again.',
|
||||||
|
action: 'OK'
|
||||||
|
};
|
||||||
|
case 401:
|
||||||
|
return {
|
||||||
|
title: 'Unauthorized',
|
||||||
|
message: 'Your session has expired. Please log in again.',
|
||||||
|
action: 'Login'
|
||||||
|
};
|
||||||
|
case 403:
|
||||||
|
return {
|
||||||
|
title: 'Access Denied',
|
||||||
|
message: fineractError || defaultMessage || 'You do not have permission to perform this action.',
|
||||||
|
action: 'OK'
|
||||||
|
};
|
||||||
|
case 404:
|
||||||
|
return {
|
||||||
|
title: 'Not Found',
|
||||||
|
message: context
|
||||||
|
? `${context} not found.`
|
||||||
|
: fineractError || defaultMessage || 'The requested resource was not found.',
|
||||||
|
action: 'OK'
|
||||||
|
};
|
||||||
|
case 409:
|
||||||
|
return {
|
||||||
|
title: 'Conflict',
|
||||||
|
message: fineractError || defaultMessage || 'The resource already exists or there is a conflict.',
|
||||||
|
action: 'OK'
|
||||||
|
};
|
||||||
|
case 500:
|
||||||
|
return {
|
||||||
|
title: 'Server Error',
|
||||||
|
message: fineractError || defaultMessage || 'An unexpected error occurred. Please try again later.',
|
||||||
|
action: 'OK'
|
||||||
|
};
|
||||||
|
case 503:
|
||||||
|
return {
|
||||||
|
title: 'Service Unavailable',
|
||||||
|
message: 'The service is temporarily unavailable. Please try again later.',
|
||||||
|
action: 'OK'
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
title: 'Error',
|
||||||
|
message: fineractError || defaultMessage || 'An unexpected error occurred.',
|
||||||
|
action: 'OK'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show error message to user via snackbar
|
||||||
|
* @param errorMessage The error message object to display
|
||||||
|
*/
|
||||||
|
private showError(errorMessage: ErrorMessage): void {
|
||||||
|
const snackBarRef = this.snackBar.open(
|
||||||
|
`${errorMessage.title}: ${errorMessage.message}`,
|
||||||
|
errorMessage.action || 'Close',
|
||||||
|
{
|
||||||
|
duration: 5000,
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
verticalPosition: 'top',
|
||||||
|
panelClass: ['error-snackbar']
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Handle action button clicks (e.g., "Login" for 401 errors)
|
||||||
|
if (errorMessage.action === 'Login') {
|
||||||
|
snackBarRef.onAction().subscribe(() => {
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show success message to user via snackbar
|
||||||
|
* @param message The success message to display
|
||||||
|
* @param action Optional action button text (defaults to 'OK')
|
||||||
|
*/
|
||||||
|
showSuccess(message: string, action: string = 'OK'): void {
|
||||||
|
this.snackBar.open(message, action, {
|
||||||
|
duration: 3000,
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
verticalPosition: 'bottom',
|
||||||
|
panelClass: ['success-snackbar']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show info message to user via snackbar
|
||||||
|
* @param message The info message to display
|
||||||
|
* @param action Optional action button text (defaults to 'OK')
|
||||||
|
*/
|
||||||
|
showInfo(message: string, action: string = 'OK'): void {
|
||||||
|
this.snackBar.open(message, action, {
|
||||||
|
duration: 4000,
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
verticalPosition: 'bottom',
|
||||||
|
panelClass: ['info-snackbar']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,9 @@
|
|||||||
|
|
||||||
/* You can add global styles to this file, and also import other style files */
|
/* You can add global styles to this file, and also import other style files */
|
||||||
|
|
||||||
|
// Error Handler Service Styles
|
||||||
|
@use 'app/core/error-handler/error-handler.component';
|
||||||
|
|
||||||
.scrollbar-styling {
|
.scrollbar-styling {
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #889 #f1f1f1;
|
scrollbar-color: #889 #f1f1f1;
|
||||||
|
|||||||
Reference in New Issue
Block a user