Improve the frontend 3
This commit is contained in:
192
frontend/src/utils/toast.ts
Normal file
192
frontend/src/utils/toast.ts
Normal file
@@ -0,0 +1,192 @@
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
/**
|
||||
* Centralized toast notification utility
|
||||
* Wraps react-hot-toast with consistent API and standardized behavior
|
||||
*/
|
||||
|
||||
export interface ToastOptions {
|
||||
/** Optional title for the toast (displayed above message) */
|
||||
title?: string;
|
||||
/** Custom duration in milliseconds (overrides default) */
|
||||
duration?: number;
|
||||
/** Toast ID for managing specific toasts */
|
||||
id?: string;
|
||||
}
|
||||
|
||||
const DEFAULT_DURATIONS = {
|
||||
success: 4000,
|
||||
error: 6000,
|
||||
warning: 5000,
|
||||
info: 4000,
|
||||
loading: 0, // infinite until dismissed
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Show a success toast notification
|
||||
* @param message - The message to display (can be translation key or direct string)
|
||||
* @param options - Optional configuration
|
||||
*/
|
||||
const success = (message: string, options?: ToastOptions): string => {
|
||||
const duration = options?.duration ?? DEFAULT_DURATIONS.success;
|
||||
|
||||
const fullMessage = options?.title
|
||||
? `${options.title}\n${message}`
|
||||
: message;
|
||||
|
||||
return toast.success(fullMessage, {
|
||||
duration,
|
||||
id: options?.id,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show an error toast notification
|
||||
* @param message - The error message to display
|
||||
* @param options - Optional configuration
|
||||
*/
|
||||
const error = (message: string, options?: ToastOptions): string => {
|
||||
const duration = options?.duration ?? DEFAULT_DURATIONS.error;
|
||||
|
||||
const fullMessage = options?.title
|
||||
? `${options.title}\n${message}`
|
||||
: message;
|
||||
|
||||
return toast.error(fullMessage, {
|
||||
duration,
|
||||
id: options?.id,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a warning toast notification
|
||||
* @param message - The warning message to display
|
||||
* @param options - Optional configuration
|
||||
*/
|
||||
const warning = (message: string, options?: ToastOptions): string => {
|
||||
const duration = options?.duration ?? DEFAULT_DURATIONS.warning;
|
||||
|
||||
const fullMessage = options?.title
|
||||
? `${options.title}\n${message}`
|
||||
: message;
|
||||
|
||||
return toast(fullMessage, {
|
||||
duration,
|
||||
id: options?.id,
|
||||
icon: '⚠️',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show an info toast notification
|
||||
* @param message - The info message to display
|
||||
* @param options - Optional configuration
|
||||
*/
|
||||
const info = (message: string, options?: ToastOptions): string => {
|
||||
const duration = options?.duration ?? DEFAULT_DURATIONS.info;
|
||||
|
||||
const fullMessage = options?.title
|
||||
? `${options.title}\n${message}`
|
||||
: message;
|
||||
|
||||
return toast(fullMessage, {
|
||||
duration,
|
||||
id: options?.id,
|
||||
icon: 'ℹ️',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a loading toast notification
|
||||
* @param message - The loading message to display
|
||||
* @param options - Optional configuration
|
||||
*/
|
||||
const loading = (message: string, options?: ToastOptions): string => {
|
||||
const duration = options?.duration ?? DEFAULT_DURATIONS.loading;
|
||||
|
||||
const fullMessage = options?.title
|
||||
? `${options.title}\n${message}`
|
||||
: message;
|
||||
|
||||
return toast.loading(fullMessage, {
|
||||
duration,
|
||||
id: options?.id,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Dismiss a specific toast by ID
|
||||
* @param toastId - The ID of the toast to dismiss
|
||||
*/
|
||||
const dismiss = (toastId?: string): void => {
|
||||
toast.dismiss(toastId);
|
||||
};
|
||||
|
||||
/**
|
||||
* Show a promise toast that updates based on promise state
|
||||
* Useful for async operations
|
||||
*/
|
||||
const promise = <T,>(
|
||||
promise: Promise<T>,
|
||||
messages: {
|
||||
loading: string;
|
||||
success: string | ((data: T) => string);
|
||||
error: string | ((error: Error) => string);
|
||||
},
|
||||
options?: ToastOptions
|
||||
): Promise<T> => {
|
||||
return toast.promise(
|
||||
promise,
|
||||
{
|
||||
loading: messages.loading,
|
||||
success: messages.success,
|
||||
error: messages.error,
|
||||
},
|
||||
{
|
||||
success: {
|
||||
duration: options?.duration ?? DEFAULT_DURATIONS.success,
|
||||
},
|
||||
error: {
|
||||
duration: options?.duration ?? DEFAULT_DURATIONS.error,
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Unified toast notification utility
|
||||
* Use this instead of importing react-hot-toast directly
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { showToast } from '@/utils/toast';
|
||||
*
|
||||
* // Simple success
|
||||
* showToast.success('Operation completed');
|
||||
*
|
||||
* // Error with title
|
||||
* showToast.error('Failed to save', { title: 'Error' });
|
||||
*
|
||||
* // Promise-based
|
||||
* showToast.promise(
|
||||
* apiCall(),
|
||||
* {
|
||||
* loading: 'Saving...',
|
||||
* success: 'Saved successfully',
|
||||
* error: 'Failed to save'
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
export const showToast = {
|
||||
success,
|
||||
error,
|
||||
warning,
|
||||
info,
|
||||
loading,
|
||||
dismiss,
|
||||
promise,
|
||||
};
|
||||
|
||||
// Re-export toast for advanced use cases (custom toasts, etc.)
|
||||
export { toast };
|
||||
Reference in New Issue
Block a user