Mandantenfähigkeit: Domain + Kunden-E-Mail-Label dynamisch pro Provider

Alle hardcoded Referenzen auf 'stressfrei-wechseln.de' und 'Stressfrei-Wechseln'
durch dynamische Werte aus der EmailProviderConfig ersetzt. Notwendig für
Multi-Mandanten-Betrieb, wenn das CRM an Dritte vermietet wird.

Schema:
- Neues Feld EmailProviderConfig.customerEmailLabel (String?)
- Wenn leer, wird Label aus Domain abgeleitet ('stressfrei-wechseln.de' → 'Stressfrei-Wechseln')

Backend:
- Neuer Endpoint GET /api/email-providers/public-settings liefert { domain, customerEmailLabel }
- Neue Service-Funktionen: getProviderPublicSettings(), deriveLabelFromDomain()
- create/updateProviderConfig erweitert um customerEmailLabel

Frontend:
- Neuer Hook useProviderSettings() mit Auto-Caching
- Neues Eingabefeld 'Bezeichnung für Kunden-E-Mails' im Provider-Modal
- Dynamische Domain-Suffix im Adress-Hinzufügen-Dialog (@<domain>)
- Tab-Label 'Stressfrei-Wechseln' im Kunden-Detail → dynamisch
- 'Stressfrei-Wechseln Adresse' in ContractForm → dynamisch
- '(Stressfrei-Wechseln)' Badge in ContractDetail → dynamisch
- 'Stressfrei-Wechseln E-Mail' im Generate-Modal → dynamisch
- Leere-Zustand-Meldungen in Tab und E-Mail-Client → dynamisch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-23 15:43:19 +02:00
parent cf4370c905
commit cdde7b4ab7
13 changed files with 156 additions and 19 deletions
+26 -5
View File
@@ -39,6 +39,8 @@ interface ProviderFormData {
// System-E-Mail
systemEmailAddress: string;
systemEmailPassword: string;
// UI-Label für Kunden-E-Mail-Adressen
customerEmailLabel: string;
isActive: boolean;
isDefault: boolean;
}
@@ -57,6 +59,7 @@ const emptyForm: ProviderFormData = {
allowSelfSignedCerts: false,
systemEmailAddress: '',
systemEmailPassword: '',
customerEmailLabel: '',
isActive: true,
isDefault: false,
};
@@ -143,6 +146,7 @@ export default function EmailProviders() {
allowSelfSignedCerts: config.allowSelfSignedCerts ?? false,
systemEmailAddress: config.systemEmailAddress || '',
systemEmailPassword: '', // Passwort wird nicht geladen
customerEmailLabel: config.customerEmailLabel || '',
isActive: config.isActive,
isDefault: config.isDefault,
});
@@ -290,6 +294,7 @@ export default function EmailProviders() {
smtpEncryption: formData.smtpEncryption,
allowSelfSignedCerts: formData.allowSelfSignedCerts,
systemEmailAddress: formData.systemEmailAddress,
customerEmailLabel: formData.customerEmailLabel?.trim() || null,
isActive: formData.isActive,
isDefault: formData.isDefault,
};
@@ -336,9 +341,9 @@ export default function EmailProviders() {
<Card className="mb-6">
<p className="text-gray-600 mb-4">
Hier konfigurieren Sie die automatische Erstellung von Stressfrei-Wechseln E-Mail-Adressen.
Wenn beim Anlegen einer Stressfrei-Adresse die Option "Bei Provider anlegen" aktiviert ist,
wird die E-Mail-Weiterleitung automatisch erstellt.
Hier konfigurieren Sie die automatische Erstellung von Kunden-E-Mail-Adressen auf Ihrer
eigenen Domain. Wenn beim Anlegen einer Adresse die Option "Bei Provider anlegen"
aktiviert ist, wird die E-Mail-Weiterleitung automatisch erstellt.
</p>
<Button onClick={openCreateModal}>
<Plus className="w-4 h-4 mr-2" />
@@ -550,6 +555,16 @@ export default function EmailProviders() {
required
/>
<Input
label="Bezeichnung für Kunden-E-Mails (UI-Label)"
value={formData.customerEmailLabel}
onChange={(e) => setFormData({ ...formData, customerEmailLabel: e.target.value })}
placeholder={`wird aus Domain abgeleitet, z.B. "${formData.domain ? formData.domain.split('.')[0].split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join('-') : 'Stressfrei-Wechseln'}"`}
/>
<p className="text-xs text-gray-500 -mt-2">
Wird überall dort angezeigt, wo es bisher "Stressfrei-Wechseln" hieß (z.B. Tab-Name, Adress-Listen). Wenn leer, wird der Name aus der Domain abgeleitet.
</p>
<Input
label="Standard-Weiterleitungsadresse"
value={formData.defaultForwardEmail}
@@ -758,7 +773,10 @@ export default function EmailProviders() {
<div>
<strong>IMAP</strong> fehlgeschlagen
{mailAccessResult.imap.server && (
<span className="text-xs opacity-75"> ({mailAccessResult.imap.server}:{mailAccessResult.imap.port})</span>
<span className="text-xs opacity-75">
{' '}({mailAccessResult.imap.server}:{mailAccessResult.imap.port}
{mailAccessResult.imap.encryption ? `, ${mailAccessResult.imap.encryption}` : ''})
</span>
)}
{mailAccessResult.imap.error && (
<div className="mt-1 text-xs">{mailAccessResult.imap.error}</div>
@@ -783,7 +801,10 @@ export default function EmailProviders() {
<div>
<strong>SMTP</strong> fehlgeschlagen
{mailAccessResult.smtp.server && (
<span className="text-xs opacity-75"> ({mailAccessResult.smtp.server}:{mailAccessResult.smtp.port})</span>
<span className="text-xs opacity-75">
{' '}({mailAccessResult.smtp.server}:{mailAccessResult.smtp.port}
{mailAccessResult.smtp.encryption ? `, ${mailAccessResult.smtp.encryption}` : ''})
</span>
)}
{mailAccessResult.smtp.error && (
<div className="mt-1 text-xs">{mailAccessResult.smtp.error}</div>