mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-02 21:03:34 +03:00
# Description of Changes Refactor frontend auth to the shared folder and hook it up to both the portal and editor so they share the same system. Also adds various tasks to help run the portal, including `task dev:portal` to spawn the portal with the backend, and `task dev:portal:proxy` to spawn the editor, portal and backend, and a reverse proxy (at localhost:3000) to allow you to use both at once to simulate how this will actually be deployed, allowing you to check whether the seamless transition between the two actually works.
149 lines
4.2 KiB
TypeScript
149 lines
4.2 KiB
TypeScript
import { useTranslation } from "react-i18next";
|
|
import "@shared/auth/ui/auth.css";
|
|
import { TextInput, PasswordInput, Button } from "@mantine/core";
|
|
|
|
// Force light mode styles for auth inputs
|
|
const authInputStyles = {
|
|
input: {
|
|
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)",
|
|
},
|
|
},
|
|
label: {
|
|
color: "var(--auth-label-text-light-only)",
|
|
},
|
|
};
|
|
|
|
interface EmailPasswordFormProps {
|
|
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;
|
|
fieldErrors?: {
|
|
email?: string;
|
|
password?: string;
|
|
mfaCode?: string;
|
|
};
|
|
}
|
|
|
|
export default function EmailPasswordForm({
|
|
email,
|
|
password,
|
|
setEmail,
|
|
setPassword,
|
|
mfaCode = "",
|
|
setMfaCode,
|
|
showMfaField = false,
|
|
requiresMfa = false,
|
|
onSubmit,
|
|
isSubmitting,
|
|
submitButtonText,
|
|
showPasswordField = true,
|
|
fieldErrors = {},
|
|
}: EmailPasswordFormProps) {
|
|
const { t } = useTranslation();
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
onSubmit();
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit}>
|
|
<div className="auth-fields">
|
|
<div className="auth-field">
|
|
<TextInput
|
|
id="email"
|
|
label={t("login.username", "Username")}
|
|
type="text"
|
|
name="username"
|
|
autoComplete="username"
|
|
placeholder={t("login.enterUsername", "Enter username")}
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
error={fieldErrors.email}
|
|
classNames={{ label: "auth-label" }}
|
|
styles={authInputStyles}
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
|
|
{showPasswordField && (
|
|
<div className="auth-field">
|
|
<PasswordInput
|
|
id="password"
|
|
label={t("login.password", "Password")}
|
|
name="current-password"
|
|
autoComplete="current-password"
|
|
placeholder={t("login.enterPassword", "Enter your password")}
|
|
value={password}
|
|
onChange={(e) => setPassword(e.target.value)}
|
|
error={fieldErrors.password}
|
|
classNames={{ label: "auth-label" }}
|
|
styles={authInputStyles}
|
|
/>
|
|
</div>
|
|
)}
|
|
{showMfaField && (
|
|
<div className="auth-field">
|
|
<TextInput
|
|
id="mfaCode"
|
|
label={t("login.mfaCode", "Authentication Code")}
|
|
type="text"
|
|
name="mfaCode"
|
|
autoComplete="one-time-code"
|
|
placeholder={t("login.enterMfaCode", "Enter 6-digit code")}
|
|
value={mfaCode}
|
|
inputMode="numeric"
|
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
setMfaCode?.(e.target.value.replace(/\D/g, "").slice(0, 6))
|
|
}
|
|
pattern="[0-9]*"
|
|
maxLength={6}
|
|
minLength={6}
|
|
error={fieldErrors.mfaCode}
|
|
classNames={{ label: "auth-label" }}
|
|
styles={authInputStyles}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<Button
|
|
type="submit"
|
|
disabled={
|
|
isSubmitting ||
|
|
!email ||
|
|
(showPasswordField && !password) ||
|
|
(requiresMfa && !mfaCode.trim())
|
|
}
|
|
className="auth-button"
|
|
fullWidth
|
|
loading={isSubmitting}
|
|
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)",
|
|
},
|
|
}}
|
|
>
|
|
{submitButtonText}
|
|
</Button>
|
|
</form>
|
|
);
|
|
}
|