2026-10-02 22:26:34 -04:00
|
|
|
import { useState } from 'react'
|
2026-10-04 08:46:40 -04:00
|
|
|
import { QueryClientProvider } from '@tanstack/react-query'
|
2026-10-02 22:26:34 -04:00
|
|
|
import { Route, Routes } from 'react-router'
|
2026-10-04 08:46:40 -04:00
|
|
|
import { ApiProvider, createQueryClient, type ApiClient } from './api/index.ts'
|
|
|
|
|
import { AuthBootstrap } from './AuthBootstrap.tsx'
|
2026-10-03 18:40:34 -04:00
|
|
|
import { PlaceholderPage } from './pages/PlaceholderPage.tsx'
|
2026-10-05 15:22:37 -04:00
|
|
|
import { OfflinePill } from './pwa/OfflinePill.tsx'
|
|
|
|
|
import { UpdatePrompt, type UpdateContainer } from './pwa/UpdatePrompt.tsx'
|
2026-10-03 18:40:34 -04:00
|
|
|
import { AppShell } from './shell/AppShell.tsx'
|
2026-10-02 22:26:34 -04:00
|
|
|
|
2026-10-03 18:40:34 -04:00
|
|
|
// Every route the app serves, with the heading its placeholder page shows.
|
|
|
|
|
export const routes = [
|
|
|
|
|
{ path: '/', title: 'Home' },
|
|
|
|
|
{ path: '/onboarding', title: 'Onboarding' },
|
|
|
|
|
{ path: '/plan', title: 'Plan' },
|
|
|
|
|
{ path: '/plan/shopping', title: 'Supply run' },
|
|
|
|
|
{ path: '/plan/shopping/archive', title: 'Supply archive' },
|
|
|
|
|
{ path: '/discover', title: 'Discover' },
|
|
|
|
|
{ path: '/quick', title: 'Quick dinners' },
|
|
|
|
|
{ path: '/cook-from-pantry', title: 'Cook from what I have' },
|
|
|
|
|
{ path: '/dinner/:id', title: 'Dinner' },
|
|
|
|
|
{ path: '/settings', title: 'Settings' },
|
|
|
|
|
{ path: '/settings/notifications', title: 'Notifications' },
|
|
|
|
|
{ path: '/settings/ai', title: 'AI assistant' },
|
|
|
|
|
{ path: '/settings/household', title: 'Household' },
|
|
|
|
|
{ path: '/settings/regulars', title: 'Regulars' },
|
|
|
|
|
{ path: '/settings/calendar', title: 'Calendar' },
|
|
|
|
|
] as const
|
2026-10-02 22:26:34 -04:00
|
|
|
|
2026-10-04 08:46:40 -04:00
|
|
|
// AppRoutes is the shell and its pages, shown once /api/v1/me has loaded.
|
|
|
|
|
export function AppRoutes() {
|
2026-10-02 22:26:34 -04:00
|
|
|
return (
|
2026-10-04 08:46:40 -04:00
|
|
|
<Routes>
|
|
|
|
|
<Route element={<AppShell />}>
|
|
|
|
|
{routes.map(({ path, title }) => (
|
|
|
|
|
<Route key={path} path={path} element={<PlaceholderPage title={title} />} />
|
|
|
|
|
))}
|
|
|
|
|
</Route>
|
|
|
|
|
</Routes>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export type AppProps = {
|
|
|
|
|
api: ApiClient
|
2026-10-05 15:22:37 -04:00
|
|
|
// navigator.serviceWorker, or undefined where there is none (and in tests).
|
|
|
|
|
serviceWorker: UpdateContainer | undefined
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function reloadPage() {
|
|
|
|
|
window.location.reload()
|
2026-10-04 08:46:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// App expects to be rendered inside a router (BrowserRouter in main.tsx,
|
|
|
|
|
// MemoryRouter in tests).
|
2026-10-05 15:22:37 -04:00
|
|
|
export default function App({ api, serviceWorker }: AppProps) {
|
2026-10-04 08:46:40 -04:00
|
|
|
const [queryClient] = useState(createQueryClient)
|
|
|
|
|
return (
|
|
|
|
|
<ApiProvider client={api}>
|
|
|
|
|
<QueryClientProvider client={queryClient}>
|
|
|
|
|
<AuthBootstrap>
|
|
|
|
|
<AppRoutes />
|
|
|
|
|
</AuthBootstrap>
|
2026-10-05 15:22:37 -04:00
|
|
|
<OfflinePill />
|
|
|
|
|
<UpdatePrompt container={serviceWorker} reload={reloadPage} />
|
2026-10-04 08:46:40 -04:00
|
|
|
</QueryClientProvider>
|
|
|
|
|
</ApiProvider>
|
2026-10-02 22:26:34 -04:00
|
|
|
)
|
|
|
|
|
}
|