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' && (
-
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}
/>
)}