feat: migrate login page to design system styling
Replace inline styles with CSS module matching the design system's LoginForm visual patterns. Uses proper DS class structure (divider, social section, form fields) while keeping username-based auth instead of the DS component's email validation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
85
ui/src/auth/LoginPage.module.css
Normal file
85
ui/src/auth/LoginPage.module.css
Normal file
@@ -0,0 +1,85 @@
|
||||
.page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
background: var(--bg-base);
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
padding: 32px;
|
||||
}
|
||||
|
||||
.loginForm {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-family: var(--font-body);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-bottom: 8px;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: var(--text-muted);
|
||||
margin: 0 0 24px;
|
||||
}
|
||||
|
||||
.error {
|
||||
width: 100%;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.socialSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.dividerLine {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
}
|
||||
|
||||
.dividerText {
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.fields {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.submitButton {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ssoButton {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { Navigate } from 'react-router';
|
||||
import { useAuthStore } from './auth-store';
|
||||
import { api } from '../api/client';
|
||||
import { Card, Input, Button, Alert, FormField } from '@cameleer/design-system';
|
||||
import styles from './LoginPage.module.css';
|
||||
|
||||
interface OidcInfo {
|
||||
clientId: string;
|
||||
@@ -50,53 +51,75 @@ export function LoginPage() {
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', background: 'var(--surface-ground)' }}>
|
||||
<Card>
|
||||
<form onSubmit={handleSubmit} style={{ padding: '2rem', minWidth: 360 }}>
|
||||
<div style={{ textAlign: 'center', marginBottom: '1.5rem' }}>
|
||||
<h1 style={{ fontSize: '1.5rem', fontWeight: 600 }}>cameleer3</h1>
|
||||
<p style={{ color: 'var(--text-secondary)', marginTop: '0.25rem', fontSize: '0.875rem' }}>
|
||||
Sign in to access the observability dashboard
|
||||
</p>
|
||||
</div>
|
||||
<div className={styles.page}>
|
||||
<Card className={styles.card}>
|
||||
<div className={styles.loginForm}>
|
||||
<div className={styles.logo}>cameleer3</div>
|
||||
<p className={styles.subtitle}>Sign in to access the observability dashboard</p>
|
||||
|
||||
{error && (
|
||||
<div className={styles.error}>
|
||||
<Alert variant="error">{error}</Alert>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{oidc && (
|
||||
<>
|
||||
<Button variant="secondary" onClick={handleOidcLogin} disabled={oidcLoading} style={{ width: '100%', marginBottom: '1rem' }}>
|
||||
{oidcLoading ? 'Redirecting...' : 'Sign in with SSO'}
|
||||
</Button>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', margin: '1rem 0' }}>
|
||||
<hr style={{ flex: 1, border: 'none', borderTop: '1px solid var(--border)' }} />
|
||||
<span style={{ color: 'var(--text-tertiary)', fontSize: '0.75rem' }}>or</span>
|
||||
<hr style={{ flex: 1, border: 'none', borderTop: '1px solid var(--border)' }} />
|
||||
<div className={styles.socialSection}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
className={styles.ssoButton}
|
||||
onClick={handleOidcLogin}
|
||||
disabled={oidcLoading}
|
||||
type="button"
|
||||
>
|
||||
{oidcLoading ? 'Redirecting...' : 'Sign in with SSO'}
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.divider}>
|
||||
<div className={styles.dividerLine} />
|
||||
<span className={styles.dividerText}>or</span>
|
||||
<div className={styles.dividerLine} />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<FormField label="Username">
|
||||
<Input
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
autoFocus
|
||||
autoComplete="username"
|
||||
/>
|
||||
</FormField>
|
||||
<form className={styles.fields} onSubmit={handleSubmit} aria-label="Sign in" noValidate>
|
||||
<FormField label="Username" htmlFor="login-username">
|
||||
<Input
|
||||
id="login-username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
placeholder="Enter your username"
|
||||
autoFocus
|
||||
autoComplete="username"
|
||||
disabled={loading}
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Password">
|
||||
<Input
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Password" htmlFor="login-password">
|
||||
<Input
|
||||
id="login-password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="••••••••"
|
||||
autoComplete="current-password"
|
||||
disabled={loading}
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
<Button variant="primary" disabled={loading || !username || !password} style={{ width: '100%', marginTop: '0.5rem' }}>
|
||||
{loading ? 'Signing in...' : 'Sign In'}
|
||||
</Button>
|
||||
|
||||
{error && <div style={{ marginTop: '1rem' }}><Alert variant="error">{error}</Alert></div>}
|
||||
</form>
|
||||
<Button
|
||||
variant="primary"
|
||||
type="submit"
|
||||
loading={loading}
|
||||
disabled={loading || !username || !password}
|
||||
className={styles.submitButton}
|
||||
>
|
||||
Sign in
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user