feat(ui): migrate budgets, workflows, and guardrails-monitor to path routes (#30236)
* feat(ui): cut budgets, workflows, and guardrails-monitor over to path routes Continues the page-by-page App Router migration (#30185, #30226). All three legacy switch arms passed only accessToken, so each route wrapper is a thin useAuthorized() + render. MIGRATED_PAGES routes the sidebar and redirects the legacy ?page= URLs; the e2e fixture picks all three up in the migration smoke and sidebar specs automatically. * refactor(ui): colocate budgets, workflows, and guardrails-monitor components budgets and workflow_runs were imported only by the legacy switch, so they move wholesale into their route folders; the budgetItem type hoists into the shared useBudgets hook, which owns the API response shape, so the hooks layer no longer imports from a page folder. GuardrailsMonitor keeps LogViewer, mockData, and MetricCard at the shared src/components home because ToolDetail and ToolPolicies import them; the rest moves. eslint suppressions are re-keyed accordingly. * fix(ui): restore MetricCard test-utils path and merge duplicate import MetricCard.test.tsx got the moved-tree depth rewrite before being moved back to src/components/GuardrailsMonitor, leaving a five-level path that escapes the project root; the suite failed at import. Also merge the two imports from useBudgets in budget_panel.tsx. Both flagged by Greptile.
This commit is contained in:
parent
1828a7c6f0
commit
3ad385a8a4
@ -10,16 +10,18 @@
|
|||||||
*
|
*
|
||||||
* Keep this in lockstep with MIGRATED_PAGES in src/utils/migratedPages.ts.
|
* Keep this in lockstep with MIGRATED_PAGES in src/utils/migratedPages.ts.
|
||||||
* Pending (add as each PR lands): the leaf-pages batch
|
* Pending (add as each PR lands): the leaf-pages batch
|
||||||
* (budgets, caching, cost-tracking, guardrails, guardrails-monitor, logs,
|
* (caching, cost-tracking, guardrails, logs, mcp-servers, memory,
|
||||||
* mcp-servers, memory, policies, prompts, search-tools, skills,
|
* policies, prompts, search-tools, skills, tag-management, tool-policies,
|
||||||
* tag-management, tool-policies, transform-request, ui-theme, vector-stores,
|
* transform-request, ui-theme, vector-stores).
|
||||||
* workflows).
|
|
||||||
*/
|
*/
|
||||||
export const MIGRATED_E2E_PAGES: Record<string, string> = {
|
export const MIGRATED_E2E_PAGES: Record<string, string> = {
|
||||||
api_ref: "api-reference",
|
api_ref: "api-reference",
|
||||||
"llm-playground": "playground",
|
"llm-playground": "playground",
|
||||||
projects: "projects",
|
projects: "projects",
|
||||||
"access-groups": "access-groups",
|
"access-groups": "access-groups",
|
||||||
|
budgets: "budgets",
|
||||||
|
workflows: "workflows",
|
||||||
|
"guardrails-monitor": "guardrails-monitor",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MIGRATED_E2E_SEGMENTS: string[] = [...new Set(Object.values(MIGRATED_E2E_PAGES))];
|
export const MIGRATED_E2E_SEGMENTS: string[] = [...new Set(Object.values(MIGRATED_E2E_PAGES))];
|
||||||
|
|||||||
@ -419,22 +419,22 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/GuardrailsMonitor/EvaluationSettingsModal.tsx": {
|
"src/app/(dashboard)/guardrails-monitor/components/EvaluationSettingsModal.tsx": {
|
||||||
"react-hooks/set-state-in-effect": {
|
"react-hooks/set-state-in-effect": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/GuardrailsMonitor/GuardrailsMonitorView.tsx": {
|
"src/app/(dashboard)/guardrails-monitor/components/GuardrailsMonitorView.tsx": {
|
||||||
"no-restricted-imports": {
|
"no-restricted-imports": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/GuardrailsMonitor/ScoreChart.test.tsx": {
|
"src/app/(dashboard)/guardrails-monitor/components/ScoreChart.test.tsx": {
|
||||||
"react/display-name": {
|
"react/display-name": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/GuardrailsMonitor/ScoreChart.tsx": {
|
"src/app/(dashboard)/guardrails-monitor/components/ScoreChart.tsx": {
|
||||||
"no-restricted-imports": {
|
"no-restricted-imports": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
@ -798,22 +798,22 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/budgets/budget_modal.tsx": {
|
"src/app/(dashboard)/budgets/components/budget_modal.tsx": {
|
||||||
"no-restricted-imports": {
|
"no-restricted-imports": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/budgets/budget_panel.test.tsx": {
|
"src/app/(dashboard)/budgets/components/budget_panel.test.tsx": {
|
||||||
"unused-imports/no-unused-imports": {
|
"unused-imports/no-unused-imports": {
|
||||||
"count": 2
|
"count": 2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/budgets/budget_panel.tsx": {
|
"src/app/(dashboard)/budgets/components/budget_panel.tsx": {
|
||||||
"no-restricted-imports": {
|
"no-restricted-imports": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/budgets/edit_budget_modal.tsx": {
|
"src/app/(dashboard)/budgets/components/edit_budget_modal.tsx": {
|
||||||
"no-restricted-imports": {
|
"no-restricted-imports": {
|
||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
@ -2163,7 +2163,7 @@
|
|||||||
"count": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"src/components/workflow_runs/index.tsx": {
|
"src/app/(dashboard)/workflows/WorkflowRuns.tsx": {
|
||||||
"no-restricted-syntax": {
|
"no-restricted-syntax": {
|
||||||
"count": 3
|
"count": 3
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import { TextInput, Accordion, AccordionHeader, AccordionBody } from "@tremor/react";
|
import { TextInput, Accordion, AccordionHeader, AccordionBody } from "@tremor/react";
|
||||||
import { Button as Button2, Modal, Form, InputNumber, Select } from "antd";
|
import { Button as Button2, Modal, Form, InputNumber, Select } from "antd";
|
||||||
import { useCreateBudget } from "@/app/(dashboard)/hooks/budgets/useBudgets";
|
import { useCreateBudget } from "@/app/(dashboard)/hooks/budgets/useBudgets";
|
||||||
import NotificationsManager from "../molecules/notifications_manager";
|
import NotificationsManager from "@/components/molecules/notifications_manager";
|
||||||
|
|
||||||
interface BudgetModalProps {
|
interface BudgetModalProps {
|
||||||
isModalVisible: boolean;
|
isModalVisible: boolean;
|
||||||
@ -21,10 +21,10 @@ import {
|
|||||||
} from "@tremor/react";
|
} from "@tremor/react";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||||
import DeleteResourceModal from "../common_components/DeleteResourceModal";
|
import DeleteResourceModal from "@/components/common_components/DeleteResourceModal";
|
||||||
import TableIconActionButton from "../common_components/IconActionButton/TableIconActionButtons/TableIconActionButton";
|
import TableIconActionButton from "@/components/common_components/IconActionButton/TableIconActionButtons/TableIconActionButton";
|
||||||
import NotificationsManager from "../molecules/notifications_manager";
|
import NotificationsManager from "@/components/molecules/notifications_manager";
|
||||||
import { useBudgets, useDeleteBudget } from "@/app/(dashboard)/hooks/budgets/useBudgets";
|
import { useBudgets, useDeleteBudget, budgetItem } from "@/app/(dashboard)/hooks/budgets/useBudgets";
|
||||||
import BudgetModal from "./budget_modal";
|
import BudgetModal from "./budget_modal";
|
||||||
import EditBudgetModal from "./edit_budget_modal";
|
import EditBudgetModal from "./edit_budget_modal";
|
||||||
import { CREATE_END_USER_CURL_COMMAND, CHAT_COMPLETIONS_CURL_COMMAND, OPENAI_SDK_PYTHON_CODE } from "./constants";
|
import { CREATE_END_USER_CURL_COMMAND, CHAT_COMPLETIONS_CURL_COMMAND, OPENAI_SDK_PYTHON_CODE } from "./constants";
|
||||||
@ -35,14 +35,6 @@ interface BudgetSettingsPageProps {
|
|||||||
accessToken: string | null;
|
accessToken: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface budgetItem {
|
|
||||||
budget_id: string;
|
|
||||||
max_budget: number | null;
|
|
||||||
rpm_limit: number | null;
|
|
||||||
tpm_limit: number | null;
|
|
||||||
updated_at: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
|
const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
|
||||||
const [isCreateModelVisible, setIsCreateModelVisible] = useState(false);
|
const [isCreateModelVisible, setIsCreateModelVisible] = useState(false);
|
||||||
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
|
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
|
||||||
@ -2,8 +2,8 @@ import React, { useEffect } from "react";
|
|||||||
import { TextInput, Accordion, AccordionHeader, AccordionBody } from "@tremor/react";
|
import { TextInput, Accordion, AccordionHeader, AccordionBody } from "@tremor/react";
|
||||||
import { Button as Button2, Modal, Form, InputNumber, Select } from "antd";
|
import { Button as Button2, Modal, Form, InputNumber, Select } from "antd";
|
||||||
import { useUpdateBudget } from "@/app/(dashboard)/hooks/budgets/useBudgets";
|
import { useUpdateBudget } from "@/app/(dashboard)/hooks/budgets/useBudgets";
|
||||||
import { budgetItem } from "./budget_panel";
|
import { budgetItem } from "@/app/(dashboard)/hooks/budgets/useBudgets";
|
||||||
import NotificationsManager from "../molecules/notifications_manager";
|
import NotificationsManager from "@/components/molecules/notifications_manager";
|
||||||
|
|
||||||
interface EditBudgetModalProps {
|
interface EditBudgetModalProps {
|
||||||
isModalVisible: boolean;
|
isModalVisible: boolean;
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import BudgetPanel from "./components/budget_panel";
|
||||||
|
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||||
|
|
||||||
|
export default function Budgets() {
|
||||||
|
const { accessToken } = useAuthorized();
|
||||||
|
return <BudgetPanel accessToken={accessToken} />;
|
||||||
|
}
|
||||||
@ -4,9 +4,9 @@ import { Button, Col, Row, Spin, Tabs } from "antd";
|
|||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { getGuardrailsUsageDetail, getGuardrailsUsageLogs } from "@/components/networking";
|
import { getGuardrailsUsageDetail, getGuardrailsUsageLogs } from "@/components/networking";
|
||||||
import { EvaluationSettingsModal } from "./EvaluationSettingsModal";
|
import { EvaluationSettingsModal } from "./EvaluationSettingsModal";
|
||||||
import { LogViewer } from "./LogViewer";
|
import { LogViewer } from "@/components/GuardrailsMonitor/LogViewer";
|
||||||
import { MetricCard } from "./MetricCard";
|
import { MetricCard } from "@/components/GuardrailsMonitor/MetricCard";
|
||||||
import type { LogEntry } from "./mockData";
|
import type { LogEntry } from "@/components/GuardrailsMonitor/mockData";
|
||||||
|
|
||||||
interface GuardrailDetailProps {
|
interface GuardrailDetailProps {
|
||||||
guardrailId: string;
|
guardrailId: string;
|
||||||
@ -4,9 +4,9 @@ import { Button, Card, Col, Row, Spin, Table, Typography } from "antd";
|
|||||||
import type { ColumnsType } from "antd/es/table";
|
import type { ColumnsType } from "antd/es/table";
|
||||||
import React, { useMemo, useState } from "react";
|
import React, { useMemo, useState } from "react";
|
||||||
import { getGuardrailsUsageOverview } from "@/components/networking";
|
import { getGuardrailsUsageOverview } from "@/components/networking";
|
||||||
import { type PerformanceRow } from "./mockData";
|
import { type PerformanceRow } from "@/components/GuardrailsMonitor/mockData";
|
||||||
import { EvaluationSettingsModal } from "./EvaluationSettingsModal";
|
import { EvaluationSettingsModal } from "./EvaluationSettingsModal";
|
||||||
import { MetricCard } from "./MetricCard";
|
import { MetricCard } from "@/components/GuardrailsMonitor/MetricCard";
|
||||||
import { ScoreChart } from "./ScoreChart";
|
import { ScoreChart } from "./ScoreChart";
|
||||||
|
|
||||||
interface GuardrailsOverviewProps {
|
interface GuardrailsOverviewProps {
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { describe, it, expect, vi } from "vitest";
|
import { describe, it, expect, vi } from "vitest";
|
||||||
import { screen } from "@testing-library/react";
|
import { screen } from "@testing-library/react";
|
||||||
import { renderWithProviders } from "../../../tests/test-utils";
|
import { renderWithProviders } from "../../../../../tests/test-utils";
|
||||||
import { ScoreChart } from "./ScoreChart";
|
import { ScoreChart } from "./ScoreChart";
|
||||||
|
|
||||||
vi.mock("@tremor/react", async (importOriginal) => {
|
vi.mock("@tremor/react", async (importOriginal) => {
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import GuardrailsMonitorView from "./components/GuardrailsMonitorView";
|
||||||
|
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||||
|
|
||||||
|
export default function GuardrailsMonitor() {
|
||||||
|
const { accessToken } = useAuthorized();
|
||||||
|
return <GuardrailsMonitorView accessToken={accessToken} />;
|
||||||
|
}
|
||||||
@ -2,7 +2,14 @@ import { useQuery, useMutation, useQueryClient, UseQueryResult } from "@tanstack
|
|||||||
import { createQueryKeys } from "../common/queryKeysFactory";
|
import { createQueryKeys } from "../common/queryKeysFactory";
|
||||||
import { getBudgetList, budgetCreateCall, budgetUpdateCall, budgetDeleteCall } from "@/components/networking";
|
import { getBudgetList, budgetCreateCall, budgetUpdateCall, budgetDeleteCall } from "@/components/networking";
|
||||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||||
import { budgetItem } from "@/components/budgets/budget_panel";
|
|
||||||
|
export interface budgetItem {
|
||||||
|
budget_id: string;
|
||||||
|
max_budget: number | null;
|
||||||
|
rpm_limit: number | null;
|
||||||
|
tpm_limit: number | null;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const budgetKeys = createQueryKeys("budgets");
|
export const budgetKeys = createQueryKeys("budgets");
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
import ModelsAndEndpointsView from "@/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView";
|
import ModelsAndEndpointsView from "@/app/(dashboard)/models-and-endpoints/ModelsAndEndpointsView";
|
||||||
import AdminPanel from "@/components/AdminPanel";
|
import AdminPanel from "@/components/AdminPanel";
|
||||||
import AgentsPanel from "@/components/agents";
|
import AgentsPanel from "@/components/agents";
|
||||||
import BudgetPanel from "@/components/budgets/budget_panel";
|
|
||||||
import CacheDashboard from "@/components/cache_dashboard";
|
import CacheDashboard from "@/components/cache_dashboard";
|
||||||
import ClaudeCodePluginsPanel from "@/components/claude_code_plugins";
|
import ClaudeCodePluginsPanel from "@/components/claude_code_plugins";
|
||||||
import { teamListCall as v2TeamListCall } from "@/app/(dashboard)/hooks/teams/useTeams";
|
import { teamListCall as v2TeamListCall } from "@/app/(dashboard)/hooks/teams/useTeams";
|
||||||
@ -12,7 +11,6 @@ import useProxySettings from "@/app/(dashboard)/hooks/proxySettings/useProxySett
|
|||||||
import LoadingScreen from "@/components/common_components/LoadingScreen";
|
import LoadingScreen from "@/components/common_components/LoadingScreen";
|
||||||
import { CostTrackingSettings } from "@/components/CostTrackingSettings";
|
import { CostTrackingSettings } from "@/components/CostTrackingSettings";
|
||||||
import GeneralSettings from "@/components/general_settings";
|
import GeneralSettings from "@/components/general_settings";
|
||||||
import GuardrailsMonitorView from "@/components/GuardrailsMonitor/GuardrailsMonitorView";
|
|
||||||
import GuardrailsPanel from "@/components/guardrails";
|
import GuardrailsPanel from "@/components/guardrails";
|
||||||
import PoliciesPanel from "@/components/policies";
|
import PoliciesPanel from "@/components/policies";
|
||||||
import { Team } from "@/components/key_team_helpers/key_list";
|
import { Team } from "@/components/key_team_helpers/key_list";
|
||||||
@ -37,7 +35,6 @@ import UserDashboard from "@/components/user_dashboard";
|
|||||||
import VectorStoreManagement from "@/components/vector_store_management";
|
import VectorStoreManagement from "@/components/vector_store_management";
|
||||||
import ToolPoliciesView from "@/components/ToolPoliciesView";
|
import ToolPoliciesView from "@/components/ToolPoliciesView";
|
||||||
import { MemoryView } from "@/components/MemoryView";
|
import { MemoryView } from "@/components/MemoryView";
|
||||||
import WorkflowRuns from "@/components/workflow_runs";
|
|
||||||
import SpendLogsTable from "@/components/view_logs";
|
import SpendLogsTable from "@/components/view_logs";
|
||||||
import ViewUserDashboard from "@/components/view_users";
|
import ViewUserDashboard from "@/components/view_users";
|
||||||
import { useAuth } from "@/contexts/AuthContext";
|
import { useAuth } from "@/contexts/AuthContext";
|
||||||
@ -385,8 +382,6 @@ function CreateKeyPageContent() {
|
|||||||
<AdminPanel proxySettings={proxySettings} />
|
<AdminPanel proxySettings={proxySettings} />
|
||||||
) : page == "logging-and-alerts" ? (
|
) : page == "logging-and-alerts" ? (
|
||||||
<Settings userID={userID} userRole={userRole} accessToken={accessToken} premiumUser={premiumUser} />
|
<Settings userID={userID} userRole={userRole} accessToken={accessToken} premiumUser={premiumUser} />
|
||||||
) : page == "budgets" ? (
|
|
||||||
<BudgetPanel accessToken={accessToken} />
|
|
||||||
) : page == "guardrails" ? (
|
) : page == "guardrails" ? (
|
||||||
<GuardrailsPanel accessToken={accessToken} userRole={userRole} />
|
<GuardrailsPanel accessToken={accessToken} userRole={userRole} />
|
||||||
) : page == "policies" ? (
|
) : page == "policies" ? (
|
||||||
@ -450,12 +445,8 @@ function CreateKeyPageContent() {
|
|||||||
<VectorStoreManagement accessToken={accessToken} userRole={userRole} userID={userID} />
|
<VectorStoreManagement accessToken={accessToken} userRole={userRole} userID={userID} />
|
||||||
) : page == "tool-policies" ? (
|
) : page == "tool-policies" ? (
|
||||||
<ToolPoliciesView accessToken={accessToken} userRole={userRole} />
|
<ToolPoliciesView accessToken={accessToken} userRole={userRole} />
|
||||||
) : page == "workflows" ? (
|
|
||||||
<WorkflowRuns accessToken={accessToken} />
|
|
||||||
) : page == "memory" ? (
|
) : page == "memory" ? (
|
||||||
<MemoryView accessToken={accessToken} userID={userID} userRole={userRole} />
|
<MemoryView accessToken={accessToken} userID={userID} userRole={userRole} />
|
||||||
) : page == "guardrails-monitor" ? (
|
|
||||||
<GuardrailsMonitorView accessToken={accessToken} />
|
|
||||||
) : page == "new_usage" ? (
|
) : page == "new_usage" ? (
|
||||||
<NewUsagePage teams={(teams as Team[]) ?? []} organizations={(organizations as Organization[]) ?? []} />
|
<NewUsagePage teams={(teams as Team[]) ?? []} organizations={(organizations as Organization[]) ?? []} />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -0,0 +1,9 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import WorkflowRuns from "./WorkflowRuns";
|
||||||
|
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||||
|
|
||||||
|
export default function Workflows() {
|
||||||
|
const { accessToken } = useAuthorized();
|
||||||
|
return <WorkflowRuns accessToken={accessToken} />;
|
||||||
|
}
|
||||||
@ -55,6 +55,15 @@ describe("migratedHref / legacyPageHref", () => {
|
|||||||
expect(MIGRATED_PAGES.projects).toBe("projects");
|
expect(MIGRATED_PAGES.projects).toBe("projects");
|
||||||
expect(MIGRATED_PAGES["access-groups"]).toBe("access-groups");
|
expect(MIGRATED_PAGES["access-groups"]).toBe("access-groups");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("maps the budgets, workflows, and guardrails-monitor sidebar ids to their routes", async () => {
|
||||||
|
vi.doMock("@/components/networking", () => ({ serverRootPath: "/" }));
|
||||||
|
const { MIGRATED_PAGES } = await import("./migratedPages");
|
||||||
|
|
||||||
|
expect(MIGRATED_PAGES.budgets).toBe("budgets");
|
||||||
|
expect(MIGRATED_PAGES.workflows).toBe("workflows");
|
||||||
|
expect(MIGRATED_PAGES["guardrails-monitor"]).toBe("guardrails-monitor");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("dev server (NODE_ENV=development)", () => {
|
describe("dev server (NODE_ENV=development)", () => {
|
||||||
|
|||||||
@ -15,6 +15,9 @@ export const MIGRATED_PAGES: Record<string, string> = {
|
|||||||
"llm-playground": "playground",
|
"llm-playground": "playground",
|
||||||
projects: "projects",
|
projects: "projects",
|
||||||
"access-groups": "access-groups",
|
"access-groups": "access-groups",
|
||||||
|
budgets: "budgets",
|
||||||
|
workflows: "workflows",
|
||||||
|
"guardrails-monitor": "guardrails-monitor",
|
||||||
};
|
};
|
||||||
|
|
||||||
function uiBase(): string {
|
function uiBase(): string {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user