Private
Public Access
Add the ability to resend unaccepted invites (#60)
Previously the only way to resend was to re-invite the same email from scratch, creating a whole new invite row. Adds a "Resend" action next to Revoke on each pending invite -- rotates the token and refreshes the 7-day expiry on the same row (the old link stops working the moment it's used, same instinct as a password-reset resend), then re-sends the invite email. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -83,6 +83,13 @@ export function revokeSiteInvite(inviteId: string): Promise<SiteInvite> {
|
||||
return apiFetch<SiteInvite>(`/api/admin/invites/${inviteId}`, { method: 'DELETE' })
|
||||
}
|
||||
|
||||
// #60: same invite row, not a new one -- a fresh token/expiry, and the old
|
||||
// link stops working the moment this is called (see the backend's own
|
||||
// resend_site_invite for why).
|
||||
export function resendSiteInvite(inviteId: string): Promise<SiteInvite> {
|
||||
return apiFetch<SiteInvite>(`/api/admin/invites/${inviteId}/resend`, { method: 'POST' })
|
||||
}
|
||||
|
||||
export function getSmtpSettings(): Promise<SmtpSettings | null> {
|
||||
return apiFetch<SmtpSettings | null>('/api/admin/settings/smtp')
|
||||
}
|
||||
|
||||
@@ -217,6 +217,14 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.admin-token-resend {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--ds-accent);
|
||||
font-size: 0.76rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.admin-issue-token {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
listSiteInvites,
|
||||
promoteUser,
|
||||
reactivateUser,
|
||||
resendSiteInvite,
|
||||
resetUserPassword,
|
||||
revokeSiteInvite,
|
||||
sendTestSmtpEmail,
|
||||
@@ -312,6 +313,15 @@ export function AdminPage() {
|
||||
})
|
||||
}
|
||||
|
||||
async function handleResendSiteInvite(invite: SiteInvite) {
|
||||
await withBusy(invite.id, async () => {
|
||||
const updated = await resendSiteInvite(invite.id)
|
||||
// Same row, refreshed expiry (a new token/link went out, see
|
||||
// resend_site_invite) -- update in place rather than a full refetch.
|
||||
setSiteInvites((prev) => prev.map((i) => (i.id === updated.id ? updated : i)))
|
||||
})
|
||||
}
|
||||
|
||||
async function handleSaveSmtpSettings(e: FormEvent) {
|
||||
e.preventDefault()
|
||||
setSmtpSaving(true)
|
||||
@@ -422,6 +432,14 @@ export function AdminPage() {
|
||||
<span className="admin-token-meta">
|
||||
Expires {new Date(invite.expires_at).toLocaleDateString()}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
className="admin-token-resend"
|
||||
disabled={busyId === invite.id}
|
||||
onClick={() => handleResendSiteInvite(invite)}
|
||||
>
|
||||
Resend
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="admin-token-revoke"
|
||||
|
||||
Reference in New Issue
Block a user