/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { StrictMode } from 'react' import ReactDOM from 'react-dom/client' import { AxiosError } from 'axios' import { QueryCache, QueryClient, QueryClientProvider, } from '@tanstack/react-query' import { RouterProvider, createRouter } from '@tanstack/react-router' import i18next from 'i18next' import { toast } from 'sonner' import { useAuthStore } from '@/stores/auth-store' import { getStatus } from '@/lib/api' import '@/lib/dayjs' import { applyFaviconToDom } from '@/lib/dom-utils' import { handleServerError } from '@/lib/handle-server-error' import { DirectionProvider } from './context/direction-provider' import { FontProvider } from './context/font-provider' import { ThemeProvider } from './context/theme-provider' import './i18n/config' // Generated Routes import { routeTree } from './routeTree.gen' // Styles import './styles/index.css' // Ensure VChart theme is initialized before any chart mounts (prevents white default theme flash) // VChart theme is driven by our ThemeProvider (html.light/html.dark) via per-chart `theme` prop. const queryClient = new QueryClient({ defaultOptions: { queries: { retry: (failureCount, error) => { // eslint-disable-next-line no-console if (import.meta.env.DEV) console.log({ failureCount, error }) if (failureCount >= 0 && import.meta.env.DEV) return false if (failureCount > 3 && import.meta.env.PROD) return false return !( error instanceof AxiosError && [401, 403].includes(error.response?.status ?? 0) ) }, refetchOnWindowFocus: import.meta.env.PROD, staleTime: 10 * 1000, // 10s }, mutations: { onError: (error) => { handleServerError(error) if (error instanceof AxiosError) { if (error.response?.status === 304) { toast.error(i18next.t('Content not modified!')) } } }, }, }, queryCache: new QueryCache({ onError: (error) => { if (error instanceof AxiosError) { if (error.response?.status === 401) { toast.error(i18next.t('Session expired!')) useAuthStore.getState().auth.reset() const redirect = `${router.history.location.href}` router.navigate({ to: '/sign-in', search: { redirect } }) } if (error.response?.status === 500) { toast.error(i18next.t('Internal Server Error!')) router.navigate({ to: '/500' }) } } }, }), }) // Create a new router instance const router = createRouter({ routeTree, context: { queryClient }, defaultPreload: 'intent', defaultPreloadStaleTime: 0, }) // Register the router instance for type safety declare module '@tanstack/react-router' { interface Register { router: typeof router } } // Render the app const rootElement = document.getElementById('root')! // Set document.title and favicon from cached status, then refresh from network ;(function initSystemBranding() { try { if (typeof window === 'undefined' || typeof document === 'undefined') return const apply = (name: string) => { document.title = name const metaTitle = document.querySelector( 'meta[name="title"]' ) as HTMLMetaElement | null if (metaTitle) metaTitle.setAttribute('content', name) } // Cache-first try { const saved = localStorage.getItem('status') if (saved) { const s = JSON.parse(saved) if (s?.system_name) apply(s.system_name) if (s?.logo) applyFaviconToDom(s.logo) } } catch { /* empty */ } // Background refresh getStatus() .then((s) => { if (s?.system_name) { apply(s.system_name as string) try { localStorage.setItem('status', JSON.stringify(s)) } catch { /* empty */ } } if (s?.logo) applyFaviconToDom(s.logo as string) }) .catch(() => { /* empty */ }) } catch { /* empty */ } })() if (!rootElement.innerHTML) { const root = ReactDOM.createRoot(rootElement) root.render( ) }