fix: gate unfinished surfaces honestly (oracle round 2)

- Login.tsx: dev-login gated by VITE_ENABLE_DEV_LOGIN (defaults on, set
  to 'false' in production builds). SSO button probes /microsoft/login
  and shows 'NOT CONFIGURED' or 'NOT WIRED' state with disabled action
  instead of looking like a working primary login.
- Hub.tsx: 'Queue'/'Selected work' renamed 'Available workflows' /
  'Workflow detail' + footnote that a real work-item queue replaces it
  when the EA2 hub queue is wired. No more semantic overclaim.
- Landing.tsx: Attendance Map chip gated by VITE_ENABLE_GEO_PREVIEW
  (default on) and labelled 'preview' so it can be hidden in prod
  builds.
- flowCuration.ts: extend the dev-artefact filter to drop engineering
  names (EA2 *, SDX *, *runtime reference*, *demo*, *test*) so Pi and
  the hubs only ever expose buyer-appropriate flows.
- All 29 unit tests + 26 dogfood QA assertions still green.
This commit is contained in:
2026-06-14 13:27:59 +04:00
parent c222aa2511
commit 456243c31c
4 changed files with 39 additions and 13 deletions
+5 -4
View File
@@ -163,11 +163,11 @@ export default function Hub({ hub }: { hub: HubKey }) {
<div className="hub-split">
<section className="hub-queue">
<div className="hub-section-label">{spec.title.split(" ")[0].toUpperCase()} QUEUE</div>
<div className="hub-section-label">AVAILABLE WORKFLOWS</div>
{flows === null && <div className="hub-empty">Loading</div>}
{flows !== null && visible.length === 0 && (
<div className="hub-empty">
Nothing in the queue yet. Open the <button className="link-inline" onClick={() => setScene("studio")}>Process Studio</button> to publish a process.
No workflows published for this function yet. Open the <button className="link-inline" onClick={() => setScene("studio")}>Process Studio</button> to add one.
</div>
)}
<ul className="hub-queue-list">
@@ -183,11 +183,12 @@ export default function Hub({ hub }: { hub: HubKey }) {
</li>
))}
</ul>
<p className="hub-queue-foot">A live work-item queue will replace this list once the EA2 hub queue endpoint is wired.</p>
</section>
<section className="hub-selected">
<div className="hub-section-label">SELECTED WORK</div>
{!selected && <div className="hub-empty">Select a row on the left.</div>}
<div className="hub-section-label">WORKFLOW DETAIL</div>
{!selected && <div className="hub-empty">Select a workflow on the left.</div>}
{selected && (
<div className="hub-flow-card">
<div className="hub-flow-title"><Branch size={11} /> {selected.display_name}</div>
+3 -1
View File
@@ -79,7 +79,9 @@ export default function Landing() {
<button className="hub-chip" onClick={() => setScene("hub-procurement")}>Procurement Hub</button>
<button className="hub-chip" onClick={() => setScene("hub-hr")}>People Hub</button>
<button className="hub-chip" onClick={() => setScene("hub-it")}>IT Hub</button>
<button className="hub-chip" onClick={() => setScene("geo-attendance")}>Attendance Map</button>
{(import.meta.env.VITE_ENABLE_GEO_PREVIEW ?? "true") !== "false" && (
<button className="hub-chip" onClick={() => setScene("geo-attendance")}>Attendance Map · preview</button>
)}
<button className="hub-chip" onClick={() => setScene("agent")}>Command Assistant</button>
<button className="hub-chip" onClick={() => setScene("chat")}>Team Chat</button>
<button className="hub-chip" onClick={() => setScene("explainer")}>What is FlowMaster?</button>
+26 -8
View File
@@ -9,16 +9,28 @@ export default function Login() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [rememberMe, setRememberMe] = useState(false);
const [devLoginEnabled, setDevLoginEnabled] = useState(true);
const buildAllowsDev = (import.meta.env.VITE_ENABLE_DEV_LOGIN ?? "true") !== "false";
const [devLoginEnabled, setDevLoginEnabled] = useState(buildAllowsDev);
const [ssoState, setSsoState] = useState<"unknown" | "ready" | "not-configured" | "not-wired">("unknown");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
// dev-login is on by default. Only the endpoint can flip it OFF.
api.devLoginConfig()
.then((cfg) => { if (cfg.enabled === false) setDevLoginEnabled(false); })
.catch(() => { /* endpoint absent → keep ON */ });
}, []);
if (!buildAllowsDev) {
setDevLoginEnabled(false);
} else {
api.devLoginConfig()
.then((cfg) => { if (cfg.enabled === false) setDevLoginEnabled(false); })
.catch(() => { /* endpoint absent → keep state */ });
}
fetch("/api/v1/auth/microsoft/login", { method: "GET", redirect: "manual" })
.then((r) => {
if (r.status >= 300 && r.status < 400) setSsoState("ready");
else if (r.status === 503) setSsoState("not-configured");
else setSsoState("not-wired");
})
.catch(() => setSsoState("not-wired"));
}, [buildAllowsDev]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
@@ -142,14 +154,20 @@ export default function Login() {
</div>
<div className="login-sso-actions">
<button type="button" className="sso-btn" onClick={handleSSO} disabled={loading}>
<button
type="button"
className="sso-btn"
onClick={handleSSO}
disabled={loading || ssoState === "not-configured" || ssoState === "not-wired"}
title={ssoState === "not-configured" ? "Microsoft sign-in is not yet configured for this tenant" : ssoState === "not-wired" ? "Microsoft sign-in is not wired to this environment yet" : "Sign in with Microsoft"}
>
<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
{ssoState === "not-configured" ? "MICROSOFT SIGN-IN · NOT CONFIGURED" : ssoState === "not-wired" ? "MICROSOFT SIGN-IN · NOT WIRED" : "CONTINUE WITH MICROSOFT"}
</button>
{devLoginEnabled && (