diff --git a/CHANGELOG.md b/CHANGELOG.md index 58b3bc0..e72c51a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project follows [Semantic Versioning](https://semver.org/). ### Fixed - Scheduling a WhatsApp message was silently sent as an email instead: the scheduled-job store never persisted the `channel` field, so the send worker always fell through to its email branch regardless of what was requested. Scheduled WhatsApp jobs now correctly send via WhatsApp. +- Scheduled emails/WhatsApp messages showed no content when viewing or editing them in "Manage scheduled", even though the message existed in storage: the list endpoint never returned the message body (`html`/`text` for email, `message` for WhatsApp), and editing a scheduled WhatsApp message saved to the wrong payload field (`text` instead of `message`), so edits were silently lost. Both are now fixed, and the email list also shows a body preview like the WhatsApp one already did. ### Added diff --git a/backend/src/controllers/scheduledEmailsController.js b/backend/src/controllers/scheduledEmailsController.js index 4d6b34d..c44f2fc 100644 --- a/backend/src/controllers/scheduledEmailsController.js +++ b/backend/src/controllers/scheduledEmailsController.js @@ -6,6 +6,7 @@ function toClient(job) { const subject = job?.payload?.subject || ''; const html = job?.payload?.html || ''; const text = job?.payload?.text || ''; + const message = job?.payload?.message || ''; return { id: job.id, kind, @@ -20,6 +21,9 @@ function toClient(job) { sentAt: job.sentAt || null, lastError: job.lastError || null, subject, + html, + text, + message, hasHtml: !!html, hasText: !!text, }; @@ -50,7 +54,7 @@ const listScheduledEmails = async (req, res) => { }; // PATCH /api/scheduled-emails/:id -// Allows editing scheduledAt, subject, html/text on queued jobs only +// Allows editing scheduledAt and message content (subject/html/text for email jobs, message for WhatsApp jobs) on queued jobs only const updateScheduledEmail = async (req, res) => { try { const { id } = req.params; @@ -58,7 +62,7 @@ const updateScheduledEmail = async (req, res) => { if (!job) return res.status(404).json({ message: 'Job not found' }); if (job.status !== 'queued') return res.status(400).json({ message: 'Only queued jobs can be edited' }); - const { scheduledAt, subject, html, text } = req.body || {}; + const { scheduledAt, subject, html, text, message } = req.body || {}; const patch = {}; if (scheduledAt) { @@ -66,14 +70,12 @@ const updateScheduledEmail = async (req, res) => { if (isNaN(when.getTime())) return res.status(400).json({ message: 'scheduledAt must be a valid ISO date-time' }); patch.scheduledAt = when.toISOString(); } - if (subject != null || html != null || text != null) { + if (subject != null || html != null || text != null || message != null) { const payload = { ...(job.payload || {}) }; if (subject != null) payload.subject = subject; - if (html != null || text != null) { - // If html provided explicitly, set html; if text provided, set text - if (html != null) payload.html = html; - if (text != null) payload.text = text; - } + if (html != null) payload.html = html; + if (text != null) payload.text = text; + if (message != null) payload.message = message; patch.payload = payload; } diff --git a/frontend/src/app/dashboard/supervisor/email-attendees/page.tsx b/frontend/src/app/dashboard/supervisor/email-attendees/page.tsx index eb452d1..4b3469b 100644 --- a/frontend/src/app/dashboard/supervisor/email-attendees/page.tsx +++ b/frontend/src/app/dashboard/supervisor/email-attendees/page.tsx @@ -261,7 +261,7 @@ function EmailAttendeesPageInner() { }; // Scheduled jobs state - type ScheduledJob = { id: string; kind: 'attendees'|'broadcast'|'unknown'; eventId?: string|null; broadcast?: boolean; channel?: string; recipient?: string|null; scheduledAt: string; createdAt: string; status: 'queued'|'sending'|'sent'|'error'; attempts: number; sentAt?: string|null; lastError?: string|null; subject?: string; hasHtml?: boolean; hasText?: boolean }; + type ScheduledJob = { id: string; kind: 'attendees'|'broadcast'|'unknown'; eventId?: string|null; broadcast?: boolean; channel?: string; recipient?: string|null; scheduledAt: string; createdAt: string; status: 'queued'|'sending'|'sent'|'error'; attempts: number; sentAt?: string|null; lastError?: string|null; subject?: string; html?: string; text?: string; hasHtml?: boolean; hasText?: boolean }; const [scheduled, setScheduled] = useState([]); const [loadingScheduled, setLoadingScheduled] = useState(false); const [editing, setEditing] = useState(null); @@ -288,7 +288,7 @@ function EmailAttendeesPageInner() { const openEdit = (job: ScheduledJob) => { setEditing(job); setEditSubject(job.subject || ''); - setEditBody(''); // body not included in list; will let user set a new one if needed + setEditBody(job.html || job.text || ''); try { setEditWhen(toLocalInputValue(new Date(job.scheduledAt))); } catch { setEditWhen(''); } }; @@ -882,6 +882,13 @@ Jane Doe {job.kind} {job.subject || '(no subject)'} +
+ {(() => { + const body = (job.html || job.text || '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim(); + if (!body) return '(no content)'; + return body.length > 80 ? body.slice(0, 80) + '…' : body; + })()} +
To: {job.recipient || 'Unknown recipients'}
diff --git a/frontend/src/app/dashboard/supervisor/whatsapp-attendees/page.tsx b/frontend/src/app/dashboard/supervisor/whatsapp-attendees/page.tsx index 4ea9121..9a01e5e 100644 --- a/frontend/src/app/dashboard/supervisor/whatsapp-attendees/page.tsx +++ b/frontend/src/app/dashboard/supervisor/whatsapp-attendees/page.tsx @@ -535,7 +535,7 @@ function WhatsAppAttendeesPageInner() { }; // ── Scheduled tab ────────────────────────────────────────────────────────── - type ScheduledJob = { id: string; kind: string; eventId?: string | null; broadcast?: boolean; channel?: string; recipient?: string | null; scheduledAt: string; createdAt: string; status: string; attempts: number; sentAt?: string | null; lastError?: string | null; subject?: string; payload?: any }; + type ScheduledJob = { id: string; kind: string; eventId?: string | null; broadcast?: boolean; channel?: string; recipient?: string | null; scheduledAt: string; createdAt: string; status: string; attempts: number; sentAt?: string | null; lastError?: string | null; subject?: string; message?: string }; const [scheduled, setScheduled] = useState([]); const [loadingScheduled, setLoadingScheduled] = useState(false); const [editing, setEditing] = useState(null); @@ -561,7 +561,7 @@ function WhatsAppAttendeesPageInner() { if (!token) { setError("Not authenticated"); return; } const body: any = {}; if (editWhen) body.scheduledAt = new Date(editWhen).toISOString(); - if (editMessage.trim()) body.text = editMessage; + if (editMessage.trim()) body.message = editMessage; await apiFetch(`/api/scheduled-emails/${encodeURIComponent(editing.id)}`, { method: "PATCH", authToken: token, body }); setInfo("Scheduled message updated."); setEditing(null); @@ -888,7 +888,7 @@ function WhatsAppAttendeesPageInner() {
{job.broadcast ? "broadcast" : "attendees"} - {job.payload?.message ? String(job.payload.message).slice(0, 60) + (String(job.payload.message).length > 60 ? "…" : "") : "(no message)"} + {job.message ? String(job.message).slice(0, 60) + (String(job.message).length > 60 ? "…" : "") : "(no message)"}
To: {job.recipient || "Unknown recipients"} @@ -902,7 +902,7 @@ function WhatsAppAttendeesPageInner() {