feat: rebuild Process Studio into multi-phase Process Creation Wizard\n\n- Replace toy Studio.tsx with proper Wizard.tsx canvas\n- Build 6-step flow: Intake -> Analyze -> Generate -> Validate -> Draft saved -> Publish\n- Implement incremental EA2 writes via wizardApi.ts (flow, batch apply)\n- Use localStorage for draft recovery across reloads\n- Apply industrial-blueprint UI styling matching core doctrine\n- Add Vitest tests for Wizard components and API wrappers
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useApp } from "../state/store";
|
||||
import { api } from "../lib/api";
|
||||
import { Bot } from "../components/icons";
|
||||
|
||||
export default function Login() {
|
||||
const setScene = useApp((s) => s.setScene);
|
||||
const loginAs = useApp((s) => s.loginAs);
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [rememberMe, setRememberMe] = useState(false);
|
||||
const [devLoginEnabled, setDevLoginEnabled] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if dev login is enabled
|
||||
api.devLoginConfig().then((cfg) => setDevLoginEnabled(cfg.enabled));
|
||||
}, []);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!email) return;
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await loginAs(email, password || undefined);
|
||||
setScene("landing");
|
||||
} catch (err) {
|
||||
setError((err as Error).message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDevLogin = async () => {
|
||||
if (!email) {
|
||||
setError("Email required for dev-login");
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
await loginAs(email, undefined);
|
||||
setScene("landing");
|
||||
} catch (err) {
|
||||
setError((err as Error).message);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSSO = () => {
|
||||
window.location.href = "/api/v1/auth/microsoft/login";
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="login-page">
|
||||
<div className="login-card">
|
||||
<div className="login-head">
|
||||
<span className="brand-mark"></span>
|
||||
<h1>FLOWMASTER AUTHENTICATION</h1>
|
||||
<div className="login-sub">MISSION CONTROL VERIFICATION REQUIRED</div>
|
||||
</div>
|
||||
|
||||
{error && <div className="login-error">{error}</div>}
|
||||
|
||||
<form className="login-form" onSubmit={handleSubmit}>
|
||||
<div className="login-field">
|
||||
<label htmlFor="email">OPERATOR_ID (EMAIL)</label>
|
||||
<input
|
||||
id="email"
|
||||
type="email"
|
||||
className="login-input"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
disabled={loading}
|
||||
autoComplete="email"
|
||||
placeholder="e.g. j.doe@flow-master.ai"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="login-field">
|
||||
<label htmlFor="password">PASSPHRASE</label>
|
||||
<input
|
||||
id="password"
|
||||
type="password"
|
||||
className="login-input"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={loading}
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="login-row">
|
||||
<label className="login-checkbox">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={rememberMe}
|
||||
onChange={(e) => setRememberMe(e.target.checked)}
|
||||
disabled={loading}
|
||||
/>
|
||||
<span>Remember me</span>
|
||||
</label>
|
||||
<a href="#" className="login-link">Recover access</a>
|
||||
</div>
|
||||
|
||||
<div className="login-actions">
|
||||
<button type="submit" className="btn btn-primary btn-lg" disabled={loading || !email}>
|
||||
{loading ? "VERIFYING..." : "SIGN IN"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div className="login-divider">
|
||||
<span>OR</span>
|
||||
</div>
|
||||
|
||||
<div className="login-sso-actions">
|
||||
<button type="button" className="sso-btn" onClick={handleSSO} disabled={loading}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 21 21">
|
||||
<rect x="1" y="1" width="9" height="9" fill="#f25022"/>
|
||||
<rect x="11" y="1" width="9" height="9" fill="#7fba00"/>
|
||||
<rect x="1" y="11" width="9" height="9" fill="#00a4ef"/>
|
||||
<rect x="11" y="11" width="9" height="9" fill="#ffb900"/>
|
||||
</svg>
|
||||
CONTINUE WITH MICROSOFT
|
||||
</button>
|
||||
|
||||
{devLoginEnabled && (
|
||||
<button type="button" className="dev-login-btn" onClick={handleDevLogin} disabled={loading || !email}>
|
||||
<Bot size={14} /> SIGN IN AS DEVELOPER (DEV-LOGIN)
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user