/)
+ if (contentMatch) {
+ return contentMatch[1].trim()
+ }
+ return html
+ }, [])
+
+ // When langData changes (e.g., after refetch), update the body content
+ useEffect(() => {
+ if (langData) {
+ if (langData.is_default) {
+ // For default templates, extract just the content portion
+ setEditBody(extractBodyContent(langData.body_html))
+ } else {
+ setEditBody(langData.body_html)
+ }
+ setEditSubject(langData.subject)
+ setIsDirty(false)
+ }
+ }, [activeLang, langData, extractBodyContent])
+
+ // Save mutation
+ const saveMutation = useMutation({
+ mutationFn: () => adminEmailTemplatesApi.updateTemplate(detail.notification_type, activeLang, {
+ subject: editSubject,
+ body_html: editBody,
+ }),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['admin', 'email-templates'] })
+ queryClient.invalidateQueries({ queryKey: ['admin', 'email-template', detail.notification_type] })
+ setIsDirty(false)
+ showToast('success', t('admin.emailTemplates.saved', 'Template saved'))
+ },
+ onError: () => {
+ showToast('error', t('common.error', 'Error'))
+ },
+ })
+
+ // Reset mutation
+ const resetMutation = useMutation({
+ mutationFn: () => adminEmailTemplatesApi.deleteTemplate(detail.notification_type, activeLang),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['admin', 'email-templates'] })
+ queryClient.invalidateQueries({ queryKey: ['admin', 'email-template', detail.notification_type] })
+ setIsDirty(false)
+ showToast('success', t('admin.emailTemplates.resetted', 'Template reset to default'))
+ },
+ onError: () => {
+ showToast('error', t('common.error', 'Error'))
+ },
+ })
+
+ // Preview mutation
+ const previewMutation = useMutation({
+ mutationFn: () => adminEmailTemplatesApi.previewTemplate(detail.notification_type, {
+ language: activeLang,
+ subject: editSubject,
+ body_html: editBody,
+ }),
+ onSuccess: (data) => {
+ setPreviewHtml(data.body_html)
+ setShowPreview(true)
+ },
+ onError: () => {
+ showToast('error', t('common.error', 'Error'))
+ },
+ })
+
+ // Send test mutation
+ const testMutation = useMutation({
+ mutationFn: () => adminEmailTemplatesApi.sendTestEmail(detail.notification_type, {
+ language: activeLang,
+ }),
+ onSuccess: (data) => {
+ showToast('success', `${t('admin.emailTemplates.testSent', 'Test email sent')} → ${data.sent_to}`)
+ },
+ onError: () => {
+ showToast('error', t('common.error', 'Error'))
+ },
+ })
+
+ // Write preview HTML into iframe
+ useEffect(() => {
+ if (showPreview && iframeRef.current && previewHtml) {
+ const doc = iframeRef.current.contentDocument
+ if (doc) {
+ doc.open()
+ doc.write(previewHtml)
+ doc.close()
+ }
+ }
+ }, [showPreview, previewHtml])
+
+ const handleSubjectChange = (value: string) => {
+ setEditSubject(value)
+ setIsDirty(true)
+ }
+
+ const handleBodyChange = (value: string) => {
+ setEditBody(value)
+ setIsDirty(true)
+ }
+
+ const label = detail.label[interfaceLang] || detail.label['en'] || detail.notification_type
+
+ return (
+ (null)
+
+ // Fetch template types list
+ const { data: typesData, isLoading: typesLoading } = useQuery({
+ queryKey: ['admin', 'email-templates'],
+ queryFn: adminEmailTemplatesApi.getTemplateTypes,
+ })
+
+ // Fetch detail for selected type
+ const { data: detailData, isLoading: detailLoading } = useQuery({
+ queryKey: ['admin', 'email-template', selectedType],
+ queryFn: () => adminEmailTemplatesApi.getTemplate(selectedType!),
+ enabled: !!selectedType,
+ })
+
+ return (
+
+ {/* Header */}
+
+ )
+}
+
+// ============ Main Page ============
+
+export default function AdminEmailTemplates() {
+ const { t, i18n } = useTranslation()
+ const currentLang = i18n.language || 'ru'
+ const [selectedType, setSelectedType] = useState
+
+
+ {/* Language tabs */}
+
+
+
+ {langData && !langData.is_default && (
+
+ Custom
+
+ )}
+
+
+ {label}
++ {detail.description[interfaceLang] || detail.description['en'] || ''} +
+
+ {Object.keys(detail.languages).map(lang => {
+ const isActive = lang === activeLang
+ const langInfo = detail.languages[lang]
+ return (
+
+ )
+ })}
+
+
+ {/* Subject */}
+
+
+ handleSubjectChange(e.target.value)}
+ className="w-full px-3 py-2.5 bg-dark-900 border border-dark-600 rounded-lg text-sm text-dark-100 placeholder-dark-500 focus:outline-none focus:ring-1 focus:ring-accent-500 focus:border-accent-500 transition-colors"
+ placeholder={t('admin.emailTemplates.subjectPlaceholder', 'Email subject line...')}
+ />
+
+
+ {/* Context variables hint */}
+ {detail.context_vars.length > 0 && (
+
+
+ )}
+
+ {/* Body HTML editor */}
+ + {t('admin.emailTemplates.variables', 'Available Variables')} +
+
+ {detail.context_vars.map(v => (
+
+ {
+ navigator.clipboard.writeText(`{${v}}`)
+ showToast('success', `Copied {${v}}`)
+ }}
+ >
+ {`{${v}}`}
+
+ ))}
+
+
+
+
+ {/* Actions */}
+
+
+
+
+
+
+
+ {langData && !langData.is_default && (
+
+ )}
+
+
+ {/* Toast */}
+ {toast && (
+
+ {toast.message}
+
+ )}
+
+ {/* Preview Modal */}
+ {showPreview && (
+
+
+ )}
+
+
+
+
+ + {t('admin.emailTemplates.preview', 'Preview')} +
+ +
+
+
+
+ {/* Page Header */}
+ setSelectedType(null)}
+ currentLang={currentLang}
+ />
+ ) : (
+ <>
+ {/* Template List */}
+ {typesLoading ? (
+
+ )
+}
diff --git a/src/pages/AdminPanel.tsx b/src/pages/AdminPanel.tsx
index 3708998..28f0f6e 100644
--- a/src/pages/AdminPanel.tsx
+++ b/src/pages/AdminPanel.tsx
@@ -119,6 +119,12 @@ const ServerStackIcon = () => (
)
+const EnvelopeIcon = () => (
+
+)
+
const CubeTransparentIcon = () => (
+
+
+
+
+
+ {/* Content */}
+ {selectedType && detailData ? (
+
+
+
+
+
+
+
+ + {t('admin.emailTemplates.title', 'Email Templates')} +
++ {t('admin.emailTemplates.description', 'Manage email notification templates')} +
+
+ {[...Array(6)].map((_, i) => (
+
+ ))}
+
+ ) : (
+
+ {typesData?.items.map(template => (
+ setSelectedType(template.type)}
+ />
+ ))}
+
+ )}
+ >
+ )}
+
+ {/* Detail loading overlay */}
+ {selectedType && detailLoading && (
+
+
+
+ )}
+