Add temporary admin contacts-census diagnostic (v0.1.0:105)

A throwaway, admin-only diagnostic for the deferred contacts <-> fundraising_contacts
consolidation: GET /api/admin/contacts-census + a Settings -> Admin "Run census" button
report the A/B/C populations (linked / contacts-only / pill-only) plus the
communications/opportunities repointing surface. Counts only, no PII -- mirrors
backend/scripts/contacts_census.sql so the numbers can be read off the box without a
shell. All pieces are tagged TEMPORARY; delete the endpoint + route + button after the
census is captured. No schema change. 45/45 tests, render-smoke green.
This commit is contained in:
Keysat
2026-06-20 21:26:06 -05:00
parent 9ffd4a1c6a
commit 6e760b19ee
7 changed files with 84 additions and 6 deletions
+27
View File
@@ -10699,6 +10699,31 @@
);
};
// TEMPORARY (v0.1.0:105) — DELETE AFTER the contacts<->fundraising_contacts census.
// Admin button that runs GET /api/admin/contacts-census and shows the counts (no PII), so the
// A/B/C populations can be read off the box without shell access. Remove with the endpoint.
const ContactsCensus = ({ token }) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const run = async () => {
setLoading(true);
try { const r = await api('/api/admin/contacts-census', {}, token); setData(r.data || {}); }
catch (err) { setData({ error: getErrorMessage(err, 'failed') }); }
finally { setLoading(false); }
};
return (
<div style={{ marginBottom: '20px', borderBottom: '1px solid var(--border)', paddingBottom: '16px' }}>
<div style={{ fontWeight: 600, marginBottom: '8px' }}>Contacts census <span style={{ fontSize: '11px', color: 'var(--text-muted)', fontWeight: 400 }}>(temporary — sizes the contacts ↔ grid consolidation)</span></div>
<button type="button" className="button-secondary" onClick={run} disabled={loading} style={{ marginBottom: '10px' }}>
{loading ? <Spinner /> : 'Run census'}
</button>
{data && (
<pre style={{ fontSize: '12px', color: 'var(--text-secondary)', background: 'var(--bg-input)', border: '1px solid var(--border)', borderRadius: '8px', padding: '10px', overflowX: 'auto', margin: 0 }}>{JSON.stringify(data, null, 2)}</pre>
)}
</div>
);
};
const SettingsPage = ({ token, onShowToast, user, onOpenAirtableImport }) => {
const [loading, setLoading] = useState(true);
const [inviteForm, setInviteForm] = useState({
@@ -11698,6 +11723,8 @@
<PurgeDeletedData token={token} onShowToast={onShowToast} />
<ContactsCensus token={token} />{/* TEMPORARY — remove after the consolidation census */}
<div>
<div style={{ fontWeight: 600, marginBottom: '10px' }}>Fundraising State Ops</div>
<div style={{ marginBottom: '12px', padding: '10px', border: '1px solid var(--border)', borderRadius: '8px' }}>