bb-plane-fork/packages/ui/src/auth-form/auth-form.tsx
sriram veeraghanta 0e6fbaee3a
[WEB-4790] fix: moved typescript parser to the base eslint config (#7658)
* fix: moved typescript parser to the base eslint config

* fix: eslint warning

* fix: type config setting

* fix: convert live eslint to cjs
2025-08-27 21:03:20 +05:30

207 lines
6.4 KiB
TypeScript

import React, { useState, useMemo } from "react";
import { E_PASSWORD_STRENGTH } from "@plane/constants";
import { Button } from "../button/button";
import { Spinner } from "../spinners/circular-spinner";
import { cn } from "../utils";
import { AuthConfirmPasswordInput } from "./auth-confirm-password-input";
import { AuthForgotPassword } from "./auth-forgot-password";
import { AuthInput } from "./auth-input";
import { AuthPasswordInput } from "./auth-password-input";
export type AuthMode = "sign-in" | "sign-up";
export interface AuthFormData {
email: string;
password: string;
confirmPassword?: string;
}
export interface AuthFormProps {
mode: AuthMode;
initialData?: Partial<AuthFormData>;
onSubmit?: (data: AuthFormData) => void;
onForgotPassword?: () => void;
onModeChange?: (mode: AuthMode) => void;
loading?: boolean;
disabled?: boolean;
className?: string;
showForgotPassword?: boolean;
showPasswordStrength?: boolean;
emailError?: string;
passwordError?: string;
confirmPasswordError?: string;
submitButtonText?: string;
alternateModeText?: string;
alternateModeButtonText?: string;
}
export const AuthForm: React.FC<AuthFormProps> = ({
mode,
initialData = {},
onSubmit,
onForgotPassword,
onModeChange,
loading = false,
disabled = false,
className = "",
showForgotPassword = true,
showPasswordStrength = true,
emailError,
passwordError,
confirmPasswordError,
submitButtonText,
alternateModeText,
alternateModeButtonText,
}) => {
const [formData, setFormData] = useState<AuthFormData>({
email: initialData.email || "",
password: initialData.password || "",
confirmPassword: initialData.confirmPassword || "",
});
const [passwordStrength, setPasswordStrength] = useState<E_PASSWORD_STRENGTH>(E_PASSWORD_STRENGTH.EMPTY);
const [_passwordsMatch, setPasswordsMatch] = useState(false);
const handleInputChange = (field: keyof AuthFormData) => (e: React.ChangeEvent<HTMLInputElement>) => {
setFormData((prev) => ({
...prev,
[field]: e.target.value,
}));
};
const handlePasswordChange = (password: string) => {
setFormData((prev) => ({
...prev,
password,
}));
};
const handlePasswordStrengthChange = (strength: E_PASSWORD_STRENGTH) => {
setPasswordStrength(strength);
};
const handleConfirmPasswordChange = (matches: boolean) => {
setPasswordsMatch(matches);
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (onSubmit && isFormValid) {
onSubmit(formData);
}
};
const handleModeChange = () => {
const newMode = mode === "sign-in" ? "sign-up" : "sign-in";
onModeChange?.(newMode);
};
const isFormValid = useMemo(() => {
const hasEmail = formData.email.length > 0;
const hasPassword = formData.password.length > 0;
if (mode === "sign-in") {
return hasEmail && hasPassword && !loading && !disabled;
} else {
const isPasswordStrong = passwordStrength === E_PASSWORD_STRENGTH.STRENGTH_VALID;
const passwordsMatch = formData.password === formData.confirmPassword && formData.password.length > 0;
return hasEmail && hasPassword && isPasswordStrong && passwordsMatch && !loading && !disabled;
}
}, [mode, formData, passwordStrength, loading, disabled]);
const getSubmitButtonText = () => {
if (submitButtonText) return submitButtonText;
return mode === "sign-in" ? "Sign In" : "Create Account";
};
const getAlternateModeText = () => {
if (alternateModeText) return alternateModeText;
return mode === "sign-in" ? "Don't have an account?" : "Already have an account?";
};
const getAlternateModeButtonText = () => {
if (alternateModeButtonText) return alternateModeButtonText;
return mode === "sign-in" ? "Sign Up" : "Sign In";
};
return (
<form onSubmit={handleSubmit} className={cn("space-y-4", className)}>
{/* Email Input */}
<AuthInput
id="email"
name="email"
type="email"
label="Email"
value={formData.email}
onChange={handleInputChange("email")}
placeholder="name@company.com"
error={emailError}
disabled={disabled}
// autoComplete="email"
required
/>
{/* Password Input */}
<AuthPasswordInput
id="password"
name="password"
label={mode === "sign-in" ? "Password" : "Set a password"}
value={formData.password}
onChange={handleInputChange("password")}
onPasswordChange={handlePasswordChange}
onPasswordStrengthChange={handlePasswordStrengthChange}
placeholder="Enter password"
error={passwordError}
showPasswordStrength={showPasswordStrength && mode === "sign-up"}
disabled={disabled}
// autoComplete={mode === "sign-in" ? "current-password" : "new-password"}
required
/>
{/* Confirm Password Input (Sign Up Only) */}
{mode === "sign-up" && (
<AuthConfirmPasswordInput
id="confirmPassword"
name="confirmPassword"
password={formData.password}
value={formData.confirmPassword}
onChange={handleInputChange("confirmPassword")}
onPasswordMatchChange={handleConfirmPasswordChange}
error={confirmPasswordError}
disabled={disabled}
// autoComplete="new-password"
required
/>
)}
{/* Forgot Password Link (Sign In Only) */}
{mode === "sign-in" && showForgotPassword && (
<div className="flex justify-end">
<AuthForgotPassword onForgotPassword={onForgotPassword} disabled={disabled} />
</div>
)}
{/* Submit Button */}
<div className="space-y-2.5">
<Button type="submit" variant="primary" className="w-full" size="lg" disabled={!isFormValid} loading={loading}>
{loading ? <Spinner height="20px" width="20px" /> : getSubmitButtonText()}
</Button>
{/* Alternate Mode Button */}
{onModeChange && (
<div className="text-center">
<span className="text-sm text-custom-text-300">{getAlternateModeText()}</span>
<button
type="button"
onClick={handleModeChange}
className="ml-1 text-sm text-custom-primary-100 hover:text-custom-primary-200 transition-colors duration-200"
disabled={disabled}
>
{getAlternateModeButtonText()}
</button>
</div>
)}
</div>
</form>
);
};