optimize password view in stressfrei addresses

This commit is contained in:
2026-02-03 15:43:00 +01:00
parent e4fdfbc95f
commit ee8bd7a8f7
5 changed files with 233 additions and 131 deletions
+152 -50
View File
@@ -12,7 +12,7 @@ import Modal from '../../components/ui/Modal';
import Input from '../../components/ui/Input';
import Select from '../../components/ui/Select';
import FileUpload from '../../components/ui/FileUpload';
import { Edit, Plus, Trash2, MapPin, CreditCard, FileText, Gauge, Eye, EyeOff, Download, Globe, UserPlus, X, Search, Mail } from 'lucide-react';
import { Edit, Plus, Trash2, MapPin, CreditCard, FileText, Gauge, Eye, EyeOff, Download, Globe, UserPlus, X, Search, Mail, Copy, Check } from 'lucide-react';
import CopyButton, { CopyableBlock } from '../../components/ui/CopyButton';
import type { Address, BankCard, IdentityDocument, Meter, Customer, CustomerRepresentative, CustomerSummary } from '../../types';
@@ -2783,6 +2783,151 @@ function StressfreiEmailsTab({
);
}
// ==================== CREDENTIALS DISPLAY ====================
function CredentialsDisplay({
credentials,
onHide,
onResetPassword,
isResettingPassword,
}: {
credentials: {
email: string;
password: string;
imap: { server: string; port: number; encryption: string } | null;
smtp: { server: string; port: number; encryption: string } | null;
};
onHide: () => void;
onResetPassword: () => void;
isResettingPassword: boolean;
}) {
const [copiedField, setCopiedField] = useState<string | null>(null);
const copyToClipboard = async (text: string, fieldName: string) => {
try {
await navigator.clipboard.writeText(text);
setCopiedField(fieldName);
setTimeout(() => setCopiedField(null), 2000);
} catch {
// Fallback für ältere Browser
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
setCopiedField(fieldName);
setTimeout(() => setCopiedField(null), 2000);
}
};
const CopyButton = ({ text, fieldName }: { text: string; fieldName: string }) => (
<button
type="button"
onClick={() => copyToClipboard(text, fieldName)}
className="p-1.5 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors"
title="In Zwischenablage kopieren"
>
{copiedField === fieldName ? (
<Check className="w-4 h-4 text-green-600" />
) : (
<Copy className="w-4 h-4" />
)}
</button>
);
const imapString = credentials.imap
? `${credentials.imap.server}:${credentials.imap.port}`
: '';
const smtpString = credentials.smtp
? `${credentials.smtp.server}:${credentials.smtp.port}`
: '';
return (
<div className="bg-gray-50 border border-gray-200 rounded-lg p-4 space-y-3">
<div className="flex justify-between items-center">
<span className="text-xs font-semibold text-gray-500 uppercase tracking-wide">
Zugangsdaten
</span>
<button
type="button"
onClick={onHide}
className="text-gray-400 hover:text-gray-600 p-1 hover:bg-gray-200 rounded"
title="Zugangsdaten ausblenden"
>
<EyeOff className="w-4 h-4" />
</button>
</div>
{/* Benutzername & Passwort */}
<div className="grid grid-cols-2 gap-3">
<div className="bg-white rounded-lg p-3 border border-gray-100">
<label className="text-xs text-gray-500 block mb-1">Benutzername</label>
<div className="flex items-center gap-2">
<code className="text-sm text-gray-900 font-mono flex-1 break-all">
{credentials.email}
</code>
<CopyButton text={credentials.email} fieldName="email" />
</div>
</div>
<div className="bg-white rounded-lg p-3 border border-gray-100">
<label className="text-xs text-gray-500 block mb-1">Passwort</label>
<div className="flex items-center gap-2">
<code className="text-sm text-gray-900 font-mono flex-1 break-all">
{credentials.password}
</code>
<CopyButton text={credentials.password} fieldName="password" />
</div>
<button
type="button"
onClick={onResetPassword}
disabled={isResettingPassword}
className="mt-2 text-xs text-blue-600 hover:text-blue-800 disabled:opacity-50"
>
{isResettingPassword ? 'Generiere...' : 'Neu generieren'}
</button>
</div>
</div>
{/* Server-Einstellungen */}
<div className="grid grid-cols-2 gap-3">
{credentials.imap && (
<div className="bg-white rounded-lg p-3 border border-gray-100">
<label className="text-xs text-gray-500 block mb-1">
IMAP (Empfang)
</label>
<div className="flex items-center gap-2">
<code className="text-sm text-gray-900 font-mono flex-1">
{imapString}
</code>
<CopyButton text={imapString} fieldName="imap" />
</div>
<span className="text-xs text-gray-400 mt-1 block">
{credentials.imap.encryption}
</span>
</div>
)}
{credentials.smtp && (
<div className="bg-white rounded-lg p-3 border border-gray-100">
<label className="text-xs text-gray-500 block mb-1">
SMTP (Versand)
</label>
<div className="flex items-center gap-2">
<code className="text-sm text-gray-900 font-mono flex-1">
{smtpString}
</code>
<CopyButton text={smtpString} fieldName="smtp" />
</div>
<span className="text-xs text-gray-400 mt-1 block">
{credentials.smtp.encryption}
</span>
</div>
)}
</div>
</div>
);
}
// ==================== STRESSFREI-EMAIL MODAL ====================
function StressfreiEmailModal({
@@ -3224,55 +3369,12 @@ function StressfreiEmailModal({
)}
</Button>
) : credentials && (
<div className="bg-white border border-gray-200 rounded-lg p-3 space-y-2">
<div className="flex justify-between items-center mb-2">
<span className="text-xs font-medium text-gray-500 uppercase">Zugangsdaten</span>
<button
type="button"
onClick={() => setShowCredentials(false)}
className="text-gray-400 hover:text-gray-600"
>
<EyeOff className="w-4 h-4" />
</button>
</div>
<div className="grid grid-cols-2 gap-2 text-xs">
<div>
<span className="text-gray-500">E-Mail:</span>
<p className="font-mono text-gray-900 break-all">{credentials.email}</p>
</div>
<div>
<span className="text-gray-500">Passwort:</span>
<div className="flex items-center gap-2">
<p className="font-mono text-gray-900 break-all select-all flex-1">{credentials.password}</p>
<button
type="button"
onClick={handleResetPassword}
disabled={isResettingPassword}
className="text-blue-600 hover:text-blue-800 text-xs whitespace-nowrap disabled:opacity-50"
title="Neues Kennwort generieren"
>
{isResettingPassword ? 'Generiere...' : 'Neu generieren'}
</button>
</div>
</div>
</div>
{credentials.imap && (
<div className="pt-2 border-t border-gray-100">
<span className="text-xs font-medium text-gray-500">IMAP (Empfang)</span>
<p className="font-mono text-xs text-gray-900">
{credentials.imap.server}:{credentials.imap.port} ({credentials.imap.encryption})
</p>
</div>
)}
{credentials.smtp && (
<div className="pt-2 border-t border-gray-100">
<span className="text-xs font-medium text-gray-500">SMTP (Versand)</span>
<p className="font-mono text-xs text-gray-900">
{credentials.smtp.server}:{credentials.smtp.port} ({credentials.smtp.encryption})
</p>
</div>
)}
</div>
<CredentialsDisplay
credentials={credentials}
onHide={() => setShowCredentials(false)}
onResetPassword={handleResetPassword}
isResettingPassword={isResettingPassword}
/>
)}
</div>
)}