fix(platform): improve member context error handling and agent autosave#580
Conversation
… form to manual save Throw UnauthenticatedError instead of returning null from getCurrentMemberContext so the frontend can distinguish auth failures from missing membership. Handle query errors gracefully in the dashboard layout by showing a spinner instead of the access denied screen. Remove redundant route-level prefetches that duplicated the parent layout query. Switch custom agent general tab from auto-save to manual save-on-blur so team and shared team changes persist immediately without debounce delays. Increase Convex UDF timeout to 5s to handle reconnect burst load.
Greptile SummaryThis PR improves error handling and user experience across member context queries and agent form interactions. Key improvements:
Test coverage: Comprehensive tests added for skip parameter behavior, error states, and graceful fallback scenarios. Confidence Score: 5/5
|
| Filename | Overview |
|---|---|
| services/platform/convex/members/queries.ts | Changed to throw UnauthenticatedError instead of returning null for better error handling |
| services/platform/app/routes/dashboard/$id.tsx | Removed prefetch loader, added isError handling, passes skip parameter to hook |
| services/platform/app/routes/dashboard/$id/custom-agents/$agentId/index.tsx | Changed from auto-save to manual save-on-blur for text fields, immediate save for team changes |
| services/platform/app/routes/dashboard/tests/dashboard-layout.test.tsx | Added comprehensive tests for skip parameter, error handling, and graceful fallback |
Sequence Diagram
sequenceDiagram
participant Frontend as Dashboard Layout
participant Hook as useCurrentMemberContext
participant Query as getCurrentMemberContext
participant RLS as getOrganizationMember
Note over Frontend,RLS: Authentication Flow
Frontend->>Hook: useCurrentMemberContext(orgId, skip=isAuthLoading)
alt Auth loading
Hook-->>Frontend: {isLoading: true} (query skipped)
else Auth ready
Hook->>Query: Query member context
alt No auth user
Query-->>Hook: throw UnauthenticatedError
Hook-->>Frontend: {isError: true, data: null}
Frontend->>Frontend: Show spinner (no cached data)
else User authenticated
Query->>RLS: getOrganizationMember()
alt Not a member
RLS-->>Query: throw UnauthorizedError
Query-->>Hook: return null
Hook-->>Frontend: {data: null, isError: false}
Frontend->>Frontend: Show access denied
else Member found
RLS-->>Query: return member
Query-->>Hook: return member context
Hook-->>Frontend: {data: {...}, isError: false}
Frontend->>Frontend: hasRole ? Show outlet : Show spinner
end
end
Note over Frontend: Error with cached data
alt Query errors but cached data exists
Hook-->>Frontend: {data: {...}, isError: true}
Frontend->>Frontend: hasRole checks first → Show outlet (graceful fallback)
end
end
Last reviewed commit: f7eef28
📝 WalkthroughWalkthroughThis PR coordinates multiple changes to improve authentication handling, query management, and form state management. Key changes include: (1) adding a Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@services/platform/app/routes/dashboard/`$id/custom-agents/$agentId/index.tsx:
- Around line 157-163: When computing newShared for the save call, filter out
the newly selected primary team (resolved) so the primary team cannot appear in
sharedWithTeamIds; i.e., derive newShared from sharedWithTeamIds but remove
resolved when resolved is truthy (and only set state via setSharedWithTeamIds
when resolved is falsy per existing logic), then call save with
{...form.getValues(), teamId: resolved, sharedWithTeamIds: newShared} so the
payload never contains the primary team in sharedWithTeamIds; update references
to newShared, resolved, sharedWithTeamIds, setSharedWithTeamIds, save and
form.getValues() accordingly.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (8)
services/platform/Dockerfileservices/platform/app/hooks/use-current-member-context.tsservices/platform/app/routes/dashboard/$id.tsxservices/platform/app/routes/dashboard/$id/custom-agents/$agentId/index.tsxservices/platform/app/routes/dashboard/$id/settings/api-keys.tsxservices/platform/app/routes/dashboard/$id/settings/index.tsxservices/platform/app/routes/dashboard/__tests__/dashboard-layout.test.tsxservices/platform/convex/members/queries.ts
💤 Files with no reviewable changes (1)
- services/platform/app/routes/dashboard/$id/settings/api-keys.tsx
Summary
getCurrentMemberContextnow throwsUnauthenticatedErrorinstead of silently returningnull, letting the frontend distinguish auth failures from missing membership. The dashboard layout handles query errors by showing a spinner instead of the access denied screen, and falls back gracefully when previous data exists.$id.tsxandapi-keys.tsxthat duplicated the parent layout query have been removed. The settings index loader now catches query failures instead of crashing.Test plan
skip=truewhen auth is loading andskip=falsewhen auth is ready🤖 Generated with Claude Code