[WEB-4488] feat: brand revamp (#7544)
* chore: empty state asset and theme improvement (#7542) * chore: empty state asset and theme improvement * chore: upgrade modal improvement and code refactor * feat: onboarding revamp and theme changes (#7541) * refactor: consolidate password strength indicator into shared UI package * chore: remove old password strength meter implementations * chore: update package dependencies for password strength refactor * chore: code refactor * chore: brand logo added * chore: terms and conditions refactor * chore: auth form refactor * chore: oauth enhancements and refactor * chore: plane new logos added * chore: auth input form field added to ui package * chore: password input component added * chore: web auth refactor * chore: update brand colors and remove onboarding-specific styles * chore: clean up unused assets * chore: profile menu text overflow * chore: theme related changes * chore: logo spinner updated * chore: onboarding constant and types updated * chore: theme changes and code refactor * feat: onboarding flow revamp * fix: build error and code refactoring * chore: code refactor * fix: build error * chore: consent option added to onboarding and code refactor * fix: build fix * chore: code refactor * chore: auth screen revamp and code refactor * chore: onboarding enhancements * chore: code refactor * chore: onboarding logic improvement * chore: code refactor * fix: onboarding pre release improvements * chore: color token updated * chore: color token updated * chore: auth screen line height and size improvements * chore: input height updated * chore: n-progress theme updated * chore: theme and logo enhancements * chore: space auth and code refactor * chore: update new brand empty states (#7543) * [WEB-4585]chore: branding updates (#7540) * chore: updated logo, og image, and loaders * chore: updated branding colors * chore: tour modal logo * chore: updated logo spinner size * chore: updated email templates logos and colors * chore: code refactor * fix: removed conditional hook render * fix: space app loader --------- Co-authored-by: Vamsi Krishna <46787868+vamsikrishnamathala@users.noreply.github.com> Co-authored-by: vamsikrishnamathala <matalav55@gmail.com>
This commit is contained in:
parent
6450793d72
commit
51e146f8ca
345 changed files with 5158 additions and 2515 deletions
207
packages/ui/src/auth-form/auth-form.tsx
Normal file
207
packages/ui/src/auth-form/auth-form.tsx
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
import React, { useState, useMemo } from "react";
|
||||
import { E_PASSWORD_STRENGTH } from "@plane/constants";
|
||||
import { cn } from "../../helpers";
|
||||
import { Button } from "../button/button";
|
||||
import { Spinner } from "../spinners/circular-spinner";
|
||||
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>
|
||||
);
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue