add notifiaction handling, error handling
This commit is contained in:
parent
ce708f5209
commit
1af288aa62
21 changed files with 4864 additions and 224 deletions
609
frontend/documentation/stores/notifications.md
Normal file
609
frontend/documentation/stores/notifications.md
Normal file
|
|
@ -0,0 +1,609 @@
|
|||
# Notifications Store
|
||||
|
||||
The notifications store provides centralized state management for application-wide notifications. It handles the creation, management, and lifecycle of toast-style notifications with support for multiple types, positions, and interactive actions.
|
||||
|
||||
## Overview
|
||||
|
||||
- **Location**: `src/stores/notifications.js`
|
||||
- **Type**: Pinia Store
|
||||
- **Purpose**: Global notification state management
|
||||
- **Integration**: Used by NotificationDisplay component
|
||||
|
||||
## Installation & Setup
|
||||
|
||||
```javascript
|
||||
// Import in your component
|
||||
import { useNotificationStore } from "@/stores/notifications";
|
||||
|
||||
// Use in component
|
||||
const notificationStore = useNotificationStore();
|
||||
```
|
||||
|
||||
## State Structure
|
||||
|
||||
### Core State Properties
|
||||
|
||||
```javascript
|
||||
state: {
|
||||
notifications: [], // Array of active notifications
|
||||
defaultDuration: 4000, // Default auto-dismiss time (ms)
|
||||
maxNotifications: 5, // Maximum concurrent notifications
|
||||
position: 'top-right', // Default position
|
||||
nextId: 1 // Auto-incrementing ID counter
|
||||
}
|
||||
```
|
||||
|
||||
### Notification Object Structure
|
||||
|
||||
```javascript
|
||||
{
|
||||
id: 1, // Unique identifier
|
||||
type: 'success', // 'success' | 'error' | 'warning' | 'info'
|
||||
title: 'Operation Complete', // Notification title
|
||||
message: 'Data saved successfully', // Main message
|
||||
duration: 4000, // Auto-dismiss time (0 = no auto-dismiss)
|
||||
persistent: false, // If true, won't auto-dismiss
|
||||
actions: [], // Array of action buttons
|
||||
data: null, // Additional data for handlers
|
||||
timestamp: '2025-11-12T10:30:00Z', // Creation timestamp
|
||||
dismissed: false, // Whether notification is dismissed
|
||||
seen: false // Whether user has interacted with it
|
||||
}
|
||||
```
|
||||
|
||||
## Getters
|
||||
|
||||
### `getNotificationsByType(type)`
|
||||
|
||||
Get all notifications of a specific type.
|
||||
|
||||
```javascript
|
||||
const errorNotifications = notificationStore.getNotificationsByType("error");
|
||||
```
|
||||
|
||||
### `activeNotifications`
|
||||
|
||||
Get all non-dismissed notifications.
|
||||
|
||||
```javascript
|
||||
const active = notificationStore.activeNotifications;
|
||||
```
|
||||
|
||||
### `activeCount`
|
||||
|
||||
Get count of active notifications.
|
||||
|
||||
```javascript
|
||||
const count = notificationStore.activeCount;
|
||||
```
|
||||
|
||||
### `hasErrorNotifications`
|
||||
|
||||
Check if there are any active error notifications.
|
||||
|
||||
```javascript
|
||||
const hasErrors = notificationStore.hasErrorNotifications;
|
||||
```
|
||||
|
||||
### `hasSuccessNotifications`
|
||||
|
||||
Check if there are any active success notifications.
|
||||
|
||||
```javascript
|
||||
const hasSuccess = notificationStore.hasSuccessNotifications;
|
||||
```
|
||||
|
||||
## Actions
|
||||
|
||||
### Core Notification Methods
|
||||
|
||||
#### `addNotification(notification)`
|
||||
|
||||
Add a new notification with full configuration options.
|
||||
|
||||
```javascript
|
||||
const notificationId = notificationStore.addNotification({
|
||||
type: "warning",
|
||||
title: "Confirm Action",
|
||||
message: "This will permanently delete the item.",
|
||||
persistent: true,
|
||||
actions: [
|
||||
{
|
||||
label: "Delete",
|
||||
variant: "danger",
|
||||
handler: () => performDelete(),
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
variant: "secondary",
|
||||
},
|
||||
],
|
||||
data: { itemId: 123 },
|
||||
});
|
||||
```
|
||||
|
||||
#### Convenience Methods
|
||||
|
||||
```javascript
|
||||
// Quick success notification
|
||||
notificationStore.addSuccess("Operation completed!");
|
||||
notificationStore.addSuccess("Custom message", "Custom Title", {
|
||||
duration: 6000,
|
||||
});
|
||||
|
||||
// Quick error notification
|
||||
notificationStore.addError("Something went wrong!");
|
||||
notificationStore.addError("Custom error", "Error Title", { persistent: true });
|
||||
|
||||
// Quick warning notification
|
||||
notificationStore.addWarning("Please confirm this action");
|
||||
|
||||
// Quick info notification
|
||||
notificationStore.addInfo("New feature available");
|
||||
```
|
||||
|
||||
### Notification Management
|
||||
|
||||
#### `dismissNotification(id)`
|
||||
|
||||
Mark a notification as dismissed (hides it but keeps in history).
|
||||
|
||||
```javascript
|
||||
notificationStore.dismissNotification(notificationId);
|
||||
```
|
||||
|
||||
#### `removeNotification(id)`
|
||||
|
||||
Completely remove a notification from the store.
|
||||
|
||||
```javascript
|
||||
notificationStore.removeNotification(notificationId);
|
||||
```
|
||||
|
||||
#### `markAsSeen(id)`
|
||||
|
||||
Mark a notification as seen (user has interacted with it).
|
||||
|
||||
```javascript
|
||||
notificationStore.markAsSeen(notificationId);
|
||||
```
|
||||
|
||||
#### `updateNotification(id, updates)`
|
||||
|
||||
Update an existing notification's properties.
|
||||
|
||||
```javascript
|
||||
notificationStore.updateNotification(notificationId, {
|
||||
type: "success",
|
||||
message: "Updated message",
|
||||
persistent: false,
|
||||
});
|
||||
```
|
||||
|
||||
### Bulk Operations
|
||||
|
||||
#### `clearType(type)`
|
||||
|
||||
Remove all notifications of a specific type.
|
||||
|
||||
```javascript
|
||||
notificationStore.clearType("error"); // Remove all error notifications
|
||||
```
|
||||
|
||||
#### `clearAll()`
|
||||
|
||||
Remove all notifications.
|
||||
|
||||
```javascript
|
||||
notificationStore.clearAll();
|
||||
```
|
||||
|
||||
#### `clearDismissed()`
|
||||
|
||||
Remove all dismissed notifications from history.
|
||||
|
||||
```javascript
|
||||
notificationStore.clearDismissed();
|
||||
```
|
||||
|
||||
### Loading Notifications
|
||||
|
||||
#### `showLoadingNotification(message, title)`
|
||||
|
||||
Show a persistent loading notification that can be updated later.
|
||||
|
||||
```javascript
|
||||
const loadingId = notificationStore.showLoadingNotification(
|
||||
"Uploading file...",
|
||||
"Please Wait",
|
||||
);
|
||||
```
|
||||
|
||||
#### `updateToSuccess(id, message, title)`
|
||||
|
||||
Update a loading notification to success and auto-dismiss.
|
||||
|
||||
```javascript
|
||||
notificationStore.updateToSuccess(
|
||||
loadingId,
|
||||
"File uploaded successfully!",
|
||||
"Upload Complete",
|
||||
);
|
||||
```
|
||||
|
||||
#### `updateToError(id, message, title)`
|
||||
|
||||
Update a loading notification to error and auto-dismiss.
|
||||
|
||||
```javascript
|
||||
notificationStore.updateToError(
|
||||
loadingId,
|
||||
"Upload failed. Please try again.",
|
||||
"Upload Failed",
|
||||
);
|
||||
```
|
||||
|
||||
### API Integration Helpers
|
||||
|
||||
#### `showApiSuccess(operation, customMessage)`
|
||||
|
||||
Show standardized success notifications for API operations.
|
||||
|
||||
```javascript
|
||||
// Uses default messages based on operation
|
||||
notificationStore.showApiSuccess("create"); // "Item created successfully"
|
||||
notificationStore.showApiSuccess("update"); // "Item updated successfully"
|
||||
notificationStore.showApiSuccess("delete"); // "Item deleted successfully"
|
||||
notificationStore.showApiSuccess("fetch"); // "Data loaded successfully"
|
||||
|
||||
// With custom message
|
||||
notificationStore.showApiSuccess("create", "New client added successfully!");
|
||||
```
|
||||
|
||||
#### `showApiError(operation, error, customMessage)`
|
||||
|
||||
Show standardized error notifications for API operations.
|
||||
|
||||
```javascript
|
||||
// Automatically extracts error message from different error formats
|
||||
notificationStore.showApiError("create", apiError);
|
||||
notificationStore.showApiError("update", "Network timeout occurred");
|
||||
notificationStore.showApiError("delete", errorObject, "Custom error message");
|
||||
```
|
||||
|
||||
### Configuration Methods
|
||||
|
||||
#### `setPosition(position)`
|
||||
|
||||
Set the global notification position.
|
||||
|
||||
```javascript
|
||||
// Available positions:
|
||||
notificationStore.setPosition("top-right"); // Default
|
||||
notificationStore.setPosition("top-left");
|
||||
notificationStore.setPosition("top-center");
|
||||
notificationStore.setPosition("bottom-right");
|
||||
notificationStore.setPosition("bottom-left");
|
||||
notificationStore.setPosition("bottom-center");
|
||||
```
|
||||
|
||||
#### `setDefaultDuration(duration)`
|
||||
|
||||
Set the default auto-dismiss duration (milliseconds).
|
||||
|
||||
```javascript
|
||||
notificationStore.setDefaultDuration(5000); // 5 seconds
|
||||
```
|
||||
|
||||
#### `setMaxNotifications(max)`
|
||||
|
||||
Set maximum number of concurrent notifications.
|
||||
|
||||
```javascript
|
||||
notificationStore.setMaxNotifications(3);
|
||||
```
|
||||
|
||||
### Advanced Workflow Helper
|
||||
|
||||
#### `withNotifications(operation, asyncFunction, options)`
|
||||
|
||||
Wrap an async operation with automatic loading/success/error notifications.
|
||||
|
||||
```javascript
|
||||
const result = await notificationStore.withNotifications(
|
||||
"save",
|
||||
async () => {
|
||||
return await saveDataToApi();
|
||||
},
|
||||
{
|
||||
loadingMessage: "Saving changes...",
|
||||
successMessage: "Changes saved successfully!",
|
||||
errorMessage: null, // Use default error handling
|
||||
showLoading: true,
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
## Action Button Configuration
|
||||
|
||||
### Action Object Structure
|
||||
|
||||
```javascript
|
||||
{
|
||||
label: 'Button Text', // Required: Button label
|
||||
variant: 'primary', // Optional: 'primary' | 'danger' | 'secondary'
|
||||
icon: 'mdi mdi-check', // Optional: Icon class
|
||||
handler: (notification) => {}, // Optional: Click handler function
|
||||
dismissAfter: true // Optional: Auto-dismiss after click (default: true)
|
||||
}
|
||||
```
|
||||
|
||||
### Action Examples
|
||||
|
||||
```javascript
|
||||
notificationStore.addNotification({
|
||||
type: "warning",
|
||||
title: "Unsaved Changes",
|
||||
message: "You have unsaved changes. What would you like to do?",
|
||||
persistent: true,
|
||||
actions: [
|
||||
{
|
||||
label: "Save",
|
||||
variant: "primary",
|
||||
icon: "mdi mdi-content-save",
|
||||
handler: (notification) => {
|
||||
saveChanges();
|
||||
// Access notification data if needed
|
||||
console.log("Saving from notification:", notification.data);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Discard",
|
||||
variant: "danger",
|
||||
icon: "mdi mdi-delete",
|
||||
handler: () => {
|
||||
discardChanges();
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Keep Editing",
|
||||
variant: "secondary",
|
||||
dismissAfter: false, // Don't dismiss, let user continue editing
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Usage Patterns
|
||||
|
||||
### Basic Notifications
|
||||
|
||||
```javascript
|
||||
const notificationStore = useNotificationStore();
|
||||
|
||||
// Simple success
|
||||
notificationStore.addSuccess("Data saved successfully!");
|
||||
|
||||
// Simple error
|
||||
notificationStore.addError("Failed to connect to server");
|
||||
|
||||
// Custom duration
|
||||
notificationStore.addWarning("Session expires in 5 minutes", "Warning", {
|
||||
duration: 8000,
|
||||
});
|
||||
```
|
||||
|
||||
### API Operation Notifications
|
||||
|
||||
```javascript
|
||||
// Method 1: Manual handling
|
||||
try {
|
||||
await apiCall();
|
||||
notificationStore.addSuccess("Operation completed successfully!");
|
||||
} catch (error) {
|
||||
notificationStore.addError(error.message, "Operation Failed");
|
||||
}
|
||||
|
||||
// Method 2: Using API helpers
|
||||
try {
|
||||
await apiCall();
|
||||
notificationStore.showApiSuccess("create");
|
||||
} catch (error) {
|
||||
notificationStore.showApiError("create", error);
|
||||
}
|
||||
|
||||
// Method 3: Using workflow helper
|
||||
await notificationStore.withNotifications("create", async () => {
|
||||
return await apiCall();
|
||||
});
|
||||
```
|
||||
|
||||
### Loading States
|
||||
|
||||
```javascript
|
||||
// Show loading notification
|
||||
const loadingId = notificationStore.showLoadingNotification("Processing...");
|
||||
|
||||
try {
|
||||
const result = await longRunningOperation();
|
||||
notificationStore.updateToSuccess(loadingId, "Operation completed!");
|
||||
} catch (error) {
|
||||
notificationStore.updateToError(loadingId, "Operation failed");
|
||||
}
|
||||
```
|
||||
|
||||
### Interactive Notifications
|
||||
|
||||
```javascript
|
||||
// Confirmation dialog
|
||||
notificationStore.addNotification({
|
||||
type: "warning",
|
||||
title: "Delete Confirmation",
|
||||
message: `Are you sure you want to delete "${itemName}"?`,
|
||||
persistent: true,
|
||||
actions: [
|
||||
{
|
||||
label: "Yes, Delete",
|
||||
variant: "danger",
|
||||
handler: async () => {
|
||||
const deleteId =
|
||||
notificationStore.showLoadingNotification("Deleting...");
|
||||
try {
|
||||
await deleteItem(itemId);
|
||||
notificationStore.updateToSuccess(
|
||||
deleteId,
|
||||
"Item deleted successfully",
|
||||
);
|
||||
refreshData();
|
||||
} catch (error) {
|
||||
notificationStore.updateToError(deleteId, "Failed to delete item");
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
variant: "secondary",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// Multi-step workflow
|
||||
notificationStore.addNotification({
|
||||
type: "info",
|
||||
title: "Export Complete",
|
||||
message: "Your data has been exported successfully.",
|
||||
actions: [
|
||||
{
|
||||
label: "Download",
|
||||
variant: "primary",
|
||||
icon: "mdi mdi-download",
|
||||
handler: () => downloadFile(),
|
||||
},
|
||||
{
|
||||
label: "Email",
|
||||
variant: "secondary",
|
||||
icon: "mdi mdi-email",
|
||||
handler: () => emailFile(),
|
||||
},
|
||||
{
|
||||
label: "View",
|
||||
variant: "secondary",
|
||||
icon: "mdi mdi-eye",
|
||||
handler: () => viewFile(),
|
||||
},
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
## Integration with Vue Components
|
||||
|
||||
### In Composition API
|
||||
|
||||
```javascript
|
||||
import { useNotificationStore } from "@/stores/notifications";
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const notificationStore = useNotificationStore();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await submitForm();
|
||||
notificationStore.addSuccess("Form submitted successfully!");
|
||||
} catch (error) {
|
||||
notificationStore.addError("Failed to submit form", "Submission Error");
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
handleSubmit,
|
||||
};
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### In Options API
|
||||
|
||||
```javascript
|
||||
import { mapActions, mapGetters } from "pinia";
|
||||
import { useNotificationStore } from "@/stores/notifications";
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters(useNotificationStore, [
|
||||
"activeCount",
|
||||
"hasErrorNotifications",
|
||||
]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useNotificationStore, ["addSuccess", "addError", "clearAll"]),
|
||||
|
||||
async saveData() {
|
||||
try {
|
||||
await this.apiCall();
|
||||
this.addSuccess("Data saved!");
|
||||
} catch (error) {
|
||||
this.addError(error.message);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Do's
|
||||
|
||||
- ✅ Use appropriate notification types for different scenarios
|
||||
- ✅ Keep messages concise and actionable
|
||||
- ✅ Use loading notifications for long-running operations
|
||||
- ✅ Provide clear action buttons for next steps
|
||||
- ✅ Set appropriate durations (longer for errors, shorter for success)
|
||||
- ✅ Use persistent notifications for critical actions requiring user input
|
||||
|
||||
### Don'ts
|
||||
|
||||
- ❌ Don't overwhelm users with too many notifications
|
||||
- ❌ Don't use persistent notifications for simple confirmations
|
||||
- ❌ Don't make notification messages too long
|
||||
- ❌ Don't forget to handle loading state cleanup
|
||||
- ❌ Don't use success notifications for every small action
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
- The store automatically limits concurrent notifications
|
||||
- Dismissed notifications are kept in history for debugging
|
||||
- Use `clearDismissed()` periodically to prevent memory leaks
|
||||
- Action handlers should be lightweight to avoid blocking the UI
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Testing
|
||||
|
||||
```javascript
|
||||
import { setActivePinia, createPinia } from "pinia";
|
||||
import { useNotificationStore } from "@/stores/notifications";
|
||||
|
||||
describe("Notifications Store", () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia());
|
||||
});
|
||||
|
||||
it("adds notifications correctly", () => {
|
||||
const store = useNotificationStore();
|
||||
const id = store.addSuccess("Test message");
|
||||
|
||||
expect(store.activeNotifications).toHaveLength(1);
|
||||
expect(store.activeNotifications[0].message).toBe("Test message");
|
||||
expect(store.activeNotifications[0].type).toBe("success");
|
||||
});
|
||||
|
||||
it("dismisses notifications", () => {
|
||||
const store = useNotificationStore();
|
||||
const id = store.addInfo("Test");
|
||||
|
||||
store.dismissNotification(id);
|
||||
expect(store.activeNotifications).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue