193 lines
6.0 KiB
TypeScript
193 lines
6.0 KiB
TypeScript
'use client';
|
||
|
||
import Modal from '@/components/ui/Modal';
|
||
import { CheckIcon } from '@heroicons/react/24/outline';
|
||
import type { UserWithAvatar } from './types';
|
||
import type { PasswordChecks } from './passwordUtils';
|
||
|
||
type ChangePasswordModalProps = {
|
||
open: boolean;
|
||
user: UserWithAvatar;
|
||
newPassword: string;
|
||
newPasswordConfirm: string;
|
||
pwChecks: PasswordChecks;
|
||
pwError: string | null;
|
||
saving: boolean;
|
||
canSubmitPw: boolean;
|
||
onNewPasswordChange: (value: string) => void;
|
||
onNewPasswordConfirmChange: (value: string) => void;
|
||
onClose: () => void;
|
||
onSubmit: () => void;
|
||
};
|
||
|
||
export default function ChangePasswordModal({
|
||
open,
|
||
user,
|
||
newPassword,
|
||
newPasswordConfirm,
|
||
pwChecks,
|
||
pwError,
|
||
saving,
|
||
canSubmitPw,
|
||
onNewPasswordChange,
|
||
onNewPasswordConfirmChange,
|
||
onClose,
|
||
onSubmit,
|
||
}: ChangePasswordModalProps) {
|
||
if (!open) return null;
|
||
|
||
return (
|
||
<Modal
|
||
open={open}
|
||
onClose={onClose}
|
||
title="Passwort ändern"
|
||
tone="warning"
|
||
variant="centered"
|
||
size="md"
|
||
primaryAction={{
|
||
label: saving ? 'Speichere …' : 'Passwort setzen',
|
||
onClick: onSubmit,
|
||
variant: 'primary',
|
||
disabled: !canSubmitPw,
|
||
}}
|
||
secondaryAction={{
|
||
label: 'Abbrechen',
|
||
onClick: onClose,
|
||
variant: 'secondary',
|
||
}}
|
||
>
|
||
<form
|
||
onSubmit={(e) => {
|
||
e.preventDefault();
|
||
onSubmit();
|
||
}}
|
||
className="space-y-3 text-sm"
|
||
>
|
||
<p className="text-xs text-gray-600 dark:text-gray-400">
|
||
Das neue Passwort gilt sofort für den Benutzer{' '}
|
||
<strong>{user.arbeitsname || user.nwkennung}</strong>.
|
||
</p>
|
||
|
||
<div>
|
||
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300">
|
||
Neues Passwort *
|
||
</label>
|
||
<input
|
||
type="password"
|
||
required
|
||
minLength={12}
|
||
className="mt-1 block w-full rounded-md border border-gray-300 bg-white px-2.5 py-1.5 text-sm text-gray-900 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100"
|
||
value={newPassword}
|
||
onChange={(e) => onNewPasswordChange(e.target.value)}
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-xs font-medium text-gray-700 dark:text-gray-300">
|
||
Passwort bestätigen *
|
||
</label>
|
||
<input
|
||
type="password"
|
||
required
|
||
minLength={12}
|
||
className="mt-1 block w-full rounded-md border border-gray-300 bg-white px-2.5 py-1.5 text-sm text-gray-900 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100"
|
||
value={newPasswordConfirm}
|
||
onChange={(e) => onNewPasswordConfirmChange(e.target.value)}
|
||
/>
|
||
</div>
|
||
|
||
<div className="mt-2 rounded-md bg-gray-50 p-2 text-xs text-gray-700 dark:bg-gray-800 dark:text-gray-300">
|
||
<p className="font-medium mb-1">Sicherheitskriterien:</p>
|
||
<ul className="space-y-0.5">
|
||
<li
|
||
className={
|
||
(pwChecks.lengthOk
|
||
? 'text-green-700 dark:text-green-500'
|
||
: 'text-gray-500 dark:text-gray-400') +
|
||
' flex items-center gap-1.5'
|
||
}
|
||
>
|
||
{pwChecks.lengthOk ? (
|
||
<CheckIcon className="h-4 w-4 shrink-0" />
|
||
) : (
|
||
<span className="inline-block w-4 text-center">•</span>
|
||
)}
|
||
<span>Mindestens 12 Zeichen</span>
|
||
</li>
|
||
|
||
<li
|
||
className={
|
||
(pwChecks.lowerOk
|
||
? 'text-green-700 dark:text-green-500'
|
||
: 'text-gray-500 dark:text-gray-400') +
|
||
' flex items-center gap-1.5'
|
||
}
|
||
>
|
||
{pwChecks.lowerOk ? (
|
||
<CheckIcon className="h-4 w-4 shrink-0" />
|
||
) : (
|
||
<span className="inline-block w-4 text-center">•</span>
|
||
)}
|
||
<span>Mindestens ein Kleinbuchstabe (a–z)</span>
|
||
</li>
|
||
|
||
<li
|
||
className={
|
||
(pwChecks.upperOk
|
||
? 'text-green-700 dark:text-green-500'
|
||
: 'text-gray-500 dark:text-gray-400') +
|
||
' flex items-center gap-1.5'
|
||
}
|
||
>
|
||
{pwChecks.upperOk ? (
|
||
<CheckIcon className="h-4 w-4 shrink-0" />
|
||
) : (
|
||
<span className="inline-block w-4 text-center">•</span>
|
||
)}
|
||
<span>Mindestens ein Großbuchstabe (A–Z)</span>
|
||
</li>
|
||
|
||
<li
|
||
className={
|
||
(pwChecks.digitOk
|
||
? 'text-green-700 dark:text-green-500'
|
||
: 'text-gray-500 dark:text-gray-400') +
|
||
' flex items-center gap-1.5'
|
||
}
|
||
>
|
||
{pwChecks.digitOk ? (
|
||
<CheckIcon className="h-4 w-4 shrink-0" />
|
||
) : (
|
||
<span className="inline-block w-4 text-center">•</span>
|
||
)}
|
||
<span>Mindestens eine Ziffer (0–9)</span>
|
||
</li>
|
||
|
||
<li
|
||
className={
|
||
(pwChecks.specialOk
|
||
? 'text-green-700 dark:text-green-500'
|
||
: 'text-gray-500 dark:text-gray-400') +
|
||
' flex items-center gap-1.5'
|
||
}
|
||
>
|
||
{pwChecks.specialOk ? (
|
||
<CheckIcon className="h-4 w-4 shrink-0" />
|
||
) : (
|
||
<span className="inline-block w-4 text-center">•</span>
|
||
)}
|
||
<span>Mindestens ein Sonderzeichen (!, ?, #, …)</span>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
{pwError && (
|
||
<p className="text-xs text-red-600 dark:text-red-400">
|
||
{pwError}
|
||
</p>
|
||
)}
|
||
</form>
|
||
</Modal>
|
||
);
|
||
}
|