mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
refactor(ui): read userRole from useAuthorized hook instead of prop drilling
Drop the userRole prop chain through GuardrailsPage → GuardrailsPanel → TeamGuardrailsTab. Each component reads userRole directly from the useAuthorized hook, matching the pattern used elsewhere in the dashboard. Tests now mock useAuthorized per case (the same pattern as top_key_view.test.tsx) instead of passing userRole as a prop. Refs LIT-2494
This commit is contained in:
parent
fe240e608e
commit
9e7aee5b76
4 changed files with 31 additions and 14 deletions
|
|
@ -4,9 +4,9 @@ import GuardrailsPanel from "@/components/guardrails";
|
|||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
const GuardrailsPage = () => {
|
||||
const { accessToken, userRole } = useAuthorized();
|
||||
const { accessToken } = useAuthorized();
|
||||
|
||||
return <GuardrailsPanel accessToken={accessToken} userRole={userRole} />;
|
||||
return <GuardrailsPanel accessToken={accessToken} />;
|
||||
};
|
||||
|
||||
export default GuardrailsPage;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { getGuardrailsList, deleteGuardrailCall } from "./networking";
|
|||
import AddGuardrailForm from "./guardrails/add_guardrail_form";
|
||||
import GuardrailTable from "./guardrails/guardrail_table";
|
||||
import { isAdminRole } from "@/utils/roles";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import GuardrailInfoView from "./guardrails/guardrail_info";
|
||||
import GuardrailTestPlayground from "./guardrails/GuardrailTestPlayground";
|
||||
import NotificationsManager from "./molecules/notifications_manager";
|
||||
|
|
@ -17,7 +18,6 @@ import { TeamGuardrailsTab } from "./guardrails/TeamGuardrailsTab";
|
|||
|
||||
interface GuardrailsPanelProps {
|
||||
accessToken: string | null;
|
||||
userRole?: string;
|
||||
}
|
||||
|
||||
interface GuardrailItem {
|
||||
|
|
@ -38,7 +38,8 @@ interface GuardrailsResponse {
|
|||
guardrails: Guardrail[];
|
||||
}
|
||||
|
||||
const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole }) => {
|
||||
const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken }) => {
|
||||
const { userRole } = useAuthorized();
|
||||
const [guardrailsList, setGuardrailsList] = useState<Guardrail[]>([]);
|
||||
const [isAddModalVisible, setIsAddModalVisible] = useState(false);
|
||||
const [isCustomCodeModalVisible, setIsCustomCodeModalVisible] = useState(false);
|
||||
|
|
@ -252,7 +253,7 @@ const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken, userRole
|
|||
{
|
||||
key: "submitted",
|
||||
label: "Submitted Guardrails",
|
||||
children: <TeamGuardrailsTab accessToken={accessToken} userRole={userRole} />,
|
||||
children: <TeamGuardrailsTab accessToken={accessToken} />,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
import NotificationsManager from "@/components/molecules/notifications_manager";
|
||||
import TeamDropdown from "@/components/common_components/team_dropdown";
|
||||
import { useRegisterGuardrail } from "@/app/(dashboard)/hooks/guardrails/useRegisterGuardrail";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { isAdminRole } from "@/utils/roles";
|
||||
|
||||
type GuardrailStatus = "active" | "pending" | "rejected";
|
||||
|
|
@ -805,10 +806,10 @@ function ConfirmDialog({
|
|||
|
||||
interface TeamGuardrailsTabProps {
|
||||
accessToken: string | null;
|
||||
userRole?: string;
|
||||
}
|
||||
|
||||
export function TeamGuardrailsTab({ accessToken, userRole }: TeamGuardrailsTabProps) {
|
||||
export function TeamGuardrailsTab({ accessToken }: TeamGuardrailsTabProps) {
|
||||
const { userRole } = useAuthorized();
|
||||
const isAdmin = userRole ? isAdminRole(userRole) : false;
|
||||
const [guardrails, setGuardrails] = useState<TeamGuardrail[]>([]);
|
||||
const [summary, setSummary] = useState({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { renderWithProviders, screen } from "./test-utils";
|
||||
import { TeamGuardrailsTab } from "../src/components/guardrails/TeamGuardrailsTab";
|
||||
import useAuthorized from "../src/app/(dashboard)/hooks/useAuthorized";
|
||||
|
||||
vi.mock("../src/components/networking", () => ({
|
||||
listGuardrailSubmissions: vi.fn(),
|
||||
|
|
@ -20,6 +21,10 @@ vi.mock("../src/components/common_components/team_dropdown", () => ({
|
|||
default: () => null,
|
||||
}));
|
||||
|
||||
vi.mock("../src/app/(dashboard)/hooks/useAuthorized", () => ({
|
||||
default: vi.fn(),
|
||||
}));
|
||||
|
||||
import { listGuardrailSubmissions } from "../src/components/networking";
|
||||
|
||||
const pendingSubmission = {
|
||||
|
|
@ -37,7 +42,19 @@ const pendingSubmission = {
|
|||
submitted_at: "2026-05-09T00:00:00Z",
|
||||
};
|
||||
|
||||
const baseAuth = {
|
||||
token: "test-token",
|
||||
accessToken: "test-token",
|
||||
userId: "user-1",
|
||||
userEmail: "user@example.com",
|
||||
premiumUser: false,
|
||||
disabledPersonalKeyCreation: null,
|
||||
showSSOBanner: false,
|
||||
};
|
||||
|
||||
describe("TeamGuardrailsTab — approve/reject role gate", () => {
|
||||
const mockUseAuthorized = vi.mocked(useAuthorized);
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(listGuardrailSubmissions).mockResolvedValue({
|
||||
|
|
@ -47,11 +64,9 @@ describe("TeamGuardrailsTab — approve/reject role gate", () => {
|
|||
});
|
||||
|
||||
it("hides Approve and Reject buttons for an internal user on a pending submission", async () => {
|
||||
renderWithProviders(
|
||||
<TeamGuardrailsTab accessToken="test-token" userRole="Internal User" />
|
||||
);
|
||||
mockUseAuthorized.mockReturnValue({ ...baseAuth, userRole: "Internal User" });
|
||||
renderWithProviders(<TeamGuardrailsTab accessToken="test-token" />);
|
||||
|
||||
// Wait for the submission row to render
|
||||
await screen.findByText("test-pending-guardrail");
|
||||
|
||||
expect(screen.queryByRole("button", { name: /approve/i })).not.toBeInTheDocument();
|
||||
|
|
@ -59,9 +74,8 @@ describe("TeamGuardrailsTab — approve/reject role gate", () => {
|
|||
});
|
||||
|
||||
it("shows Approve and Reject buttons for an admin on a pending submission", async () => {
|
||||
renderWithProviders(
|
||||
<TeamGuardrailsTab accessToken="test-token" userRole="Admin" />
|
||||
);
|
||||
mockUseAuthorized.mockReturnValue({ ...baseAuth, userRole: "Admin" });
|
||||
renderWithProviders(<TeamGuardrailsTab accessToken="test-token" />);
|
||||
|
||||
await screen.findByText("test-pending-guardrail");
|
||||
|
||||
|
|
@ -70,6 +84,7 @@ describe("TeamGuardrailsTab — approve/reject role gate", () => {
|
|||
});
|
||||
|
||||
it("hides Approve and Reject buttons when userRole is undefined (defaults to non-admin)", async () => {
|
||||
mockUseAuthorized.mockReturnValue({ ...baseAuth, userRole: undefined });
|
||||
renderWithProviders(<TeamGuardrailsTab accessToken="test-token" />);
|
||||
|
||||
await screen.findByText("test-pending-guardrail");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue