E-Mail generierung

This commit is contained in:
titver968
2025-12-08 11:11:28 +01:00
parent 2a052478f0
commit b240c7ab12
2 changed files with 172 additions and 15 deletions

View File

@@ -20,7 +20,7 @@
wunsch3?: { id: number; name: string };
timestamp: number;
id: number;
status?: 'pending' | 'accepted' | 'rejected'; // processing entfernt
status?: 'pending' | 'accepted' | 'rejected';
assignedDienststelle?: { id: number; name: string };
processedBy?: string;
processedAt?: number;
@@ -35,7 +35,7 @@
let isLoading = true;
let error = '';
// Filter für Status (processing entfernt)
// Filter für Status
let statusFilter: 'all' | 'pending' | 'accepted' | 'rejected' = 'all';
let filteredAnmeldungen: Anmeldung[] = [];
@@ -63,7 +63,16 @@ Ihr Praktikumsteam`;
let isLoadingEmailConfig = false;
let isSavingEmailConfig = false;
// Status-Badge Funktionen (processing entfernt)
// E-Mail Preview Modal State
let showEmailPreview = false;
let emailPreviewData: {
to: string;
subject: string;
body: string;
} | null = null;
let emailCopied = false;
// Status-Badge Funktionen
function getStatusColor(status: string): string {
switch (status) {
case 'pending': return 'bg-yellow-100 text-yellow-800';
@@ -110,7 +119,6 @@ Ihr Praktikumsteam`;
const data = await res.json();
// Prüfen ob es ein Array ist
if (!Array.isArray(data)) {
console.error('❌ Antwort ist kein Array:', data);
throw new Error('Antwort vom Server ist kein Array');
@@ -143,7 +151,6 @@ Ihr Praktikumsteam`;
emailTemplate = config.template;
} catch (err) {
console.error('Fehler beim Laden der E-Mail-Konfiguration:', err);
// Fallback auf Standard-Werte bei Fehler
} finally {
isLoadingEmailConfig = false;
}
@@ -208,8 +215,8 @@ Ihr Praktikumsteam`;
showDialog = false;
// E-Mail senden nach erfolgreichem Annehmen
await sendAcceptanceEmail(selectedAnmeldungId, event.detail.dienststelleId);
// E-Mail Vorschau öffnen nach erfolgreichem Annehmen
openEmailPreview(selectedAnmeldungId, event.detail.dienststelleId);
selectedAnmeldungId = null;
await loadAnmeldungen();
@@ -219,13 +226,15 @@ Ihr Praktikumsteam`;
}
}
async function sendAcceptanceEmail(anmeldungId: number, dienststelleId: number) {
function openEmailPreview(anmeldungId: number, dienststelleId: number) {
const anmeldung = anmeldungen.find(a => a.id === anmeldungId);
if (!anmeldung) return;
// Dienststelle finden
const dienststelle = availableWishes.find(w => w.id === dienststelleId);
const dienststelleName = dienststelle ? dienststelle.name.replace(/^\d+\.\s*Wunsch:\s*/, '') : 'Unbekannte Dienststelle';
const dienststelleName = dienststelle
? dienststelle.name.replace(/^\d+\.\s*Wunsch:\s*/, '')
: 'Unbekannte Dienststelle';
// E-Mail Text personalisieren
const personalizedEmail = emailTemplate
@@ -234,11 +243,43 @@ Ihr Praktikumsteam`;
.replace(/\{nachname\}/g, anmeldung.nachname)
.replace(/\{dienststelle\}/g, dienststelleName);
// mailto: Link erstellen
const mailtoLink = `mailto:${anmeldung.email}?subject=${encodeURIComponent(emailSubject)}&body=${encodeURIComponent(personalizedEmail)}`;
// Modal mit Vorschau öffnen
emailPreviewData = {
to: anmeldung.email,
subject: emailSubject,
body: personalizedEmail
};
emailCopied = false;
showEmailPreview = true;
}
async function copyAndOpenMail() {
if (!emailPreviewData) return;
// Standard E-Mail Client öffnen
window.location.href = mailtoLink;
try {
await navigator.clipboard.writeText(emailPreviewData.body);
emailCopied = true;
// Kurz warten, dann Mail öffnen
setTimeout(() => {
const mailtoLink = `mailto:${emailPreviewData!.to}?subject=${encodeURIComponent(emailPreviewData!.subject)}`;
window.location.href = mailtoLink;
}, 500);
// Modal nach 2 Sekunden schließen
setTimeout(() => {
closeEmailPreview();
}, 2000);
} catch (err) {
console.error('Clipboard-Fehler:', err);
error = 'Konnte Text nicht kopieren. Bitte manuell markieren und kopieren.';
}
}
function closeEmailPreview() {
showEmailPreview = false;
emailPreviewData = null;
emailCopied = false;
}
async function handleReject(event: CustomEvent<{id: number}>) {
@@ -305,7 +346,7 @@ Ihr Praktikumsteam`;
<main class="max-w-7xl mx-auto px-4 py-6">
<!-- Filter und E-Mail Konfiguration -->
<div class="mb-6 flex justify-between items-center">
<!-- Status Filter (processing entfernt) -->
<!-- Status Filter -->
<div class="flex items-center space-x-4">
<label for="status-filter" class="text-sm font-medium text-gray-700">Filter:</label>
<select
@@ -337,7 +378,7 @@ Ihr Praktikumsteam`;
</button>
</div>
<!-- Status Übersicht (processing entfernt) -->
<!-- Status Übersicht -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div class="bg-white rounded-lg border border-gray-200 p-4">
<div class="flex items-center">
@@ -504,6 +545,7 @@ Ihr Praktikumsteam`;
</main>
</div>
<!-- Dienststellen Dialog -->
{#if showDialog}
<DienststellenDialog
wishes={availableWishes}
@@ -511,4 +553,119 @@ Ihr Praktikumsteam`;
on:confirm={handleConfirmAccept}
on:cancel={closeDialog}
/>
{/if}
<!-- E-Mail Vorschau Modal -->
{#if showEmailPreview && emailPreviewData}
<div class="fixed inset-0 z-50 overflow-y-auto">
<div class="flex min-h-screen items-center justify-center p-4">
<!-- Backdrop -->
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
class="fixed inset-0 bg-black bg-opacity-50 transition-opacity"
on:click={closeEmailPreview}
></div>
<!-- Modal -->
<div class="relative bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden">
<!-- Header -->
<div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center bg-white">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 bg-blue-100 rounded-full flex items-center justify-center">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
</svg>
</div>
<h3 class="text-lg font-medium text-gray-900">E-Mail Vorschau</h3>
</div>
<button
on:click={closeEmailPreview}
class="text-gray-400 hover:text-gray-600 transition-colors"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- Content -->
<div class="px-6 py-4 overflow-y-auto max-h-[60vh]">
<div class="space-y-4">
<!-- Empfänger -->
<div class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg">
<span class="text-sm font-medium text-gray-500 w-16">An:</span>
<span class="text-sm text-gray-900">{emailPreviewData.to}</span>
</div>
<!-- Betreff -->
<div class="flex items-center space-x-3 p-3 bg-gray-50 rounded-lg">
<span class="text-sm font-medium text-gray-500 w-16">Betreff:</span>
<span class="text-sm text-gray-900">{emailPreviewData.subject}</span>
</div>
<!-- Nachricht (editierbar) -->
<div>
<label for="email-body" class="block text-sm font-medium text-gray-700 mb-2">
Nachricht
<span class="font-normal text-gray-500">(kann bearbeitet werden)</span>
</label>
<textarea
id="email-body"
bind:value={emailPreviewData.body}
rows="14"
class="w-full border border-gray-300 rounded-lg px-4 py-3 text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 resize-none"
disabled={emailCopied}
></textarea>
</div>
</div>
</div>
<!-- Footer -->
<div class="px-6 py-4 border-t border-gray-200 bg-gray-50">
{#if emailCopied}
<div class="flex items-center justify-center py-3">
<div class="flex items-center space-x-3 text-green-600">
<div class="w-8 h-8 bg-green-100 rounded-full flex items-center justify-center">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
<span class="font-medium">Text kopiert Outlook öffnet sich...</span>
</div>
</div>
{:else}
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
<div class="flex items-start space-x-2 text-sm text-gray-600">
<svg class="w-5 h-5 text-blue-500 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span>
Der Text wird kopiert und Outlook geöffnet.<br class="hidden sm:inline" />
Dann mit <kbd class="px-1.5 py-0.5 bg-gray-200 rounded text-xs font-mono">Strg</kbd> + <kbd class="px-1.5 py-0.5 bg-gray-200 rounded text-xs font-mono">V</kbd> einfügen.
</span>
</div>
<div class="flex space-x-3 w-full sm:w-auto">
<button
on:click={closeEmailPreview}
class="flex-1 sm:flex-none px-4 py-2.5 text-sm text-gray-600 hover:text-gray-800 hover:bg-gray-100 rounded-lg transition-colors"
>
Abbrechen
</button>
<button
on:click={copyAndOpenMail}
class="flex-1 sm:flex-none bg-blue-600 hover:bg-blue-700 text-white px-5 py-2.5 rounded-lg text-sm font-medium inline-flex items-center justify-center transition-colors"
>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
</svg>
Kopieren & E-Mail öffnen
</button>
</div>
</div>
{/if}
</div>
</div>
</div>
</div>
{/if}