import React, { useEffect, useMemo, useState } from 'react'; import axios from 'axios'; import { useToast } from '../contexts/ToastContext'; interface EmailTemplate { template_key: string; name: string; subject: string; html_body: string; text_body: string; variables: string; is_active: boolean; } interface EmailTemplateManagementProps { searchTerm?: string; statusFilter?: 'all' | 'active' | 'inactive'; refreshToken?: number; } type TemplateSortKey = 'name' | 'key' | 'subject' | 'variables' | 'status'; const parseTemplateVariables = (variables: string): string[] => { try { const parsed = JSON.parse(variables); return Array.isArray(parsed) ? parsed : []; } catch { return variables .split(',') .map((value) => value.trim()) .filter(Boolean); } }; const EmailTemplateManagement: React.FC = ({ searchTerm = '', statusFilter = 'all', refreshToken = 0 }) => { const toast = useToast(); const [templates, setTemplates] = useState([]); const [loading, setLoading] = useState(true); const [editingTemplate, setEditingTemplate] = useState(null); const [sortKey, setSortKey] = useState('name'); const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc'); useEffect(() => { void fetchTemplates(); }, []); useEffect(() => { if (refreshToken > 0) { void fetchTemplates(); } }, [refreshToken]); const fetchTemplates = async () => { try { setLoading(true); const token = localStorage.getItem('token'); const response = await axios.get('/api/v1/email-templates/', { headers: { Authorization: `Bearer ${token}` } }); setTemplates(response.data); } catch (error) { console.error('Error fetching email templates:', error); toast.error('Failed to load email templates.'); } finally { setLoading(false); } }; const handleSaveTemplate = async (updatedTemplate: EmailTemplate) => { try { const token = localStorage.getItem('token'); await axios.put(`/api/v1/email-templates/${updatedTemplate.template_key}`, updatedTemplate, { headers: { Authorization: `Bearer ${token}` } }); setEditingTemplate(null); toast.success('Email template updated.'); void fetchTemplates(); } catch (error) { console.error('Error updating email template:', error); toast.error('Failed to update email template.'); } }; const filteredTemplates = useMemo(() => { const normalizedSearch = searchTerm.trim().toLowerCase(); return templates.filter((template) => { const matchesSearch = normalizedSearch === '' || [ template.name, template.template_key, template.subject ].some((value) => value.toLowerCase().includes(normalizedSearch)); const matchesStatus = statusFilter === 'all' || (statusFilter === 'active' && template.is_active) || (statusFilter === 'inactive' && !template.is_active); return matchesSearch && matchesStatus; }); }, [searchTerm, statusFilter, templates]); const sortedTemplates = useMemo(() => { const compareValues = (left: string | number, right: string | number) => { if (typeof left === 'number' && typeof right === 'number') return left - right; return String(left).localeCompare(String(right), undefined, { numeric: true, sensitivity: 'base' }); }; const sorted = [...filteredTemplates].sort((left, right) => { let result = 0; switch (sortKey) { case 'name': result = compareValues(left.name, right.name); break; case 'key': result = compareValues(left.template_key, right.template_key); break; case 'subject': result = compareValues(left.subject, right.subject); break; case 'variables': result = compareValues(parseTemplateVariables(left.variables).length, parseTemplateVariables(right.variables).length); break; case 'status': result = compareValues(left.is_active ? 0 : 1, right.is_active ? 0 : 1); break; } if (result === 0) { result = compareValues(left.name, right.name); } return sortDirection === 'asc' ? result : -result; }); return sorted; }, [filteredTemplates, sortDirection, sortKey]); const toggleSort = (nextKey: TemplateSortKey) => { if (sortKey === nextKey) { setSortDirection((prev) => (prev === 'asc' ? 'desc' : 'asc')); return; } setSortKey(nextKey); setSortDirection('asc'); }; const renderSortArrow = (active: boolean, direction: 'asc' | 'desc') => ( ); if (loading) { return

Loading email templates...

; } return (
{sortedTemplates.length === 0 ? ( ) : ( sortedTemplates.map((template) => { const variables = parseTemplateVariables(template.variables); return ( setEditingTemplate(template)}> ); }) )}
Actions
No templates match the current filters.
{template.name}
{variables.length} variable{variables.length === 1 ? '' : 's'}
{template.template_key} {template.subject} {variables.length > 0 ? (
{variables.slice(0, 3).map((variable) => ( {variable} ))} {variables.length > 3 && +{variables.length - 3} more}
) : ( None )}
{template.is_active ? 'Active' : 'Inactive'}
{editingTemplate && ( setEditingTemplate(null)} /> )}
); }; interface EmailTemplateEditFormProps { template: EmailTemplate; onSave: (template: EmailTemplate) => void; onCancel: () => void; } const EmailTemplateEditForm: React.FC = ({ template, onSave, onCancel }) => { const [previewMode, setPreviewMode] = useState<'rendered' | 'html' | 'text'>('rendered'); const [formData, setFormData] = useState({ name: template.name, subject: template.subject, html_body: template.html_body, text_body: template.text_body, variables: (() => { try { const vars = JSON.parse(template.variables); return Array.isArray(vars) ? vars : []; } catch { return []; } })(), is_active: template.is_active }); const handleChange = (field: keyof typeof formData, value: any) => { setFormData((prev) => ({ ...prev, [field]: value })); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); onSave({ template_key: template.template_key, ...formData, variables: JSON.stringify(formData.variables) }); }; const previewDocument = useMemo(() => { return ` ${formData.html_body} `; }, [formData.html_body]); return (