Skip to content

feat: add Org Repos table page at /orgs#7

Open
sweetmantech wants to merge 1 commit intomainfrom
agent/-u0ajm7x8fbr-api---we-want-to--1773335319458
Open

feat: add Org Repos table page at /orgs#7
sweetmantech wants to merge 1 commit intomainfrom
agent/-u0ajm7x8fbr-api---we-want-to--1773335319458

Conversation

@sweetmantech
Copy link
Copy Markdown
Contributor

@sweetmantech sweetmantech commented Mar 15, 2026

Summary

  • Adds a new /orgs page to the admin dashboard displaying a table of all org-level GitHub repos
  • Columns: Repository (linked), Commits, Latest 5 Commits, Earliest Commit, Latest Commit, Submodule Installs
  • Adds "View Org Repos" nav button to the AdminDashboard home screen
  • All data is fetched from GET /api/admins/sandboxes/orgs via React Query + Privy auth

Test plan

  • Sign in as admin and verify "View Org Repos" button appears on dashboard
  • Navigate to /orgs and verify the table loads with correct data
  • Verify sorting works on numeric and date columns
  • Verify repo name links open the correct GitHub URLs

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added a new "Org Repos" page for viewing organization repositories
    • Displays a sortable table with repository details including commit counts, latest and earliest commit dates, and submodule information
    • Added navigation button in the admin dashboard to access the Org Repos view
    • Includes loading, error, and empty states for data handling

Adds a new /orgs page to the admin dashboard that displays a table
of all org-level GitHub repositories with commit stats and submodule
installation counts, backed by GET /api/admins/sandboxes/orgs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
admin Error Error Mar 15, 2026 8:54pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 15, 2026

📝 Walkthrough

Walkthrough

Introduces a new Org Repos feature enabling admins to view and sort organization repositories. Adds app/orgs route with OrgsPage component, OrgsTable with sortable columns, useAdminOrgs hook for authenticated data fetching, and OrgsNavButton navigation element. Includes TypeScript interfaces for org repository metadata and API responses.

Changes

Cohort / File(s) Summary
App Route & Navigation
app/orgs/page.tsx, components/Home/OrgsNavButton.tsx, components/Home/AdminDashboard.tsx
Adds new /orgs route and navigation button to AdminDashboard. OrgsNavButton component provides a styled link to the new orgs page.
Org Display Components
components/Orgs/OrgsPage.tsx, components/Orgs/OrgsTable.tsx, components/Orgs/orgsColumns.tsx
Implements OrgsPage container, OrgsTable with TanStack Data Table integration, and column definitions. Table features sortable columns for commits, dates, and submodule counts, with commit message truncation and external link icons.
Data Fetching & State
components/Orgs/OrgsTableContainer.tsx, hooks/useAdminOrgs.ts, lib/fetchAdminOrgs.ts
Adds client-side component to manage data loading, error, and empty states. useAdminOrgs hook integrates with Privy authentication and TanStack Query. fetchAdminOrgs function handles GET requests to /api/admins/sandboxes/orgs with error handling.
Type Definitions
types/org.ts
Exports OrgRepoRow interface for repository analytics (name, URL, commits, timestamps, submodule installs) and AdminOrgsResponse wrapper for API responses.

Sequence Diagram

sequenceDiagram
    actor User
    participant OrgsPage as OrgsPage Component
    participant OrgsTableContainer as OrgsTableContainer<br/>(Client)
    participant useAdminOrgs as useAdminOrgs Hook
    participant Privy as Privy Auth
    participant Query as TanStack Query
    participant API as /api/admins/sandboxes/orgs
    participant OrgsTable as OrgsTable & Columns

    User->>OrgsPage: Navigate to /orgs
    OrgsPage->>OrgsTableContainer: Render
    OrgsTableContainer->>useAdminOrgs: Call hook
    useAdminOrgs->>Privy: Get access token
    Privy-->>useAdminOrgs: Return token
    useAdminOrgs->>Query: Execute query with fetchAdminOrgs
    Query->>API: GET with auth header
    API-->>Query: Return org repos data
    Query-->>useAdminOrgs: Return data/loading/error state
    useAdminOrgs-->>OrgsTableContainer: repos array
    OrgsTableContainer->>OrgsTable: Pass repos data
    OrgsTable->>OrgsTable: Initialize sorting (latest_commit_at desc)
    OrgsTable-->>User: Render sortable table with columns
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A warren of repos now revealed,
Org dashboards with columns that deal,
Sorted by commits, latest and neat,
Privy-authenticated, oh how sweet! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding a new Org Repos table page at the /orgs route, which is the primary objective of this changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch agent/-u0ajm7x8fbr-api---we-want-to--1773335319458
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/orgs/page.tsx`:
- Around line 1-4: The /orgs route currently renders OrgsPage directly; wrap it
with the same client-side admin/auth guard used elsewhere by creating a client
component (e.g., OrgsContent) that imports usePrivy and useIsAdmin, checks
ready/authenticated and isAdmin (returning null or a loading skeleton while
ready/isLoading, and an access-denied UI when not authenticated or not admin),
and then renders OrgsPage; update the default export in app/orgs/page.tsx to
render OrgsContent instead of OrgsPage so the route enforces authenticated +
isAdmin gating.

In `@components/Orgs/orgsColumns.tsx`:
- Around line 13-15: The anchor element in orgsColumns.tsx that uses
target="_blank" currently sets rel="noreferrer" — update this to include
noopener (e.g., rel="noreferrer noopener") to explicitly prevent opener access;
locate the anchor with attributes target="_blank" and className="flex
items-center gap-1 font-medium hover:underline" and modify its rel value
accordingly.

In `@hooks/useAdminOrgs.ts`:
- Around line 12-16: The query cache key in useAdminOrgs is currently static
("admin","orgs") which can leak data across account switches; update
useAdminOrgs to include the current user's ID from usePrivy (e.g., queryKey:
["admin","orgs", user.id]) and change the enabled condition to wait for the user
id (e.g., enabled: ready && authenticated && !!user?.id); apply the same fix to
useAdminSandboxes (replace its static queryKey with ["admin","sandboxes",
user.id] and guard its enabled flag on user.id) so cache entries are isolated
per authenticated user.

In `@lib/fetchAdminOrgs.ts`:
- Around line 21-22: The code in lib/fetchAdminOrgs.ts returns data.repos from
the parsed JSON without checking the API-level status field; update the success
path in the function that calls res.json() (the variable named data) to validate
data.status (or equivalent) and treat non-success values as errors: if
data.status !== 'success' (or if data.error/message exists) throw or return an
explicit error instead of silently returning data.repos ?? []; ensure the thrown
error includes the API error message/details so callers can surface the failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f8bf6c1c-3076-48d4-b853-8ec2f7a1af1d

📥 Commits

Reviewing files that changed from the base of the PR and between 3fcd006 and 90f3a51.

📒 Files selected for processing (10)
  • app/orgs/page.tsx
  • components/Home/AdminDashboard.tsx
  • components/Home/OrgsNavButton.tsx
  • components/Orgs/OrgsPage.tsx
  • components/Orgs/OrgsTable.tsx
  • components/Orgs/OrgsTableContainer.tsx
  • components/Orgs/orgsColumns.tsx
  • hooks/useAdminOrgs.ts
  • lib/fetchAdminOrgs.ts
  • types/org.ts

Comment on lines +1 to +4
import OrgsPage from "@/components/Orgs/OrgsPage";

export default function Page() {
return <OrgsPage />;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add the same admin/auth guard used by other admin surfaces.

This route renders admin content directly, while components/Home/HomeContent.tsx (Lines 1-38) enforces authenticated + isAdmin checks. /orgs should apply equivalent gating to prevent unprivileged users from accessing the page shell.

🔐 Suggested route-level adjustment
-import OrgsPage from "@/components/Orgs/OrgsPage";
+import OrgsContent from "@/components/Orgs/OrgsContent";

 export default function Page() {
-  return <OrgsPage />;
+  return <OrgsContent />;
 }
// components/Orgs/OrgsContent.tsx
"use client";

import { usePrivy } from "@privy-io/react-auth";
import { useIsAdmin } from "@/hooks/useIsAdmin";
import OrgsPage from "@/components/Orgs/OrgsPage";

export default function OrgsContent() {
  const { ready, authenticated } = usePrivy();
  const { data: isAdmin, isLoading } = useIsAdmin();

  if (!ready || isLoading) return null; // or skeleton
  if (!authenticated || !isAdmin) return <div>Access Denied</div>;

  return <OrgsPage />;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/orgs/page.tsx` around lines 1 - 4, The /orgs route currently renders
OrgsPage directly; wrap it with the same client-side admin/auth guard used
elsewhere by creating a client component (e.g., OrgsContent) that imports
usePrivy and useIsAdmin, checks ready/authenticated and isAdmin (returning null
or a loading skeleton while ready/isLoading, and an access-denied UI when not
authenticated or not admin), and then renders OrgsPage; update the default
export in app/orgs/page.tsx to render OrgsContent instead of OrgsPage so the
route enforces authenticated + isAdmin gating.

Comment on lines +13 to +15
target="_blank"
rel="noreferrer"
className="flex items-center gap-1 font-medium hover:underline"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Harden external-link opener isolation.

For target="_blank" links, include noopener in rel to explicitly prevent opener access.

💡 Suggested fix
-        rel="noreferrer"
+        rel="noopener noreferrer"
📝 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.

Suggested change
target="_blank"
rel="noreferrer"
className="flex items-center gap-1 font-medium hover:underline"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-1 font-medium hover:underline"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/Orgs/orgsColumns.tsx` around lines 13 - 15, The anchor element in
orgsColumns.tsx that uses target="_blank" currently sets rel="noreferrer" —
update this to include noopener (e.g., rel="noreferrer noopener") to explicitly
prevent opener access; locate the anchor with attributes target="_blank" and
className="flex items-center gap-1 font-medium hover:underline" and modify its
rel value accordingly.

Comment on lines +12 to +16
const { ready, authenticated, getAccessToken } = usePrivy();

return useQuery({
queryKey: ["admin", "orgs"],
queryFn: async () => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== React Query version(s) =="
fd package.json --exec jq -r '.dependencies["@tanstack/react-query"] // .devDependencies["@tanstack/react-query"] // empty' {}

echo
echo "== Privy hook usage patterns =="
rg -n -C2 '\busePrivy\(' --type=ts --type=tsx

echo
echo "== Admin query key patterns =="
rg -n -C2 'queryKey:\s*\["admin"' --type=ts --type=tsx

Repository: recoupable/admin

Length of output: 174


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# First, find and display the useAdminOrgs.ts file
echo "== Content of hooks/useAdminOrgs.ts =="
find . -name "useAdminOrgs.ts" -type f -exec cat -n {} +

echo
echo "== Privy hook usage patterns (fixed) =="
rg -n -C2 'usePrivy' --type ts

echo
echo "== Admin query key patterns =="
rg -n -C2 'queryKey.*admin' --type ts

Repository: recoupable/admin

Length of output: 4473


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check useAdminSandboxes which was mentioned in search results
echo "== Content of hooks/useAdminSandboxes.ts =="
cat -n hooks/useAdminSandboxes.ts

echo
echo "== Content of hooks/useIsAdmin.ts =="
cat -n hooks/useIsAdmin.ts

Repository: recoupable/admin

Length of output: 2034


Scope the query cache key by authenticated user.

The static queryKey: ["admin", "orgs"] without a user identifier can leak cached org data across account switches in the same client session. Since usePrivy() exposes a user property (as seen in other components), include the user ID in the queryKey to isolate cache entries per user. Update the enabled condition to ensure the query doesn't run until the user ID is available.

Suggested fix
 export function useAdminOrgs() {
-  const { ready, authenticated, getAccessToken } = usePrivy();
+  const { ready, authenticated, user, getAccessToken } = usePrivy();
 
   return useQuery({
-    queryKey: ["admin", "orgs"],
+    queryKey: ["admin", "orgs", user?.id],
     queryFn: async () => {
       const token = await getAccessToken();
       if (!token) throw new Error("Not authenticated");
       return fetchAdminOrgs(token);
     },
-    enabled: ready && authenticated,
+    enabled: ready && authenticated && !!user?.id,
   });
 }

Similar patterns exist in useAdminSandboxes (lines 15, 21) and should receive the same treatment.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useAdminOrgs.ts` around lines 12 - 16, The query cache key in
useAdminOrgs is currently static ("admin","orgs") which can leak data across
account switches; update useAdminOrgs to include the current user's ID from
usePrivy (e.g., queryKey: ["admin","orgs", user.id]) and change the enabled
condition to wait for the user id (e.g., enabled: ready && authenticated &&
!!user?.id); apply the same fix to useAdminSandboxes (replace its static
queryKey with ["admin","sandboxes", user.id] and guard its enabled flag on
user.id) so cache entries are isolated per authenticated user.

Comment on lines +21 to +22
const data = await res.json();
return data.repos ?? [];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle logical API errors in successful HTTP responses.

The current success path ignores status from the response body. If the backend returns { status: "error" } with HTTP 200, this will silently return an empty list instead of surfacing the failure.

💡 Suggested fix
 import { API_BASE_URL } from "@/lib/consts";
-import type { OrgRepoRow } from "@/types/org";
+import type { AdminOrgsResponse, OrgRepoRow } from "@/types/org";
@@
-  const data = await res.json();
-  return data.repos ?? [];
+  const data = (await res.json()) as AdminOrgsResponse & { message?: string };
+  if (data.status !== "success") {
+    throw new Error(data.message ?? "Failed to load org repos");
+  }
+  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/fetchAdminOrgs.ts` around lines 21 - 22, The code in
lib/fetchAdminOrgs.ts returns data.repos from the parsed JSON without checking
the API-level status field; update the success path in the function that calls
res.json() (the variable named data) to validate data.status (or equivalent) and
treat non-success values as errors: if data.status !== 'success' (or if
data.error/message exists) throw or return an explicit error instead of silently
returning data.repos ?? []; ensure the thrown error includes the API error
message/details so callers can surface the failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant