From 08a6fe2bfa2ad937bc2e340ab47b35399857fed2 Mon Sep 17 00:00:00 2001 From: Swayambhu Date: Fri, 6 Feb 2026 09:44:02 +0530 Subject: [PATCH] refactor: migrate Ant Design notifications to use `App.useApp()` context via a new global provider. 1. notifications_manager.tsx - Hybrid notification approach: Added notificationInstance variable to store the context-based instance Added setNotificationInstance() function to inject the instance from context Created getNotification() helper that prefers context instance, falls back to static Added COMMON_NOTIFICATION_PROPS (exported) with showProgress: true and pauseOnHover: true All notification methods ( error , warning , info , success , fromBackend ) now spread COMMON_NOTIFICATION_PROPS 2. AntdGlobalProvider.tsx - New context provider: Wraps app with Antd's component Uses App.useApp() hook to get the context-based notification instance Injects it into NotificationManager via setNotificationInstance() --- ui/litellm-dashboard/src/app/layout.tsx | 6 ++- .../molecules/notifications_manager.tsx | 38 ++++++++++++------- .../src/contexts/AntdGlobalProvider.tsx | 25 ++++++++++++ 3 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 ui/litellm-dashboard/src/contexts/AntdGlobalProvider.tsx diff --git a/ui/litellm-dashboard/src/app/layout.tsx b/ui/litellm-dashboard/src/app/layout.tsx index 95c485fe2f0..1233da9046f 100644 --- a/ui/litellm-dashboard/src/app/layout.tsx +++ b/ui/litellm-dashboard/src/app/layout.tsx @@ -2,6 +2,8 @@ import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; +import AntdGlobalProvider from "@/contexts/AntdGlobalProvider"; + const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { @@ -17,7 +19,9 @@ export default function RootLayout({ }>) { return ( - {children} + + {children} + ); } diff --git a/ui/litellm-dashboard/src/components/molecules/notifications_manager.tsx b/ui/litellm-dashboard/src/components/molecules/notifications_manager.tsx index 71b14bee885..59b048b412c 100644 --- a/ui/litellm-dashboard/src/components/molecules/notifications_manager.tsx +++ b/ui/litellm-dashboard/src/components/molecules/notifications_manager.tsx @@ -1,8 +1,18 @@ import React from "react"; -import { notification } from "antd"; +import { notification as staticNotification } from "antd"; +import type { NotificationInstance } from "antd/es/notification/interface"; import { parseErrorMessage } from "../shared/errorUtils"; import { ArgsProps } from "antd/es/notification"; +let notificationInstance: NotificationInstance | null = null; + +export const setNotificationInstance = (instance: NotificationInstance) => { + notificationInstance = instance; +}; + +// Helper to get the best available notification instance +const getNotification = () => notificationInstance || staticNotification; + type Placement = "top" | "topLeft" | "topRight" | "bottom" | "bottomLeft" | "bottomRight"; type NotificationConfig = { @@ -251,7 +261,7 @@ function looksErrorPayload(input: any, status?: number): boolean { const NotificationManager = { error(input: string | NotificationConfig) { const cfg = normalize(input, "Error"); - notification.error({ + getNotification().error({ ...COMMON_NOTIFICATION_PROPS, ...cfg, placement: cfg.placement ?? defaultPlacement(), @@ -261,7 +271,7 @@ const NotificationManager = { warning(input: string | NotificationConfig) { const cfg = normalize(input, "Warning"); - notification.warning({ + getNotification().warning({ ...COMMON_NOTIFICATION_PROPS, ...cfg, placement: cfg.placement ?? defaultPlacement(), @@ -271,7 +281,7 @@ const NotificationManager = { info(input: string | NotificationConfig) { const cfg = normalize(input, "Info"); - notification.info({ + getNotification().info({ ...COMMON_NOTIFICATION_PROPS, ...cfg, placement: cfg.placement ?? defaultPlacement(), @@ -281,7 +291,7 @@ const NotificationManager = { success(input: string | React.ReactNode | NotificationConfig) { if (React.isValidElement(input)) { - notification.success({ + getNotification().success({ ...COMMON_NOTIFICATION_PROPS, message: "Success", description: input, @@ -291,7 +301,7 @@ const NotificationManager = { return; } const cfg = normalize(input as string | NotificationConfig, "Success"); - notification.success({ + getNotification().success({ ...COMMON_NOTIFICATION_PROPS, ...cfg, placement: cfg.placement ?? defaultPlacement(), @@ -316,11 +326,11 @@ const NotificationManager = { title === "Content Blocked" || title === "Integration Error" ) { - notification.warning({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 7 }); + getNotification().warning({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 7 }); return; } if (title === "Server Error") { - notification.error({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 8 }); + getNotification().error({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 8 }); return; } if ( @@ -331,10 +341,10 @@ const NotificationManager = { title === "Error" || title === "Already Exists" ) { - notification.error({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 6 }); + getNotification().error({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 6 }); return; } - notification.info({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 4 }); + getNotification().info({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 4 }); return; } @@ -343,18 +353,18 @@ const NotificationManager = { const payload = { ...base, message: cls?.title ?? "Info" }; if (cls?.kind === "success") { - notification.success({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 3.5 }); + getNotification().success({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 3.5 }); return; } if (cls?.kind === "warning") { - notification.warning({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 6 }); + getNotification().warning({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 6 }); return; } - notification.info({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 4 }); + getNotification().info({ ...COMMON_NOTIFICATION_PROPS, ...payload, duration: extra?.duration ?? 4 }); }, clear() { - notification.destroy(); + getNotification().destroy(); }, }; diff --git a/ui/litellm-dashboard/src/contexts/AntdGlobalProvider.tsx b/ui/litellm-dashboard/src/contexts/AntdGlobalProvider.tsx new file mode 100644 index 00000000000..b10aa715ed5 --- /dev/null +++ b/ui/litellm-dashboard/src/contexts/AntdGlobalProvider.tsx @@ -0,0 +1,25 @@ +"use client"; + +import React, { useEffect } from "react"; +import { App } from "antd"; +import { setNotificationInstance } from "@/components/molecules/notifications_manager"; + +// Inner component to use the hook +const AntdAppInit = () => { + const { notification } = App.useApp(); + + useEffect(() => { + setNotificationInstance(notification); + }, [notification]); + + return null; +}; + +export default function AntdGlobalProvider({ children }: { children: React.ReactNode }) { + return ( + + + {children} + + ); +}