diff --git a/clients/ui/frontend/src/index.tsx b/clients/ui/frontend/src/index.tsx index 69cb0727..5a3c1e65 100644 --- a/clients/ui/frontend/src/index.tsx +++ b/clients/ui/frontend/src/index.tsx @@ -6,6 +6,7 @@ import App from './app/App'; import { BrowserStorageContextProvider } from './shared/components/browserStorage/BrowserStorageContext'; import { NotificationContextProvider } from './app/context/NotificationContext'; import { NamespaceSelectorContextProvider } from './shared/context/NamespaceSelectorContext'; +import DashboardScriptLoader from './shared/context/DashboardScriptLoader'; const theme = createTheme({ cssVariables: true }); const root = ReactDOM.createRoot(document.getElementById('root')!); @@ -16,9 +17,11 @@ root.render( - - - + + + + + diff --git a/clients/ui/frontend/src/shared/api/apiUtils.ts b/clients/ui/frontend/src/shared/api/apiUtils.ts index 19d831ff..c61ff3f5 100644 --- a/clients/ui/frontend/src/shared/api/apiUtils.ts +++ b/clients/ui/frontend/src/shared/api/apiUtils.ts @@ -189,8 +189,3 @@ export const isModelRegistryResponse = (response: unknown): response is Model export const assembleModelRegistryBody = (data: T): ModelRegistryBody => ({ data, }); - -export const getNamespaceQueryParam = (): string | null => { - const params = new URLSearchParams(window.location.search); - return params.get('ns'); -}; diff --git a/clients/ui/frontend/src/shared/context/DashboardScriptLoader.tsx b/clients/ui/frontend/src/shared/context/DashboardScriptLoader.tsx new file mode 100644 index 00000000..0acd3d57 --- /dev/null +++ b/clients/ui/frontend/src/shared/context/DashboardScriptLoader.tsx @@ -0,0 +1,57 @@ +import React, { useEffect, useState } from 'react'; +import { Bullseye, Spinner } from '@patternfly/react-core'; +import { isIntegrated } from '~/shared/utilities/const'; + +type DashboardScriptLoaderProps = { + children: React.ReactNode; +}; + +const loadScript = (src: string, onLoad: () => void, onError: () => void) => { + const script = document.createElement('script'); + script.src = src; + script.async = true; + script.onload = onLoad; + script.onerror = onError; + document.head.appendChild(script); +}; + +/* eslint-disable no-console */ +const DashboardScriptLoader: React.FC = ({ children }) => { + const [scriptLoaded, setScriptLoaded] = useState(false); + + useEffect(() => { + const scriptUrl = '/dashboard_lib.bundle.js'; + + if (!isIntegrated()) { + console.warn( + 'DashboardScriptLoader: Script not loaded because deployment mode is not integrated', + ); + setScriptLoaded(true); + return; + } + + fetch(scriptUrl, { method: 'HEAD' }) + .then((response) => { + if (response.ok) { + loadScript( + scriptUrl, + () => setScriptLoaded(true), + () => console.error('Failed to load the script'), + ); + } else { + console.warn('Script not found'); + } + }) + .catch((error) => console.error('Error checking script existence', error)); + }, []); + + return !scriptLoaded ? ( + + + + ) : ( + <>{children} + ); +}; + +export default DashboardScriptLoader; diff --git a/clients/ui/frontend/src/shared/context/NamespaceSelectorContext.tsx b/clients/ui/frontend/src/shared/context/NamespaceSelectorContext.tsx index 372e9372..610052a3 100644 --- a/clients/ui/frontend/src/shared/context/NamespaceSelectorContext.tsx +++ b/clients/ui/frontend/src/shared/context/NamespaceSelectorContext.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import useNamespaces from '~/shared/hooks/useNamespaces'; import { Namespace } from '~/shared/types'; +import { isIntegrated } from '~/shared/utilities/const'; export type NamespaceSelectorContextType = { namespacesLoaded: boolean; @@ -31,6 +32,13 @@ export const NamespaceSelectorContextProvider: React.FC ); +declare global { + interface Window { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + centraldashboard: any; + } +} + const EnabledNamespaceSelectorContextProvider: React.FC = ({ children, }) => { @@ -40,6 +48,24 @@ const EnabledNamespaceSelectorContextProvider: React.FC 0 ? namespaces[0] : null; + React.useEffect(() => { + if (isIntegrated()) { + // Initialize the central dashboard client + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + window.centraldashboard.CentralDashboardEventHandler.init((cdeh: any) => { + // eslint-disable-next-line no-param-reassign + cdeh.onNamespaceSelected = (newNamespace: string) => { + setPreferredNamespace({ name: newNamespace }); + }; + }); + } catch (err) { + /* eslint-disable no-console */ + console.error('Failed to initialize central dashboard client', err); + } + } + }, []); + const contextValue = React.useMemo( () => ({ namespacesLoaded: isLoaded, diff --git a/clients/ui/frontend/src/shared/hooks/useQueryParamNamespaces.ts b/clients/ui/frontend/src/shared/hooks/useQueryParamNamespaces.ts index e24ce3de..23f4900d 100644 --- a/clients/ui/frontend/src/shared/hooks/useQueryParamNamespaces.ts +++ b/clients/ui/frontend/src/shared/hooks/useQueryParamNamespaces.ts @@ -1,12 +1,10 @@ import React from 'react'; import { NamespaceSelectorContext } from '~/shared/context/NamespaceSelectorContext'; -import { isStandalone } from '~/shared/utilities/const'; -import { getNamespaceQueryParam } from '~/shared/api/apiUtils'; import { useDeepCompareMemoize } from '~/shared/utilities/useDeepCompareMemoize'; const useQueryParamNamespaces = (): Record => { const { preferredNamespace: namespaceSelector } = React.useContext(NamespaceSelectorContext); - const namespace = isStandalone() ? namespaceSelector?.name : getNamespaceQueryParam(); + const namespace = namespaceSelector?.name; return useDeepCompareMemoize({ namespace }); };