diff --git a/admin-compliance/app/sdk/email-templates/_components/EditorTab.tsx b/admin-compliance/app/sdk/email-templates/_components/EditorTab.tsx index 441d0cc..782db02 100644 --- a/admin-compliance/app/sdk/email-templates/_components/EditorTab.tsx +++ b/admin-compliance/app/sdk/email-templates/_components/EditorTab.tsx @@ -15,11 +15,16 @@ interface EditorTabProps { onPublish: () => void onPreview: () => void onBack: () => void + onSubmitForReview?: () => void + onApprove?: (comment?: string) => void + onReject?: (comment: string) => void + onSendTest?: (email: string) => void } export function EditorTab({ template, version, subject, html, previewHtml, saving, onSubjectChange, onHtmlChange, onSave, onPublish, onPreview, onBack, + onSubmitForReview, onApprove, onReject, onSendTest, }: EditorTabProps) { if (!template) { return ( @@ -46,30 +51,56 @@ export function EditorTab({ )} -
- - {version && version.status !== 'published' && ( - + )} + {/* Submit for Review — only for draft */} + {version && version.status === 'draft' && onSubmitForReview && ( + + )} + {/* Approve — only for review status (DSB) */} + {version && version.status === 'review' && onApprove && ( + + )} + {/* Reject — only for review status (DSB) */} + {version && version.status === 'review' && onReject && ( + + )} + {/* Publish — only for approved */} + {version && version.status === 'approved' && ( + )} + {/* Preview + Test — always when version exists */} {version && ( - + <> + + {onSendTest && ( + + )} + )}
diff --git a/admin-compliance/app/sdk/email-templates/_hooks/useEmailTemplates.ts b/admin-compliance/app/sdk/email-templates/_hooks/useEmailTemplates.ts index b51b6d6..41c1bdb 100644 --- a/admin-compliance/app/sdk/email-templates/_hooks/useEmailTemplates.ts +++ b/admin-compliance/app/sdk/email-templates/_hooks/useEmailTemplates.ts @@ -7,6 +7,7 @@ import { SendLog, Settings, TabId, + TemplateApproval, TemplateType, TemplateVersion, getHeaders, @@ -194,6 +195,72 @@ export function useEmailTemplates(activeTab: TabId) { } }, [settingsForm]) + // Workflow actions + const submitForReview = useCallback(async () => { + if (!editorVersion) return + setSaving(true) + try { + const res = await fetch(`${API_BASE}/versions/${editorVersion.id}/submit`, { + method: 'POST', headers: getHeaders(), + }) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + const updated = await res.json() + setEditorVersion(updated) + await loadTemplates() + } catch (e: any) { setError(e.message) } finally { setSaving(false) } + }, [editorVersion, loadTemplates]) + + const approveVersion = useCallback(async (comment?: string) => { + if (!editorVersion) return + setSaving(true) + try { + const res = await fetch(`${API_BASE}/versions/${editorVersion.id}/approve`, { + method: 'POST', headers: getHeaders(), + body: JSON.stringify({ comment: comment || '' }), + }) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + const updated = await res.json() + setEditorVersion(updated) + await loadTemplates() + } catch (e: any) { setError(e.message) } finally { setSaving(false) } + }, [editorVersion, loadTemplates]) + + const rejectVersion = useCallback(async (comment: string) => { + if (!editorVersion) return + setSaving(true) + try { + const res = await fetch(`${API_BASE}/versions/${editorVersion.id}/reject`, { + method: 'POST', headers: getHeaders(), + body: JSON.stringify({ comment }), + }) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + const updated = await res.json() + setEditorVersion(updated) + await loadTemplates() + } catch (e: any) { setError(e.message) } finally { setSaving(false) } + }, [editorVersion, loadTemplates]) + + const sendTestEmail = useCallback(async (recipientEmail: string) => { + if (!editorVersion) return + try { + const res = await fetch(`${API_BASE}/versions/${editorVersion.id}/send-test`, { + method: 'POST', headers: getHeaders(), + body: JSON.stringify({ recipient: recipientEmail }), + }) + if (!res.ok) throw new Error(`HTTP ${res.status}`) + return await res.json() + } catch (e: any) { setError(e.message) } + }, [editorVersion]) + + const loadApprovalHistory = useCallback(async (versionId: string): Promise => { + try { + const res = await fetch(`${API_BASE}/versions/${versionId}/approvals`, { headers: getHeaders() }) + if (!res.ok) return [] + const data = await res.json() + return Array.isArray(data) ? data : data.approvals || [] + } catch { return [] } + }, []) + const initializeDefaults = useCallback(async () => { try { const res = await fetch(`${API_BASE}/initialize`, { @@ -222,6 +289,8 @@ export function useEmailTemplates(activeTab: TabId) { setSettingsForm, // Actions openEditor, saveVersion, publishVersion, loadPreview, + submitForReview, approveVersion, rejectVersion, + sendTestEmail, loadApprovalHistory, saveSettings2, initializeDefaults, } } diff --git a/admin-compliance/app/sdk/email-templates/_types.ts b/admin-compliance/app/sdk/email-templates/_types.ts index 417121f..54d59ab 100644 --- a/admin-compliance/app/sdk/email-templates/_types.ts +++ b/admin-compliance/app/sdk/email-templates/_types.ts @@ -42,6 +42,16 @@ export interface SendLog { sent_at: string | null } +export interface TemplateApproval { + id: string + version_id: string + action: string // submitted, approved, rejected + actor_id: string + actor_name?: string + comment?: string + created_at: string +} + export interface Settings { sender_name: string sender_email: string diff --git a/admin-compliance/app/sdk/email-templates/page.tsx b/admin-compliance/app/sdk/email-templates/page.tsx index 32ef2c0..4368d25 100644 --- a/admin-compliance/app/sdk/email-templates/page.tsx +++ b/admin-compliance/app/sdk/email-templates/page.tsx @@ -22,6 +22,8 @@ export default function EmailTemplatesPage() { setEditorSubject, setEditorHtml, setSettingsForm, openEditor, saveVersion, publishVersion, loadPreview, + submitForReview, approveVersion, rejectVersion, + sendTestEmail, loadApprovalHistory, saveSettings2, initializeDefaults, } = useEmailTemplates(activeTab) @@ -68,6 +70,10 @@ export default function EmailTemplatesPage() { onPublish={publishVersion} onPreview={loadPreview} onBack={() => setActiveTab('templates')} + onSubmitForReview={submitForReview} + onApprove={approveVersion} + onReject={rejectVersion} + onSendTest={sendTestEmail} /> )}