fix(ui): split paginated team table from shared global teams state

Greptile reviewed the previous commit and pointed out that the page-level
v2TeamListCall I removed was also the only writer that populated the
shared `teams` state for non-Teams pages (Models, Users, Agents). Removing
it would have left those pages with `teams === null` until the user
visited Teams or API Keys, which in turn broke `isUserTeamAdmin` checks
on Models and similar lookups elsewhere.

Split the two roles instead of conflating them:

- Restore the page-level v2TeamListCall(size=100) in app/page.tsx as the
  sole writer of the shared `teams` state. Models, Users, Agents and
  OldTeams' own cross-team lookups (is_team_admin, in-place edit
  propagation) all read from this list.
- Give OldTeams its own local `paginatedTeams` state for the table.
  fetchTeamsV2 and the filter-change handler now write to local state
  only; displayTeams reads from it. This stops OldTeams from clobbering
  the global list with a 10-row paginated subset.

Net effect: race between two writers is gone (different state slots),
non-Teams pages keep working as before, and OldTeams' table renders its
paginated view consistently across reloads. Verified live in the browser.
This commit is contained in:
Ryan Crabbe 2026-05-05 15:35:14 -07:00
parent c0208e9e76
commit 2c801febff
No known key found for this signature in database
2 changed files with 10 additions and 3 deletions

View file

@ -8,6 +8,7 @@ import AgentsPanel from "@/components/agents";
import BudgetPanel from "@/components/budgets/budget_panel";
import CacheDashboard from "@/components/cache_dashboard";
import ClaudeCodePluginsPanel from "@/components/claude_code_plugins";
import { teamListCall as v2TeamListCall } from "@/app/(dashboard)/hooks/teams/useTeams";
import LoadingScreen from "@/components/common_components/LoadingScreen";
import { CostTrackingSettings } from "@/components/CostTrackingSettings";
import GeneralSettings from "@/components/general_settings";
@ -352,6 +353,11 @@ function CreateKeyPageContent() {
if (accessToken && userID && userRole) {
fetchUserModels(userID, userRole, accessToken, setUserModels);
}
if (accessToken && userID && userRole) {
v2TeamListCall(accessToken, 1, 100, {
userID: userRole !== "Admin" && userRole !== "Admin Viewer" ? userID : null,
}).then((response) => setTeams(response.teams ?? [])).catch(console.error);
}
if (accessToken) {
fetchOrganizations(accessToken, setOrganizations);
}

View file

@ -197,6 +197,7 @@ const Teams: React.FC<TeamProps> = ({
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [totalTeams, setTotalTeams] = useState(0);
const [paginatedTeams, setPaginatedTeams] = useState<Team[] | null>(null);
const [currentOrg, setCurrentOrg] = useState<Organization | null>(null);
const [currentOrgForCreateTeam, setCurrentOrgForCreateTeam] = useState<Organization | null>(null);
const [filters, setFilters] = useState<FilterState>({
@ -240,7 +241,7 @@ const Teams: React.FC<TeamProps> = ({
sortOrder: sortOrder || null,
},
);
setTeams(response.teams ?? []);
setPaginatedTeams(response.teams ?? []);
setTotalTeams(response.total ?? 0);
} catch (err: any) {
setFetchError(err?.message || "Failed to fetch teams");
@ -659,7 +660,7 @@ const Teams: React.FC<TeamProps> = ({
sortOrder: newFilters.sort_order || null,
},
);
setTeams(response.teams ?? []);
setPaginatedTeams(response.teams ?? []);
setTotalTeams(response.total ?? 0);
} catch (error) {
console.error("Error fetching teams:", error);
@ -863,7 +864,7 @@ const Teams: React.FC<TeamProps> = ({
},
], [userRole, perTeamInfo, organizationsData, organizations]);
const displayTeams = useMemo(() => teams ?? [], [teams]);
const displayTeams = useMemo(() => paginatedTeams ?? [], [paginatedTeams]);
const renderTeamsContent = () => {
if (isLoading) {