diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b1a03a6..d6523ad 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -22,6 +22,7 @@ import { hintsEnabled, setHintsEnabled } from "./lib/hints"; import { useConfirm } from "./components/ConfirmProvider"; import type { PrefsController } from "./components/SettingsPanel"; import Welcome from "./components/Welcome"; +import SetupWizard from "./components/SetupWizard"; import Header from "./components/Header"; import NavSidebar from "./components/NavSidebar"; import Sidebar from "./components/Sidebar"; @@ -265,6 +266,15 @@ export default function App() { }, []); // eslint-disable-line react-hooks/exhaustive-deps const queryClient = useQueryClient(); + // First-run install gate: a fresh instance reports configured=false and runs in setup mode. + // We check this before anything else and render the wizard; `me` is held back until we know the + // instance is set up (otherwise it would 503 against the setup-mode lock). + const setupQuery = useQuery({ + queryKey: ["setup-status"], + queryFn: api.setupStatus, + staleTime: Infinity, + }); + const needsSetup = setupQuery.data?.configured === false; // Poll the session so an account suspended/deleted by an admin is noticed even with no // interaction (option 1): the refetch 401s → the 401 handler below drops to the login page. // Only poll while signed in (data present) — polling on the public login page is pointless and @@ -272,6 +282,7 @@ export default function App() { const meQuery = useQuery({ queryKey: ["me"], queryFn: api.me, + enabled: setupQuery.isSuccess && !needsSetup, refetchInterval: (query) => (query.state.data ? 60_000 : false), }); @@ -445,6 +456,13 @@ export default function App() { // (we can't show our in-app confirm there), and unsaved prefs are local-only — they // revert to the saved server baseline on the next load — so there's nothing to lose. + // First-run: a fresh instance is in setup mode (all other routes are locked) → show the wizard. + if (setupQuery.isLoading) + return ( +
{t("setup.google.skipHint")}
+{t("setup.smtp.testOk")}
} + {testState === "fail" &&{t("setup.smtp.testFail")}
} +{t("setup.smtp.skipHint")}
+{error}
} + +{desc}
+{t("setup.token.body")}
+