2026-03-14 14:44:40 -05:00
|
|
|
import { useState } from "react";
|
|
|
|
|
import { Navigate } from "react-router-dom";
|
|
|
|
|
|
|
|
|
|
import { useAuth } from "../../auth/AuthProvider";
|
|
|
|
|
|
|
|
|
|
export function LoginPage() {
|
|
|
|
|
const { login, token } = useAuth();
|
|
|
|
|
const [email, setEmail] = useState("admin@mrp.local");
|
|
|
|
|
const [password, setPassword] = useState("ChangeMe123!");
|
|
|
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
|
|
|
|
|
|
if (token) {
|
|
|
|
|
return <Navigate to="/" replace />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
setError(null);
|
|
|
|
|
setIsSubmitting(true);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await login(email, password);
|
|
|
|
|
} catch (submissionError) {
|
|
|
|
|
setError(submissionError instanceof Error ? submissionError.message : "Unable to sign in.");
|
|
|
|
|
} finally {
|
|
|
|
|
setIsSubmitting(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex min-h-screen items-center justify-center px-4 py-8">
|
|
|
|
|
<div className="grid w-full max-w-5xl overflow-hidden rounded-[32px] border border-line/70 bg-surface/90 shadow-panel backdrop-blur lg:grid-cols-[1.2fr_0.8fr]">
|
|
|
|
|
<section className="bg-brand px-8 py-12 text-white md:px-12">
|
|
|
|
|
<p className="text-xs font-semibold uppercase tracking-[0.26em] text-white/75">MRP Codex</p>
|
|
|
|
|
<h1 className="mt-6 text-4xl font-extrabold">A streamlined manufacturing operating system.</h1>
|
|
|
|
|
<p className="mt-5 max-w-xl text-base text-white/82">
|
|
|
|
|
This foundation release establishes authentication, company settings, brand theming, file persistence, and planning scaffolding.
|
|
|
|
|
</p>
|
|
|
|
|
</section>
|
|
|
|
|
<section className="px-8 py-12 md:px-12">
|
2026-03-14 22:03:51 -05:00
|
|
|
<h2 className="text-xl font-bold text-text">Sign in</h2>
|
2026-03-14 14:44:40 -05:00
|
|
|
<p className="mt-2 text-sm text-muted">Use the seeded admin account to access the initial platform shell.</p>
|
|
|
|
|
<form className="mt-8 space-y-5" onSubmit={handleSubmit}>
|
|
|
|
|
<label className="block">
|
|
|
|
|
<span className="mb-2 block text-sm font-semibold text-text">Email</span>
|
|
|
|
|
<input
|
|
|
|
|
value={email}
|
|
|
|
|
onChange={(event) => setEmail(event.target.value)}
|
2026-03-14 22:08:29 -05:00
|
|
|
className="w-full rounded-2xl border border-line/70 bg-page px-2 py-2 text-text outline-none transition focus:border-brand"
|
2026-03-14 14:44:40 -05:00
|
|
|
/>
|
|
|
|
|
</label>
|
|
|
|
|
<label className="block">
|
|
|
|
|
<span className="mb-2 block text-sm font-semibold text-text">Password</span>
|
|
|
|
|
<input
|
|
|
|
|
type="password"
|
|
|
|
|
value={password}
|
|
|
|
|
onChange={(event) => setPassword(event.target.value)}
|
2026-03-14 22:08:29 -05:00
|
|
|
className="w-full rounded-2xl border border-line/70 bg-page px-2 py-2 text-text outline-none transition focus:border-brand"
|
2026-03-14 14:44:40 -05:00
|
|
|
/>
|
|
|
|
|
</label>
|
2026-03-14 15:47:29 -05:00
|
|
|
{error ? <div className="rounded-2xl border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-200 dark:text-red-200">{error}</div> : null}
|
2026-03-14 14:44:40 -05:00
|
|
|
<button
|
|
|
|
|
type="submit"
|
|
|
|
|
disabled={isSubmitting}
|
|
|
|
|
className="w-full rounded-2xl bg-text px-4 py-3 text-sm font-semibold text-page transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60"
|
|
|
|
|
>
|
|
|
|
|
{isSubmitting ? "Signing in..." : "Enter workspace"}
|
|
|
|
|
</button>
|
|
|
|
|
</form>
|
|
|
|
|
</section>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|