# 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); }); }); ```