25ac540171
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
92 lines
2.5 KiB
TypeScript
92 lines
2.5 KiB
TypeScript
// App shell — loads the session, applies the entry-routing rule (SD-0001 §6.5), and renders
|
|
// the matching screen. The rule is exhaustive: no state lands nowhere (BUC-4). The verify
|
|
// response carries the same shape as /me, so onAuthed feeds the session directly.
|
|
import { useEffect, useState } from "react";
|
|
import { getMe, type StorefrontResult, type VerifyResult } from "./api";
|
|
import { routeFor, type SessionState } from "./routing";
|
|
import Admin from "./screens/Admin";
|
|
import CreateStorefront from "./screens/CreateStorefront";
|
|
import Landing from "./screens/Landing";
|
|
import SignIn from "./screens/SignIn";
|
|
|
|
type Door = "signup" | "login";
|
|
type Welcome = "new" | "back" | null;
|
|
|
|
export default function App() {
|
|
const [session, setSession] = useState<SessionState | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [door, setDoor] = useState<Door | null>(null);
|
|
const [welcome, setWelcome] = useState<Welcome>(null);
|
|
|
|
async function reload() {
|
|
setLoading(true);
|
|
setSession(await getMe());
|
|
setLoading(false);
|
|
}
|
|
|
|
useEffect(() => {
|
|
void reload();
|
|
}, []);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="screen screen--plain">
|
|
<main className="screen__main">
|
|
<p className="note">Loading…</p>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const route = routeFor(session ?? { account: null, storefront: null });
|
|
|
|
if (route === "landing") {
|
|
if (door) {
|
|
return (
|
|
<SignIn
|
|
door={door}
|
|
onBack={() => setDoor(null)}
|
|
onAuthed={(result: VerifyResult) => {
|
|
setWelcome(result.created ? "new" : "back");
|
|
setDoor(null);
|
|
setSession({ account: result.account, storefront: result.storefront });
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
return <Landing onGetStarted={() => setDoor("signup")} onLogIn={() => setDoor("login")} />;
|
|
}
|
|
|
|
const email = session!.account!.email;
|
|
|
|
function signedOut() {
|
|
setWelcome(null);
|
|
setDoor(null);
|
|
setSession(null);
|
|
}
|
|
|
|
if (route === "create-storefront") {
|
|
return (
|
|
<CreateStorefront
|
|
email={email}
|
|
welcome={welcome}
|
|
onCreated={(sf: StorefrontResult) => {
|
|
setWelcome(null);
|
|
setSession({ account: { email }, storefront: sf });
|
|
}}
|
|
onAlreadyOwns={() => void reload()}
|
|
onSignedOut={signedOut}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Admin
|
|
storefrontName={session!.storefront!.name}
|
|
email={email}
|
|
welcome={welcome}
|
|
onSignedOut={signedOut}
|
|
/>
|
|
);
|
|
}
|