feat(setup): install-wizard UI (epic 6c)
- Multi-step first-run wizard (intro → admin → Google → SMTP → finish), shown by App while the instance reports configured=false; the one-time token comes from the setup URL (?token=) and is sent as X-Setup-Token on every step. Google/SMTP steps appear only when secrets can be stored. - App holds the 'me' query until setup status is known (so it doesn't 503 against the setup lock); a missing/invalid token shows a 'use the link from the logs' screen. EN/HU/DE.
This commit is contained in:
parent
c33a04c353
commit
e4b997d754
6 changed files with 461 additions and 0 deletions
|
|
@ -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 (
|
||||
<div className="min-h-screen grid place-items-center text-muted">{t("common.loading")}</div>
|
||||
);
|
||||
if (needsSetup) return <SetupWizard />;
|
||||
|
||||
if (meQuery.isLoading)
|
||||
return (
|
||||
<div className="min-h-screen grid place-items-center text-muted">{t("common.loading")}</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue