57 lines
1.6 KiB
TypeScript
57 lines
1.6 KiB
TypeScript
|
|
import { toast } from "sonner";
|
||
|
|
import { ApiClientError } from "@/lib/api/client";
|
||
|
|
|
||
|
|
export type NotifyOptions = {
|
||
|
|
description?: string;
|
||
|
|
duration?: number;
|
||
|
|
};
|
||
|
|
|
||
|
|
function baseOptions(opts?: NotifyOptions) {
|
||
|
|
return {
|
||
|
|
description: opts?.description,
|
||
|
|
duration: opts?.duration ?? 4000,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Toast notifications — use for transient success/error/info across the app */
|
||
|
|
export const notify = {
|
||
|
|
success(message: string, opts?: NotifyOptions) {
|
||
|
|
toast.success(message, baseOptions(opts));
|
||
|
|
},
|
||
|
|
error(message: string, opts?: NotifyOptions) {
|
||
|
|
toast.error(message, { ...baseOptions(opts), duration: opts?.duration ?? 5500 });
|
||
|
|
},
|
||
|
|
warning(message: string, opts?: NotifyOptions) {
|
||
|
|
toast.warning(message, baseOptions(opts));
|
||
|
|
},
|
||
|
|
info(message: string, opts?: NotifyOptions) {
|
||
|
|
toast.info(message, baseOptions(opts));
|
||
|
|
},
|
||
|
|
loading(message: string) {
|
||
|
|
return toast.loading(message);
|
||
|
|
},
|
||
|
|
dismiss(id?: string | number) {
|
||
|
|
toast.dismiss(id);
|
||
|
|
},
|
||
|
|
promise<T>(
|
||
|
|
promise: Promise<T>,
|
||
|
|
messages: { loading: string; success: string; error?: string }
|
||
|
|
) {
|
||
|
|
return toast.promise(promise, {
|
||
|
|
loading: messages.loading,
|
||
|
|
success: messages.success,
|
||
|
|
error: messages.error ?? messages.loading,
|
||
|
|
});
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
export function getErrorMessage(err: unknown, fallback: string): string {
|
||
|
|
if (err instanceof ApiClientError) return err.message;
|
||
|
|
if (err instanceof Error && err.message) return err.message;
|
||
|
|
return fallback;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function notifyError(err: unknown, fallback: string) {
|
||
|
|
notify.error(getErrorMessage(err, fallback));
|
||
|
|
}
|