156 lines
5.2 KiB
TypeScript
156 lines
5.2 KiB
TypeScript
// components/auth/LoginForm.tsx
|
||
'use client';
|
||
|
||
import * as React from 'react';
|
||
import Button from '@/components/ui/Button';
|
||
|
||
type LoginValues = {
|
||
identifier: string;
|
||
password: string;
|
||
};
|
||
|
||
export interface LoginFormProps {
|
||
title?: string;
|
||
subtitle?: string;
|
||
onSubmit?: (values: LoginValues) => void | Promise<void>;
|
||
isSubmitting?: boolean;
|
||
errorMessage?: string | null;
|
||
}
|
||
|
||
const LoginForm: React.FC<LoginFormProps> = ({
|
||
title = 'Bitte melde dich an',
|
||
subtitle,
|
||
onSubmit,
|
||
isSubmitting,
|
||
errorMessage,
|
||
}) => {
|
||
const [form, setForm] = React.useState<LoginValues>({
|
||
identifier: '',
|
||
password: '',
|
||
});
|
||
|
||
const handleChange =
|
||
(field: keyof LoginValues) =>
|
||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||
const value = e.target.value;
|
||
setForm((prev) => ({ ...prev, [field]: value }));
|
||
};
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
if (!onSubmit) return;
|
||
await onSubmit(form);
|
||
};
|
||
|
||
return (
|
||
<div className="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||
{/* Header */}
|
||
<div className="sm:mx-auto sm:w-full sm:max-w-md">
|
||
<img
|
||
alt="Your Company"
|
||
src="https://tailwindcss.com/plus-assets/img/logos/mark.svg?color=indigo&shade=600"
|
||
className="mx-auto h-10 w-auto dark:hidden"
|
||
/>
|
||
<img
|
||
alt="Your Company"
|
||
src="https://tailwindcss.com/plus-assets/img/logos/mark.svg?color=indigo&shade=500"
|
||
className="mx-auto h-10 w-auto not-dark:hidden"
|
||
/>
|
||
<h2 className="mt-6 text-center text-2xl/9 font-bold tracking-tight text-gray-900 dark:text-white">
|
||
{title}
|
||
</h2>
|
||
{subtitle && (
|
||
<p className="mt-2 text-center text-sm text-gray-500 dark:text-gray-400">
|
||
{subtitle}
|
||
</p>
|
||
)}
|
||
</div>
|
||
|
||
{/* Card */}
|
||
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-[480px]">
|
||
<div className="bg-white px-6 py-12 shadow-sm sm:rounded-lg sm:px-12 dark:bg-gray-800/50 dark:shadow-none dark:outline dark:-outline-offset-1 dark:outline-white/10">
|
||
<form className="space-y-6" onSubmit={handleSubmit}>
|
||
{/* Benutzername / E-Mail */}
|
||
<div>
|
||
<label
|
||
htmlFor="identifier"
|
||
className="block text-sm/6 font-medium text-gray-900 dark:text-white"
|
||
>
|
||
NW-Kennung oder E-Mail
|
||
</label>
|
||
<div className="mt-2">
|
||
<input
|
||
id="identifier"
|
||
name="identifier"
|
||
type="text"
|
||
required
|
||
autoComplete="username"
|
||
value={form.identifier}
|
||
onChange={handleChange('identifier')}
|
||
className="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-indigo-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Passwort */}
|
||
<div>
|
||
<label
|
||
htmlFor="password"
|
||
className="block text-sm/6 font-medium text-gray-900 dark:text-white"
|
||
>
|
||
Passwort
|
||
</label>
|
||
<div className="mt-2">
|
||
<input
|
||
id="password"
|
||
name="password"
|
||
type="password"
|
||
required
|
||
autoComplete="current-password"
|
||
value={form.password}
|
||
onChange={handleChange('password')}
|
||
className="block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-indigo-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Optional: Passwort vergessen Link – kannst du lassen oder entfernen */}
|
||
<div className="flex items-center justify-end">
|
||
<div className="text-sm/6">
|
||
<a
|
||
href="#"
|
||
className="font-semibold text-indigo-600 hover:text-indigo-500 dark:text-indigo-400 dark:hover:text-indigo-300"
|
||
>
|
||
Passwort vergessen?
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Error */}
|
||
{errorMessage && (
|
||
<p className="text-sm text-red-600 dark:text-red-400">
|
||
{errorMessage}
|
||
</p>
|
||
)}
|
||
|
||
{/* Submit */}
|
||
<div>
|
||
<Button
|
||
type="submit"
|
||
variant="primary"
|
||
size="lg"
|
||
className="w-full justify-center"
|
||
disabled={isSubmitting}
|
||
>
|
||
{isSubmitting ? 'Wird angemeldet…' : 'Anmelden'}
|
||
</Button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default LoginForm;
|