diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsx index 8b62099a64..5133afc64f 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsx @@ -10,6 +10,8 @@ import dynamic from "next/dynamic"; import { useCallback, useEffect, useRef, useState } from "react"; import { ChatContainer } from "./components/ChatContainer/ChatContainer"; import { DeleteChatDialog } from "./components/DeleteChatDialog/DeleteChatDialog"; +import { MobileDrawer } from "./components/MobileDrawer/MobileDrawer"; +import { MobileHeader } from "./components/MobileHeader/MobileHeader"; import { NotificationBanner } from "./components/NotificationBanner/NotificationBanner"; import { NotificationDialog } from "./components/NotificationDialog/NotificationDialog"; import { RateLimitResetDialog } from "./components/RateLimitResetDialog/RateLimitResetDialog"; @@ -88,8 +90,16 @@ export function CopilotPage() { hasMoreMessages, isLoadingMore, loadMore, - // Mobile + // Mobile drawer isMobile, + isDrawerOpen, + sessions, + isLoadingSessions, + handleOpenDrawer, + handleCloseDrawer, + handleDrawerOpenChange, + handleSelectSession, + handleNewChat, // Delete functionality sessionToDelete, isDeleting, @@ -119,6 +129,7 @@ export function CopilotPage() { const isBillingEnabled = useGetFlag(Flag.ENABLE_PLATFORM_PAYMENT); const isArtifactsEnabled = useGetFlag(Flag.ARTIFACTS); + const isNewSidebar = useGetFlag(Flag.NEW_SIDEBAR); const { credits, fetchCredits } = useCredits({ fetchInitialCredits: true }); const hasInsufficientCredits = credits !== null && resetCost != null && credits < resetCost; @@ -155,6 +166,9 @@ export function CopilotPage() { onDragLeave={handleDragLeave} onDrop={handleDrop} > + {isMobile && !isNewSidebar && ( + + )} {isDryRun && (
@@ -206,6 +220,18 @@ export function CopilotPage() { onCancel={handleCancelDelete} /> )} + {isMobile && !isNewSidebar && ( + + )} 0} diff --git a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatSessionList/ChatSessionList.tsx b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatSessionList/ChatSessionList.tsx index 741745a76e..e6899c4307 100644 --- a/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatSessionList/ChatSessionList.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatSessionList/ChatSessionList.tsx @@ -28,8 +28,10 @@ import { useCopilotUIStore } from "@/app/(platform)/copilot/store"; import { Button } from "@/components/atoms/Button/Button"; import { Dialog } from "@/components/molecules/Dialog/Dialog"; import { DeleteChatDialog } from "@/app/(platform)/copilot/components/DeleteChatDialog/DeleteChatDialog"; +import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag"; export function ChatSessionList() { + const isNewSidebar = useGetFlag(Flag.NEW_SIDEBAR); const isMobile = useIsMobile(); const pathname = usePathname(); const router = useRouter(); @@ -223,6 +225,11 @@ export function ChatSessionList() { return ( <> + {!isNewSidebar && ( +
+ All tasks +
+ )}
{isLoadingSessions ? (
diff --git a/autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx b/autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx index efdd4d40e4..f9386f3816 100644 --- a/autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx +++ b/autogpt_platform/frontend/src/components/layout/AppSidebar/AppSidebar.tsx @@ -43,6 +43,7 @@ import { UsageLimits } from "@/app/(platform)/copilot/components/UsageLimits/Usa import { NotificationToggle } from "@/app/(platform)/copilot/components/ChatSidebar/components/NotificationToggle/NotificationToggle"; import { AgentActivityDropdown } from "@/components/layout/Navbar/components/AgentActivityDropdown/AgentActivityDropdown"; import { useTallyPopup } from "@/components/molecules/TallyPoup/useTallyPopup"; +import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag"; interface Props { dynamicContent?: ReactNode; @@ -54,6 +55,7 @@ export function AppSidebar({ dynamicContent }: Props) { const pathname = usePathname(); const { state: tallyState } = useTallyPopup(); const { isLoggedIn } = useSupabase(); + const isNewSidebar = useGetFlag(Flag.NEW_SIDEBAR); const [loadingHref, setLoadingHref] = useState(null); const [isTasksOpen, setIsTasksOpen] = useState(true); @@ -64,36 +66,64 @@ export function AppSidebar({ dynamicContent }: Props) { const homeHref = "/copilot"; - const navLinks = [ - { - name: "Search", - href: "/marketplace/search", - icon: MagnifyingGlass, - testId: "sidebar-link-search", - showWhenCollapsed: true, - }, - { - name: "Workflows", - href: "/library", - icon: Books, - testId: "sidebar-link-workflows", - showWhenCollapsed: true, - }, - { - name: "Explore", - href: "/marketplace", - icon: ShoppingBag, - testId: "sidebar-link-marketplace", - showWhenCollapsed: false, - }, - { - name: "Builder", - href: "/build", - icon: PenNibStraight, - testId: "sidebar-link-build", - showWhenCollapsed: false, - }, - ]; + const navLinks = isNewSidebar + ? [ + { + name: "Search", + href: "/marketplace/search", + icon: MagnifyingGlass, + testId: "sidebar-link-search", + showWhenCollapsed: true, + }, + { + name: "Workflows", + href: "/library", + icon: Books, + testId: "sidebar-link-workflows", + showWhenCollapsed: true, + }, + { + name: "Explore", + href: "/marketplace", + icon: ShoppingBag, + testId: "sidebar-link-marketplace", + showWhenCollapsed: false, + }, + { + name: "Builder", + href: "/build", + icon: PenNibStraight, + testId: "sidebar-link-build", + showWhenCollapsed: false, + }, + ] + : [ + { + name: "Workflows", + href: "/library", + icon: Books, + testId: "sidebar-link-workflows", + showWhenCollapsed: true, + }, + { + name: "Explore", + href: "/marketplace", + icon: ShoppingBag, + testId: "sidebar-link-marketplace", + showWhenCollapsed: true, + }, + { + name: "Builder", + href: "/build", + icon: PenNibStraight, + testId: "sidebar-link-build", + showWhenCollapsed: true, + }, + ]; + + const filteredNavLinks = isNewSidebar + ? navLinks.filter((link) => !isCollapsed || link.showWhenCollapsed) + : navLinks; function isActive(href: string) { if (href === homeHref) { @@ -120,12 +150,7 @@ export function AppSidebar({ dynamicContent }: Props) { )} > {!isCollapsed && ( - +
@@ -140,9 +165,9 @@ export function AppSidebar({ dynamicContent }: Props) {
-
+
)} - {isCollapsed && ( + {isCollapsed && isNewSidebar && (
)} + {isCollapsed && !isNewSidebar && ( +
+ + + +
+ )} {/* Navigation links */} -
- + + {isCollapsed && !isNewSidebar && ( + + + + )} + {isCollapsed && !isNewSidebar && ( + + + + )} + + + + !isActive(homeHref) && setLoadingHref(homeHref) + } + > + {loadingHref === homeHref && isCollapsed ? ( + + ) : ( + + )} + {!isCollapsed && New Task} + {loadingHref === homeHref && !isCollapsed && ( + + )} + + + + {filteredNavLinks.map((link) => ( + + - !isActive(homeHref) && setLoadingHref(homeHref) + !isActive(link.href) && setLoadingHref(link.href) } > - {loadingHref === homeHref && isCollapsed ? ( + {loadingHref === link.href && isCollapsed ? ( ) : ( - + )} - {!isCollapsed && New Task} - {loadingHref === homeHref && !isCollapsed && ( + {!isCollapsed && ( + {link.name} + )} + {loadingHref === link.href && !isCollapsed && ( )} - {navLinks - .filter((link) => !isCollapsed || link.showWhenCollapsed) - .map((link) => ( - - - - !isActive(link.href) && setLoadingHref(link.href) - } - > - {loadingHref === link.href && isCollapsed ? ( - - ) : ( - - )} - {!isCollapsed && ( - {link.name} - )} - {loadingHref === link.href && !isCollapsed && ( - - )} - - - - ))} - -
+ ))} +
{dynamicContent && ( - {!isCollapsed && ( + {!isCollapsed && isNewSidebar && ( <> - Feedback + + Feedback + )} {!isCollapsed &&
} diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx index 90f19c44e3..f5dad3a518 100644 --- a/autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx +++ b/autogpt_platform/frontend/src/components/layout/Navbar/Navbar.tsx @@ -2,17 +2,20 @@ import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store"; import { okData } from "@/app/api/helpers"; +import { IconType } from "@/components/__legacy__/ui/icons"; import { PreviewBanner } from "@/components/layout/Navbar/components/PreviewBanner/PreviewBanner"; import { SidebarTrigger } from "@/components/ui/sidebar"; import { isLogoutInProgress } from "@/lib/autogpt-server-api/helpers"; import { useBreakpoint } from "@/lib/hooks/useBreakpoint"; import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; import { environment } from "@/services/environment"; +import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag"; import { List } from "@phosphor-icons/react"; import { AccountMenu } from "./components/AccountMenu/AccountMenu"; import { LoginButton } from "./components/LoginButton"; +import { MobileNavBar } from "./components/MobileNavbar/MobileNavBar"; import { Wallet } from "./components/Wallet/Wallet"; -import { getAccountMenuItems } from "./helpers"; +import { getAccountMenuItems, loggedInLinks } from "./helpers"; export function Navbar() { const { user, isLoggedIn, isUserLoading } = useSupabase(); @@ -21,6 +24,7 @@ export function Navbar() { const dynamicMenuItems = getAccountMenuItems(user?.role); const previewBranchName = environment.getPreviewStealingDev(); const logoutInProgress = isLogoutInProgress(); + const isNewSidebar = useGetFlag(Flag.NEW_SIDEBAR); const { data: profile, isLoading: isProfileLoading } = useGetV2GetUserProfile( { @@ -35,6 +39,12 @@ export function Navbar() { const isLoadingProfile = isProfileLoading || isUserLoading; const shouldShowPreviewBanner = Boolean(isLoggedIn && previewBranchName); + const actualLoggedInLinks = [ + { name: "Home", href: "/copilot" }, + { name: "Agents", href: "/library" }, + ...loggedInLinks, + ]; + if (isUserLoading) { return null; } @@ -67,8 +77,8 @@ export function Navbar() {
) : null} - {/* Mobile top bar: credits + hamburger on right */} - {isLoggedIn && isSmallScreen ? ( + {/* Mobile top bar: new sidebar trigger + wallet (new) or MobileNavBar (old) */} + {isLoggedIn && isSmallScreen && isNewSidebar ? (
@@ -76,6 +86,47 @@ export function Navbar() {
) : null} + + {isLoggedIn && isSmallScreen && !isNewSidebar ? ( +
+ + { + return { + icon: + link.href === "/marketplace" + ? IconType.Marketplace + : link.href === "/build" + ? IconType.Builder + : link.href === "/copilot" + ? IconType.Chat + : link.href === "/library" + ? IconType.Library + : link.href === "/monitor" + ? IconType.Library + : IconType.LayoutDashboard, + text: link.name, + href: link.href, + }; + }) + .filter((item) => item !== null) as Array<{ + icon: IconType; + text: string; + href: string; + }>, + }, + ...dynamicMenuItems, + ]} + userEmail={profile?.name} + avatarSrc={profile?.avatar_url ?? ""} + /> +
+ ) : null} ); } diff --git a/autogpt_platform/frontend/src/services/feature-flags/use-get-flag.ts b/autogpt_platform/frontend/src/services/feature-flags/use-get-flag.ts index e16f5b765a..28b2c4a22f 100644 --- a/autogpt_platform/frontend/src/services/feature-flags/use-get-flag.ts +++ b/autogpt_platform/frontend/src/services/feature-flags/use-get-flag.ts @@ -11,6 +11,7 @@ export enum Flag { ARTIFACTS = "artifacts", CHAT_MODE_OPTION = "chat-mode-option", BUILDER_CHAT_PANEL = "builder-chat-panel", + NEW_SIDEBAR = "new-sidebar", } const isPwMockEnabled = process.env.NEXT_PUBLIC_PW_TEST === "true"; @@ -22,6 +23,7 @@ const defaultFlags = { [Flag.ARTIFACTS]: false, [Flag.CHAT_MODE_OPTION]: false, [Flag.BUILDER_CHAT_PANEL]: false, + [Flag.NEW_SIDEBAR]: false, }; type FlagValues = typeof defaultFlags;