Skip to content

Commit 9d9eded

Browse files
icecrasher321claude
andcommitted
fix(invitations): serialize the join-preview reads, revert the removal refetch
Two corrections to this branch's own review fixes. The pending-invitations list computed each row's join preview with Promise.all. Every preview issues several queries, and the endpoint is hit whenever the workspace switcher opens, so that held one pooled connection per pending invitation for as long as the slowest one took. The loop is sequential now; the list is a handful of rows, so the latency is not worth the pool pressure. The removal-impact refetch on confirm is reverted. It changed behaviour — a click could silently do nothing — and it did not actually close the window it targeted: the credential set can still change between the refetch and the independent DELETE, because the removal endpoint neither receives nor revalidates the disclosed set. Closing that properly means passing the disclosure to the endpoint and revalidating there, which is a feature rather than a review fix, so the prior behaviour stands until it is done deliberately. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 7e167ed commit 9d9eded

2 files changed

Lines changed: 18 additions & 34 deletions

File tree

apps/sim/app/api/invitations/route.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,25 @@ export const GET = withRouteHandler(async () => {
2828
* accept — the same consent contract the emailed `/invite` page honours.
2929
* Disclosure-only, so a preview failure degrades to `null` (the client
3030
* shows a generic notice) rather than hiding the invitation.
31+
*
32+
* Sequential on purpose: each preview issues several queries, and this
33+
* endpoint is hit whenever the workspace switcher opens. Fanning them out
34+
* with `Promise.all` would hold one pooled connection per pending
35+
* invitation for the length of the slowest one. The list is a handful of
36+
* rows, so the added latency is not worth the pool pressure.
3137
*/
32-
const previews = await Promise.all(
33-
invitations.map(async (inv) => {
34-
try {
35-
return await getInvitationJoinPreview(session.user.id, inv)
36-
} catch (previewError) {
37-
logger.warn('Failed to compute join preview for pending invitation', {
38-
invitationId: inv.id,
39-
error: previewError,
40-
})
41-
return null
42-
}
43-
})
44-
)
38+
const previews: Array<Awaited<ReturnType<typeof getInvitationJoinPreview>> | null> = []
39+
for (const inv of invitations) {
40+
try {
41+
previews.push(await getInvitationJoinPreview(session.user.id, inv))
42+
} catch (previewError) {
43+
logger.warn('Failed to compute join preview for pending invitation', {
44+
invitationId: inv.id,
45+
error: previewError,
46+
})
47+
previews.push(null)
48+
}
49+
}
4550

4651
return NextResponse.json({
4752
invitations: invitations.map(

apps/sim/app/workspace/[workspaceId]/settings/components/team-management/team-management.tsx

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@ export function TeamManagement({
8787
data: removalImpactCredentials,
8888
isFetching: isRemovalImpactFetching,
8989
isError: isRemovalImpactError,
90-
refetch: refetchRemovalImpact,
9190
} = useMemberRemovalImpact(organizationId, removeMemberDialog.memberId, {
9291
enabled: removeMemberDialog.open,
9392
})
@@ -179,25 +178,6 @@ export function TeamManagement({
179178
if (!session?.user || !memberId) return
180179

181180
try {
182-
/**
183-
* Re-verify the disclosure at the moment of confirmation. `isFetching`
184-
* holds the button only while a request is in flight; a credential the
185-
* member gained after the fetch settled would otherwise be removed
186-
* without ever having been disclosed. On a change the dialog stays open
187-
* showing the refreshed warning, so the admin confirms against what is
188-
* actually true — the same consent contract the invite flow uses.
189-
*/
190-
const refreshed = await refetchRemovalImpact()
191-
if (refreshed.data) {
192-
const current = [...new Set(refreshed.data.map((credential) => credential.displayName))]
193-
if (
194-
current.length !== disclosedBreakingCredentials.length ||
195-
current.some((name) => !disclosedBreakingCredentials.includes(name))
196-
) {
197-
return
198-
}
199-
}
200-
201181
await removeMemberMutation.mutateAsync({
202182
memberId,
203183
orgId: organizationId,
@@ -219,7 +199,6 @@ export function TeamManagement({
219199
}, [
220200
removeMemberDialog.memberId,
221201
removeMemberDialog.isSelfRemoval,
222-
disclosedBreakingCredentials,
223202
session?.user?.id,
224203
organizationId,
225204
removeMemberMutation,

0 commit comments

Comments
 (0)