Files
flowmaster-mission-control-…/src/scenes/Login.tsx
T
shad 66dafc085d
build-and-publish / test (push) Has been cancelled
build-and-publish / image (push) Has been cancelled
fix(login): default devLoginEnabled to true on canvas
The /internal/dev-login-config endpoint is a fm-shell Next.js internal
route that doesn't exist on the canvas nginx. Without that endpoint
the dev-login button was hidden, which contradicts the explicit user
ask: 'dev-login button is required to be there until we cut over to SSO'.

Fix: default state to true, treat the config endpoint as 'flip OFF only'.
If the endpoint returns enabled:false we hide. Otherwise (404, network
error, etc.) we keep the button visible.

Confidence: high
Scope-risk: narrow
Not-tested: real /internal/dev-login-config returning enabled:false (would
   need that endpoint mounted on canvas — out of scope for now)
2026-06-14 11:23:47 +04:00

146 lines
4.7 KiB
TypeScript

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(true);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
// The dev-login button is intentionally on by default on canvas
// (the user explicitly asked for it as a documented exception until
// we cut over fully to SSO). The endpoint config only flips it OFF.
api.devLoginConfig()
.then((cfg) => { if (cfg.enabled === false) setDevLoginEnabled(false); })
.catch(() => { /* endpoint not present — keep default ON */ });
}, []);
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>
);
}