refactor(admin): split email-templates page.tsx into colocated components
Extract tabs nav, templates grid, editor split view, settings form, logs table, and data-loading/actions hook into _components/ and _hooks/. page.tsx reduced from 816 to 88 LOC. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
'use client'
|
||||
|
||||
import { CATEGORIES, EmailTemplate, STATUS_BADGE, TemplateVersion } from '../_types'
|
||||
|
||||
interface EditorTabProps {
|
||||
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
|
||||
}
|
||||
|
||||
export function EditorTab({
|
||||
template, version, subject, html, previewHtml, saving,
|
||||
onSubjectChange, onHtmlChange, onSave, onPublish, onPreview, onBack,
|
||||
}: EditorTabProps) {
|
||||
if (!template) {
|
||||
return (
|
||||
<div className="text-center py-12 text-gray-500">
|
||||
Waehlen Sie ein Template aus der Liste.
|
||||
<br />
|
||||
<button onClick={onBack} className="mt-2 text-purple-600 underline">Zurueck zur Liste</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const cat = CATEGORIES[template.category] || CATEGORIES.general
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<button onClick={onBack} className="text-gray-500 hover:text-gray-700">← Zurueck</button>
|
||||
<h2 className="text-lg font-semibold">{template.name}</h2>
|
||||
<span className={`px-2 py-0.5 rounded text-xs ${cat.bgColor} ${cat.color}`}>{cat.label}</span>
|
||||
{version && (
|
||||
<span className={`px-2 py-0.5 rounded text-xs ${(STATUS_BADGE[version.status] || STATUS_BADGE.draft).color}`}>
|
||||
{(STATUS_BADGE[version.status] || STATUS_BADGE.draft).label}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={onSave}
|
||||
disabled={saving}
|
||||
className="px-3 py-1.5 bg-purple-600 text-white rounded-lg text-sm hover:bg-purple-700 disabled:opacity-50"
|
||||
>
|
||||
{saving ? 'Speichern...' : 'Version speichern'}
|
||||
</button>
|
||||
{version && version.status !== 'published' && (
|
||||
<button
|
||||
onClick={onPublish}
|
||||
disabled={saving}
|
||||
className="px-3 py-1.5 bg-green-600 text-white rounded-lg text-sm hover:bg-green-700 disabled:opacity-50"
|
||||
>
|
||||
Publizieren
|
||||
</button>
|
||||
)}
|
||||
{version && (
|
||||
<button
|
||||
onClick={onPreview}
|
||||
className="px-3 py-1.5 border border-gray-300 text-gray-700 rounded-lg text-sm hover:bg-gray-50"
|
||||
>
|
||||
Vorschau
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Variables */}
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
<span className="text-xs text-gray-500 mr-1">Variablen:</span>
|
||||
{(template.variables || []).map(v => (
|
||||
<button
|
||||
key={v}
|
||||
onClick={() => onHtmlChange(html + `{{${v}}}`)}
|
||||
className="px-2 py-0.5 bg-purple-50 text-purple-700 rounded text-xs font-mono hover:bg-purple-100 transition-colors"
|
||||
>
|
||||
{`{{${v}}}`}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Split View */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{/* Editor */}
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Betreff</label>
|
||||
<input
|
||||
type="text"
|
||||
value={subject}
|
||||
onChange={e => 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..."
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">HTML-Inhalt</label>
|
||||
<textarea
|
||||
value={html}
|
||||
onChange={e => onHtmlChange(e.target.value)}
|
||||
rows={20}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm font-mono focus:ring-2 focus:ring-purple-500 focus:border-transparent resize-y"
|
||||
placeholder="<p>Sehr geehrte(r) {{user_name}},</p>"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Preview */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">Vorschau</label>
|
||||
<div className="border border-gray-200 rounded-lg bg-white p-4 min-h-[400px]">
|
||||
{previewHtml ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: previewHtml }} />
|
||||
) : (
|
||||
<div dangerouslySetInnerHTML={{ __html: html }} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user