optimize password view in stressfrei addresses

This commit is contained in:
dufyfduck 2026-02-03 15:43:00 +01:00
parent 35938133d6
commit 2444310a28
5 changed files with 233 additions and 131 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,8 +5,8 @@
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>OpenCRM</title> <title>OpenCRM</title>
<script type="module" crossorigin src="/assets/index-CitfypIw.js"></script> <script type="module" crossorigin src="/assets/index-BdT2l8pM.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B7w5p8ZY.css"> <link rel="stylesheet" crossorigin href="/assets/index-DCjAycJ5.css">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -12,7 +12,7 @@ import Modal from '../../components/ui/Modal';
import Input from '../../components/ui/Input'; import Input from '../../components/ui/Input';
import Select from '../../components/ui/Select'; import Select from '../../components/ui/Select';
import FileUpload from '../../components/ui/FileUpload'; 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 CopyButton, { CopyableBlock } from '../../components/ui/CopyButton';
import type { Address, BankCard, IdentityDocument, Meter, Customer, CustomerRepresentative, CustomerSummary } from '../../types'; 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 ==================== // ==================== STRESSFREI-EMAIL MODAL ====================
function StressfreiEmailModal({ function StressfreiEmailModal({
@ -3224,55 +3369,12 @@ function StressfreiEmailModal({
)} )}
</Button> </Button>
) : credentials && ( ) : credentials && (
<div className="bg-white border border-gray-200 rounded-lg p-3 space-y-2"> <CredentialsDisplay
<div className="flex justify-between items-center mb-2"> credentials={credentials}
<span className="text-xs font-medium text-gray-500 uppercase">Zugangsdaten</span> onHide={() => setShowCredentials(false)}
<button onResetPassword={handleResetPassword}
type="button" isResettingPassword={isResettingPassword}
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>
)} )}
</div> </div>
)} )}