mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-09 15:17:59 -05:00
Merge branch 'dev' into claude/dedupe-discord-notifications-exSqa
This commit is contained in:
2
autogpt_platform/frontend/pnpm-lock.yaml
generated
2
autogpt_platform/frontend/pnpm-lock.yaml
generated
@@ -13011,7 +13011,7 @@ snapshots:
|
||||
minimatch: 3.1.2
|
||||
node-abort-controller: 3.1.1
|
||||
schema-utils: 3.3.0
|
||||
semver: 7.7.2
|
||||
semver: 7.7.3
|
||||
tapable: 2.2.3
|
||||
typescript: 5.9.3
|
||||
webpack: 5.101.3(esbuild@0.25.9)
|
||||
|
||||
@@ -4,19 +4,12 @@ import {
|
||||
OutputActions,
|
||||
OutputItem,
|
||||
} from "@/app/(platform)/library/agents/[id]/components/NewAgentLibraryView/components/selected-views/OutputRenderers";
|
||||
import { Dialog } from "@/components/molecules/Dialog/Dialog";
|
||||
import { beautifyString } from "@/lib/utils";
|
||||
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
|
||||
import { Clipboard, Maximize2 } from "lucide-react";
|
||||
import React, { FC, useMemo, useState } from "react";
|
||||
import { Button } from "../../../../../components/__legacy__/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../../../../../components/__legacy__/ui/dialog";
|
||||
import { ContentRenderer } from "../../../../../components/__legacy__/ui/render";
|
||||
import { ScrollArea } from "../../../../../components/__legacy__/ui/scroll-area";
|
||||
import { Separator } from "../../../../../components/__legacy__/ui/separator";
|
||||
@@ -120,138 +113,155 @@ const ExpandableOutputDialog: FC<ExpandableOutputDialogProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent className="flex h-[90vh] w-[90vw] max-w-4xl flex-col">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center justify-between pr-8">
|
||||
<div className="flex items-center gap-2">
|
||||
<Maximize2 size={20} />
|
||||
Full Output Preview
|
||||
</div>
|
||||
{enableEnhancedOutputHandling && (
|
||||
<div className="flex items-center gap-3">
|
||||
<label
|
||||
htmlFor="enhanced-rendering-toggle"
|
||||
className="cursor-pointer select-none text-sm font-normal text-gray-600"
|
||||
>
|
||||
Enhanced Rendering
|
||||
</label>
|
||||
<Switch
|
||||
id="enhanced-rendering-toggle"
|
||||
checked={useEnhancedRenderer}
|
||||
onCheckedChange={setUseEnhancedRenderer}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Execution ID: <span className="font-mono text-xs">{execId}</span>
|
||||
<br />
|
||||
Pin:{" "}
|
||||
<span className="font-semibold">{beautifyString(pinName)}</span>
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex-1 overflow-hidden">
|
||||
{useEnhancedRenderer && outputItems.length > 0 && (
|
||||
<div className="border-b px-4 py-2">
|
||||
<OutputActions
|
||||
items={outputItems.map((item) => ({
|
||||
value: item.value,
|
||||
metadata: item.metadata,
|
||||
renderer: item.renderer,
|
||||
}))}
|
||||
<Dialog
|
||||
title={
|
||||
<div className="flex items-center justify-between pr-8">
|
||||
<div className="flex items-center gap-2">
|
||||
<Maximize2 size={20} />
|
||||
Full Output Preview
|
||||
</div>
|
||||
{enableEnhancedOutputHandling && (
|
||||
<div className="flex items-center gap-3">
|
||||
<label
|
||||
htmlFor="enhanced-rendering-toggle"
|
||||
className="cursor-pointer select-none text-sm font-normal text-gray-600"
|
||||
>
|
||||
Enhanced Rendering
|
||||
</label>
|
||||
<Switch
|
||||
id="enhanced-rendering-toggle"
|
||||
checked={useEnhancedRenderer}
|
||||
onCheckedChange={setUseEnhancedRenderer}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<ScrollArea className="h-full">
|
||||
<div className="p-4">
|
||||
{data.length > 0 ? (
|
||||
useEnhancedRenderer ? (
|
||||
<div className="space-y-4">
|
||||
{outputItems.map((item) => (
|
||||
<OutputItem
|
||||
key={item.key}
|
||||
value={item.value}
|
||||
metadata={item.metadata}
|
||||
renderer={item.renderer}
|
||||
label={item.label}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{data.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="rounded-lg border bg-gray-50 p-4"
|
||||
>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-gray-600">
|
||||
Item {index + 1} of {data.length}
|
||||
</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
const itemData =
|
||||
typeof item === "object"
|
||||
? JSON.stringify(item, null, 2)
|
||||
: String(item);
|
||||
navigator.clipboard
|
||||
.writeText(itemData)
|
||||
.then(() => {
|
||||
toast({
|
||||
title: `Item ${index + 1} copied to clipboard!`,
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
}}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Clipboard size={14} />
|
||||
Copy Item
|
||||
</Button>
|
||||
</div>
|
||||
<Separator className="mb-3" />
|
||||
<div className="whitespace-pre-wrap break-words font-mono text-sm">
|
||||
<ContentRenderer
|
||||
value={item}
|
||||
truncateLongData={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="py-8 text-center text-gray-500">
|
||||
No data available
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
}
|
||||
controlled={{
|
||||
isOpen,
|
||||
set: (open) => {
|
||||
if (!open) onClose();
|
||||
},
|
||||
}}
|
||||
onClose={onClose}
|
||||
styling={{
|
||||
maxWidth: "56rem",
|
||||
width: "90vw",
|
||||
height: "90vh",
|
||||
}}
|
||||
>
|
||||
<Dialog.Content>
|
||||
<div className="flex h-full flex-col">
|
||||
<div className="pb-4">
|
||||
<p className="text-sm text-zinc-600">
|
||||
Execution ID: <span className="font-mono text-xs">{execId}</span>
|
||||
<br />
|
||||
Pin:{" "}
|
||||
<span className="font-semibold">{beautifyString(pinName)}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="flex justify-between">
|
||||
<div className="text-sm text-gray-600">
|
||||
{data.length} item{data.length !== 1 ? "s" : ""} total
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{!useEnhancedRenderer && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={copyData}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Clipboard size={16} />
|
||||
Copy All
|
||||
</Button>
|
||||
<div className="flex flex-1 flex-col overflow-hidden">
|
||||
{useEnhancedRenderer && outputItems.length > 0 && (
|
||||
<div className="border-b px-4 py-2">
|
||||
<OutputActions
|
||||
items={outputItems.map((item) => ({
|
||||
value: item.value,
|
||||
metadata: item.metadata,
|
||||
renderer: item.renderer,
|
||||
}))}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Button onClick={onClose}>Close</Button>
|
||||
<ScrollArea className="h-full">
|
||||
<div className="p-4">
|
||||
{data.length > 0 ? (
|
||||
useEnhancedRenderer ? (
|
||||
<div className="space-y-4">
|
||||
{outputItems.map((item) => (
|
||||
<OutputItem
|
||||
key={item.key}
|
||||
value={item.value}
|
||||
metadata={item.metadata}
|
||||
renderer={item.renderer}
|
||||
label={item.label}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{data.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="rounded-lg border bg-gray-50 p-4"
|
||||
>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-gray-600">
|
||||
Item {index + 1} of {data.length}
|
||||
</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
const itemData =
|
||||
typeof item === "object"
|
||||
? JSON.stringify(item, null, 2)
|
||||
: String(item);
|
||||
navigator.clipboard
|
||||
.writeText(itemData)
|
||||
.then(() => {
|
||||
toast({
|
||||
title: `Item ${index + 1} copied to clipboard!`,
|
||||
duration: 2000,
|
||||
});
|
||||
});
|
||||
}}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Clipboard size={14} />
|
||||
Copy Item
|
||||
</Button>
|
||||
</div>
|
||||
<Separator className="mb-3" />
|
||||
<div className="whitespace-pre-wrap break-words font-mono text-sm">
|
||||
<ContentRenderer
|
||||
value={item}
|
||||
truncateLongData={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<div className="py-8 text-center text-gray-500">
|
||||
No data available
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
|
||||
<Dialog.Footer className="flex justify-between">
|
||||
<div className="text-sm text-gray-600">
|
||||
{data.length} item{data.length !== 1 ? "s" : ""} total
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
{!useEnhancedRenderer && (
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={copyData}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
<Clipboard size={16} />
|
||||
Copy All
|
||||
</Button>
|
||||
)}
|
||||
<Button onClick={onClose}>Close</Button>
|
||||
</div>
|
||||
</Dialog.Footer>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import React, { useCallback } from "react";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { AgentRunDraftView } from "@/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view";
|
||||
import { Dialog } from "@/components/molecules/Dialog/Dialog";
|
||||
import type {
|
||||
CredentialsMetaInput,
|
||||
GraphMeta,
|
||||
} from "@/lib/autogpt-server-api/types";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogDescription,
|
||||
} from "@/components/__legacy__/ui/dialog";
|
||||
import { AgentRunDraftView } from "@/app/(platform)/library/agents/[id]/components/OldAgentLibraryView/components/agent-run-draft-view";
|
||||
|
||||
interface RunInputDialogProps {
|
||||
isOpen: boolean;
|
||||
@@ -70,21 +64,33 @@ export function RunnerInputDialog({
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={doClose}>
|
||||
<DialogContent className="flex w-[90vw] max-w-4xl flex-col p-10">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-2xl">Run your agent</DialogTitle>
|
||||
<DialogDescription className="mt-2">{graph.name}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<AgentRunDraftView
|
||||
className="p-0"
|
||||
graph={graph}
|
||||
doRun={doRun ? handleRun : undefined}
|
||||
onRun={doRun ? undefined : doClose}
|
||||
doCreateSchedule={doCreateSchedule ? handleSchedule : undefined}
|
||||
onCreateSchedule={doCreateSchedule ? undefined : doClose}
|
||||
/>
|
||||
</DialogContent>
|
||||
<Dialog
|
||||
title="Run your agent"
|
||||
controlled={{
|
||||
isOpen,
|
||||
set: (open) => {
|
||||
if (!open) doClose();
|
||||
},
|
||||
}}
|
||||
onClose={doClose}
|
||||
styling={{
|
||||
maxWidth: "56rem",
|
||||
width: "90vw",
|
||||
}}
|
||||
>
|
||||
<Dialog.Content>
|
||||
<div className="flex flex-col p-10">
|
||||
<p className="mt-2 text-sm text-zinc-600">{graph.name}</p>
|
||||
<AgentRunDraftView
|
||||
className="p-0"
|
||||
graph={graph}
|
||||
doRun={doRun ? handleRun : undefined}
|
||||
onRun={doRun ? undefined : doClose}
|
||||
doCreateSchedule={doCreateSchedule ? handleSchedule : undefined}
|
||||
onCreateSchedule={doCreateSchedule ? undefined : doClose}
|
||||
/>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Metadata } from "next";
|
||||
import { Suspense } from "react";
|
||||
import {
|
||||
prefetchGetV2ListStoreAgentsQuery,
|
||||
prefetchGetV2ListStoreCreatorsQuery,
|
||||
} from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { getQueryClient } from "@/lib/react-query/queryClient";
|
||||
import { dehydrate, HydrationBoundary } from "@tanstack/react-query";
|
||||
import { Metadata } from "next";
|
||||
import { Suspense } from "react";
|
||||
import { MainMarkeplacePage } from "./components/MainMarketplacePage/MainMarketplacePage";
|
||||
import { MainMarketplacePageLoading } from "./components/MainMarketplacePageLoading";
|
||||
|
||||
@@ -48,11 +48,6 @@ export const metadata: Metadata = {
|
||||
description: "Find and use AI Agents created by our community",
|
||||
images: ["/images/store-twitter.png"],
|
||||
},
|
||||
icons: {
|
||||
icon: "/favicon.ico",
|
||||
shortcut: "/favicon-16x16.png",
|
||||
apple: "/apple-touch-icon.png",
|
||||
},
|
||||
};
|
||||
|
||||
export default async function MarketplacePage(): Promise<React.ReactElement> {
|
||||
|
||||
@@ -15,9 +15,21 @@ import { environment } from "@/services/environment";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
const isDev = environment.isDev();
|
||||
const isLocal = environment.isLocal();
|
||||
|
||||
const faviconPath = isDev
|
||||
? "/favicon-dev.ico"
|
||||
: isLocal
|
||||
? "/favicon-local.ico"
|
||||
: "/favicon.ico";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "AutoGPT Platform",
|
||||
description: "Your one stop shop to creating AI Agents",
|
||||
icons: {
|
||||
icon: faviconPath,
|
||||
},
|
||||
};
|
||||
|
||||
export default async function RootLayout({
|
||||
@@ -27,8 +39,6 @@ export default async function RootLayout({
|
||||
}>) {
|
||||
const headersList = await headers();
|
||||
const host = headersList.get("host") || "";
|
||||
const isDev = environment.isDev();
|
||||
const isLocal = environment.isLocal();
|
||||
|
||||
return (
|
||||
<html
|
||||
@@ -37,16 +47,6 @@ export default async function RootLayout({
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<head>
|
||||
<link
|
||||
rel="icon"
|
||||
href={
|
||||
isLocal
|
||||
? "/favicon-local.ico"
|
||||
: isDev
|
||||
? "/favicon-dev.ico"
|
||||
: "/favicon.ico"
|
||||
}
|
||||
/>
|
||||
<SetupAnalytics
|
||||
host={host}
|
||||
ga={{
|
||||
|
||||
@@ -1,13 +1,157 @@
|
||||
"use client";
|
||||
|
||||
import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { IconAutoGPTLogo, IconType } from "@/components/__legacy__/ui/icons";
|
||||
import { PreviewBanner } from "@/components/layout/Navbar/components/PreviewBanner/PreviewBanner";
|
||||
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 { AccountMenu } from "./components/AccountMenu/AccountMenu";
|
||||
import { AgentActivityDropdown } from "./components/AgentActivityDropdown/AgentActivityDropdown";
|
||||
import { LoginButton } from "./components/LoginButton";
|
||||
import { MobileNavBar } from "./components/MobileNavbar/MobileNavBar";
|
||||
import { NavbarLink } from "./components/NavbarLink";
|
||||
import { NavbarLoading } from "./components/NavbarLoading";
|
||||
import { Wallet } from "./components/Wallet/Wallet";
|
||||
import { getAccountMenuItems, loggedInLinks, loggedOutLinks } from "./helpers";
|
||||
|
||||
import { NavbarView } from "./components/NavbarView";
|
||||
import { getNavbarAccountData } from "./data";
|
||||
|
||||
export async function Navbar() {
|
||||
const { isLoggedIn } = await getNavbarAccountData();
|
||||
export function Navbar() {
|
||||
const { user, isLoggedIn, isUserLoading } = useSupabase();
|
||||
const breakpoint = useBreakpoint();
|
||||
const isSmallScreen = breakpoint === "sm" || breakpoint === "base";
|
||||
const dynamicMenuItems = getAccountMenuItems(user?.role);
|
||||
const isChatEnabled = useGetFlag(Flag.CHAT);
|
||||
const previewBranchName = environment.getPreviewStealingDev();
|
||||
|
||||
const { data: profile, isLoading: isProfileLoading } = useGetV2GetUserProfile(
|
||||
{
|
||||
query: {
|
||||
select: okData,
|
||||
enabled: isLoggedIn && !!user,
|
||||
// Include user ID in query key to ensure cache invalidation when user changes
|
||||
queryKey: ["/api/store/profile", user?.id],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const isLoadingProfile = isProfileLoading || isUserLoading;
|
||||
|
||||
const shouldShowPreviewBanner = Boolean(isLoggedIn && previewBranchName);
|
||||
|
||||
const actualLoggedInLinks =
|
||||
isChatEnabled === true
|
||||
? loggedInLinks.concat([{ name: "Chat", href: "/chat" }])
|
||||
: loggedInLinks;
|
||||
|
||||
if (isUserLoading) {
|
||||
return <NavbarLoading />;
|
||||
}
|
||||
|
||||
return (
|
||||
<NavbarView isLoggedIn={isLoggedIn} previewBranchName={previewBranchName} />
|
||||
<>
|
||||
<div className="sticky top-0 z-40 w-full">
|
||||
{shouldShowPreviewBanner && previewBranchName ? (
|
||||
<PreviewBanner branchName={previewBranchName} />
|
||||
) : null}
|
||||
<nav className="border-zinc-[#EFEFF0] inline-flex h-[60px] w-full items-center border border-[#EFEFF0] bg-[#F3F4F6]/20 p-3 backdrop-blur-[26px]">
|
||||
{/* Left section */}
|
||||
{!isSmallScreen ? (
|
||||
<div className="flex flex-1 items-center gap-5">
|
||||
{isLoggedIn
|
||||
? actualLoggedInLinks.map((link) => (
|
||||
<NavbarLink
|
||||
key={link.name}
|
||||
name={link.name}
|
||||
href={link.href}
|
||||
/>
|
||||
))
|
||||
: loggedOutLinks.map((link) => (
|
||||
<NavbarLink
|
||||
key={link.name}
|
||||
name={link.name}
|
||||
href={link.href}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Centered logo */}
|
||||
<div className="static h-auto w-[4.5rem] md:absolute md:left-1/2 md:top-1/2 md:w-[5.5rem] md:-translate-x-1/2 md:-translate-y-1/2">
|
||||
<IconAutoGPTLogo className="h-full w-full" />
|
||||
</div>
|
||||
|
||||
{/* Right section */}
|
||||
{isLoggedIn && !isSmallScreen ? (
|
||||
<div className="flex flex-1 items-center justify-end gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<AgentActivityDropdown />
|
||||
{profile && <Wallet key={profile.username} />}
|
||||
<AccountMenu
|
||||
userName={profile?.username}
|
||||
userEmail={profile?.name}
|
||||
avatarSrc={profile?.avatar_url ?? ""}
|
||||
menuItemGroups={dynamicMenuItems}
|
||||
isLoading={isLoadingProfile}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : !isLoggedIn ? (
|
||||
<div className="flex w-full items-center justify-end">
|
||||
<LoginButton />
|
||||
</div>
|
||||
) : null}
|
||||
{/* <ThemeToggle /> */}
|
||||
</nav>
|
||||
</div>
|
||||
{/* Mobile Navbar - Adjust positioning */}
|
||||
<>
|
||||
{isLoggedIn && isSmallScreen ? (
|
||||
<div className="fixed right-0 top-2 z-50 flex items-center gap-0">
|
||||
<Wallet />
|
||||
<MobileNavBar
|
||||
userName={profile?.username}
|
||||
menuItemGroups={[
|
||||
{
|
||||
groupName: "Navigation",
|
||||
items: actualLoggedInLinks
|
||||
.map((link) => {
|
||||
if (link.name === "Chat" && !isChatEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
icon:
|
||||
link.name === "Marketplace"
|
||||
? IconType.Marketplace
|
||||
: link.name === "Library"
|
||||
? IconType.Library
|
||||
: link.name === "Build"
|
||||
? IconType.Builder
|
||||
: link.name === "Chat"
|
||||
? IconType.Chat
|
||||
: link.name === "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 ?? ""}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,45 +6,42 @@ import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||
import { cn } from "@/lib/utils";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useTransition } from "react";
|
||||
|
||||
export function AccountLogoutOption() {
|
||||
const [isLoggingOut, setIsLoggingOut] = useState(false);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const supabase = useSupabase();
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
|
||||
async function handleLogout() {
|
||||
setIsLoggingOut(true);
|
||||
try {
|
||||
await supabase.logOut();
|
||||
router.push("/login");
|
||||
} catch (e) {
|
||||
Sentry.captureException(e);
|
||||
toast({
|
||||
title: "Error logging out",
|
||||
description:
|
||||
"Something went wrong when logging out. Please try again. If the problem persists, please contact support.",
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
setIsLoggingOut(false);
|
||||
}, 3000);
|
||||
}
|
||||
function handleLogout() {
|
||||
startTransition(async () => {
|
||||
try {
|
||||
await supabase.logOut();
|
||||
router.replace("/login");
|
||||
} catch (e) {
|
||||
Sentry.captureException(e);
|
||||
toast({
|
||||
title: "Error logging out",
|
||||
description:
|
||||
"Something went wrong when logging out. Please try again. If the problem persists, please contact support.",
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"inline-flex w-full items-center justify-start gap-2.5",
|
||||
isLoggingOut && "justify-center opacity-50",
|
||||
isPending && "justify-center opacity-50",
|
||||
)}
|
||||
onClick={handleLogout}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
{isLoggingOut ? (
|
||||
{isPending ? (
|
||||
<LoadingSpinner className="size-5" />
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -5,16 +5,15 @@ export function NavbarLoading() {
|
||||
return (
|
||||
<nav className="sticky top-0 z-40 hidden h-16 items-center rounded-bl-2xl rounded-br-2xl border border-white/50 bg-white/5 p-3 backdrop-blur-[26px] md:inline-flex">
|
||||
<div className="flex flex-1 items-center gap-6">
|
||||
<Skeleton className="h-4 w-20 bg-white/20" />
|
||||
<Skeleton className="h-4 w-16 bg-white/20" />
|
||||
<Skeleton className="h-4 w-12 bg-white/20" />
|
||||
<Skeleton className="h-6 w-24 bg-zinc-200/60" />
|
||||
<Skeleton className="h-6 w-24 bg-zinc-200/60" />
|
||||
<Skeleton className="h-6 w-16 bg-zinc-200/60" />
|
||||
</div>
|
||||
<div className="absolute left-1/2 top-1/2 h-10 w-[88.87px] -translate-x-1/2 -translate-y-1/2">
|
||||
<IconAutoGPTLogo className="h-full w-full" />
|
||||
</div>
|
||||
<div className="flex flex-1 items-center justify-end gap-4">
|
||||
<Skeleton className="h-8 w-8 rounded-full bg-white/20" />
|
||||
<Skeleton className="h-8 w-8 rounded-full bg-white/20" />
|
||||
<Skeleton className="h-8 w-8 rounded-full bg-zinc-200/60" />
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { IconAutoGPTLogo, IconType } from "@/components/__legacy__/ui/icons";
|
||||
import { PreviewBanner } from "@/components/layout/Navbar/components/PreviewBanner/PreviewBanner";
|
||||
import { useBreakpoint } from "@/lib/hooks/useBreakpoint";
|
||||
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||
import { Flag, useGetFlag } from "@/services/feature-flags/use-get-flag";
|
||||
import { useMemo } from "react";
|
||||
import { okData } from "@/app/api/helpers";
|
||||
import { getAccountMenuItems, loggedInLinks, loggedOutLinks } from "../helpers";
|
||||
import { AccountMenu } from "./AccountMenu/AccountMenu";
|
||||
import { AgentActivityDropdown } from "./AgentActivityDropdown/AgentActivityDropdown";
|
||||
import { LoginButton } from "./LoginButton";
|
||||
import { MobileNavBar } from "./MobileNavbar/MobileNavBar";
|
||||
import { NavbarLink } from "./NavbarLink";
|
||||
import { Wallet } from "./Wallet/Wallet";
|
||||
interface NavbarViewProps {
|
||||
isLoggedIn: boolean;
|
||||
previewBranchName?: string | null;
|
||||
}
|
||||
|
||||
export function NavbarView({ isLoggedIn, previewBranchName }: NavbarViewProps) {
|
||||
const { user } = useSupabase();
|
||||
const breakpoint = useBreakpoint();
|
||||
const isSmallScreen = breakpoint === "sm" || breakpoint === "base";
|
||||
const dynamicMenuItems = getAccountMenuItems(user?.role);
|
||||
const isChatEnabled = useGetFlag(Flag.CHAT);
|
||||
|
||||
const { data: profile, isLoading: isProfileLoading } = useGetV2GetUserProfile(
|
||||
{
|
||||
query: {
|
||||
select: okData,
|
||||
enabled: isLoggedIn && !!user,
|
||||
// Include user ID in query key to ensure cache invalidation when user changes
|
||||
queryKey: ["/api/store/profile", user?.id],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const { isUserLoading } = useSupabase();
|
||||
const isLoadingProfile = isProfileLoading || isUserLoading;
|
||||
|
||||
const linksWithChat = useMemo(() => {
|
||||
const chatLink = { name: "Chat", href: "/chat" };
|
||||
return isChatEnabled ? [...loggedInLinks, chatLink] : loggedInLinks;
|
||||
}, [isChatEnabled]);
|
||||
|
||||
const shouldShowPreviewBanner = Boolean(isLoggedIn && previewBranchName);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="sticky top-0 z-40 w-full">
|
||||
{shouldShowPreviewBanner && previewBranchName ? (
|
||||
<PreviewBanner branchName={previewBranchName} />
|
||||
) : null}
|
||||
<nav className="border-zinc-[#EFEFF0] inline-flex h-[60px] w-full items-center border border-[#EFEFF0] bg-[#F3F4F6]/20 p-3 backdrop-blur-[26px]">
|
||||
{/* Left section */}
|
||||
{!isSmallScreen ? (
|
||||
<div className="flex flex-1 items-center gap-5">
|
||||
{isLoggedIn
|
||||
? linksWithChat.map((link) => (
|
||||
<NavbarLink
|
||||
key={link.name}
|
||||
name={link.name}
|
||||
href={link.href}
|
||||
/>
|
||||
))
|
||||
: loggedOutLinks.map((link) => (
|
||||
<NavbarLink
|
||||
key={link.name}
|
||||
name={link.name}
|
||||
href={link.href}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Centered logo */}
|
||||
<div className="static h-auto w-[4.5rem] md:absolute md:left-1/2 md:top-1/2 md:w-[5.5rem] md:-translate-x-1/2 md:-translate-y-1/2">
|
||||
<IconAutoGPTLogo className="h-full w-full" />
|
||||
</div>
|
||||
|
||||
{/* Right section */}
|
||||
{isLoggedIn && !isSmallScreen ? (
|
||||
<div className="flex flex-1 items-center justify-end gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<AgentActivityDropdown />
|
||||
{profile && <Wallet key={profile.username} />}
|
||||
<AccountMenu
|
||||
userName={profile?.username}
|
||||
userEmail={profile?.name}
|
||||
avatarSrc={profile?.avatar_url ?? ""}
|
||||
menuItemGroups={dynamicMenuItems}
|
||||
isLoading={isLoadingProfile}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : !isLoggedIn ? (
|
||||
<div className="flex w-full items-center justify-end">
|
||||
<LoginButton />
|
||||
</div>
|
||||
) : null}
|
||||
{/* <ThemeToggle /> */}
|
||||
</nav>
|
||||
</div>
|
||||
{/* Mobile Navbar - Adjust positioning */}
|
||||
<>
|
||||
{isLoggedIn && isSmallScreen ? (
|
||||
<div className="fixed right-0 top-2 z-50 flex items-center gap-0">
|
||||
<Wallet />
|
||||
<MobileNavBar
|
||||
userName={profile?.username}
|
||||
menuItemGroups={[
|
||||
{
|
||||
groupName: "Navigation",
|
||||
items: linksWithChat.map((link) => ({
|
||||
icon:
|
||||
link.name === "Marketplace"
|
||||
? IconType.Marketplace
|
||||
: link.name === "Library"
|
||||
? IconType.Library
|
||||
: link.name === "Build"
|
||||
? IconType.Builder
|
||||
: link.name === "Chat"
|
||||
? IconType.Chat
|
||||
: link.name === "Monitor"
|
||||
? IconType.Library
|
||||
: IconType.LayoutDashboard,
|
||||
text: link.name,
|
||||
href: link.href,
|
||||
})),
|
||||
},
|
||||
...dynamicMenuItems,
|
||||
]}
|
||||
userEmail={profile?.name}
|
||||
avatarSrc={profile?.avatar_url ?? ""}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { prefetchGetV2GetUserProfileQuery } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { getQueryClient } from "@/lib/react-query/queryClient";
|
||||
import { getServerUser } from "@/lib/supabase/server/getServerUser";
|
||||
|
||||
export async function getNavbarAccountData() {
|
||||
const { user } = await getServerUser();
|
||||
const isLoggedIn = Boolean(user);
|
||||
const queryClient = getQueryClient();
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return {
|
||||
profile: null,
|
||||
isLoggedIn,
|
||||
};
|
||||
}
|
||||
try {
|
||||
await prefetchGetV2GetUserProfileQuery(queryClient);
|
||||
} catch (error) {
|
||||
console.error("Error fetching profile:", error);
|
||||
}
|
||||
|
||||
return {
|
||||
isLoggedIn,
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { DEFAULT_SEARCH_TERMS } from "@/app/(platform)/marketplace/components/HeroSection/helpers";
|
||||
import { useFlags } from "launchdarkly-react-client-sdk";
|
||||
import { environment } from "../environment";
|
||||
|
||||
export enum Flag {
|
||||
BETA_BLOCKS = "beta-blocks",
|
||||
@@ -40,7 +39,7 @@ const mockFlags = {
|
||||
[Flag.BETA_BLOCKS]: [],
|
||||
[Flag.NEW_BLOCK_MENU]: false,
|
||||
[Flag.NEW_AGENT_RUNS]: false,
|
||||
[Flag.GRAPH_SEARCH]: true,
|
||||
[Flag.GRAPH_SEARCH]: false,
|
||||
[Flag.ENABLE_ENHANCED_OUTPUT_HANDLING]: false,
|
||||
[Flag.NEW_FLOW_EDITOR]: false,
|
||||
[Flag.BUILDER_VIEW_SWITCH]: false,
|
||||
@@ -48,17 +47,20 @@ const mockFlags = {
|
||||
[Flag.AGENT_FAVORITING]: false,
|
||||
[Flag.MARKETPLACE_SEARCH_TERMS]: DEFAULT_SEARCH_TERMS,
|
||||
[Flag.ENABLE_PLATFORM_PAYMENT]: false,
|
||||
[Flag.CHAT]: true,
|
||||
[Flag.CHAT]: false,
|
||||
};
|
||||
|
||||
export function useGetFlag<T extends Flag>(flag: T): FlagValues[T] | null {
|
||||
const currentFlags = useFlags<FlagValues>();
|
||||
const flagValue = currentFlags[flag];
|
||||
const isCloud = environment.isCloud();
|
||||
|
||||
if ((isPwMockEnabled && !isCloud) || flagValue === undefined) {
|
||||
const envEnabled = process.env.NEXT_PUBLIC_LAUNCHDARKLY_ENABLED === "true";
|
||||
const clientId = process.env.NEXT_PUBLIC_LAUNCHDARKLY_CLIENT_ID;
|
||||
const isLaunchDarklyConfigured = envEnabled && clientId;
|
||||
|
||||
if (!isLaunchDarklyConfigured || isPwMockEnabled) {
|
||||
return mockFlags[flag];
|
||||
}
|
||||
|
||||
return flagValue;
|
||||
return flagValue ?? mockFlags[flag];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user