fix: Email template improvements
This commit is contained in:
@@ -30,6 +30,7 @@ import {
|
||||
CascadeDeleteResult,
|
||||
EmailTemplate,
|
||||
UpdateEmailTemplateRequest,
|
||||
PreviewTemplateResponse,
|
||||
// User management types
|
||||
ManagedUser,
|
||||
ListUsersResponse,
|
||||
@@ -278,15 +279,15 @@ export const adminApi = {
|
||||
const response = await apiClient.put<EmailTemplate>(`/admin/email-templates/${key}`, data);
|
||||
return response.data;
|
||||
},
|
||||
preview: async (key: string, variables: Record<string, string>): Promise<{ subject: string; body: string }> => {
|
||||
const response = await apiClient.post<{ subject: string; body: string }>(
|
||||
preview: async (key: string, variables: Record<string, string>): Promise<PreviewTemplateResponse> => {
|
||||
const response = await apiClient.post<PreviewTemplateResponse>(
|
||||
`/admin/email-templates/${key}/preview`,
|
||||
{ variables }
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
sendTest: async (key: string): Promise<{ message?: string; error?: string; subject: string; body: string }> => {
|
||||
const response = await apiClient.post<{ message?: string; error?: string; subject: string; body: string }>(
|
||||
sendTest: async (key: string): Promise<{ message?: string; error?: string }> => {
|
||||
const response = await apiClient.post<{ message?: string; error?: string }>(
|
||||
`/admin/email-templates/${key}/test`
|
||||
);
|
||||
return response.data;
|
||||
|
||||
@@ -38,6 +38,8 @@ export const AdminEmailTemplatesMobileScreen: React.FC = () => {
|
||||
const [editIsActive, setEditIsActive] = useState(true);
|
||||
const [previewSubject, setPreviewSubject] = useState('');
|
||||
const [previewBody, setPreviewBody] = useState('');
|
||||
const [previewHtml, setPreviewHtml] = useState('');
|
||||
const [showHtmlPreview, setShowHtmlPreview] = useState(false);
|
||||
|
||||
// Queries
|
||||
const { data: templates, isLoading } = useQuery({
|
||||
@@ -66,6 +68,7 @@ export const AdminEmailTemplatesMobileScreen: React.FC = () => {
|
||||
onSuccess: (data) => {
|
||||
setPreviewSubject(data.subject);
|
||||
setPreviewBody(data.body);
|
||||
setPreviewHtml(data.html || '');
|
||||
},
|
||||
onError: () => {
|
||||
toast.error('Failed to generate preview');
|
||||
@@ -117,6 +120,8 @@ export const AdminEmailTemplatesMobileScreen: React.FC = () => {
|
||||
setPreviewTemplate(null);
|
||||
setPreviewSubject('');
|
||||
setPreviewBody('');
|
||||
setPreviewHtml('');
|
||||
setShowHtmlPreview(false);
|
||||
}, []);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
@@ -304,6 +309,23 @@ export const AdminEmailTemplatesMobileScreen: React.FC = () => {
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Toggle HTML/Text Preview */}
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-slate-700">Show HTML Preview</span>
|
||||
<button
|
||||
onClick={() => setShowHtmlPreview(!showHtmlPreview)}
|
||||
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors min-h-[44px] min-w-[44px] ${
|
||||
showHtmlPreview ? 'bg-blue-600' : 'bg-gray-200'
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
|
||||
showHtmlPreview ? 'translate-x-6' : 'translate-x-1'
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1">
|
||||
Subject
|
||||
@@ -313,14 +335,29 @@ export const AdminEmailTemplatesMobileScreen: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1">
|
||||
Body
|
||||
</label>
|
||||
<div className="px-3 py-2 bg-gray-50 border border-gray-300 rounded-lg font-mono text-sm whitespace-pre-wrap">
|
||||
{previewBody}
|
||||
{showHtmlPreview ? (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1">
|
||||
HTML Preview
|
||||
</label>
|
||||
<div className="border border-gray-300 rounded-lg overflow-hidden bg-gray-50">
|
||||
<iframe
|
||||
srcDoc={previewHtml}
|
||||
style={{ width: '100%', height: '400px', border: 'none' }}
|
||||
title="Email HTML Preview"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1">
|
||||
Body (Plain Text)
|
||||
</label>
|
||||
<div className="px-3 py-2 bg-gray-50 border border-gray-300 rounded-lg font-mono text-sm whitespace-pre-wrap">
|
||||
{previewBody}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@@ -210,6 +210,12 @@ export interface UpdateEmailTemplateRequest {
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface PreviewTemplateResponse {
|
||||
subject: string;
|
||||
body: string;
|
||||
html: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// User Management types (subscription tiers)
|
||||
// ============================================
|
||||
|
||||
@@ -59,6 +59,8 @@ export const AdminEmailTemplatesPage: React.FC = () => {
|
||||
const [editIsActive, setEditIsActive] = useState(true);
|
||||
const [previewSubject, setPreviewSubject] = useState('');
|
||||
const [previewBody, setPreviewBody] = useState('');
|
||||
const [previewHtml, setPreviewHtml] = useState('');
|
||||
const [showHtmlPreview, setShowHtmlPreview] = useState(false);
|
||||
|
||||
// Queries
|
||||
const { data: templates, isLoading } = useQuery({
|
||||
@@ -87,6 +89,7 @@ export const AdminEmailTemplatesPage: React.FC = () => {
|
||||
onSuccess: (data) => {
|
||||
setPreviewSubject(data.subject);
|
||||
setPreviewBody(data.body);
|
||||
setPreviewHtml(data.html || '');
|
||||
setPreviewDialogOpen(true);
|
||||
},
|
||||
onError: () => {
|
||||
@@ -141,6 +144,8 @@ export const AdminEmailTemplatesPage: React.FC = () => {
|
||||
setPreviewDialogOpen(false);
|
||||
setPreviewSubject('');
|
||||
setPreviewBody('');
|
||||
setPreviewHtml('');
|
||||
setShowHtmlPreview(false);
|
||||
}, []);
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
@@ -362,6 +367,16 @@ export const AdminEmailTemplatesPage: React.FC = () => {
|
||||
This preview uses sample data to show how the template will appear.
|
||||
</Alert>
|
||||
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={showHtmlPreview}
|
||||
onChange={(e) => setShowHtmlPreview(e.target.checked)}
|
||||
/>
|
||||
}
|
||||
label="Show HTML Preview"
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Subject"
|
||||
fullWidth
|
||||
@@ -371,19 +386,41 @@ export const AdminEmailTemplatesPage: React.FC = () => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Body"
|
||||
fullWidth
|
||||
multiline
|
||||
rows={12}
|
||||
value={previewBody}
|
||||
InputProps={{
|
||||
readOnly: true,
|
||||
}}
|
||||
inputProps={{
|
||||
style: { fontFamily: 'monospace' },
|
||||
}}
|
||||
/>
|
||||
{showHtmlPreview ? (
|
||||
<Box>
|
||||
<Typography variant="subtitle2" gutterBottom>
|
||||
HTML Preview
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
border: '1px solid #e2e8f0',
|
||||
borderRadius: 1,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#f8f9fa',
|
||||
}}
|
||||
>
|
||||
<iframe
|
||||
srcDoc={previewHtml}
|
||||
style={{ width: '100%', height: '500px', border: 'none' }}
|
||||
title="Email HTML Preview"
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<TextField
|
||||
label="Body (Plain Text)"
|
||||
fullWidth
|
||||
multiline
|
||||
rows={12}
|
||||
value={previewBody}
|
||||
InputProps={{
|
||||
readOnly: true,
|
||||
}}
|
||||
inputProps={{
|
||||
style: { fontFamily: 'monospace' },
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
||||
Reference in New Issue
Block a user