mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-07-09 07:25:43 +08:00
feat: Browser autofill support for login form (#521)
* fix: add name and autocomplete attributes to auth form inputs Password managers (1Password, Bitwarden, etc.) rely on the HTML `name` and `autocomplete` attributes to detect and fill credential fields. The login and setup forms were missing both, preventing password managers from offering autofill. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: add JSDoc docstrings to auth form components Adds JSDoc comments to all exported functions and the internal validateSetupForm helper in the auth form files, bringing docstring coverage above the 80% threshold required by CodeRabbit. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: explicitly set name props on SetupForm credential inputs The three AuthInputField calls in SetupForm were relying on the id-to-name fallback (name={name ?? id}) inside AuthInputField. Adding explicit name props makes the password-manager contract self-contained in SetupForm and resilient to future id changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Benjamin <1159333+benjaminburzan@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -6,8 +6,16 @@ type AuthInputFieldProps = {
|
|||||||
placeholder: string;
|
placeholder: string;
|
||||||
isDisabled: boolean;
|
isDisabled: boolean;
|
||||||
type?: 'text' | 'password' | 'email';
|
type?: 'text' | 'password' | 'email';
|
||||||
|
name?: string;
|
||||||
|
autoComplete?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A labelled input field for authentication forms.
|
||||||
|
* Renders a `<label>` / `<input>` pair and forwards browser autofill hints
|
||||||
|
* (`name`, `autoComplete`) so that password managers can identify and fill
|
||||||
|
* the field correctly.
|
||||||
|
*/
|
||||||
export default function AuthInputField({
|
export default function AuthInputField({
|
||||||
id,
|
id,
|
||||||
label,
|
label,
|
||||||
@@ -16,6 +24,8 @@ export default function AuthInputField({
|
|||||||
placeholder,
|
placeholder,
|
||||||
isDisabled,
|
isDisabled,
|
||||||
type = 'text',
|
type = 'text',
|
||||||
|
name,
|
||||||
|
autoComplete,
|
||||||
}: AuthInputFieldProps) {
|
}: AuthInputFieldProps) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -25,6 +35,8 @@ export default function AuthInputField({
|
|||||||
<input
|
<input
|
||||||
id={id}
|
id={id}
|
||||||
type={type}
|
type={type}
|
||||||
|
name={name ?? id}
|
||||||
|
autoComplete={autoComplete}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(event) => onChange(event.target.value)}
|
onChange={(event) => onChange(event.target.value)}
|
||||||
className="w-full rounded-md border border-border bg-background px-3 py-2 text-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500"
|
className="w-full rounded-md border border-border bg-background px-3 py-2 text-foreground focus:border-transparent focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ const initialState: LoginFormState = {
|
|||||||
password: '',
|
password: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login form component.
|
||||||
|
* Handles credential input with browser autofill support (`autocomplete`
|
||||||
|
* attributes) so that password managers can offer to fill saved credentials.
|
||||||
|
*/
|
||||||
export default function LoginForm() {
|
export default function LoginForm() {
|
||||||
const { t } = useTranslation('auth');
|
const { t } = useTranslation('auth');
|
||||||
const { login } = useAuth();
|
const { login } = useAuth();
|
||||||
@@ -63,6 +68,7 @@ export default function LoginForm() {
|
|||||||
onChange={(value) => updateField('username', value)}
|
onChange={(value) => updateField('username', value)}
|
||||||
placeholder={t('login.placeholders.username')}
|
placeholder={t('login.placeholders.username')}
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
|
autoComplete="username"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AuthInputField
|
<AuthInputField
|
||||||
@@ -73,6 +79,7 @@ export default function LoginForm() {
|
|||||||
placeholder={t('login.placeholders.password')}
|
placeholder={t('login.placeholders.password')}
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
type="password"
|
type="password"
|
||||||
|
autoComplete="current-password"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AuthErrorAlert errorMessage={errorMessage} />
|
<AuthErrorAlert errorMessage={errorMessage} />
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ const initialState: SetupFormState = {
|
|||||||
confirmPassword: '',
|
confirmPassword: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the account-setup form state.
|
||||||
|
* @returns An error message string if validation fails, or `null` when the
|
||||||
|
* form is valid.
|
||||||
|
*/
|
||||||
function validateSetupForm(formState: SetupFormState): string | null {
|
function validateSetupForm(formState: SetupFormState): string | null {
|
||||||
if (!formState.username.trim() || !formState.password || !formState.confirmPassword) {
|
if (!formState.username.trim() || !formState.password || !formState.confirmPassword) {
|
||||||
return 'Please fill in all fields.';
|
return 'Please fill in all fields.';
|
||||||
@@ -37,6 +42,12 @@ function validateSetupForm(formState: SetupFormState): string | null {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account setup / registration form.
|
||||||
|
* Uses `autoComplete="new-password"` on password fields so that password
|
||||||
|
* managers recognise this as a registration flow and offer to save the new
|
||||||
|
* credentials after submission.
|
||||||
|
*/
|
||||||
export default function SetupForm() {
|
export default function SetupForm() {
|
||||||
const { register } = useAuth();
|
const { register } = useAuth();
|
||||||
|
|
||||||
@@ -79,31 +90,37 @@ export default function SetupForm() {
|
|||||||
<form onSubmit={handleSubmit} className="space-y-4">
|
<form onSubmit={handleSubmit} className="space-y-4">
|
||||||
<AuthInputField
|
<AuthInputField
|
||||||
id="username"
|
id="username"
|
||||||
|
name="username"
|
||||||
label="Username"
|
label="Username"
|
||||||
value={formState.username}
|
value={formState.username}
|
||||||
onChange={(value) => updateField('username', value)}
|
onChange={(value) => updateField('username', value)}
|
||||||
placeholder="Enter your username"
|
placeholder="Enter your username"
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
|
autoComplete="username"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AuthInputField
|
<AuthInputField
|
||||||
id="password"
|
id="password"
|
||||||
|
name="password"
|
||||||
label="Password"
|
label="Password"
|
||||||
value={formState.password}
|
value={formState.password}
|
||||||
onChange={(value) => updateField('password', value)}
|
onChange={(value) => updateField('password', value)}
|
||||||
placeholder="Enter your password"
|
placeholder="Enter your password"
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
type="password"
|
type="password"
|
||||||
|
autoComplete="new-password"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AuthInputField
|
<AuthInputField
|
||||||
id="confirmPassword"
|
id="confirmPassword"
|
||||||
|
name="confirmPassword"
|
||||||
label="Confirm Password"
|
label="Confirm Password"
|
||||||
value={formState.confirmPassword}
|
value={formState.confirmPassword}
|
||||||
onChange={(value) => updateField('confirmPassword', value)}
|
onChange={(value) => updateField('confirmPassword', value)}
|
||||||
placeholder="Confirm your password"
|
placeholder="Confirm your password"
|
||||||
isDisabled={isSubmitting}
|
isDisabled={isSubmitting}
|
||||||
type="password"
|
type="password"
|
||||||
|
autoComplete="new-password"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<AuthErrorAlert errorMessage={errorMessage} />
|
<AuthErrorAlert errorMessage={errorMessage} />
|
||||||
|
|||||||
Reference in New Issue
Block a user