Refactor MemberTransferDialog to improve configuration handling and UI

Updated the MemberTransferDialog component to enhance user experience by displaying configuration summaries and handling missing configurations more effectively. Removed redundant form sections and added conditional rendering for login credentials. Improved validation logic for transfer settings and introduced new styles for better visual clarity.
This commit is contained in:
Torsten Schulz (local)
2025-11-05 15:39:34 +01:00
parent 1f47a11091
commit c05cfbbe38

View File

@@ -12,160 +12,81 @@
</div>
<div v-else class="transfer-form">
<div class="form-section">
<h4>Server-Konfiguration</h4>
<div class="form-group">
<label for="server">Server-Basis-URL:</label>
<input
type="text"
id="server"
v-model="config.server"
placeholder="https://example.com"
class="form-input"
readonly
/>
<span class="hint">Aus Einstellungen geladen (nur lesend)</span>
</div>
<div v-if="!hasConfig" class="config-missing">
<p><strong>Keine Konfiguration gefunden</strong></p>
<p>Bitte konfigurieren Sie zuerst die Mitgliederübertragung in den Einstellungen.</p>
<router-link to="/member-transfer-settings" class="btn-link" @click="handleClose">
Zu den Einstellungen
</router-link>
</div>
<div class="form-section">
<h4>Login-Konfiguration</h4>
<div class="form-group">
<label for="loginEndpoint">Login-Endpoint Pfad:</label>
<input
type="text"
id="loginEndpoint"
v-model="config.loginEndpoint"
placeholder="/api/auth/login"
class="form-input"
/>
<span class="hint">Optional: Relativer Pfad zum Login-Endpoint</span>
</div>
<div class="form-group">
<label for="loginFormat">Login-Format:</label>
<select id="loginFormat" v-model="config.loginFormat" class="form-select">
<option value="json">JSON</option>
<option value="form-data">Form Data</option>
<option value="x-www-form-urlencoded">URL Encoded</option>
</select>
</div>
<div class="form-group">
<label>Login-Daten:</label>
<div class="credentials-group">
<div class="credential-row">
<input
type="text"
v-model="loginCredentials.username"
placeholder="Benutzername / Email"
class="form-input"
/>
<input
type="password"
v-model="loginCredentials.password"
placeholder="Passwort"
class="form-input"
/>
<div v-else>
<div class="config-summary">
<h4>Übertragungskonfiguration</h4>
<div class="summary-info">
<div class="summary-row">
<span class="summary-label">Server:</span>
<span class="summary-value">{{ config.server }}</span>
</div>
<div class="credential-row">
<input
type="text"
v-model="loginCredentials.additionalField1"
:placeholder="additionalField1Placeholder"
class="form-input"
/>
<input
type="text"
v-model="loginCredentials.additionalField2"
:placeholder="additionalField2Placeholder"
class="form-input"
/>
<div class="summary-row">
<span class="summary-label">Endpoint:</span>
<span class="summary-value">{{ config.transferEndpoint }}</span>
</div>
<div class="summary-row">
<span class="summary-label">Methode:</span>
<span class="summary-value">{{ config.transferMethod }}</span>
</div>
<div class="summary-row">
<span class="summary-label">Format:</span>
<span class="summary-value">{{ config.transferFormat }}</span>
</div>
<div class="summary-row">
<span class="summary-label">Modus:</span>
<span class="summary-value">{{ config.useBulkMode ? 'Bulk-Import' : 'Einzeln' }}</span>
</div>
</div>
<span class="hint">Zusätzliche Felder werden nur verwendet, wenn sie ausgefüllt sind</span>
</div>
</div>
<div class="form-section">
<h4>Übertragungs-Konfiguration</h4>
<div class="form-group">
<label for="transferEndpoint">Übertragungs-Endpoint Pfad: <span class="required">*</span></label>
<input
type="text"
id="transferEndpoint"
v-model="config.transferEndpoint"
placeholder="/api/members/bulk"
class="form-input"
required
/>
<span class="hint">Relativer Pfad zum Übertragungs-Endpoint</span>
</div>
<div class="form-row">
<div class="form-group">
<label for="transferMethod">HTTP-Methode:</label>
<select id="transferMethod" v-model="config.transferMethod" class="form-select">
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="GET">GET</option>
</select>
</div>
<div class="form-group">
<label for="transferFormat">Übertragungs-Format:</label>
<select id="transferFormat" v-model="config.transferFormat" class="form-select">
<option value="json">JSON</option>
<option value="xml">XML</option>
<option value="form-data">Form Data</option>
<option value="x-www-form-urlencoded">URL Encoded</option>
</select>
</div>
<router-link to="/member-transfer-settings" class="btn-link-small" @click="handleClose">
Konfiguration bearbeiten
</router-link>
</div>
<div class="form-group">
<label class="checkbox-item">
<input type="checkbox" v-model="config.useBulkMode" />
<span>Bulk-Import-Modus (alle Mitglieder auf einmal übertragen)</span>
</label>
<span class="hint">Wenn aktiviert, werden alle Mitglieder in einem Request als Array übertragen. Das Template definiert das Format für ein einzelnes Mitglied.</span>
</div>
<div class="form-group">
<label for="transferTemplate">Übertragungs-Template: <span class="required">*</span></label>
<textarea
id="transferTemplate"
v-model="config.transferTemplate"
rows="8"
class="form-textarea"
placeholder='{"name": "{{firstName}} {{lastName}}", "email": "{{email}}", "phone": "{{phone}}"}'
required
></textarea>
<div class="template-help">
<strong>Verfügbare Platzhalter:</strong>
<ul>
<li><code>{{firstName}}</code> - Vorname</li>
<li><code>{{lastName}}</code> - Nachname</li>
<li><code>{{fullName}}</code> - Vollständiger Name</li>
<li><code>{{email}}</code> - E-Mail-Adresse</li>
<li><code>{{phone}}</code> - Telefonnummer</li>
<li><code>{{street}}</code> - Straße</li>
<li><code>{{city}}</code> - Ort</li>
<li><code>{{birthDate}}</code> - Geburtsdatum (YYYY-MM-DD)</li>
<li><code>{{geburtsdatum}}</code> - Geburtsdatum (YYYY-MM-DD, alternativ)</li>
<li><code>{{address}}</code> - Kombinierte Adresse (Straße, Ort)</li>
<li><code>{{ttr}}</code> - TTR-Wert</li>
<li><code>{{qttr}}</code> - QTTR-Wert</li>
<li><code>{{gender}}</code> - Geschlecht</li>
</ul>
<p><strong>Beispiel JSON (für einzelnes Mitglied):</strong></p>
<pre>{{ jsonExample }}</pre>
<p v-if="config.useBulkMode" class="bulk-hint">
<strong>Bulk-Modus aktiv:</strong> Das Template wird für jedes Mitglied angewendet und automatisch in <code>{"members": [...]}</code> gewrappt.
</p>
<div v-if="config.loginEndpoint" class="form-section">
<h4>Login-Daten (optional überschreiben)</h4>
<p class="section-hint">Die Login-Daten werden aus den Einstellungen verwendet. Sie können sie hier überschreiben, falls nötig.</p>
<div class="form-group">
<label>Login-Daten:</label>
<div class="credentials-group">
<div class="credential-row">
<input
type="text"
v-model="loginCredentials.username"
placeholder="Benutzername / Email"
class="form-input"
/>
<input
type="password"
v-model="loginCredentials.password"
placeholder="Passwort (leer lassen für gespeichertes)"
class="form-input"
/>
</div>
<div class="credential-row">
<input
type="text"
v-model="loginCredentials.additionalField1"
:placeholder="additionalField1Placeholder"
class="form-input"
/>
<input
type="text"
v-model="loginCredentials.additionalField2"
:placeholder="additionalField2Placeholder"
class="form-input"
/>
</div>
</div>
<span class="hint">Nur ausfüllen, wenn Sie die gespeicherten Login-Daten überschreiben möchten. Leere Felder verwenden die gespeicherten Werte.</span>
</div>
</div>
</div>
@@ -204,6 +125,10 @@ export default {
...mapGetters(['currentClub']),
isValid() {
return this.hasConfig && !!(this.config.server && this.config.transferEndpoint && this.config.transferTemplate);
},
hasConfig() {
return !!(this.config.server && this.config.transferEndpoint && this.config.transferTemplate);
},
@@ -219,17 +144,6 @@ export default {
return 'Feldname: Wert (z.B. client_secret: xyz789)';
}
return 'Zusätzliches Feld (z.B. client_secret)';
},
jsonExample() {
return JSON.stringify({
firstName: "{{firstName}}",
lastName: "{{lastName}}",
geburtsdatum: "{{geburtsdatum}}",
email: "{{email}}",
phone: "{{phone}}",
address: "{{address}}"
}, null, 2);
}
},
data() {
@@ -242,7 +156,8 @@ export default {
transferMethod: 'POST',
transferFormat: 'json',
transferTemplate: '',
useBulkMode: false
useBulkMode: false,
bulkWrapperTemplate: ''
},
loginCredentials: {
username: '',
@@ -283,14 +198,15 @@ export default {
this.config.transferFormat = savedConfig.transferFormat || 'json';
this.config.transferTemplate = savedConfig.transferTemplate || '';
this.config.useBulkMode = savedConfig.useBulkMode || false;
this.config.bulkWrapperTemplate = savedConfig.bulkWrapperTemplate || '';
// Login-Credentials (Passwort wird nicht zurückgegeben, nur wenn vorhanden)
if (savedConfig.loginCredentials) {
this.loginCredentials.username = savedConfig.loginCredentials.username || '';
this.loginCredentials.password = ''; // Passwort wird nicht zurückgegeben
// Zusätzliche Felder können nicht direkt zugewiesen werden, da sie verschlüsselt sind
// Benutzer muss diese neu eingeben
}
// Login-Credentials zurücksetzen (werden nur verwendet, wenn im Dialog eingegeben)
this.loginCredentials = {
username: '',
password: '',
additionalField1: '',
additionalField2: ''
};
}
} catch (error) {
// Keine Konfiguration vorhanden - das ist OK, Dialog bleibt leer
@@ -315,7 +231,8 @@ export default {
transferMethod: 'POST',
transferFormat: 'json',
transferTemplate: '',
useBulkMode: false
useBulkMode: false,
bulkWrapperTemplate: ''
};
this.loginCredentials = {
username: '',
@@ -367,7 +284,8 @@ export default {
transferMethod: this.config.transferMethod,
transferFormat: this.config.transferFormat,
transferTemplate: this.config.transferTemplate,
useBulkMode: this.config.useBulkMode
useBulkMode: this.config.useBulkMode,
bulkWrapperTemplate: this.config.bulkWrapperTemplate || null
};
// Login-Konfiguration nur hinzufügen, wenn Endpoint vorhanden
@@ -578,6 +496,92 @@ export default {
color: #666;
}
.config-missing {
text-align: center;
padding: 2rem;
background-color: #fff3cd;
border: 1px solid #ffc107;
border-radius: 6px;
color: #856404;
}
.config-missing p {
margin: 0.5rem 0;
}
.btn-link {
display: inline-block;
margin-top: 1rem;
padding: 0.75rem 1.5rem;
background-color: #007bff;
color: white;
text-decoration: none;
border-radius: 4px;
font-weight: 500;
transition: background-color 0.2s;
}
.btn-link:hover {
background-color: #0056b3;
}
.config-summary {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 6px;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.config-summary h4 {
margin: 0 0 1rem 0;
color: #333;
font-size: 1.1em;
}
.summary-info {
display: flex;
flex-direction: column;
gap: 0.5rem;
margin-bottom: 1rem;
}
.summary-row {
display: flex;
gap: 1rem;
}
.summary-label {
font-weight: 600;
color: #555;
min-width: 100px;
}
.summary-value {
color: #333;
font-family: 'Courier New', monospace;
font-size: 0.9em;
}
.btn-link-small {
display: inline-block;
color: #007bff;
text-decoration: none;
font-size: 0.9em;
font-weight: 500;
}
.btn-link-small:hover {
text-decoration: underline;
}
.section-hint {
margin: 0 0 1rem 0;
font-size: 0.9em;
color: #666;
font-style: italic;
}
.btn-primary {
background-color: #007bff;
color: white;