Candidate UI: sync local state from server-returned candidate

Per Codex polish note: promote/reject/re-preview now Object.assign the
server-returned candidate onto the local one — keeps status/updated_at/preview
(and any future fields) in sync, while preserving the transient UI fields
(_cat/_activate/_err). Promote uses res.candidate then loadStats().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
jay
2026-06-09 10:39:32 -04:00
parent 1a8d1b3bf1
commit 373571b476
+6 -4
View File
@@ -139,26 +139,28 @@
addBusy = false;
}
}
// Merge the server-returned candidate (keeps status/updated_at/preview in sync)
// while preserving the transient UI-only fields (_cat/_activate/_err).
async function repreviewCandidate(c) {
c._err = '';
try { const u = await postJSON(`/api/admin/candidates/${c.id}/preview`); c.preview = u.preview; }
try { Object.assign(c, await postJSON(`/api/admin/candidates/${c.id}/preview`)); }
catch (e) { c._err = e?.message || 'Re-preview failed.'; }
}
async function promoteCandidate(c) {
c._err = '';
try {
await postJSON(`/api/admin/candidates/${c.id}/promote`, {
const res = await postJSON(`/api/admin/candidates/${c.id}/promote`, {
default_category: (c._cat || '').trim() || null,
active: !!c._activate,
});
c.status = 'promoted';
Object.assign(c, res.candidate);
await loadStats(); // surface the new (paused) source in the table below
} catch (e) {
c._err = e?.message || 'Promote failed.';
}
}
async function rejectCandidate(c) {
try { await postJSON(`/api/admin/candidates/${c.id}/reject`); c.status = 'rejected'; }
try { Object.assign(c, await postJSON(`/api/admin/candidates/${c.id}/reject`)); }
catch { /* leave as-is */ }
}