Skip to content
12 changes: 12 additions & 0 deletions src/components/auth/view/AuthInputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ type AuthInputFieldProps = {
placeholder: string;
isDisabled: boolean;
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({
id,
label,
Expand All @@ -16,6 +24,8 @@ export default function AuthInputField({
placeholder,
isDisabled,
type = 'text',
name,
autoComplete,
}: AuthInputFieldProps) {
return (
<div>
Expand All @@ -25,6 +35,8 @@ export default function AuthInputField({
<input
id={id}
type={type}
name={name ?? id}
autoComplete={autoComplete}
value={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"
Expand Down
7 changes: 7 additions & 0 deletions src/components/auth/view/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ const initialState: LoginFormState = {
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() {
const { t } = useTranslation('auth');
const { login } = useAuth();
Expand Down Expand Up @@ -63,6 +68,7 @@ export default function LoginForm() {
onChange={(value) => updateField('username', value)}
placeholder={t('login.placeholders.username')}
isDisabled={isSubmitting}
autoComplete="username"
/>

<AuthInputField
Expand All @@ -73,6 +79,7 @@ export default function LoginForm() {
placeholder={t('login.placeholders.password')}
isDisabled={isSubmitting}
type="password"
autoComplete="current-password"
/>

<AuthErrorAlert errorMessage={errorMessage} />
Expand Down
17 changes: 17 additions & 0 deletions src/components/auth/view/SetupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ const initialState: SetupFormState = {
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 {
if (!formState.username.trim() || !formState.password || !formState.confirmPassword) {
return 'Please fill in all fields.';
Expand All @@ -37,6 +42,12 @@ function validateSetupForm(formState: SetupFormState): string | 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() {
const { register } = useAuth();

Expand Down Expand Up @@ -79,31 +90,37 @@ export default function SetupForm() {
<form onSubmit={handleSubmit} className="space-y-4">
<AuthInputField
id="username"
name="username"
label="Username"
value={formState.username}
onChange={(value) => updateField('username', value)}
placeholder="Enter your username"
isDisabled={isSubmitting}
autoComplete="username"
/>

<AuthInputField
id="password"
name="password"
label="Password"
value={formState.password}
onChange={(value) => updateField('password', value)}
placeholder="Enter your password"
isDisabled={isSubmitting}
type="password"
autoComplete="new-password"
/>

<AuthInputField
id="confirmPassword"
name="confirmPassword"
label="Confirm Password"
value={formState.confirmPassword}
onChange={(value) => updateField('confirmPassword', value)}
placeholder="Confirm your password"
isDisabled={isSubmitting}
type="password"
autoComplete="new-password"
/>

<AuthErrorAlert errorMessage={errorMessage} />
Expand Down