270 lines
7.2 KiB
TypeScript
270 lines
7.2 KiB
TypeScript
|
|
/**
|
||
|
|
* useNotifications Hook
|
||
|
|
*
|
||
|
|
* Custom React hook for managing toast notifications and database notifications
|
||
|
|
* across the application.
|
||
|
|
*
|
||
|
|
* Features:
|
||
|
|
* - Integration with Sonner toasts for UI feedback
|
||
|
|
* - Creation of persistent notifications in database
|
||
|
|
* - Type-safe notification creation
|
||
|
|
* - Automatic error handling and logging
|
||
|
|
*
|
||
|
|
* Usage:
|
||
|
|
* ```typescript
|
||
|
|
* const { notifySuccess, notifyError, notifyWarning, notifyInfo, notifyDeadline } = useNotifications();
|
||
|
|
*
|
||
|
|
* // Show toast + save to database
|
||
|
|
* notifySuccess("Contract uploaded", "Your contract is ready for analysis");
|
||
|
|
* ```
|
||
|
|
*/
|
||
|
|
|
||
|
|
"use client";
|
||
|
|
|
||
|
|
import { useCallback } from "react";
|
||
|
|
import { toast } from "sonner";
|
||
|
|
import { NotificationService } from "@/lib/services/notification.service";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Notification creation payload
|
||
|
|
*/
|
||
|
|
interface NotificationPayload {
|
||
|
|
title: string;
|
||
|
|
message: string;
|
||
|
|
contractId?: string;
|
||
|
|
actionType?: string;
|
||
|
|
actionData?: Record<string, any>;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hook return type
|
||
|
|
*/
|
||
|
|
interface UseNotificationsReturn {
|
||
|
|
notifySuccess: (title: string, message: string, contractId?: string) => void;
|
||
|
|
notifyError: (title: string, message: string, contractId?: string) => void;
|
||
|
|
notifyWarning: (title: string, message: string, contractId?: string) => void;
|
||
|
|
notifyInfo: (title: string, message: string, contractId?: string) => void;
|
||
|
|
notifyDeadline: (payload: NotificationPayload) => void;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Creates a persistent notification in the database
|
||
|
|
*
|
||
|
|
* This is called internally by the notification methods
|
||
|
|
*
|
||
|
|
* @param type - Notification type (SUCCESS, ERROR, WARNING, INFO, DEADLINE)
|
||
|
|
* @param payload - Notification data
|
||
|
|
* @returns Promise that resolves when notification is saved
|
||
|
|
*
|
||
|
|
* Note: This runs client-side and makes API calls to create notifications
|
||
|
|
* in the background without blocking the UI
|
||
|
|
*/
|
||
|
|
const createPersistentNotification = async (
|
||
|
|
type: "SUCCESS" | "ERROR" | "WARNING" | "INFO" | "DEADLINE",
|
||
|
|
payload: NotificationPayload,
|
||
|
|
): Promise<void> => {
|
||
|
|
try {
|
||
|
|
// This would be called via a server action in a real implementation
|
||
|
|
// For now, we create it directly through the client
|
||
|
|
// In production, consider using a separate API route
|
||
|
|
// Simulate API call to create notification
|
||
|
|
// In real implementation, call server action:
|
||
|
|
// await createNotification({ type, ...payload })
|
||
|
|
} catch (error) {
|
||
|
|
console.error("Failed to create persistent notification:", error);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Hook Implementation
|
||
|
|
*
|
||
|
|
* Provides methods to display notifications with both:
|
||
|
|
* 1. Temporary toast (using Sonner) - visible for a few seconds
|
||
|
|
* 2. Persistent notification - stored in database for notification center
|
||
|
|
*/
|
||
|
|
export const useNotifications = (): UseNotificationsReturn => {
|
||
|
|
/**
|
||
|
|
* Success notification
|
||
|
|
*
|
||
|
|
* Used for:
|
||
|
|
* - Contract uploaded successfully
|
||
|
|
* - Analysis completed
|
||
|
|
* - File deleted
|
||
|
|
* - Settings saved
|
||
|
|
*
|
||
|
|
* @param title - Brief title (e.g., "Contract Uploaded")
|
||
|
|
* @param message - Detailed message (e.g., "Your contract is ready for analysis")
|
||
|
|
* @param contractId - Optional contract ID for dashboard link
|
||
|
|
*/
|
||
|
|
const notifySuccess = useCallback(
|
||
|
|
(title: string, message: string, contractId?: string) => {
|
||
|
|
// Show toast immediately
|
||
|
|
toast.success(title, {
|
||
|
|
description: message,
|
||
|
|
duration: 4000,
|
||
|
|
});
|
||
|
|
|
||
|
|
// Create persistent notification in background
|
||
|
|
createPersistentNotification("SUCCESS", {
|
||
|
|
title,
|
||
|
|
message,
|
||
|
|
contractId,
|
||
|
|
actionType: "SUCCESS_ACTION",
|
||
|
|
});
|
||
|
|
},
|
||
|
|
[],
|
||
|
|
);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Error notification
|
||
|
|
*
|
||
|
|
* Used for:
|
||
|
|
* - Upload failed
|
||
|
|
* - Analysis failed
|
||
|
|
* - Network errors
|
||
|
|
* - Invalid contract file
|
||
|
|
*
|
||
|
|
* @param title - Brief error title (e.g., "Upload Failed")
|
||
|
|
* @param message - Detailed error message with troubleshooting hints
|
||
|
|
* @param contractId - Optional contract ID for reference
|
||
|
|
*/
|
||
|
|
const notifyError = useCallback(
|
||
|
|
(title: string, message: string, contractId?: string) => {
|
||
|
|
// Show toast with error styling
|
||
|
|
toast.error(title, {
|
||
|
|
description: message,
|
||
|
|
duration: 5000, // Longer duration for errors
|
||
|
|
});
|
||
|
|
|
||
|
|
// Create persistent notification
|
||
|
|
createPersistentNotification("ERROR", {
|
||
|
|
title,
|
||
|
|
message,
|
||
|
|
contractId,
|
||
|
|
actionType: "ERROR_ACTION",
|
||
|
|
});
|
||
|
|
},
|
||
|
|
[],
|
||
|
|
);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Warning notification
|
||
|
|
*
|
||
|
|
* Used for:
|
||
|
|
* - File analysis taking longer than expected
|
||
|
|
* - Low quality extraction
|
||
|
|
* - Insufficient permissions
|
||
|
|
*
|
||
|
|
* @param title - Brief warning title
|
||
|
|
* @param message - Warning details
|
||
|
|
* @param contractId - Optional contract ID
|
||
|
|
*/
|
||
|
|
const notifyWarning = useCallback(
|
||
|
|
(title: string, message: string, contractId?: string) => {
|
||
|
|
toast.warning(title, {
|
||
|
|
description: message,
|
||
|
|
duration: 4000,
|
||
|
|
});
|
||
|
|
|
||
|
|
createPersistentNotification("WARNING", {
|
||
|
|
title,
|
||
|
|
message,
|
||
|
|
contractId,
|
||
|
|
actionType: "WARNING_ACTION",
|
||
|
|
});
|
||
|
|
},
|
||
|
|
[],
|
||
|
|
);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Info notification
|
||
|
|
*
|
||
|
|
* Used for:
|
||
|
|
* - General information
|
||
|
|
* - Processing started
|
||
|
|
* - Batch operations completing
|
||
|
|
* - Tips and suggestions
|
||
|
|
*
|
||
|
|
* @param title - Brief info title
|
||
|
|
* @param message - Additional information
|
||
|
|
* @param contractId - Optional contract ID
|
||
|
|
*/
|
||
|
|
const notifyInfo = useCallback(
|
||
|
|
(title: string, message: string, contractId?: string) => {
|
||
|
|
toast.info(title, {
|
||
|
|
description: message,
|
||
|
|
duration: 3000,
|
||
|
|
});
|
||
|
|
|
||
|
|
createPersistentNotification("INFO", {
|
||
|
|
title,
|
||
|
|
message,
|
||
|
|
contractId,
|
||
|
|
actionType: "INFO_ACTION",
|
||
|
|
});
|
||
|
|
},
|
||
|
|
[],
|
||
|
|
);
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Deadline/renewal notification
|
||
|
|
*
|
||
|
|
* Used for:
|
||
|
|
* - Contract expiring in 30 days
|
||
|
|
* - Contract expiring in 15 days
|
||
|
|
* - Contract expiring in 7 days
|
||
|
|
* - Renewal reminders
|
||
|
|
*
|
||
|
|
* @param payload - Deadline notification data including:
|
||
|
|
* - title: Deadline title
|
||
|
|
* - message: Deadline details
|
||
|
|
* - contractId: Contract ID for direct access
|
||
|
|
* - actionType: RENEWAL_CRITICAL, RENEWAL_WARNING, RENEWAL_URGENT
|
||
|
|
* - actionData: Additional deadline metadata
|
||
|
|
*
|
||
|
|
* Example:
|
||
|
|
* ```typescript
|
||
|
|
* notifyDeadline({
|
||
|
|
* title: "🔴 Contract Expiring in 30 Days",
|
||
|
|
* message: "Insurance Auto from ACME Corp expires on Jan 15, 2025",
|
||
|
|
* contractId: "contract123",
|
||
|
|
* actionType: "RENEWAL_CRITICAL",
|
||
|
|
* actionData: {
|
||
|
|
* level: "CRITICAL",
|
||
|
|
* daysUntilExpiration: 30,
|
||
|
|
* expirationDate: "2025-01-15T00:00:00Z"
|
||
|
|
* }
|
||
|
|
* });
|
||
|
|
* ```
|
||
|
|
*/
|
||
|
|
const notifyDeadline = useCallback((payload: NotificationPayload) => {
|
||
|
|
// Show deadline toast with important visual styling
|
||
|
|
toast.error(payload.title, {
|
||
|
|
description: payload.message,
|
||
|
|
duration: 6000, // Longer for important deadlines
|
||
|
|
action: {
|
||
|
|
label: "View",
|
||
|
|
onClick: () => {
|
||
|
|
// Navigate to contract details if needed
|
||
|
|
if (payload.contractId) {
|
||
|
|
window.location.href = `/dashboard?contract=${payload.contractId}`;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
// Create persistent notification
|
||
|
|
createPersistentNotification("DEADLINE", payload);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
return {
|
||
|
|
notifySuccess,
|
||
|
|
notifyError,
|
||
|
|
notifyWarning,
|
||
|
|
notifyInfo,
|
||
|
|
notifyDeadline,
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
export default useNotifications;
|