optimize password view in stressfrei addresses
This commit is contained in:
-1
File diff suppressed because one or more lines are too long
+78
-78
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user