refactor(admin): split consent-management, control-library, incidents, training pages
Agent-completed splits committed after agents hit rate limits before committing their work. All 4 pages now under 500 LOC: - consent-management: 1303 -> 193 LOC (+ 7 _components, _hooks, _data, _types) - control-library: 1210 -> 298 LOC (+ _components, _types) - incidents: 1150 -> 373 LOC (+ _components) - training: 1127 -> 366 LOC (+ _components) Verification: next build clean (142 pages generated). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
'use client'
|
||||
|
||||
import type { ApiEmailTemplate, EmailTemplateData } from '../_types'
|
||||
import { emailTemplates, emailCategories } from '../_data'
|
||||
import { ApiTemplateEditor } from './ApiTemplateEditor'
|
||||
|
||||
export function EmailsTab({
|
||||
apiEmailTemplates,
|
||||
templatesLoading,
|
||||
savingTemplateId,
|
||||
savedTemplates,
|
||||
setShowCreateTemplateModal,
|
||||
saveApiEmailTemplate,
|
||||
setPreviewTemplate,
|
||||
setEditingTemplate,
|
||||
}: {
|
||||
apiEmailTemplates: ApiEmailTemplate[]
|
||||
templatesLoading: boolean
|
||||
savingTemplateId: string | null
|
||||
savedTemplates: Record<string, EmailTemplateData>
|
||||
setShowCreateTemplateModal: (v: boolean) => void
|
||||
saveApiEmailTemplate: (t: { id: string; subject: string; body: string }) => void
|
||||
setPreviewTemplate: (t: EmailTemplateData | null) => void
|
||||
setEditingTemplate: (t: EmailTemplateData | null) => void
|
||||
}) {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-slate-900">E-Mail Vorlagen</h2>
|
||||
<p className="text-sm text-slate-500 mt-1">
|
||||
{apiEmailTemplates.length > 0
|
||||
? `${apiEmailTemplates.length} DSGVO-Vorlagen aus der Datenbank`
|
||||
: '16 Lifecycle-Vorlagen fuer automatisierte Kommunikation'}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowCreateTemplateModal(true)}
|
||||
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors text-sm font-medium"
|
||||
>
|
||||
+ Neue Vorlage
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* API-backed templates section */}
|
||||
{templatesLoading ? (
|
||||
<div className="text-center py-8 text-slate-500">Lade Vorlagen aus der Datenbank...</div>
|
||||
) : apiEmailTemplates.length > 0 ? (
|
||||
<div className="space-y-4 mb-8">
|
||||
<h3 className="text-sm font-semibold text-slate-700 uppercase tracking-wider mb-3">DSGVO-Pflichtvorlagen</h3>
|
||||
{apiEmailTemplates.map((template) => (
|
||||
<ApiTemplateEditor
|
||||
key={template.id}
|
||||
template={template}
|
||||
saving={savingTemplateId === template.id}
|
||||
onSave={(subject, body) => saveApiEmailTemplate({ id: template.id, subject, body })}
|
||||
onPreview={(subject, body) => setPreviewTemplate({ key: template.template_key, subject, body })}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Category Filter for static templates */}
|
||||
{apiEmailTemplates.length === 0 && (
|
||||
<>
|
||||
<div className="flex flex-wrap gap-2 mb-6">
|
||||
<span className="text-sm text-slate-500 py-1">Filter:</span>
|
||||
{emailCategories.map((cat) => (
|
||||
<span key={cat.key} className={`px-3 py-1 rounded-full text-xs font-medium ${cat.color}`}>
|
||||
{cat.label}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Templates grouped by category (fallback when no API data) */}
|
||||
{emailCategories.map((category) => (
|
||||
<div key={category.key} className="mb-8">
|
||||
<h3 className="text-sm font-semibold text-slate-700 uppercase tracking-wider mb-3 flex items-center gap-2">
|
||||
<span className={`w-2 h-2 rounded-full ${category.color.split(' ')[0]}`}></span>
|
||||
{category.label}
|
||||
</h3>
|
||||
<div className="grid gap-3">
|
||||
{emailTemplates
|
||||
.filter((t) => t.category === category.key)
|
||||
.map((template) => (
|
||||
<div
|
||||
key={template.key}
|
||||
className="border border-slate-200 rounded-lg p-4 flex items-center justify-between hover:border-purple-300 transition-colors bg-white"
|
||||
>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`w-10 h-10 rounded-lg ${category.color} flex items-center justify-center text-lg`}>
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-medium text-slate-900">{template.name}</h4>
|
||||
<p className="text-sm text-slate-500">{template.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="px-2 py-1 bg-green-100 text-green-700 rounded text-xs">
|
||||
{savedTemplates[template.key] ? 'Angepasst' : 'Aktiv'}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => {
|
||||
const existing = savedTemplates[template.key]
|
||||
setEditingTemplate({
|
||||
key: template.key,
|
||||
subject: existing?.subject || `Betreff: ${template.name}`,
|
||||
body: existing?.body || `Sehr geehrte(r) {{name}},\n\n${template.description}\n\nMit freundlichen Gruessen\nIhr Datenschutz-Team`,
|
||||
})
|
||||
}}
|
||||
className="px-3 py-1.5 text-sm text-slate-600 hover:text-slate-900 border border-slate-300 rounded-lg hover:border-slate-400"
|
||||
>
|
||||
Bearbeiten
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const existing = savedTemplates[template.key]
|
||||
setPreviewTemplate({
|
||||
key: template.key,
|
||||
subject: existing?.subject || `Betreff: ${template.name}`,
|
||||
body: existing?.body || `Sehr geehrte(r) {{name}},\n\n${template.description}\n\nMit freundlichen Gruessen\nIhr Datenschutz-Team`,
|
||||
})
|
||||
}}
|
||||
className="px-3 py-1.5 text-sm text-slate-600 hover:text-slate-900 border border-slate-300 rounded-lg hover:border-slate-400"
|
||||
>
|
||||
Vorschau
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user