@bernierllc/email-ui
Version:
React UI components for email management, templates, scheduling, and analytics
108 lines (106 loc) • 6.65 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
/*
Copyright (c) 2025 Bernier LLC
This file is licensed to the client under a limited-use license.
The client may use and modify this code *only within the scope of the project it was delivered for*.
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
*/
import { useState, useEffect } from 'react';
// Integration detection helpers
const detectLogger = () => {
try {
// Try to detect logger in various contexts
if (globalThis.__BERNIER_LOGGER__) {
return globalThis.__BERNIER_LOGGER__;
}
return null;
}
catch {
return null;
}
};
const detectNeverHub = () => {
try {
if (globalThis.__NEVERHUB_ADAPTER__) {
return globalThis.__NEVERHUB_ADAPTER__;
}
return null;
}
catch {
return null;
}
};
export const TemplateEditor = ({ template, onSave, onCancel, isLoading = false, className = '', style, }) => {
const [formData, setFormData] = useState({
name: template?.name || '',
subject: template?.subject || '',
content: template?.content || '',
category: template?.category || '',
tags: template?.tags || [],
});
const [previewMode, setPreviewMode] = useState(false);
// Integration state
const [logger] = useState(() => detectLogger());
const [neverhub] = useState(() => detectNeverHub());
// Component lifecycle logging
useEffect(() => {
logger?.info('TemplateEditor mounted', {
templateId: template?.id,
hasIntegrations: {
logger: !!logger,
neverhub: !!neverhub
}
});
return () => {
logger?.debug('TemplateEditor unmounting');
};
}, [logger, neverhub, template?.id]);
const extractVariables = (text) => {
const matches = text.match(/\{\{(\w+)\}\}/g);
return matches ? [...new Set(matches.map(m => m.slice(2, -2)))] : [];
};
const handleSubmit = async (e) => {
e.preventDefault();
try {
logger?.info('Template save initiated', {
templateId: template?.id,
templateName: formData.name
});
const extractedVariables = [
...extractVariables(formData.subject),
...extractVariables(formData.content),
];
const templateData = {
...formData,
variables: extractedVariables,
};
await onSave(templateData);
// Log success and publish NeverHub event
logger?.info('Template saved successfully', {
templateId: template?.id,
variableCount: extractedVariables.length
});
if (neverhub) {
await neverhub.publishEvent({
type: 'email.template.saved',
data: {
templateId: template?.id,
templateName: formData.name,
variableCount: extractedVariables.length,
isUpdate: !!template?.id
}
}).catch(err => {
logger?.error('Failed to publish template saved event', err);
});
}
}
catch (error) {
logger?.error('Template save failed', error);
throw error; // Re-throw to allow parent components to handle
}
};
const handleInputChange = (field, value) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
return (_jsxs("div", { className: `template-editor ${className}`, style: style, children: [_jsxs("div", { className: "template-editor__header", children: [_jsx("h2", { className: "template-editor__title", children: template?.id ? 'Edit Template' : 'Create Template' }), _jsx("div", { className: "template-editor__actions", children: _jsx("button", { type: "button", onClick: () => setPreviewMode(!previewMode), className: "btn btn--secondary", children: previewMode ? 'Edit' : 'Preview' }) })] }), _jsxs("form", { onSubmit: handleSubmit, className: "template-editor__form", children: [_jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: "name", className: "form-label", children: "Template Name" }), _jsx("input", { id: "name", type: "text", value: formData.name, onChange: (e) => handleInputChange('name', e.target.value), className: "form-input", placeholder: "Enter template name", required: true })] }), _jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: "subject", className: "form-label", children: "Subject Line" }), _jsx("input", { id: "subject", type: "text", value: formData.subject, onChange: (e) => handleInputChange('subject', e.target.value), className: "form-input", placeholder: "Enter email subject", required: true })] }), _jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: "category", className: "form-label", children: "Category" }), _jsx("input", { id: "category", type: "text", value: formData.category, onChange: (e) => handleInputChange('category', e.target.value), className: "form-input", placeholder: "Template category (optional)" })] }), _jsxs("div", { className: "form-group", children: [_jsx("label", { htmlFor: "content", className: "form-label", children: "Email Content" }), previewMode ? (_jsx("div", { className: "template-preview", children: _jsx("div", { dangerouslySetInnerHTML: { __html: formData.content } }) })) : (_jsx("textarea", { id: "content", rows: 12, value: formData.content, onChange: (e) => handleInputChange('content', e.target.value), className: "form-textarea", placeholder: "Enter email content. Use {{variable}} for dynamic content.", required: true }))] }), extractVariables(formData.content).length > 0 && (_jsxs("div", { className: "template-editor__variables", children: [_jsx("h3", { children: "Detected Variables:" }), _jsx("div", { className: "variable-tags", children: extractVariables(formData.content).map((variable) => (_jsx("span", { className: "variable-tag", children: variable }, variable))) })] })), _jsxs("div", { className: "template-editor__footer", children: [onCancel && (_jsx("button", { type: "button", onClick: onCancel, className: "btn btn--secondary", disabled: isLoading, children: "Cancel" })), _jsx("button", { type: "submit", className: "btn btn--primary", disabled: isLoading, children: isLoading ? 'Saving...' : 'Save Template' })] })] })] }));
};