Initial commit. Spun up via the docs/STARTER.md recipe: cp from vibespace, reset git, rename package, set brand to "Arcadia Admin" with Shield icon in app/lib/identity.ts. Inherits the full Crema sibling-lib wiring including @crema/arcadia-client (typed HTTP + Phoenix Channels realtime against arcadia-core) and @crema/arcadia-auth-ui (login/signup/password-reset/2FA forms). The /login route already renders <LoginForm>; <ArcadiaProvider> in app/root.tsx reads VITE_ARCADIA_URL (default localhost:4000) and VITE_ARCADIA_TENANT (default "default"). CLAUDE.md and README rewritten to frame this as the admin app for arcadia-core. docs/STARTER.md removed — arcadia-admin is a leaf consumer, not a downstream starter. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
58 lines
2.0 KiB
TypeScript
58 lines
2.0 KiB
TypeScript
import { useEffect } from "react"
|
|
import { useNavigate, useSearchParams } from "react-router"
|
|
|
|
import { LoginForm } from "@crema/arcadia-auth-ui"
|
|
import { useBrand } from "~/lib/identity"
|
|
import { pageTitle } from "~/lib/page-meta"
|
|
import { useSession, persistFromArcadiaLogin } from "~/lib/session"
|
|
|
|
export const meta = () => pageTitle("Sign in")
|
|
|
|
export default function LoginRoute() {
|
|
const navigate = useNavigate()
|
|
const [params] = useSearchParams()
|
|
const session = useSession()
|
|
const brand = useBrand()
|
|
const BrandIcon = brand.icon
|
|
|
|
const next = params.get("next") || "/"
|
|
|
|
// Already signed in? Bounce.
|
|
useEffect(() => {
|
|
if (session) navigate(next, { replace: true })
|
|
}, [session, next, navigate])
|
|
|
|
return (
|
|
<div
|
|
className="relative isolate flex min-h-svh items-center justify-center p-4"
|
|
style={{ background: "var(--background)" }}
|
|
>
|
|
<LoginForm
|
|
brand={
|
|
<div className="flex items-center gap-2">
|
|
<span
|
|
className="flex size-8 items-center justify-center rounded-lg"
|
|
style={{ background: "var(--primary)", color: "var(--primary-foreground)" }}
|
|
>
|
|
<BrandIcon className="size-4" />
|
|
</span>
|
|
<span className="text-sm font-semibold">{brand.name}</span>
|
|
</div>
|
|
}
|
|
heading={`Sign in to ${brand.name}`}
|
|
subhead="Use your arcadia credentials. In dev seeds: admin@example.com / AdminP@ssw0rd."
|
|
onSuccess={async ({ tokens, user, twoFactorRequired, twoFactorChallenge }) => {
|
|
if (twoFactorRequired && twoFactorChallenge) {
|
|
navigate(`/login/2fa?challenge=${encodeURIComponent(twoFactorChallenge)}&next=${encodeURIComponent(next)}`)
|
|
return
|
|
}
|
|
persistFromArcadiaLogin(tokens, user)
|
|
navigate(next, { replace: true })
|
|
}}
|
|
onForgotPassword={() => navigate("/login/forgot")}
|
|
onSignup={() => navigate("/signup")}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|