"use client" import { useState, useEffect } from 'react' import { motion, AnimatePresence } from 'framer-motion' import { Copy, Check, Eye, EyeOff, RefreshCw, Info, Key } from 'lucide-react' import { generatePassword, calculateEntropy, estimateTimeToCrack } from '@/utils/passwordGenerator' interface PasswordOptions { length: number includeUppercase: boolean includeLowercase: boolean includeNumbers: boolean includeSymbols: boolean excludeSimilar: boolean } export function PasswordGenerator() { const [mounted, setMounted] = useState(false) const [password, setPassword] = useState('') const [showPassword, setShowPassword] = useState(true) const [copied, setCopied] = useState(false) const [options, setOptions] = useState({ length: 16, includeUppercase: true, includeLowercase: true, includeNumbers: true, includeSymbols: true, excludeSimilar: false, }) // Mount and load settings from localStorage useEffect(() => { setMounted(true) const savedOptions = localStorage.getItem('passmaster-settings') if (savedOptions) { try { const parsed = JSON.parse(savedOptions) setOptions(prev => ({ ...prev, ...parsed })) } catch (error) { console.error('Failed to load saved settings:', error) } } }, []) // Save settings to localStorage when options change useEffect(() => { if (mounted) { localStorage.setItem('passmaster-settings', JSON.stringify(options)) } }, [options, mounted]) // Prevent hydration mismatch if (!mounted) { return (
) } const handleGenerate = () => { const newPassword = generatePassword(options) setPassword(newPassword) setCopied(false) } const handleCopy = async () => { if (password) { try { await navigator.clipboard.writeText(password) setCopied(true) setTimeout(() => setCopied(false), 2000) } catch (error) { console.error('Failed to copy password:', error) // Fallback for older browsers const textArea = document.createElement('textarea') textArea.value = password document.body.appendChild(textArea) textArea.select() document.execCommand('copy') document.body.removeChild(textArea) setCopied(true) setTimeout(() => setCopied(false), 2000) } } } const getStrengthLevel = (entropy: number) => { if (entropy < 40) return { level: 'Schwach', color: 'strength-weak', bg: 'bg-red-500' } if (entropy < 60) return { level: 'Mittel', color: 'strength-ok', bg: 'bg-yellow-500' } if (entropy < 80) return { level: 'Stark', color: 'strength-strong', bg: 'bg-blue-500' } return { level: 'Sehr Stark', color: 'strength-excellent', bg: 'bg-green-500' } } const entropy = password ? calculateEntropy(password) : 0 const timeToCrack = password ? estimateTimeToCrack(password) : '' const strength = getStrengthLevel(entropy) return (
{/* Generated Password */}
{copied ? ( ) : ( )} {copied ? 'Kopiert!' : 'Kopieren'}
{/* Password Strength */} {password && (
Stärke: {strength.level}
Entropy: {entropy.toFixed(1)} bits Zeit zum Knacken: {timeToCrack}
)}
{/* Options */}
{/* Length Slider */}
setOptions({ ...options, length: parseInt(e.target.value) })} className="w-full h-2 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer slider" />
8 128
{/* Character Options */}

Zeichentypen

{[ { key: 'includeUppercase', label: 'Großbuchstaben (A-Z)' }, { key: 'includeLowercase', label: 'Kleinbuchstaben (a-z)' }, { key: 'includeNumbers', label: 'Zahlen (0-9)' }, { key: 'includeSymbols', label: 'Symbole (!@#$%^&*)' }, ].map(({ key, label }) => ( ))}

Optionen

{/* Generate Button */} Passwort generieren {/* ARIA Live Region for Copy Feedback */}
{copied && 'Passwort in Zwischenablage kopiert'}
) }