Normalize table action columns (#16657)

This commit is contained in:
yuneng-jiang
2025-11-14 14:11:01 -08:00
committed by GitHub
parent b1922e19f8
commit 7e22f4abc6
8 changed files with 172 additions and 68 deletions
@@ -1,14 +1,4 @@
import {
Button,
Icon,
Table,
TableBody,
TableCell,
TableHead,
TableHeaderCell,
TableRow,
Text,
} from "@tremor/react";
import { Button, Icon, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text } from "@tremor/react";
import { Tooltip } from "antd";
import { formatNumberWithCommas } from "@/utils/dataUtils";
import { PencilAltIcon, TrashIcon } from "@heroicons/react/outline";
@@ -755,6 +755,7 @@ const Teams: React.FC<TeamProps> = ({
<TableHeaderCell>Models</TableHeaderCell>
<TableHeaderCell>Organization</TableHeaderCell>
<TableHeaderCell>Info</TableHeaderCell>
<TableHeaderCell>Actions</TableHeaderCell>
</TableRow>
</TableHead>
@@ -937,20 +938,28 @@ const Teams: React.FC<TeamProps> = ({
<TableCell>
{userRole == "Admin" ? (
<>
<Icon
icon={PencilAltIcon}
size="sm"
onClick={() => {
setSelectedTeamId(team.team_id);
setEditTeam(true);
}}
/>
<Icon
onClick={() => handleDelete(team.team_id)}
icon={TrashIcon}
size="sm"
data-testid="delete-team-button"
/>
<Tooltip title="Edit team">
{" "}
<Icon
icon={PencilAltIcon}
size="sm"
className="cursor-pointer hover:text-blue-600"
onClick={() => {
setSelectedTeamId(team.team_id);
setEditTeam(true);
}}
/>
</Tooltip>
<Tooltip title="Delete team">
{" "}
<Icon
onClick={() => handleDelete(team.team_id)}
icon={TrashIcon}
size="sm"
className="cursor-pointer hover:text-red-600"
data-testid="delete-team-button"
/>
</Tooltip>
</>
) : null}
</TableCell>
@@ -159,7 +159,7 @@ const GuardrailTable: React.FC<GuardrailTableProps> = ({
},
{
id: "actions",
header: "",
header: "Actions",
cell: ({ row }) => {
const guardrail = row.original;
const isConfigGuardrail = guardrail.guardrail_definition_location === GuardrailDefinitionLocation.CONFIG;
@@ -177,16 +177,17 @@ const GuardrailTable: React.FC<GuardrailTableProps> = ({
/>
</Tooltip>
) : (
<Icon
icon={TrashIcon}
size="sm"
onClick={() =>
guardrail.guardrail_id &&
onDeleteClick(guardrail.guardrail_id, guardrail.guardrail_name || "Unnamed Guardrail")
}
className="cursor-pointer hover:text-red-500"
tooltip="Delete guardrail"
/>
<Tooltip title="Delete guardrail">
<Icon
icon={TrashIcon}
size="sm"
onClick={() =>
guardrail.guardrail_id &&
onDeleteClick(guardrail.guardrail_id, guardrail.guardrail_name || "Unnamed Guardrail")
}
className="cursor-pointer hover:text-red-500"
/>
</Tooltip>
)}
</div>
);
@@ -290,23 +290,25 @@ export const columns = (
},
{
id: "actions",
header: "",
header: () => <span className="text-sm font-semibold">Actions</span>,
cell: ({ row }) => {
const model = row.original;
const canEditModel = userRole === "Admin" || model.model_info?.created_by === userID;
return (
<div className="flex items-center justify-end gap-2 pr-4">
<Icon
icon={TrashIcon}
size="sm"
onClick={() => {
if (canEditModel) {
setSelectedModelId(model.model_info.id);
setEditModel(false);
}
}}
className={!canEditModel ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
/>
<Tooltip title="Delete model">
<Icon
icon={TrashIcon}
size="sm"
onClick={() => {
if (canEditModel) {
setSelectedModelId(model.model_info.id);
setEditModel(false);
}
}}
className={!canEditModel ? "opacity-50 cursor-not-allowed" : "cursor-pointer hover:text-red-600"}
/>
</Tooltip>
</div>
);
},
@@ -0,0 +1,33 @@
import { render } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import React from "react";
vi.mock("./vector_store_management/VectorStoreSelector", () => ({
__esModule: true,
default: () => null,
}));
vi.mock("./mcp_server_management/MCPServerSelector", () => ({
__esModule: true,
default: () => null,
}));
import OrganizationsTable from "./organizations";
describe("OrganizationsTable", () => {
it("should render the OrganizationsTable component", () => {
const setOrganizations = vi.fn();
const { getByText } = render(
<OrganizationsTable
organizations={[]}
userRole="Admin"
userModels={[]}
accessToken={null}
setOrganizations={setOrganizations}
premiumUser={true}
/>,
);
expect(getByText("+ Create New Organization")).toBeInTheDocument();
});
});
@@ -370,19 +370,27 @@ const OrganizationsTable: React.FC<OrganizationsTableProps> = ({
<TableCell>
{userRole === "Admin" && (
<>
<Icon
icon={PencilAltIcon}
size="sm"
onClick={() => {
setSelectedOrgId(org.organization_id);
setEditOrg(true);
}}
/>
<Icon
onClick={() => handleDelete(org.organization_id)}
icon={TrashIcon}
size="sm"
/>
<Tooltip title="Edit organization">
{" "}
<Icon
icon={PencilAltIcon}
size="sm"
className="cursor-pointer hover:text-blue-600"
onClick={() => {
setSelectedOrgId(org.organization_id);
setEditOrg(true);
}}
/>
</Tooltip>
<Tooltip title="Delete organization">
{" "}
<Icon
onClick={() => handleDelete(org.organization_id)}
icon={TrashIcon}
size="sm"
className="cursor-pointer hover:text-red-600"
/>
</Tooltip>
</>
)}
</TableCell>
@@ -109,17 +109,32 @@ export const columns = (
},
{
id: "actions",
header: "",
header: "Actions",
cell: ({ row }) => (
<div className="flex gap-2">
<Tooltip title="Edit user details" zIndex={9999}>
<Icon icon={PencilAltIcon} size="sm" onClick={() => handleUserClick(row.original.user_id, true)} />
<Tooltip title="Edit user details">
<Icon
icon={PencilAltIcon}
size="sm"
onClick={() => handleUserClick(row.original.user_id, true)}
className="cursor-pointer hover:text-blue-600"
/>
</Tooltip>
<Tooltip title="Delete user" zIndex={9999}>
<Icon icon={TrashIcon} size="sm" onClick={() => handleDelete(row.original.user_id)} />
<Tooltip title="Delete user">
<Icon
icon={TrashIcon}
size="sm"
onClick={() => handleDelete(row.original.user_id)}
className="cursor-pointer hover:text-red-600"
/>
</Tooltip>
<Tooltip title="Reset Password" zIndex={9999}>
<Icon icon={RefreshIcon} size="sm" onClick={() => handleResetPassword(row.original.user_id)} />
<Tooltip title="Reset Password">
<Icon
icon={RefreshIcon}
size="sm"
onClick={() => handleResetPassword(row.original.user_id)}
className="cursor-pointer hover:text-green-600"
/>
</Tooltip>
</div>
),
@@ -0,0 +1,46 @@
import { render } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import React from "react";
import { UserDataTable } from "./table";
describe("UserDataTable", () => {
it("should render the UserDataTable component", () => {
const filters = {
email: "",
user_id: "",
user_role: "",
sso_user_id: "",
team: "",
model: "",
min_spend: null,
max_spend: null,
sort_by: "",
sort_order: "asc" as const,
};
const updateFilters = vi.fn();
const { getByText } = render(
<UserDataTable
data={[]}
columns={[]}
accessToken={null}
userRole={"Admin"}
possibleUIRoles={null}
filters={filters}
updateFilters={updateFilters}
initialFilters={filters}
teams={[]}
handleEdit={vi.fn()}
handleDelete={vi.fn()}
handleResetPassword={vi.fn()}
userListResponse={{ users: [], total: 0, page: 1, page_size: 25, total_pages: 1 }}
currentPage={1}
handlePageChange={vi.fn()}
/>,
);
expect(getByText("Filters")).toBeInTheDocument();
});
});