optimize password view in stressfrei addresses
This commit is contained in:
parent
35938133d6
commit
2444310a28
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
|
|
@ -5,8 +5,8 @@
|
|||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>OpenCRM</title>
|
||||
<script type="module" crossorigin src="/assets/index-CitfypIw.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-B7w5p8ZY.css">
|
||||
<script type="module" crossorigin src="/assets/index-BdT2l8pM.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DCjAycJ5.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in New Issue