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";