feat(templates): add support for HTML templates
This commit is contained in:
committed by
Linus Rath
parent
0b62afb0f8
commit
0a30b2fb3a
@@ -1092,7 +1092,7 @@ export function EmailComposer({
|
||||
: template.body;
|
||||
|
||||
// In plain text mode, use template body as-is; otherwise convert to HTML
|
||||
const bodyContent = plainTextMode
|
||||
const bodyContent = plainTextMode || template.isHTML
|
||||
? filledBody
|
||||
: `<p>${filledBody.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>')}</p>`;
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ export function PlaceholderFillModal({
|
||||
<div className="mt-4 pt-4 border-t border-border">
|
||||
<p className="text-xs font-medium text-muted-foreground mb-2">{t('preview')}</p>
|
||||
<div className="text-sm text-foreground whitespace-pre-wrap p-3 rounded-md bg-muted/50 border border-border max-h-32 overflow-y-auto">
|
||||
{preview}
|
||||
{template.isHTML ? <div dangerouslySetInnerHTML={{ __html: preview }}></div> : preview}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useState, useMemo } from 'react';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Star, Plus } from 'lucide-react';
|
||||
import { Star, Plus, Square, CheckSquare } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { validateTemplateName } from '@/lib/template-utils';
|
||||
import { BUILT_IN_PLACEHOLDERS } from '@/lib/template-types';
|
||||
@@ -38,6 +38,7 @@ export function TemplateForm({ template, initialData, onSave, onCancel }: Templa
|
||||
const [category, setCategory] = useState(template?.category || '');
|
||||
const [subject, setSubject] = useState(template?.subject || initialData?.subject || '');
|
||||
const [body, setBody] = useState(template?.body || initialData?.body || '');
|
||||
const [isHTML, setIsHTML] = useState(template?.isHTML || false);
|
||||
const [toRecipients, setToRecipients] = useState(
|
||||
template?.defaultRecipients?.to?.join(', ') || initialData?.to?.join(', ') || ''
|
||||
);
|
||||
@@ -76,6 +77,7 @@ export function TemplateForm({ template, initialData, onSave, onCancel }: Templa
|
||||
name: name.trim(),
|
||||
subject,
|
||||
body,
|
||||
isHTML,
|
||||
category: category.trim(),
|
||||
defaultRecipients: to.length || cc.length || bcc.length
|
||||
? { to: to.length ? to : undefined, cc: cc.length ? cc : undefined, bcc: bcc.length ? bcc : undefined }
|
||||
@@ -190,6 +192,14 @@ export function TemplateForm({ template, initialData, onSave, onCancel }: Templa
|
||||
rows={6}
|
||||
className="mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary resize-y"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsHTML(!isHTML)}
|
||||
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
{isHTML ? <CheckSquare className={cn('w-4 h-4')}></CheckSquare> : <Square className={cn('w-4 h-4')}></Square>}
|
||||
HTML
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
|
||||
|
||||
@@ -20,6 +20,7 @@ function makeTemplate(overrides: Partial<EmailTemplate> = {}): EmailTemplate {
|
||||
name: 'Test Template',
|
||||
subject: '',
|
||||
body: '',
|
||||
isHTML: false,
|
||||
category: '',
|
||||
isFavorite: false,
|
||||
createdAt: '2026-01-01T00:00:00Z',
|
||||
|
||||
@@ -3,6 +3,7 @@ export interface EmailTemplate {
|
||||
name: string;
|
||||
subject: string;
|
||||
body: string;
|
||||
isHTML?: boolean;
|
||||
category: string;
|
||||
defaultRecipients?: {
|
||||
to?: string[];
|
||||
|
||||
@@ -173,7 +173,8 @@ export function importTemplates(json: string): ImportResult {
|
||||
id: generateUUID(),
|
||||
name: sanitizeText(t.name),
|
||||
subject: sanitizeText(t.subject),
|
||||
body: sanitizeText(t.body),
|
||||
body: t.isHTML ? String(t.body || '') : sanitizeText(t.body),
|
||||
isHTML: Boolean(t.isHTML),
|
||||
category: sanitizeText(t.category),
|
||||
defaultRecipients: recipients && typeof recipients === 'object'
|
||||
? {
|
||||
|
||||
Reference in New Issue
Block a user