refactoredUI feature flag

This commit is contained in:
= 2025-10-06 10:28:48 -07:00
parent 3f53d6b7ad
commit 53ee3e2634
6 changed files with 78 additions and 30 deletions

View file

@ -44,7 +44,7 @@ interface MenuItem {
icon?: React.ReactNode;
}
const Sidebar: React.FC<SidebarProps> = ({ accessToken, userRole, defaultSelectedKey, collapsed = false }) => {
const Sidebar2: React.FC<SidebarProps> = ({ accessToken, userRole, defaultSelectedKey, collapsed = false }) => {
const menuItems: MenuItem[] = [
{ key: "1", page: "api-keys", label: "Virtual Keys", icon: <KeyOutlined style={{ fontSize: 18 }} /> },
{
@ -288,4 +288,4 @@ const Sidebar: React.FC<SidebarProps> = ({ accessToken, userRole, defaultSelecte
);
};
export default Sidebar;
export default Sidebar2;

View file

@ -3,7 +3,7 @@
import React from "react";
import Navbar from "@/components/navbar";
import { ThemeProvider } from "@/contexts/ThemeContext";
import Sidebar from "@/app/(console)/components/Sidebar";
import Sidebar2 from "@/app/(console)/components/Sidebar2";
import useAuthorized from "@/app/(console)/hooks/useAuthorized";
export default function Layout({ children }: { children: React.ReactNode }) {
@ -31,7 +31,7 @@ export default function Layout({ children }: { children: React.ReactNode }) {
/>
<div className="flex flex-1 overflow-auto">
<div className="mt-2">
<Sidebar
<Sidebar2
collapsed={sidebarCollapsed}
accessToken={accessToken}
userRole={userRole}

View file

@ -39,6 +39,8 @@ import VectorStoreManagement from "@/components/vector_store_management";
import UIThemeSettings from "@/components/ui_theme_settings";
import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";
import { cx } from "@/lib/cva.config";
import useFeatureFlags from "@/hooks/useFeatureFlags";
import Sidebar2 from "@/app/(console)/components/Sidebar2";
function getCookie(name: string) {
const cookieValue = document.cookie.split("; ").find((row) => row.startsWith(name + "="));
@ -116,6 +118,8 @@ export default function CreateKeyPage() {
const [authLoading, setAuthLoading] = useState(true);
const [userID, setUserID] = useState<string | null>(null);
const { refactoredUIFlag, setRefactoredUIFlag } = useFeatureFlags();
const invitation_id = searchParams.get("invitation_id");
// Get page from URL, default to 'api-keys' if not present
@ -254,16 +258,22 @@ export default function CreateKeyPage() {
isPublicPage={false}
sidebarCollapsed={sidebarCollapsed}
onToggleSidebar={toggleSidebar}
refactoredUIFlag={refactoredUIFlag}
setRefactoredUIFlag={setRefactoredUIFlag}
/>
<div className="flex flex-1 overflow-auto">
<div className="mt-2">
<Sidebar
accessToken={accessToken}
setPage={updatePage}
userRole={userRole}
defaultSelectedKey={page}
collapsed={sidebarCollapsed}
/>
{refactoredUIFlag ? (
<Sidebar2 accessToken={accessToken} userRole={userRole} />
) : (
<Sidebar
accessToken={accessToken}
setPage={updatePage}
userRole={userRole}
defaultSelectedKey={page}
collapsed={sidebarCollapsed}
/>
)}
</div>
{page == "api-keys" ? (

View file

@ -12,17 +12,29 @@ import {
AppstoreOutlined,
DatabaseOutlined,
FileTextOutlined,
LineOutlined,
LineChartOutlined,
SafetyOutlined,
ExperimentOutlined,
ThunderboltOutlined,
LockOutlined,
ToolOutlined,
TagsOutlined,
BgColorsOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
} from "@ant-design/icons";
import { all_admin_roles, rolesWithWriteAccess, internalUserRoles, isAdminRole } from "../utils/roles";
import {
old_admin_roles,
v2_admin_role_names,
all_admin_roles,
rolesAllowedToSeeUsage,
rolesWithWriteAccess,
internalUserRoles,
isAdminRole,
} from "../utils/roles";
import UsageIndicator from "./usage_indicator";
import { ConfigProvider } from "antd";
import { useRouter } from "next/navigation";
const { Sider } = Layout;
// Define the props type
@ -45,7 +57,6 @@ interface MenuItem {
}
const Sidebar: React.FC<SidebarProps> = ({ accessToken, setPage, userRole, defaultSelectedKey, collapsed = false }) => {
const router = useRouter();
// Note: If a menu item does not have a role, it is visible to all roles.
const menuItems: MenuItem[] = [
{
@ -237,19 +248,6 @@ const Sidebar: React.FC<SidebarProps> = ({ accessToken, setPage, userRole, defau
return true;
});
const navigateToPage = (page: string) => {
if (page === "api-keys") {
// Go to clean route and DO NOT call setPage to avoid any parent sync that re-adds ?page=api-keys
router.replace("/virtual-keys");
return;
}
const newSearchParams = new URLSearchParams(window.location.search);
newSearchParams.set("page", page);
// Use absolute root to replace everything after the domain
router.replace(`/?${newSearchParams.toString()}`);
setPage(page);
};
return (
<Layout style={{ minHeight: "100vh" }}>
<Sider
@ -293,9 +291,21 @@ const Sidebar: React.FC<SidebarProps> = ({ accessToken, setPage, userRole, defau
key: child.key,
icon: child.icon,
label: child.label,
onClick: () => navigateToPage(child.page),
onClick: () => {
const newSearchParams = new URLSearchParams(window.location.search);
newSearchParams.set("page", child.page);
window.history.pushState(null, "", `?${newSearchParams.toString()}`);
setPage(child.page);
},
})),
onClick: !item.children ? () => navigateToPage(item.page) : undefined,
onClick: !item.children
? () => {
const newSearchParams = new URLSearchParams(window.location.search);
newSearchParams.set("page", item.page);
window.history.pushState(null, "", `?${newSearchParams.toString()}`);
setPage(item.page);
}
: undefined,
}))}
/>
</ConfigProvider>

View file

@ -1,7 +1,7 @@
import Link from "next/link";
import React, { useState, useEffect } from "react";
import type { MenuProps } from "antd";
import { Dropdown, Tooltip } from "antd";
import { Dropdown, Tooltip, Switch } from "antd";
import { getProxyBaseUrl, Organization } from "@/components/networking";
import { defaultOrg } from "@/components/common_components/default_org";
import {
@ -19,6 +19,7 @@ import { clearTokenCookies } from "@/utils/cookieUtils";
import { fetchProxySettings } from "@/utils/proxyUtils";
import { useTheme } from "@/contexts/ThemeContext";
import { clearMCPAuthTokens } from "./mcp_tools/mcp_auth_storage";
import useFeatureFlags from "@/hooks/useFeatureFlags";
interface NavbarProps {
userID: string | null;
@ -31,6 +32,8 @@ interface NavbarProps {
isPublicPage: boolean;
sidebarCollapsed?: boolean;
onToggleSidebar?: () => void;
refactoredUIFlag: boolean;
setRefactoredUIFlag: React.Dispatch<React.SetStateAction<boolean>>;
}
const Navbar: React.FC<NavbarProps> = ({
@ -44,6 +47,8 @@ const Navbar: React.FC<NavbarProps> = ({
isPublicPage = false,
sidebarCollapsed = false,
onToggleSidebar,
refactoredUIFlag,
setRefactoredUIFlag,
}) => {
const baseUrl = getProxyBaseUrl();
const [logoutUrl, setLogoutUrl] = useState("");
@ -79,6 +84,8 @@ const Navbar: React.FC<NavbarProps> = ({
const userItems: MenuProps["items"] = [
{
key: "user-info",
// Prevent dropdown from closing when interacting with the toggle
onClick: (info) => info.domEvent?.stopPropagation(),
label: (
<div className="px-3 py-3 border-b border-gray-100">
<div className="flex items-center justify-between mb-3">
@ -115,6 +122,18 @@ const Navbar: React.FC<NavbarProps> = ({
{userEmail || "Unknown"}
</span>
</div>
{/* NEW: Feature flag label + toggle below the email field */}
<div className="flex items-center text-sm pt-2 mt-2 border-t border-gray-100">
<span className="text-gray-500 text-xs">Refactored UI</span>
<Switch
className="ml-auto"
size="small"
checked={refactoredUIFlag}
onChange={(checked) => setRefactoredUIFlag(checked)}
aria-label="Toggle refactored UI feature flag"
/>
</div>
</div>
</div>
),

View file

@ -0,0 +1,9 @@
import { useState } from "react";
const useFeatureFlags = () => {
const [refactoredUIFlag, setRefactoredUIFlag] = useState<boolean>(false);
return { refactoredUIFlag, setRefactoredUIFlag };
};
export default useFeatureFlags;