Files
WealthySmart/frontend/src/App.tsx
Carlos Escalante 7b7c741ba2 Agent: resolve "today" in the browser's timezone, not hardcoded CR
The CR-hardcoded today broke two scenarios: the owner traveling, and
any future non-CR user. Now the provider sends the browser's IANA
timezone (X-Client-Timezone) with every CopilotKit request — the
runtime forwards x-* headers to the agent on its own — and the backend
binds it to a per-request ContextVar next to the DB session.
get_current_date and the future-cuota bounds use it; the tool also
reports the timezone so the model can echo it. Unknown or absent zones
(n8n posts, tests) fall back to Costa Rica; comma-joined duplicate
header values are tolerated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-04 19:33:27 -06:00

101 lines
3.5 KiB
TypeScript

import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom";
import { CopilotKit } from "@copilotkit/react-core";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Toaster } from "sonner";
import { AuthProvider, useAuth } from "./AuthContext";
import { ThemeProvider } from "./contexts/theme-context";
import { PrivacyProvider } from "./contexts/privacy-context";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 30_000,
retry: 1,
},
},
});
import Layout from "./components/Layout";
import LoginPage from "./pages/Login";
import Inicio from "./pages/Inicio";
import Asistente from "./pages/Asistente";
import Analytics from "./pages/Analytics";
import Budget from "./pages/Budget";
import Salarios from "./pages/Salarios";
import Financiamientos from "./pages/Financiamientos";
import Pensions from "./pages/Pensions";
import Proyecciones from "./pages/Proyecciones";
import ServiciosMunicipales from "./pages/ServiciosMunicipales";
import SyncStatus from "./pages/SyncStatus";
import Planificador from "./pages/Planificador";
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const { isAuthenticated, isLoading } = useAuth();
if (isLoading) return null;
return isAuthenticated ? <>{children}</> : <Navigate to="/login" replace />;
}
function AppRoutes() {
const { isAuthenticated, isLoading } = useAuth();
if (isLoading) return null;
return (
<Routes>
<Route
path="/login"
element={isAuthenticated ? <Navigate to="/" replace /> : <LoginPage />}
/>
<Route
element={
<ProtectedRoute>
<Layout />
</ProtectedRoute>
}
>
<Route index element={<Inicio />} />
<Route path="/asistente" element={<Asistente />} />
<Route path="/budget" element={<Budget />} />
<Route path="/analytics" element={<Analytics />} />
<Route path="/proyecciones" element={<Proyecciones />} />
<Route path="/salarios" element={<Salarios />} />
<Route path="/financiamientos" element={<Financiamientos />} />
<Route path="/pensions" element={<Pensions />} />
<Route path="/servicios-municipales" element={<ServiciosMunicipales />} />
<Route path="/sync" element={<SyncStatus />} />
<Route path="/planificador" element={<Planificador />} />
<Route path="/transactions" element={<Navigate to="/budget" replace />} />
<Route path="/transfers" element={<Navigate to="/budget" replace />} />
</Route>
</Routes>
);
}
export default function App() {
return (
<BrowserRouter>
<ThemeProvider>
<PrivacyProvider>
<AuthProvider>
<QueryClientProvider client={queryClient}>
<CopilotKit
runtimeUrl="/api/copilotkit"
agent="wealthysmart"
headers={{
// Lets the agent resolve "hoy"/"este mes" in the user's
// local time wherever they are; backend falls back to
// Costa Rica when absent.
"X-Client-Timezone":
Intl.DateTimeFormat().resolvedOptions().timeZone,
}}
>
<AppRoutes />
<Toaster richColors position="top-right" closeButton />
</CopilotKit>
</QueryClientProvider>
</AuthProvider>
</PrivacyProvider>
</ThemeProvider>
</BrowserRouter>
);
}