agent: @U0AJM7X8FBR Admin - please update the mono/admin repo with a table show#8
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughIntroduces a new admin sandbox feature for viewing GitHub organization repositories with commit statistics. Adds a page route, React components for data display, a TanStack Query hook for fetching org repo data, a fetch utility for API calls, and TypeScript types for the org repository domain. Changes
Sequence DiagramsequenceDiagram
actor User
participant OrgsTableContainer
participant useAdminSandboxOrgs
participant Privy as Privy Auth
participant fetchUtil as fetchAdminSandboxOrgs
participant API as API Endpoint
participant OrgsTable
User->>OrgsTableContainer: Page loads
OrgsTableContainer->>useAdminSandboxOrgs: Hook executes
useAdminSandboxOrgs->>Privy: Get access token
Privy-->>useAdminSandboxOrgs: Return token
useAdminSandboxOrgs->>fetchUtil: Call with token
fetchUtil->>API: GET /api/admins/sandboxes/orgs
API-->>fetchUtil: Return OrgRepoRow[]
fetchUtil-->>useAdminSandboxOrgs: Return repos
useAdminSandboxOrgs-->>OrgsTableContainer: Return data via TanStack Query
OrgsTableContainer->>OrgsTable: Pass repos
OrgsTable-->>User: Render sortable table
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
components/Orgs/orgsColumns.tsx (1)
22-38: Consider extracting a reusable sortable-header renderer.
The sort button/icon block is repeated across columns; extracting a small helper would reduce duplication and future drift.Also applies to: 58-73, 80-95, 102-117
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/Orgs/orgsColumns.tsx` around lines 22 - 38, The header render for sortable columns is duplicated: extract a reusable SortableHeader component or helper function (e.g., SortableHeader or renderSortableHeader) that accepts the column object and a title, and replace each header: ({ column }) => (...) block (currently tied to accessorKey like "total_commits" and using column.toggleSorting, column.getIsSorted, Button, ArrowUp, ArrowDown, ArrowUpDown) with a call to that helper; the helper should render the same Button, call column.toggleSorting(column.getIsSorted() === "asc"), and display the correct icon based on column.getIsSorted() to eliminate repetition across the headers at the referenced locations.lib/fetchAdminSandboxOrgs.ts (1)
23-24: Harden response parsing before returningrepos.
Current JSON parsing assumesreposis an array; a malformed payload can propagate invalid data into the table.♻️ Proposed refactor
import { API_BASE_URL } from "@/lib/consts"; -import type { OrgRepoRow } from "@/types/org"; +import type { AdminSandboxOrgsResponse, OrgRepoRow } from "@/types/org"; @@ - const data = await res.json(); - return data.repos ?? []; + const data: Partial<AdminSandboxOrgsResponse> = await res.json(); + return Array.isArray(data.repos) ? data.repos : []; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/fetchAdminSandboxOrgs.ts` around lines 23 - 24, The JSON parsing in fetchAdminSandboxOrgs currently returns data.repos unchecked which can propagate malformed values; after awaiting res.json(), validate that the parsed value is an object and that data.repos is an array before returning it (e.g., check typeof data === "object" && Array.isArray(data.repos")), and if validation fails return an empty array (or safely map/filter to produce an array of expected repo shapes). Update the function around the lines using res.json(), data, and repos to perform this defensive check and avoid returning non-array values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@components/Orgs/OrgsTableContainer.tsx`:
- Around line 25-31: The empty-state branch in OrgsTableContainer currently only
checks repos and shows "No org repo data found."—update the component to
explicitly handle the Privy/query disabled state by checking the hook's ready
and authenticated flags (or accepting them as props) before showing the empty
message: if !ready show a loading/placeholder, if ready && !authenticated show
an auth prompt/message, if authenticated && !isLoading && !error &&
repos?.length === 0 show the "No org repo data found" message; reference the
OrgsTableContainer component and the repos/isLoading/error/ready/authenticated
values from the query hook to implement these conditional returns.
In `@hooks/useAdminSandboxOrgs.ts`:
- Around line 15-21: The query cache key in useAdminSandboxOrgs is too generic
(queryKey: ["admin","sandboxes","orgs"]) and can return another user's data
after logout/sign-in; change the query key to include the authenticated identity
(for example include getAccessToken() result or a stable user id) so cache is
scoped per user, or alternatively ensure you invalidate related queries on auth
changes (see LoginButton logout flow) by calling queryClient.invalidateQueries
or queryClient.removeQueries for the keys used by useAdminSandboxOrgs and other
auth-bound hooks (useAdminSandboxes, useIsAdmin).
---
Nitpick comments:
In `@components/Orgs/orgsColumns.tsx`:
- Around line 22-38: The header render for sortable columns is duplicated:
extract a reusable SortableHeader component or helper function (e.g.,
SortableHeader or renderSortableHeader) that accepts the column object and a
title, and replace each header: ({ column }) => (...) block (currently tied to
accessorKey like "total_commits" and using column.toggleSorting,
column.getIsSorted, Button, ArrowUp, ArrowDown, ArrowUpDown) with a call to that
helper; the helper should render the same Button, call
column.toggleSorting(column.getIsSorted() === "asc"), and display the correct
icon based on column.getIsSorted() to eliminate repetition across the headers at
the referenced locations.
In `@lib/fetchAdminSandboxOrgs.ts`:
- Around line 23-24: The JSON parsing in fetchAdminSandboxOrgs currently returns
data.repos unchecked which can propagate malformed values; after awaiting
res.json(), validate that the parsed value is an object and that data.repos is
an array before returning it (e.g., check typeof data === "object" &&
Array.isArray(data.repos")), and if validation fails return an empty array (or
safely map/filter to produce an array of expected repo shapes). Update the
function around the lines using res.json(), data, and repos to perform this
defensive check and avoid returning non-array values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e02d4192-ecab-4948-b410-4ed475294d24
📒 Files selected for processing (8)
app/sandboxes/orgs/page.tsxcomponents/Orgs/OrgsPage.tsxcomponents/Orgs/OrgsTable.tsxcomponents/Orgs/OrgsTableContainer.tsxcomponents/Orgs/orgsColumns.tsxhooks/useAdminSandboxOrgs.tslib/fetchAdminSandboxOrgs.tstypes/org.ts
| if (!repos || repos.length === 0) { | ||
| return ( | ||
| <div className="flex items-center justify-center py-12 text-sm text-gray-400"> | ||
| No org repo data found. | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether auth-gating exists before rendering this container and whether disabled-query states are handled.
rg -nP --type=tsx 'useAdminSandboxOrgs\(' -C2
rg -nP --type=tsx 'No org repo data found' -C2
rg -nP --type=ts --type=tsx 'usePrivy\(|authenticated|ready|redirect\(' -C2Repository: recoupable/admin
Length of output: 143
🏁 Script executed:
#!/bin/bash
# Find TypeScript/TSX files and search for relevant patterns
fd -e tsx -e ts | head -20
# Search for the component and hook usage
rg "useAdminSandboxOrgs" -A 5 -B 2
# Search for auth-related patterns
rg "usePrivy\|authenticated\|ready" -A 2 -B 2
# Find the specific error message
rg "No org repo data found"
# Check if route has auth protection
rg "middleware\|auth\|redirect" | grep -E "(middleware|auth|route)" | head -20Repository: recoupable/admin
Length of output: 2137
🏁 Script executed:
#!/bin/bash
# Read the hook implementation
echo "=== Hook Implementation ==="
cat -n hooks/useAdminSandboxOrgs.ts
echo ""
echo "=== Component Implementation ==="
cat -n components/Orgs/OrgsTableContainer.tsx
echo ""
echo "=== Parent page that uses this component ==="
cat -n components/Orgs/OrgsPage.tsxRepository: recoupable/admin
Length of output: 2830
🏁 Script executed:
#!/bin/bash
# Check the route page
echo "=== Route page ==="
cat -n app/sandboxes/orgs/page.tsx
echo ""
echo "=== Check for middleware ==="
find . -name "middleware.*" -o -name "*middleware*" | grep -v node_modules
echo ""
echo "=== Check for auth guards in app layout ==="
cat -n app/layout.tsx | head -50Repository: recoupable/admin
Length of output: 1682
Handle disabled query state explicitly in OrgsTableContainer.
When the query is disabled (user not authenticated or Privy not ready), isLoading and error are both falsy, causing the component to display "No org repo data found." This is misleading UX that masks authentication state instead of indicating why data cannot be loaded.
Add explicit ready and authenticated checks before showing the empty state. The hook already has access to Privy state; the container needs to mirror these checks or pass them through.
Suggested approach
"use client";
import { useAdminSandboxOrgs } from "@/hooks/useAdminSandboxOrgs";
import OrgsTable from "@/components/Orgs/OrgsTable";
+import { usePrivy } from "@privy-io/react-auth";
export default function OrgsTableContainer() {
+ const { ready, authenticated } = usePrivy();
const { data: repos, isLoading, error } = useAdminSandboxOrgs();
+
+ if (!ready) {
+ return <div className="flex items-center justify-center py-12 text-sm text-gray-500">Loading…</div>;
+ }
+
+ if (!authenticated) {
+ return <div className="flex items-center justify-center py-12 text-sm text-gray-400">Sign in to view org repos.</div>;
+ }
if (isLoading) {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!repos || repos.length === 0) { | |
| return ( | |
| <div className="flex items-center justify-center py-12 text-sm text-gray-400"> | |
| No org repo data found. | |
| </div> | |
| ); | |
| } | |
| "use client"; | |
| import { useAdminSandboxOrgs } from "@/hooks/useAdminSandboxOrgs"; | |
| import OrgsTable from "@/components/Orgs/OrgsTable"; | |
| import { usePrivy } from "@privy-io/react-auth"; | |
| export default function OrgsTableContainer() { | |
| const { ready, authenticated } = usePrivy(); | |
| const { data: repos, isLoading, error } = useAdminSandboxOrgs(); | |
| if (!ready) { | |
| return <div className="flex items-center justify-center py-12 text-sm text-gray-500">Loading…</div>; | |
| } | |
| if (!authenticated) { | |
| return <div className="flex items-center justify-center py-12 text-sm text-gray-400">Sign in to view org repos.</div>; | |
| } | |
| if (isLoading) { | |
| return ( | |
| <div className="flex items-center justify-center py-12 text-sm text-gray-500"> | |
| Loading org repos... | |
| </div> | |
| ); | |
| } | |
| if (error) { | |
| return ( | |
| <div className="flex items-center justify-center py-12 text-sm text-red-400"> | |
| Error loading org repos. | |
| </div> | |
| ); | |
| } | |
| if (!repos || repos.length === 0) { | |
| return ( | |
| <div className="flex items-center justify-center py-12 text-sm text-gray-400"> | |
| No org repo data found. | |
| </div> | |
| ); | |
| } | |
| return <OrgsTable repos={repos} />; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@components/Orgs/OrgsTableContainer.tsx` around lines 25 - 31, The empty-state
branch in OrgsTableContainer currently only checks repos and shows "No org repo
data found."—update the component to explicitly handle the Privy/query disabled
state by checking the hook's ready and authenticated flags (or accepting them as
props) before showing the empty message: if !ready show a loading/placeholder,
if ready && !authenticated show an auth prompt/message, if authenticated &&
!isLoading && !error && repos?.length === 0 show the "No org repo data found"
message; reference the OrgsTableContainer component and the
repos/isLoading/error/ready/authenticated values from the query hook to
implement these conditional returns.
| queryKey: ["admin", "sandboxes", "orgs"], | ||
| queryFn: async () => { | ||
| const token = await getAccessToken(); | ||
| if (!token) throw new Error("Not authenticated"); | ||
| return fetchAdminSandboxOrgs(token); | ||
| }, | ||
| enabled: ready && authenticated, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether query cache is user-scoped or explicitly cleared on auth transitions.
rg -nP --type=ts --type=tsx 'queryKey:\s*\[\s*"admin"\s*,\s*"sandboxes"(?:\s*,\s*"orgs")?' -C2
rg -nP --type=ts --type=tsx 'removeQueries|resetQueries|clear\(|invalidateQueries|signOut|logout|logOut' -C2Repository: recoupable/admin
Length of output: 115
🏁 Script executed:
#!/bin/bash
# Find the target file and understand the codebase structure
find . -name "useAdminSandboxOrgs.ts" -o -name "useAdminSandboxOrgs.tsx" 2>/dev/null
# Also look for hooks directory
fd -type f 'useAdmin' . 2>/dev/null | head -20Repository: recoupable/admin
Length of output: 90
🏁 Script executed:
#!/bin/bash
# Search for cache clearing patterns without type restrictions
rg "queryKey.*admin.*sandbox" -B 2 -A 5 2>/dev/null
rg "invalidateQueries|removeQueries|resetQueries|clearCache|signOut|logout" -B 1 -A 2 2>/dev/null | head -50Repository: recoupable/admin
Length of output: 1483
🏁 Script executed:
#!/bin/bash
# Look for Privy auth setup and cache clearing
rg "usePrivy|signOut|logout|authentication" -B 1 -A 1 2>/dev/null | head -80Repository: recoupable/admin
Length of output: 2343
🏁 Script executed:
#!/bin/bash
# Check query client setup and cache clearing handlers
rg "QueryClient|new QueryClient" -B 2 -A 5 2>/dev/null
rg "logout|signOut" -B 3 -A 5 2>/dev/null | grep -A 5 -B 3 "logout\|signOut"Repository: recoupable/admin
Length of output: 1844
🏁 Script executed:
#!/bin/bash
# Check useIsAdmin.ts for same pattern
cat hooks/useIsAdmin.ts 2>/dev/nullRepository: recoupable/admin
Length of output: 869
🏁 Script executed:
#!/bin/bash
# Check where useAdminSandboxOrgs is used and if there's cache invalidation nearby
rg "useAdminSandboxOrgs" -B 2 -A 2 2>/dev/nullRepository: recoupable/admin
Length of output: 1004
🏁 Script executed:
#!/bin/bash
# Check PrivyProvider for logout handlers or cache clearing
cat providers/PrivyProvider.tsx 2>/dev/nullRepository: recoupable/admin
Length of output: 973
🏁 Script executed:
#!/bin/bash
# Check full useAdminSandboxOrgs.ts implementation
cat hooks/useAdminSandboxOrgs.ts 2>/dev/nullRepository: recoupable/admin
Length of output: 785
Scope the query cache key to the authenticated identity.
Using a static key for authenticated data risks serving stale cached data after logout/sign-in/user-switch in the same browser session. When logout() is called in LoginButton, the query cache is not cleared, so subsequent logins will retrieve data cached under the previous user's session.
This pattern affects multiple auth-bound queries (useAdminSandboxOrgs, useAdminSandboxes, useIsAdmin). Either scope query keys to user identity or implement explicit cache invalidation on auth state changes.
🔐 Proposed fix
export function useAdminSandboxOrgs() {
- const { ready, authenticated, getAccessToken } = usePrivy();
+ const { ready, authenticated, user, getAccessToken } = usePrivy();
return useQuery({
- queryKey: ["admin", "sandboxes", "orgs"],
+ queryKey: ["admin", "sandboxes", "orgs", user?.id],
queryFn: async () => {
const token = await getAccessToken();
if (!token) throw new Error("Not authenticated");
return fetchAdminSandboxOrgs(token);
},
- enabled: ready && authenticated,
+ enabled: ready && authenticated && !!user?.id,
});
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@hooks/useAdminSandboxOrgs.ts` around lines 15 - 21, The query cache key in
useAdminSandboxOrgs is too generic (queryKey: ["admin","sandboxes","orgs"]) and
can return another user's data after logout/sign-in; change the query key to
include the authenticated identity (for example include getAccessToken() result
or a stable user id) so cache is scoped per user, or alternatively ensure you
invalidate related queries on auth changes (see LoginButton logout flow) by
calling queryClient.invalidateQueries or queryClient.removeQueries for the keys
used by useAdminSandboxOrgs and other auth-bound hooks (useAdminSandboxes,
useIsAdmin).
Automated PR from coding agent.
Summary by CodeRabbit