email: single-mailbox enroll field on Email Capture panel — v0.1.0:60

Adds a "Test with a single mailbox first" input (pre-filled with the admin's own
address) + Enroll this mailbox button calling the enroll-one endpoint, so capture
can be tried on one mailbox before enrolling the whole domain. runAction now sends
an optional JSON body. Enroll-all stays.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Keysat
2026-06-06 12:10:09 -05:00
parent ee02ccfd64
commit 1850bc4431
4 changed files with 53 additions and 9 deletions
+31 -5
View File
@@ -9955,6 +9955,7 @@
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [busy, setBusy] = useState('');
const [oneEmail, setOneEmail] = useState(() => user?.email || '');
const load = useCallback(async () => {
let s;
@@ -9991,12 +9992,14 @@
return () => { cancelled = true; };
}, [load]);
const runAction = async (key, endpoint, successMsg, confirmMsg) => {
const runAction = async (key, endpoint, successMsg, confirmMsg, body) => {
if (busy) return;
if (confirmMsg && !window.confirm(confirmMsg)) return;
try {
setBusy(key);
const res = await api(endpoint, { method: 'POST' }, token);
const opts = { method: 'POST' };
if (body) opts.body = JSON.stringify(body);
const res = await api(endpoint, opts, token);
onShowToast(typeof successMsg === 'function' ? successMsg(res) : successMsg, 'success');
} catch (err) {
onShowToast(getErrorMessage(err, 'Action failed'), 'error');
@@ -10060,12 +10063,35 @@
{isAdmin && (
<div className="section">
<div className="section-title">Actions</div>
<div className="index-action-buttons">
<div className="index-action-hint" style={{ marginTop: 0, marginBottom: '8px' }}>
Test with a single mailbox first:
</div>
<div className="index-action-buttons" style={{ alignItems: 'center', marginBottom: '16px' }}>
<input
className="text-input"
type="email"
placeholder="name@ten31.xyz"
value={oneEmail}
onChange={(e) => setOneEmail(e.target.value)}
style={{ maxWidth: '260px' }}
/>
<button
onClick={() => runAction('enroll', '/api/email/accounts/enroll-all', (r) => `Enrolled ${r?.count ?? 0} mailbox(es)`, 'Enroll all Ten31 mailboxes for capture? This connects each active @ten31.xyz users Gmail via the service account. Domain-wide delegation must already be authorized in Google Workspace admin.')}
onClick={() => {
const addr = (oneEmail || '').trim();
if (!addr) { onShowToast('Enter a mailbox address', 'error'); return; }
runAction('enroll-one', '/api/email/accounts/enroll', (r) => `Enrolled ${r?.email || addr}`, `Enroll just ${addr} for capture?`, { email: addr });
}}
disabled={!enabled || !!busy}
>
{busy === 'enroll' ? 'Enrolling…' : 'Enroll Ten31 mailboxes'}
{busy === 'enroll-one' ? 'Enrolling…' : 'Enroll this mailbox'}
</button>
</div>
<div className="index-action-buttons">
<button
onClick={() => runAction('enroll', '/api/email/accounts/enroll-all', (r) => `Enrolled ${r?.count ?? 0} mailbox(es)`, 'Enroll ALL Ten31 mailboxes for capture? This connects every active @ten31.xyz users Gmail via the service account. Domain-wide delegation must already be authorized in Google Workspace admin.')}
disabled={!enabled || !!busy}
>
{busy === 'enroll' ? 'Enrolling…' : 'Enroll all Ten31 mailboxes'}
</button>
<button
onClick={() => runAction('sync', '/api/email/sync/run-now', 'Sync started')}
+3 -2
View File
@@ -24,8 +24,9 @@ export const PACKAGE_TITLE = 'Ten31 Database'
// * 0.1.0:56 (Thesis Workshop redesign: edit/choose/delete + approve-as-current)
// * 0.1.0:57 (redaction fix: magnitude regex no longer eats the word after an amount)
// * 0.1.0:58 (seed 5 Architect positioning framings into the Workshop as candidate options)
// * Current: 0.1.0:59 (Email Capture admin panel + matched email into the grounding corpus)
export const PACKAGE_VERSION = '0.1.0:59'
// * 0.1.0:59 (Email Capture admin panel + matched email into the grounding corpus)
// * Current: 0.1.0:60 (Email Capture: single-mailbox enroll field for testing)
export const PACKAGE_VERSION = '0.1.0:60'
export const DATA_MOUNT_PATH = '/data'
export const WEB_PORT = 8080
+3 -2
View File
@@ -20,8 +20,9 @@ import { v_0_1_0_56 } from './v0.1.0.56'
import { v_0_1_0_57 } from './v0.1.0.57'
import { v_0_1_0_58 } from './v0.1.0.58'
import { v_0_1_0_59 } from './v0.1.0.59'
import { v_0_1_0_60 } from './v0.1.0.60'
export const versionGraph = VersionGraph.of({
current: v_0_1_0_59,
other: [v_0_1_0_39, v_0_1_0_40, v_0_1_0_41, v_0_1_0_42, v_0_1_0_43, v_0_1_0_44, v_0_1_0_45, v_0_1_0_46, v_0_1_0_47, v_0_1_0_48, v_0_1_0_49, v_0_1_0_50, v_0_1_0_51, v_0_1_0_52, v_0_1_0_53, v_0_1_0_54, v_0_1_0_55, v_0_1_0_56, v_0_1_0_57, v_0_1_0_58],
current: v_0_1_0_60,
other: [v_0_1_0_39, v_0_1_0_40, v_0_1_0_41, v_0_1_0_42, v_0_1_0_43, v_0_1_0_44, v_0_1_0_45, v_0_1_0_46, v_0_1_0_47, v_0_1_0_48, v_0_1_0_49, v_0_1_0_50, v_0_1_0_51, v_0_1_0_52, v_0_1_0_53, v_0_1_0_54, v_0_1_0_55, v_0_1_0_56, v_0_1_0_57, v_0_1_0_58, v_0_1_0_59],
})
+16
View File
@@ -0,0 +1,16 @@
import { VersionInfo } from '@start9labs/start-sdk'
// Email Capture: add a single-mailbox enroll field (pre-filled with the admin's own
// address) so an admin can test capture on one mailbox before enrolling the whole
// domain. Calls the existing enroll-one endpoint; the enroll-all button stays. No
// schema migration.
export const v_0_1_0_60 = VersionInfo.of({
version: '0.1.0:60',
releaseNotes: {
en_US: [
'Email Capture: you can now enroll a single mailbox (pre-filled with your own address)',
'to test capture before turning it on for the whole team. The enroll-all option stays.',
].join(' '),
},
migrations: { up: async () => {}, down: async () => {} },
})