Files
silo/web/src/pages/LoginPage.tsx
Forbes c7857fdfc9 fix(web): standardize font sizes to style guide scale
Map fontWeight: 700 → 600 in non-title contexts (LoginPage, FileDropZone).
Align FileDropZone badge padding to 4px grid.

Closes #70
2026-02-14 13:36:07 -06:00

203 lines
5.2 KiB
TypeScript

import { useEffect, useState, type FormEvent } from "react";
import { useAuth } from "../hooks/useAuth";
import { get } from "../api/client";
import type { AuthConfig } from "../api/types";
export function LoginPage() {
const { login } = useAuth();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [submitting, setSubmitting] = useState(false);
const [oidcEnabled, setOidcEnabled] = useState(false);
useEffect(() => {
get<AuthConfig>("/api/auth/config")
.then((cfg) => setOidcEnabled(cfg.oidc_enabled))
.catch(() => {});
}, []);
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
if (!username.trim() || !password) return;
setError("");
setSubmitting(true);
try {
await login(username.trim(), password);
} catch (err) {
setError(err instanceof Error ? err.message : "Login failed");
} finally {
setSubmitting(false);
}
};
return (
<div style={containerStyle}>
<div style={cardStyle}>
<h1 style={titleStyle}>Silo</h1>
<p style={subtitleStyle}>Product Lifecycle Management</p>
{error && <div style={errorStyle}>{error}</div>}
<form onSubmit={handleSubmit}>
<div style={formGroupStyle}>
<label style={labelStyle}>Username</label>
<input
className="silo-input"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Username or LDAP uid"
autoFocus
required
style={inputStyle}
/>
</div>
<div style={formGroupStyle}>
<label style={labelStyle}>Password</label>
<input
className="silo-input"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
style={inputStyle}
/>
</div>
<button type="submit" disabled={submitting} style={btnPrimaryStyle}>
{submitting ? "Signing in..." : "Sign In"}
</button>
</form>
{oidcEnabled && (
<>
<div style={dividerStyle}>
<span style={dividerLineStyle} />
<span
style={{
padding: "0 1rem",
color: "var(--ctp-overlay0)",
fontSize: "var(--font-body)",
}}
>
or
</span>
<span style={dividerLineStyle} />
</div>
<a href="/auth/oidc" style={btnOidcStyle}>
Sign in with Keycloak
</a>
</>
)}
</div>
</div>
);
}
const containerStyle: React.CSSProperties = {
display: "flex",
justifyContent: "center",
alignItems: "center",
minHeight: "100vh",
backgroundColor: "var(--ctp-base)",
};
const cardStyle: React.CSSProperties = {
backgroundColor: "var(--ctp-surface0)",
borderRadius: "1rem",
padding: "2.5rem",
width: "100%",
maxWidth: 400,
margin: "1rem",
};
const titleStyle: React.CSSProperties = {
color: "var(--ctp-mauve)",
textAlign: "center",
fontSize: "2rem",
fontWeight: 600,
marginBottom: "0.25rem",
};
const subtitleStyle: React.CSSProperties = {
color: "var(--ctp-subtext0)",
textAlign: "center",
fontSize: "var(--font-body)",
marginBottom: "2rem",
};
const errorStyle: React.CSSProperties = {
color: "var(--ctp-red)",
background: "rgba(243, 139, 168, 0.1)",
border: "1px solid rgba(243, 139, 168, 0.2)",
padding: "0.75rem 1rem",
borderRadius: "0.5rem",
marginBottom: "1rem",
fontSize: "var(--font-body)",
};
const formGroupStyle: React.CSSProperties = {
marginBottom: "1.25rem",
};
const labelStyle: React.CSSProperties = {
display: "block",
marginBottom: "0.5rem",
fontWeight: 500,
color: "var(--ctp-subtext1)",
fontSize: "var(--font-body)",
};
const inputStyle: React.CSSProperties = {
width: "100%",
padding: "0.75rem 1rem",
backgroundColor: "var(--ctp-base)",
border: "1px solid var(--ctp-surface1)",
borderRadius: "0.5rem",
color: "var(--ctp-text)",
fontSize: "var(--font-body)",
boxSizing: "border-box",
};
const btnPrimaryStyle: React.CSSProperties = {
display: "block",
width: "100%",
padding: "0.75rem 1.5rem",
borderRadius: "0.25rem",
fontWeight: 500,
fontSize: "0.75rem",
cursor: "pointer",
border: "none",
backgroundColor: "var(--ctp-mauve)",
color: "var(--ctp-crust)",
textAlign: "center",
};
const dividerStyle: React.CSSProperties = {
display: "flex",
alignItems: "center",
margin: "1.5rem 0",
};
const dividerLineStyle: React.CSSProperties = {
flex: 1,
borderTop: "1px solid var(--ctp-surface1)",
};
const btnOidcStyle: React.CSSProperties = {
display: "block",
width: "100%",
padding: "0.75rem 1.5rem",
borderRadius: "0.25rem",
fontWeight: 500,
fontSize: "0.75rem",
cursor: "pointer",
border: "none",
backgroundColor: "var(--ctp-blue)",
color: "var(--ctp-crust)",
textAlign: "center",
textDecoration: "none",
boxSizing: "border-box",
};