Drop accepted/revoked invites from the pending invites list (#61)

list_site_invites returned every invite ever sent, so the admin UI's
"Pending invites" section kept showing accepted/revoked rows forever
(just relabeled with a status badge) instead of dropping them. Filter
the query to pending only, and have the revoke action remove its row
from local state immediately instead of leaving a relabeled one behind.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 19:13:23 -06:00
co-authored by Claude Sonnet 5
parent ed88eb0205
commit d42bf114dd
4 changed files with 70 additions and 44 deletions
-29
View File
@@ -185,35 +185,6 @@
margin-bottom: var(--sp-2);
}
.invite-status-badge {
display: inline-flex;
align-items: center;
border-radius: var(--radius-pill);
font-size: 0.68rem;
font-weight: 800;
padding: 2px 8px;
text-transform: capitalize;
flex: none;
}
.invite-status-pending {
border: 1px solid var(--ds-border);
background: transparent;
color: var(--ds-muted);
}
.invite-status-accepted {
border: 1px solid color-mix(in srgb, var(--ds-accent) 50%, transparent);
background: color-mix(in srgb, var(--ds-accent) 14%, transparent);
color: var(--ds-accent);
}
.invite-status-revoked {
border: 1px solid color-mix(in srgb, var(--ds-danger) 50%, transparent);
background: color-mix(in srgb, var(--ds-danger) 14%, transparent);
color: var(--ds-danger);
}
.admin-token-list {
display: flex;
flex-direction: column;
+13 -15
View File
@@ -305,8 +305,10 @@ export function AdminPage() {
async function handleRevokeSiteInvite(invite: SiteInvite) {
await withBusy(invite.id, async () => {
const updated = await revokeSiteInvite(invite.id)
setSiteInvites((prev) => prev.map((i) => (i.id === updated.id ? updated : i)))
await revokeSiteInvite(invite.id)
// #61: the list is pending-only (server-filtered), so a revoked
// invite drops out of it rather than sticking around relabeled.
setSiteInvites((prev) => prev.filter((i) => i.id !== invite.id))
})
}
@@ -417,21 +419,17 @@ export function AdminPage() {
{siteInvites.map((invite) => (
<div key={invite.id} className="admin-token-row">
<span className="admin-token-scopes">{invite.email}</span>
<span className={`invite-status-badge invite-status-${invite.status}`}>{invite.status}</span>
<span className="admin-token-meta">
{invite.status === 'pending' &&
`Expires ${new Date(invite.expires_at).toLocaleDateString()}`}
Expires {new Date(invite.expires_at).toLocaleDateString()}
</span>
{invite.status === 'pending' && (
<button
type="button"
className="admin-token-revoke"
disabled={busyId === invite.id}
onClick={() => handleRevokeSiteInvite(invite)}
>
Revoke
</button>
)}
<button
type="button"
className="admin-token-revoke"
disabled={busyId === invite.id}
onClick={() => handleRevokeSiteInvite(invite)}
>
Revoke
</button>
</div>
))}
</div>