2026-04-10 17:41:19 +01:00
|
|
|
import { useTranslation } from "react-i18next";
|
2026-06-22 17:22:36 +01:00
|
|
|
import "@shared/auth/ui/auth.css";
|
2026-04-10 17:41:19 +01:00
|
|
|
import { TextInput, PasswordInput, Button } from "@mantine/core";
|
2025-10-24 10:49:52 +01:00
|
|
|
|
2026-01-09 10:30:34 +00:00
|
|
|
// Force light mode styles for auth inputs
|
|
|
|
|
const authInputStyles = {
|
|
|
|
|
input: {
|
2026-04-10 17:41:19 +01:00
|
|
|
backgroundColor: "var(--auth-input-bg-light-only)",
|
|
|
|
|
color: "var(--auth-input-text-light-only)",
|
|
|
|
|
borderColor: "var(--auth-input-border-light-only)",
|
|
|
|
|
"&:focus": {
|
|
|
|
|
borderColor: "var(--auth-border-focus-light-only)",
|
2026-01-09 10:30:34 +00:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
label: {
|
2026-04-10 17:41:19 +01:00
|
|
|
color: "var(--auth-label-text-light-only)",
|
2026-01-09 10:30:34 +00:00
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-24 10:49:52 +01:00
|
|
|
interface EmailPasswordFormProps {
|
2026-04-10 17:41:19 +01:00
|
|
|
email: string;
|
|
|
|
|
password: string;
|
|
|
|
|
setEmail: (email: string) => void;
|
|
|
|
|
setPassword: (password: string) => void;
|
|
|
|
|
mfaCode?: string;
|
|
|
|
|
setMfaCode?: (code: string) => void;
|
|
|
|
|
showMfaField?: boolean;
|
|
|
|
|
requiresMfa?: boolean;
|
|
|
|
|
onSubmit: () => void;
|
|
|
|
|
isSubmitting: boolean;
|
|
|
|
|
submitButtonText: string;
|
|
|
|
|
showPasswordField?: boolean;
|
2025-10-24 10:49:52 +01:00
|
|
|
fieldErrors?: {
|
2026-04-10 17:41:19 +01:00
|
|
|
email?: string;
|
|
|
|
|
password?: string;
|
|
|
|
|
mfaCode?: string;
|
|
|
|
|
};
|
2025-10-24 10:49:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function EmailPasswordForm({
|
|
|
|
|
email,
|
|
|
|
|
password,
|
|
|
|
|
setEmail,
|
|
|
|
|
setPassword,
|
2026-04-10 17:41:19 +01:00
|
|
|
mfaCode = "",
|
2026-01-23 22:34:57 +01:00
|
|
|
setMfaCode,
|
|
|
|
|
showMfaField = false,
|
|
|
|
|
requiresMfa = false,
|
2025-10-24 10:49:52 +01:00
|
|
|
onSubmit,
|
|
|
|
|
isSubmitting,
|
|
|
|
|
submitButtonText,
|
|
|
|
|
showPasswordField = true,
|
2026-04-10 17:41:19 +01:00
|
|
|
fieldErrors = {},
|
2025-10-24 10:49:52 +01:00
|
|
|
}: EmailPasswordFormProps) {
|
2025-10-27 17:22:56 +01:00
|
|
|
const { t } = useTranslation();
|
2025-10-24 10:49:52 +01:00
|
|
|
|
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
2025-10-27 17:22:56 +01:00
|
|
|
e.preventDefault();
|
|
|
|
|
onSubmit();
|
|
|
|
|
};
|
2025-10-24 10:49:52 +01:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<form onSubmit={handleSubmit}>
|
|
|
|
|
<div className="auth-fields">
|
|
|
|
|
<div className="auth-field">
|
2025-12-25 02:23:49 +01:00
|
|
|
<TextInput
|
2025-10-24 10:49:52 +01:00
|
|
|
id="email"
|
2026-04-10 17:41:19 +01:00
|
|
|
label={t("login.username", "Username")}
|
2025-10-24 10:49:52 +01:00
|
|
|
type="text"
|
|
|
|
|
name="username"
|
|
|
|
|
autoComplete="username"
|
2026-04-10 17:41:19 +01:00
|
|
|
placeholder={t("login.enterUsername", "Enter username")}
|
2025-10-24 10:49:52 +01:00
|
|
|
value={email}
|
|
|
|
|
onChange={(e) => setEmail(e.target.value)}
|
2025-12-25 02:23:49 +01:00
|
|
|
error={fieldErrors.email}
|
2026-04-10 17:41:19 +01:00
|
|
|
classNames={{ label: "auth-label" }}
|
2026-01-09 10:30:34 +00:00
|
|
|
styles={authInputStyles}
|
2026-01-23 22:34:57 +01:00
|
|
|
autoFocus
|
2025-10-24 10:49:52 +01:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{showPasswordField && (
|
|
|
|
|
<div className="auth-field">
|
2025-12-25 02:23:49 +01:00
|
|
|
<PasswordInput
|
2025-10-24 10:49:52 +01:00
|
|
|
id="password"
|
2026-06-22 17:22:36 +01:00
|
|
|
label={t("login.password", "Password")}
|
2025-10-24 10:49:52 +01:00
|
|
|
name="current-password"
|
|
|
|
|
autoComplete="current-password"
|
2026-06-22 17:22:36 +01:00
|
|
|
placeholder={t("login.enterPassword", "Enter your password")}
|
2025-10-24 10:49:52 +01:00
|
|
|
value={password}
|
|
|
|
|
onChange={(e) => setPassword(e.target.value)}
|
2025-12-25 02:23:49 +01:00
|
|
|
error={fieldErrors.password}
|
2026-04-10 17:41:19 +01:00
|
|
|
classNames={{ label: "auth-label" }}
|
2026-01-09 10:30:34 +00:00
|
|
|
styles={authInputStyles}
|
2025-10-24 10:49:52 +01:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2026-01-23 22:34:57 +01:00
|
|
|
{showMfaField && (
|
|
|
|
|
<div className="auth-field">
|
|
|
|
|
<TextInput
|
|
|
|
|
id="mfaCode"
|
2026-06-22 17:22:36 +01:00
|
|
|
label={t("login.mfaCode", "Authentication Code")}
|
2026-01-23 22:34:57 +01:00
|
|
|
type="text"
|
|
|
|
|
name="mfaCode"
|
|
|
|
|
autoComplete="one-time-code"
|
2026-04-10 17:41:19 +01:00
|
|
|
placeholder={t("login.enterMfaCode", "Enter 6-digit code")}
|
2026-01-23 22:34:57 +01:00
|
|
|
value={mfaCode}
|
|
|
|
|
inputMode="numeric"
|
2026-04-10 17:41:19 +01:00
|
|
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
|
|
|
setMfaCode?.(e.target.value.replace(/\D/g, "").slice(0, 6))
|
|
|
|
|
}
|
2026-01-23 22:34:57 +01:00
|
|
|
pattern="[0-9]*"
|
|
|
|
|
maxLength={6}
|
|
|
|
|
minLength={6}
|
|
|
|
|
error={fieldErrors.mfaCode}
|
2026-04-10 17:41:19 +01:00
|
|
|
classNames={{ label: "auth-label" }}
|
2026-01-23 22:34:57 +01:00
|
|
|
styles={authInputStyles}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
2025-10-24 10:49:52 +01:00
|
|
|
</div>
|
|
|
|
|
|
2025-12-25 02:23:49 +01:00
|
|
|
<Button
|
2025-10-24 10:49:52 +01:00
|
|
|
type="submit"
|
2026-04-17 10:50:16 +01:00
|
|
|
disabled={
|
|
|
|
|
isSubmitting ||
|
|
|
|
|
!email ||
|
|
|
|
|
(showPasswordField && !password) ||
|
|
|
|
|
(requiresMfa && !mfaCode.trim())
|
|
|
|
|
}
|
2025-10-24 10:49:52 +01:00
|
|
|
className="auth-button"
|
2025-12-25 02:23:49 +01:00
|
|
|
fullWidth
|
|
|
|
|
loading={isSubmitting}
|
2026-06-22 17:22:36 +01:00
|
|
|
styles={{
|
|
|
|
|
// Own the brand colour inline so the host app's Mantine primaryColor
|
|
|
|
|
// can't win over .auth-button (editor vs portal Mantine themes
|
|
|
|
|
// differ). The fallback keeps it red even if auth-theme.css is absent.
|
|
|
|
|
root: {
|
|
|
|
|
backgroundColor: "var(--auth-button-bg-light-only, #af3434)",
|
|
|
|
|
color: "var(--auth-button-text-light-only, #ffffff)",
|
|
|
|
|
},
|
|
|
|
|
}}
|
2025-10-24 10:49:52 +01:00
|
|
|
>
|
|
|
|
|
{submitButtonText}
|
2025-12-25 02:23:49 +01:00
|
|
|
</Button>
|
2025-10-24 10:49:52 +01:00
|
|
|
</form>
|
2025-10-27 17:22:56 +01:00
|
|
|
);
|
2025-10-24 10:49:52 +01:00
|
|
|
}
|