Fix scheduled job list/edit dropping message content

toClient() never returned the message body (html/text for email,
message for WhatsApp), so "Manage scheduled" showed nothing to view
or edit even though the content existed in storage. Editing a
scheduled WhatsApp message also saved to the wrong payload field
(text instead of message), silently discarding the edit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 00:00:43 +02:00
co-authored by Claude Sonnet 5
parent 75b2f8ccc5
commit be499f0d66
4 changed files with 24 additions and 14 deletions
@@ -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<ScheduledJob[]>([]);
const [loadingScheduled, setLoadingScheduled] = useState(false);
const [editing, setEditing] = useState<ScheduledJob | null>(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 <jane@example.com>
<span className="inline-block px-2 py-0.5 text-xs rounded border bg-gray-50">{job.kind}</span>
<span className="truncate">{job.subject || '(no subject)'}</span>
</div>
<div className="text-xs text-gray-600 mt-1 truncate">
{(() => {
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;
})()}
</div>
<div className="text-xs text-gray-600 mt-1">
<span className="mr-2">To: {job.recipient || 'Unknown recipients'}</span>
</div>
@@ -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<ScheduledJob[]>([]);
const [loadingScheduled, setLoadingScheduled] = useState(false);
const [editing, setEditing] = useState<ScheduledJob | null>(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() {
<div className="min-w-0">
<div className="text-sm font-medium text-gray-900 flex items-center gap-2">
<span className="inline-block px-2 py-0.5 text-xs rounded border bg-green-50 text-green-700">{job.broadcast ? "broadcast" : "attendees"}</span>
<span className="truncate text-gray-700">{job.payload?.message ? String(job.payload.message).slice(0, 60) + (String(job.payload.message).length > 60 ? "…" : "") : "(no message)"}</span>
<span className="truncate text-gray-700">{job.message ? String(job.message).slice(0, 60) + (String(job.message).length > 60 ? "…" : "") : "(no message)"}</span>
</div>
<div className="text-xs text-gray-600 mt-1">
<span className="mr-2">To: {job.recipient || "Unknown recipients"}</span>
@@ -902,7 +902,7 @@ function WhatsAppAttendeesPageInner() {
</div>
<div className="flex items-center gap-2 shrink-0">
<button type="button" disabled={job.status !== "queued"} className="px-2 py-1 text-xs rounded border bg-white hover:bg-gray-50 disabled:opacity-50"
onClick={() => { setEditing(job); setEditMessage(job.payload?.message || ""); try { setEditWhen(toLocalInputValue(new Date(job.scheduledAt))); } catch { setEditWhen(""); } }}>
onClick={() => { setEditing(job); setEditMessage(job.message || ""); try { setEditWhen(toLocalInputValue(new Date(job.scheduledAt))); } catch { setEditWhen(""); } }}>
Edit
</button>
<button type="button" disabled={job.status !== "queued"} className="px-2 py-1 text-xs rounded border bg-white hover:bg-gray-50 disabled:opacity-50" onClick={() => removeJob(job)}>