'use client' import React, { useState, useEffect, useCallback } from 'react' import { useSDK } from '@/lib/sdk' import { StepHeader } from '@/components/sdk/StepHeader' // ============================================================================= // TYPES // ============================================================================= interface EmailTemplate { id: string template_type: string name: string description: string | null category: string is_active: boolean sort_order: number variables: string[] latest_version?: TemplateVersion | null created_at: string | null updated_at: string | null } interface TemplateVersion { id: string template_id: string version: string language: string subject: string body_html: string body_text: string | null status: string submitted_at: string | null published_at: string | null created_at: string | null } interface TemplateType { type: string name: string category: string variables: string[] } interface SendLog { id: string template_type: string recipient: string subject: string status: string sent_at: string | null } interface Settings { sender_name: string sender_email: string reply_to: string | null logo_url: string | null primary_color: string secondary_color: string footer_text: string company_name: string | null company_address: string | null } type TabId = 'templates' | 'editor' | 'settings' | 'logs' const API_BASE = '/api/sdk/v1/compliance/email-templates' function getHeaders(): HeadersInit { if (typeof window === 'undefined') return { 'Content-Type': 'application/json' } return { 'Content-Type': 'application/json', 'X-Tenant-ID': localStorage.getItem('bp_tenant_id') || '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e', 'X-User-ID': localStorage.getItem('bp_user_id') || '', } } // ============================================================================= // CATEGORY CONFIG // ============================================================================= const CATEGORIES: Record = { general: { label: 'Allgemein', color: 'text-gray-700', bgColor: 'bg-gray-100' }, dsr: { label: 'Betroffenenrechte', color: 'text-blue-700', bgColor: 'bg-blue-100' }, consent: { label: 'Einwilligung', color: 'text-green-700', bgColor: 'bg-green-100' }, breach: { label: 'Datenpanne', color: 'text-red-700', bgColor: 'bg-red-100' }, vendor: { label: 'Dienstleister', color: 'text-purple-700', bgColor: 'bg-purple-100' }, training: { label: 'Schulung', color: 'text-orange-700', bgColor: 'bg-orange-100' }, } const STATUS_BADGE: Record = { draft: { label: 'Entwurf', color: 'bg-gray-100 text-gray-700' }, review: { label: 'Pruefung', color: 'bg-yellow-100 text-yellow-700' }, approved: { label: 'Genehmigt', color: 'bg-blue-100 text-blue-700' }, published: { label: 'Publiziert', color: 'bg-green-100 text-green-700' }, } // ============================================================================= // PAGE // ============================================================================= export default function EmailTemplatesPage() { const sdk = useSDK() const [activeTab, setActiveTab] = useState('templates') const [templates, setTemplates] = useState([]) const [templateTypes, setTemplateTypes] = useState([]) const [settings, setSettings] = useState(null) const [logs, setLogs] = useState([]) const [logsTotal, setLogsTotal] = useState(0) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [selectedCategory, setSelectedCategory] = useState(null) // Editor state const [selectedTemplate, setSelectedTemplate] = useState(null) const [editorSubject, setEditorSubject] = useState('') const [editorHtml, setEditorHtml] = useState('') const [editorVersion, setEditorVersion] = useState(null) const [saving, setSaving] = useState(false) const [previewHtml, setPreviewHtml] = useState(null) // Settings form const [settingsForm, setSettingsForm] = useState(null) const [savingSettings, setSavingSettings] = useState(false) // ============================================================================= // DATA LOADING // ============================================================================= const loadTemplates = useCallback(async () => { try { const url = selectedCategory ? `${API_BASE}?category=${selectedCategory}` : API_BASE const res = await fetch(url, { headers: getHeaders() }) if (!res.ok) throw new Error(`HTTP ${res.status}`) const data = await res.json() setTemplates(Array.isArray(data) ? data : []) } catch (e: any) { setError(e.message) } }, [selectedCategory]) const loadTypes = useCallback(async () => { try { const res = await fetch(`${API_BASE}/types`, { headers: getHeaders() }) if (res.ok) { const data = await res.json() setTemplateTypes(Array.isArray(data) ? data : []) } } catch { /* ignore */ } }, []) const loadSettings = useCallback(async () => { try { const res = await fetch(`${API_BASE}/settings`, { headers: getHeaders() }) if (res.ok) { const data = await res.json() setSettings(data) setSettingsForm(data) } } catch { /* ignore */ } }, []) const loadLogs = useCallback(async () => { try { const res = await fetch(`${API_BASE}/logs?limit=50`, { headers: getHeaders() }) if (res.ok) { const data = await res.json() setLogs(data.logs || []) setLogsTotal(data.total || 0) } } catch { /* ignore */ } }, []) useEffect(() => { setLoading(true) Promise.all([loadTemplates(), loadTypes(), loadSettings()]) .finally(() => setLoading(false)) }, [loadTemplates, loadTypes, loadSettings]) useEffect(() => { if (activeTab === 'logs') loadLogs() }, [activeTab, loadLogs]) useEffect(() => { loadTemplates() }, [selectedCategory, loadTemplates]) // ============================================================================= // EDITOR ACTIONS // ============================================================================= const openEditor = useCallback(async (template: EmailTemplate) => { setSelectedTemplate(template) setActiveTab('editor') setPreviewHtml(null) if (template.latest_version) { setEditorSubject(template.latest_version.subject) setEditorHtml(template.latest_version.body_html) setEditorVersion(template.latest_version) } else { // Load default content try { const res = await fetch(`${API_BASE}/default/${template.template_type}`, { headers: getHeaders() }) if (res.ok) { const data = await res.json() setEditorSubject(data.default_subject || '') setEditorHtml(data.default_body_html || '') } } catch { /* ignore */ } setEditorVersion(null) } }, []) const saveVersion = useCallback(async () => { if (!selectedTemplate) return setSaving(true) try { const res = await fetch(`${API_BASE}/${selectedTemplate.id}/versions`, { method: 'POST', headers: getHeaders(), body: JSON.stringify({ version: editorVersion ? `${parseFloat(editorVersion.version) + 0.1}` : '1.0', language: 'de', subject: editorSubject, body_html: editorHtml, }), }) if (!res.ok) throw new Error(`HTTP ${res.status}`) const version = await res.json() setEditorVersion(version) await loadTemplates() } catch (e: any) { setError(e.message) } finally { setSaving(false) } }, [selectedTemplate, editorSubject, editorHtml, editorVersion, loadTemplates]) const publishVersion = useCallback(async () => { if (!editorVersion) return setSaving(true) try { const res = await fetch(`${API_BASE}/versions/${editorVersion.id}/publish`, { method: 'POST', headers: getHeaders(), }) if (!res.ok) throw new Error(`HTTP ${res.status}`) const updated = await res.json() setEditorVersion(updated) await loadTemplates() } catch (e: any) { setError(e.message) } finally { setSaving(false) } }, [editorVersion, loadTemplates]) const loadPreview = useCallback(async () => { if (!editorVersion) return try { const res = await fetch(`${API_BASE}/versions/${editorVersion.id}/preview`, { method: 'POST', headers: getHeaders(), body: JSON.stringify({ variables: {} }), }) if (res.ok) { const data = await res.json() setPreviewHtml(data.body_html) } } catch { /* ignore */ } }, [editorVersion]) const saveSettings2 = useCallback(async () => { if (!settingsForm) return setSavingSettings(true) try { const res = await fetch(`${API_BASE}/settings`, { method: 'PUT', headers: getHeaders(), body: JSON.stringify(settingsForm), }) if (res.ok) { const data = await res.json() setSettings(data) setSettingsForm(data) } } catch (e: any) { setError(e.message) } finally { setSavingSettings(false) } }, [settingsForm]) const initializeDefaults = useCallback(async () => { try { const res = await fetch(`${API_BASE}/initialize`, { method: 'POST', headers: getHeaders(), }) if (res.ok) { await loadTemplates() } } catch (e: any) { setError(e.message) } }, [loadTemplates]) // ============================================================================= // RENDER // ============================================================================= const tabs: { id: TabId; label: string }[] = [ { id: 'templates', label: 'Templates' }, { id: 'editor', label: 'Editor' }, { id: 'settings', label: 'Einstellungen' }, { id: 'logs', label: 'Logs' }, ] return (
{error && (
{error}
)} {/* Tab Navigation */}
{/* Tab Content */} {activeTab === 'templates' && ( )} {activeTab === 'editor' && ( setActiveTab('templates')} /> )} {activeTab === 'settings' && settingsForm && ( )} {activeTab === 'logs' && ( )}
) } // ============================================================================= // TAB COMPONENTS // ============================================================================= function TemplatesTab({ templates, loading, selectedCategory, onCategoryChange, onEdit, onInitialize, }: { templates: EmailTemplate[] loading: boolean selectedCategory: string | null onCategoryChange: (cat: string | null) => void onEdit: (t: EmailTemplate) => void onInitialize: () => void }) { return (
{/* Category Pills */}
{Object.entries(CATEGORIES).map(([key, cat]) => ( ))}
{loading ? (
Lade Templates...
) : templates.length === 0 ? (

Keine Templates vorhanden.

) : (
{templates.map(t => ( onEdit(t)} /> ))}
)}
) } function TemplateCard({ template, onEdit }: { template: EmailTemplate; onEdit: () => void }) { const cat = CATEGORIES[template.category] || CATEGORIES.general const version = template.latest_version const status = version ? (STATUS_BADGE[version.status] || STATUS_BADGE.draft) : STATUS_BADGE.draft return (

{template.name}

{cat.label}
{status.label}
{template.description && (

{template.description}

)}
{(template.variables || []).slice(0, 4).map(v => ( {`{{${v}}}`} ))} {(template.variables || []).length > 4 && ( +{template.variables.length - 4} )}
{version && (
v{version.version} · {version.created_at ? new Date(version.created_at).toLocaleDateString('de-DE') : ''}
)}
) } function EditorTab({ template, version, subject, html, previewHtml, saving, onSubjectChange, onHtmlChange, onSave, onPublish, onPreview, onBack, }: { template: EmailTemplate | null version: TemplateVersion | null subject: string html: string previewHtml: string | null saving: boolean onSubjectChange: (v: string) => void onHtmlChange: (v: string) => void onSave: () => void onPublish: () => void onPreview: () => void onBack: () => void }) { if (!template) { return (
Waehlen Sie ein Template aus der Liste.
) } const cat = CATEGORIES[template.category] || CATEGORIES.general return (

{template.name}

{cat.label} {version && ( {(STATUS_BADGE[version.status] || STATUS_BADGE.draft).label} )}
{version && version.status !== 'published' && ( )} {version && ( )}
{/* Variables */}
Variablen: {(template.variables || []).map(v => ( ))}
{/* Split View */}
{/* Editor */}
onSubjectChange(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent" placeholder="E-Mail Betreff..." />