diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index cea3da359..6e9f72ace 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -7,6 +7,7 @@ import { ToolWorkflowProvider } from "./contexts/ToolWorkflowContext"; import { HotkeyProvider } from "./contexts/HotkeyContext"; import { SidebarProvider } from "./contexts/SidebarContext"; import { PreferencesProvider } from "./contexts/PreferencesContext"; +import { AppConfigProvider } from "./contexts/AppConfigContext"; import ErrorBoundary from "./components/shared/ErrorBoundary"; import HomePage from "./pages/HomePage"; import { useScarfTracking } from "./hooks/useScarfTracking"; @@ -38,16 +39,21 @@ const LoadingFallback = () => ( ); -export default function App() { - // Initialize scarf tracking (mounts once at app startup) +// Component to initialize scarf tracking (must be inside AppConfigProvider) +function ScarfTrackingInitializer() { useScarfTracking(); + return null; +} +export default function App() { return ( }> - - + + + + @@ -67,6 +73,7 @@ export default function App() { + diff --git a/frontend/src/components/layout/Workbench.tsx b/frontend/src/components/layout/Workbench.tsx index f2ea13d21..d7c46a8d0 100644 --- a/frontend/src/components/layout/Workbench.tsx +++ b/frontend/src/components/layout/Workbench.tsx @@ -6,7 +6,7 @@ import { useFileHandler } from '../../hooks/useFileHandler'; import { useFileState } from '../../contexts/FileContext'; import { useNavigationState, useNavigationActions } from '../../contexts/NavigationContext'; import { useViewer } from '../../contexts/ViewerContext'; -import { useAppConfig } from '../../hooks/useAppConfig'; +import { useAppConfig } from '../../contexts/AppConfigContext'; import './Workbench.css'; import TopControls from '../shared/TopControls'; diff --git a/frontend/src/components/shared/QuickAccessBar.tsx b/frontend/src/components/shared/QuickAccessBar.tsx index 7a8dd7fdd..dc0623cc1 100644 --- a/frontend/src/components/shared/QuickAccessBar.tsx +++ b/frontend/src/components/shared/QuickAccessBar.tsx @@ -13,7 +13,7 @@ import './quickAccessBar/QuickAccessBar.css'; import AllToolsNavButton from './AllToolsNavButton'; import ActiveToolButton from "./quickAccessBar/ActiveToolButton"; import AppConfigModal from './AppConfigModal'; -import { useAppConfig } from '../../hooks/useAppConfig'; +import { useAppConfig } from '../../contexts/AppConfigContext'; import { isNavButtonActive, getNavButtonStyle, diff --git a/frontend/src/components/shared/config/configSections/Overview.tsx b/frontend/src/components/shared/config/configSections/Overview.tsx index d3f250f49..7d6e2f543 100644 --- a/frontend/src/components/shared/config/configSections/Overview.tsx +++ b/frontend/src/components/shared/config/configSections/Overview.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { Stack, Text, Code, Group, Badge, Alert, Loader } from '@mantine/core'; -import { useAppConfig } from '../../../../hooks/useAppConfig'; +import { useAppConfig } from '../../../../contexts/AppConfigContext'; const Overview: React.FC = () => { const { config, loading, error } = useAppConfig(); diff --git a/frontend/src/components/tools/certSign/CertificateTypeSettings.tsx b/frontend/src/components/tools/certSign/CertificateTypeSettings.tsx index 2d514a161..fde6aa146 100644 --- a/frontend/src/components/tools/certSign/CertificateTypeSettings.tsx +++ b/frontend/src/components/tools/certSign/CertificateTypeSettings.tsx @@ -1,6 +1,6 @@ import { Stack, Button } from "@mantine/core"; import { CertSignParameters } from "../../../hooks/tools/certSign/useCertSignParameters"; -import { useAppConfig } from "../../../hooks/useAppConfig"; +import { useAppConfig } from "../../../contexts/AppConfigContext"; interface CertificateTypeSettingsProps { parameters: CertSignParameters; diff --git a/frontend/src/constants/app.ts b/frontend/src/constants/app.ts index c83cffcfd..89407093c 100644 --- a/frontend/src/constants/app.ts +++ b/frontend/src/constants/app.ts @@ -1,4 +1,4 @@ -import { useAppConfig } from '../hooks/useAppConfig'; +import { useAppConfig } from '../contexts/AppConfigContext'; // Get base URL from app config with fallback export const getBaseUrl = (): string => { diff --git a/frontend/src/hooks/useAppConfig.ts b/frontend/src/contexts/AppConfigContext.tsx similarity index 60% rename from frontend/src/hooks/useAppConfig.ts rename to frontend/src/contexts/AppConfigContext.tsx index 71609ab9e..5271bbb10 100644 --- a/frontend/src/hooks/useAppConfig.ts +++ b/frontend/src/contexts/AppConfigContext.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import React, { createContext, useContext, useState, useEffect } from 'react'; export interface AppConfig { baseUrl?: string; @@ -28,17 +28,21 @@ export interface AppConfig { error?: string; } -interface UseAppConfigReturn { +interface AppConfigContextValue { config: AppConfig | null; loading: boolean; error: string | null; refetch: () => Promise; } +// Create context +const AppConfigContext = createContext(undefined); + /** - * Custom hook to fetch and manage application configuration + * Provider component that fetches and provides app configuration + * Should be placed at the top level of the app, before any components that need config */ -export function useAppConfig(): UseAppConfigReturn { +export const AppConfigProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [config, setConfig] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -56,11 +60,11 @@ export function useAppConfig(): UseAppConfigReturn { const data: AppConfig = await response.json(); setConfig(data); - console.warn('Fetched app config:', data); + console.warn('[AppConfig] Fetched app config:', data); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred'; setError(errorMessage); - console.error('Failed to fetch app config:', err); + console.error('[AppConfig] Failed to fetch app config:', err); } finally { setLoading(false); } @@ -70,11 +74,31 @@ export function useAppConfig(): UseAppConfigReturn { fetchConfig(); }, []); - return { + const value: AppConfigContextValue = { config, loading, error, refetch: fetchConfig, }; + + return ( + + {children} + + ); +}; + +/** + * Hook to access application configuration + * Must be used within AppConfigProvider + */ +export function useAppConfig(): AppConfigContextValue { + const context = useContext(AppConfigContext); + + if (context === undefined) { + throw new Error('useAppConfig must be used within AppConfigProvider'); + } + + return context; } diff --git a/frontend/src/hooks/useCookieConsent.ts b/frontend/src/hooks/useCookieConsent.ts index 6c9ed477b..88de503a0 100644 --- a/frontend/src/hooks/useCookieConsent.ts +++ b/frontend/src/hooks/useCookieConsent.ts @@ -1,7 +1,7 @@ import { useEffect, useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { BASE_PATH } from '../constants/app'; -import { useAppConfig } from './useAppConfig'; +import { useAppConfig } from '../contexts/AppConfigContext'; declare global { interface Window { diff --git a/frontend/src/hooks/useScarfTracking.ts b/frontend/src/hooks/useScarfTracking.ts index 2fc0ce68c..6a2375587 100644 --- a/frontend/src/hooks/useScarfTracking.ts +++ b/frontend/src/hooks/useScarfTracking.ts @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { useAppConfig } from './useAppConfig'; +import { useAppConfig } from '../contexts/AppConfigContext'; import { useCookieConsent } from './useCookieConsent'; import { setScarfConfig, firePixel } from '../utils/scarfTracking'; diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx index 6458935a6..4e7bd1c25 100644 --- a/frontend/src/pages/HomePage.tsx +++ b/frontend/src/pages/HomePage.tsx @@ -6,7 +6,7 @@ import { useSidebarContext } from "../contexts/SidebarContext"; import { useDocumentMeta } from "../hooks/useDocumentMeta"; import { BASE_PATH, getBaseUrl } from "../constants/app"; import { useMediaQuery } from "@mantine/hooks"; -import { useAppConfig } from "../hooks/useAppConfig"; +import { useAppConfig } from "../contexts/AppConfigContext"; import AppsIcon from '@mui/icons-material/AppsRounded'; import ToolPanel from "../components/tools/ToolPanel";