{isLoggedIn
@@ -50,11 +41,12 @@ export async function Navbar() {
{isLoggedIn ? (
@@ -91,7 +83,7 @@ export async function Navbar() {
...accountMenuItems,
]}
userEmail={profile?.name}
- avatarSrc={profile?.avatar_url}
+ avatarSrc={profile?.avatar_url ?? ""}
/>
) : null}
diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/AgentNotifications.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/AgentNotifications.tsx
new file mode 100644
index 0000000000..6ed584bcfe
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/AgentNotifications.tsx
@@ -0,0 +1,52 @@
+"use client";
+
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import { Bell } from "@phosphor-icons/react";
+import { NotificationDropdown } from "./components/NotificationDropdown";
+import { formatNotificationCount } from "./helpers";
+import { useAgentNotifications } from "./useAgentNotifications";
+
+export function AgentNotifications() {
+ const { activeExecutions, recentCompletions, recentFailures, isConnected } =
+ useAgentNotifications();
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/components/NotificationDropdown.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/components/NotificationDropdown.tsx
new file mode 100644
index 0000000000..078609d9bb
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/components/NotificationDropdown.tsx
@@ -0,0 +1,81 @@
+"use client";
+
+import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
+import { Text } from "@/components/atoms/Text/Text";
+import { Bell } from "@phosphor-icons/react";
+import { AgentExecutionWithInfo } from "../helpers";
+import { NotificationItem } from "./NotificationItem";
+
+interface NotificationDropdownProps {
+ activeExecutions: AgentExecutionWithInfo[];
+ recentCompletions: AgentExecutionWithInfo[];
+ recentFailures: AgentExecutionWithInfo[];
+}
+
+export function NotificationDropdown({
+ activeExecutions,
+ recentCompletions,
+ recentFailures,
+}: NotificationDropdownProps) {
+ // Combine and sort all executions - running/queued at top, then by most recent
+ function getSortedExecutions() {
+ const allExecutions = [
+ ...activeExecutions.map((e) => ({ ...e, type: "running" as const })),
+ ...recentCompletions.map((e) => ({ ...e, type: "completed" as const })),
+ ...recentFailures.map((e) => ({ ...e, type: "failed" as const })),
+ ];
+
+ return allExecutions.sort((a, b) => {
+ // Running/queued always at top
+ const aIsActive =
+ a.status === AgentExecutionStatus.RUNNING ||
+ a.status === AgentExecutionStatus.QUEUED;
+ const bIsActive =
+ b.status === AgentExecutionStatus.RUNNING ||
+ b.status === AgentExecutionStatus.QUEUED;
+
+ if (aIsActive && !bIsActive) return -1;
+ if (!aIsActive && bIsActive) return 1;
+
+ // Within same category, sort by most recent
+ const aTime = aIsActive ? a.started_at : a.ended_at;
+ const bTime = bIsActive ? b.started_at : b.ended_at;
+
+ if (!aTime || !bTime) return 0;
+ return new Date(bTime).getTime() - new Date(aTime).getTime();
+ });
+ }
+
+ const sortedExecutions = getSortedExecutions();
+
+ return (
+
+ {/* Header */}
+
+
+ Agent Activity
+
+
+
+ {/* Content */}
+
+ {sortedExecutions.length > 0 ? (
+
+ {sortedExecutions.map((execution) => (
+
+ ))}
+
+ ) : (
+
+ )}
+
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/components/NotificationItem.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/components/NotificationItem.tsx
new file mode 100644
index 0000000000..fc1c591c42
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/components/NotificationItem.tsx
@@ -0,0 +1,88 @@
+"use client";
+
+import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
+import { Text } from "@/components/atoms/Text/Text";
+import {
+ CheckCircle,
+ CircleNotchIcon,
+ Clock,
+ WarningOctagonIcon,
+} from "@phosphor-icons/react";
+import type { AgentExecutionWithInfo } from "../helpers";
+import {
+ formatTimeAgo,
+ getExecutionDuration,
+ getStatusColorClass,
+} from "../helpers";
+
+interface NotificationItemProps {
+ execution: AgentExecutionWithInfo;
+ type: "running" | "completed" | "failed";
+}
+
+export function NotificationItem({ execution, type }: NotificationItemProps) {
+ function getStatusIcon() {
+ switch (type) {
+ case "running":
+ return execution.status === AgentExecutionStatus.QUEUED ? (
+
+ ) : (
+
+ );
+ case "completed":
+ return (
+
+ );
+ case "failed":
+ return
;
+ default:
+ return null;
+ }
+ }
+
+ function getTimeDisplay() {
+ if (type === "running") {
+ const timeAgo = formatTimeAgo(execution.started_at.toString());
+ return `Started ${timeAgo}, ${getExecutionDuration(execution)} running`;
+ }
+
+ if (execution.ended_at) {
+ const timeAgo = formatTimeAgo(execution.ended_at.toString());
+ return type === "completed"
+ ? `Completed ${timeAgo}`
+ : `Failed ${timeAgo}`;
+ }
+
+ return "Unknown";
+ }
+
+ return (
+
+ {/* Icon + Agent Name */}
+
+ {getStatusIcon()}
+
+ {execution.agent_name}
+
+
+
+ {/* Agent Message - Indented */}
+
+ {execution.agent_description ? (
+
+ {execution.agent_description}
+
+ ) : null}
+
+ {/* Time - Indented */}
+
+ {getTimeDisplay()}
+
+
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/components/NotificationSection.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/components/NotificationSection.tsx
new file mode 100644
index 0000000000..f44588310e
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/components/NotificationSection.tsx
@@ -0,0 +1,26 @@
+"use client";
+
+import { ReactNode } from "react";
+
+interface NotificationSectionProps {
+ title: string;
+ count: number;
+ colorClass: string;
+ children: ReactNode;
+}
+
+export function NotificationSection({
+ title,
+ count,
+ colorClass,
+ children,
+}: NotificationSectionProps) {
+ return (
+
+
+ {title} ({count})
+
+
{children}
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/helpers.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/helpers.tsx
new file mode 100644
index 0000000000..20f828e0b2
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/helpers.tsx
@@ -0,0 +1,326 @@
+import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
+import { GraphExecutionMeta as GeneratedGraphExecutionMeta } from "@/app/api/__generated__/models/graphExecutionMeta";
+import { MyAgent } from "@/app/api/__generated__/models/myAgent";
+import type { GraphExecution } from "@/lib/autogpt-server-api/types";
+
+export function formatTimeAgo(dateStr: string): string {
+ const date = new Date(dateStr);
+ const now = new Date();
+ const diffMs = now.getTime() - date.getTime();
+ const diffMins = Math.floor(diffMs / 60000);
+
+ if (diffMins < 1) return "Just now";
+ if (diffMins < 60) return `${diffMins}m ago`;
+ const diffHours = Math.floor(diffMins / 60);
+ if (diffHours < 24) return `${diffHours}h ago`;
+ const diffDays = Math.floor(diffHours / 24);
+ return `${diffDays}d ago`;
+}
+
+export function getStatusDisplayText(
+ execution: GeneratedGraphExecutionMeta,
+): string {
+ switch (execution.status) {
+ case AgentExecutionStatus.QUEUED:
+ return "Queued";
+ case AgentExecutionStatus.RUNNING:
+ return "Running";
+ case AgentExecutionStatus.COMPLETED:
+ return "Completed";
+ case AgentExecutionStatus.FAILED:
+ return "Failed";
+ case AgentExecutionStatus.TERMINATED:
+ return "Stopped";
+ case AgentExecutionStatus.INCOMPLETE:
+ return "Incomplete";
+ default:
+ return execution.status;
+ }
+}
+
+export function getStatusColorClass(
+ execution: GeneratedGraphExecutionMeta,
+): string {
+ switch (execution.status) {
+ case AgentExecutionStatus.QUEUED:
+ return "text-yellow-600";
+ case AgentExecutionStatus.RUNNING:
+ return "text-blue-600";
+ case AgentExecutionStatus.COMPLETED:
+ return "text-green-600";
+ case AgentExecutionStatus.FAILED:
+ case AgentExecutionStatus.TERMINATED:
+ return "text-red-600";
+ case AgentExecutionStatus.INCOMPLETE:
+ return "text-gray-600";
+ default:
+ return "text-gray-600";
+ }
+}
+
+export function truncateGraphId(graphId: string, length: number = 8): string {
+ return `${graphId.slice(0, length)}...`;
+}
+
+export function getExecutionDuration(
+ execution: GeneratedGraphExecutionMeta,
+): string {
+ if (!execution.started_at) return "Unknown";
+
+ const start = new Date(execution.started_at);
+ const end = execution.ended_at ? new Date(execution.ended_at) : new Date();
+ const durationMs = end.getTime() - start.getTime();
+ const durationSec = Math.floor(durationMs / 1000);
+
+ if (durationSec < 60) return `${durationSec}s`;
+ const durationMin = Math.floor(durationSec / 60);
+ if (durationMin < 60) return `${durationMin}m ${durationSec % 60}s`;
+ const durationHr = Math.floor(durationMin / 60);
+ return `${durationHr}h ${durationMin % 60}m`;
+}
+
+export function shouldShowNotificationBadge(totalCount: number): boolean {
+ return totalCount > 0;
+}
+
+export function formatNotificationCount(count: number): string {
+ if (count > 99) return "99+";
+ return count.toString();
+}
+
+export interface AgentExecutionWithInfo extends GeneratedGraphExecutionMeta {
+ agent_name: string;
+ agent_description: string;
+}
+
+export interface NotificationState {
+ activeExecutions: AgentExecutionWithInfo[];
+ recentCompletions: AgentExecutionWithInfo[];
+ recentFailures: AgentExecutionWithInfo[];
+ totalCount: number;
+}
+
+export function createAgentInfoMap(
+ agents: MyAgent[],
+): Map
{
+ const agentMap = new Map();
+
+ agents.forEach((agent) => {
+ agentMap.set(agent.agent_id, {
+ name: agent.agent_name,
+ description: agent.description,
+ });
+ });
+
+ return agentMap;
+}
+
+export function convertLegacyExecutionToGenerated(
+ execution: GraphExecution,
+): GeneratedGraphExecutionMeta {
+ return {
+ id: execution.id,
+ user_id: execution.user_id,
+ graph_id: execution.graph_id,
+ graph_version: execution.graph_version,
+ preset_id: execution.preset_id,
+ status: execution.status as AgentExecutionStatus,
+ started_at: execution.started_at.toISOString(),
+ ended_at: execution.ended_at.toISOString(),
+ stats: execution.stats || {
+ cost: 0,
+ duration: 0,
+ duration_cpu_only: 0,
+ node_exec_time: 0,
+ node_exec_time_cpu_only: 0,
+ node_exec_count: 0,
+ },
+ };
+}
+
+export function enrichExecutionWithAgentInfo(
+ execution: GeneratedGraphExecutionMeta,
+ agentInfoMap: Map,
+): AgentExecutionWithInfo {
+ const agentInfo = agentInfoMap.get(execution.graph_id);
+ return {
+ ...execution,
+ agent_name: agentInfo?.name || `Graph ${execution.graph_id.slice(0, 8)}...`,
+ agent_description: agentInfo?.description ?? "",
+ };
+}
+
+export function isActiveExecution(
+ execution: GeneratedGraphExecutionMeta,
+): boolean {
+ const status = execution.status;
+ return (
+ status === AgentExecutionStatus.RUNNING ||
+ status === AgentExecutionStatus.QUEUED
+ );
+}
+
+export function isRecentCompletion(
+ execution: GeneratedGraphExecutionMeta,
+ thirtyMinutesAgo: Date,
+): boolean {
+ const status = execution.status;
+ return (
+ status === AgentExecutionStatus.COMPLETED &&
+ !!execution.ended_at &&
+ new Date(execution.ended_at) > thirtyMinutesAgo
+ );
+}
+
+export function isRecentFailure(
+ execution: GeneratedGraphExecutionMeta,
+ thirtyMinutesAgo: Date,
+): boolean {
+ const status = execution.status;
+ return (
+ (status === AgentExecutionStatus.FAILED ||
+ status === AgentExecutionStatus.TERMINATED) &&
+ !!execution.ended_at &&
+ new Date(execution.ended_at) > thirtyMinutesAgo
+ );
+}
+
+export function isRecentNotification(
+ execution: AgentExecutionWithInfo,
+ thirtyMinutesAgo: Date,
+): boolean {
+ return execution.ended_at
+ ? new Date(execution.ended_at) > thirtyMinutesAgo
+ : false;
+}
+
+export function categorizeExecutions(
+ executions: GeneratedGraphExecutionMeta[],
+ agentInfoMap: Map,
+): NotificationState {
+ const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
+
+ const enrichedExecutions = executions.map((execution) =>
+ enrichExecutionWithAgentInfo(execution, agentInfoMap),
+ );
+
+ const activeExecutions = enrichedExecutions
+ .filter(isActiveExecution)
+ .slice(0, 10);
+
+ const recentCompletions = enrichedExecutions
+ .filter((execution) => isRecentCompletion(execution, twentyFourHoursAgo))
+ .slice(0, 10);
+
+ const recentFailures = enrichedExecutions
+ .filter((execution) => isRecentFailure(execution, twentyFourHoursAgo))
+ .slice(0, 10);
+
+ return {
+ activeExecutions,
+ recentCompletions,
+ recentFailures,
+ totalCount:
+ activeExecutions.length +
+ recentCompletions.length +
+ recentFailures.length,
+ };
+}
+
+export function removeExecutionFromAllCategories(
+ state: NotificationState,
+ executionId: string,
+): NotificationState {
+ return {
+ activeExecutions: state.activeExecutions.filter(
+ (e) => e.id !== executionId,
+ ),
+ recentCompletions: state.recentCompletions.filter(
+ (e) => e.id !== executionId,
+ ),
+ recentFailures: state.recentFailures.filter((e) => e.id !== executionId),
+ totalCount: state.totalCount, // Will be recalculated later
+ };
+}
+
+export function addExecutionToCategory(
+ state: NotificationState,
+ execution: AgentExecutionWithInfo,
+): NotificationState {
+ const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
+ const newState = { ...state };
+
+ if (isActiveExecution(execution)) {
+ newState.activeExecutions = [execution, ...newState.activeExecutions].slice(
+ 0,
+ 10,
+ );
+ } else if (isRecentCompletion(execution, twentyFourHoursAgo)) {
+ newState.recentCompletions = [
+ execution,
+ ...newState.recentCompletions,
+ ].slice(0, 10);
+ } else if (isRecentFailure(execution, twentyFourHoursAgo)) {
+ newState.recentFailures = [execution, ...newState.recentFailures].slice(
+ 0,
+ 10,
+ );
+ }
+
+ return newState;
+}
+
+export function cleanupOldNotifications(
+ state: NotificationState,
+): NotificationState {
+ const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
+
+ return {
+ ...state,
+ recentCompletions: state.recentCompletions.filter((e) =>
+ isRecentNotification(e, twentyFourHoursAgo),
+ ),
+ recentFailures: state.recentFailures.filter((e) =>
+ isRecentNotification(e, twentyFourHoursAgo),
+ ),
+ };
+}
+
+export function calculateTotalCount(
+ state: NotificationState,
+): NotificationState {
+ return {
+ ...state,
+ totalCount:
+ state.activeExecutions.length +
+ state.recentCompletions.length +
+ state.recentFailures.length,
+ };
+}
+
+export function handleExecutionUpdate(
+ currentState: NotificationState,
+ execution: GraphExecution,
+ agentInfoMap: Map,
+): NotificationState {
+ // Convert and enrich the execution
+ const convertedExecution = convertLegacyExecutionToGenerated(execution);
+ const enrichedExecution = enrichExecutionWithAgentInfo(
+ convertedExecution,
+ agentInfoMap,
+ );
+
+ // Remove from all categories first
+ let newState = removeExecutionFromAllCategories(currentState, execution.id);
+
+ // Add to appropriate category
+ newState = addExecutionToCategory(newState, enrichedExecution);
+
+ // Clean up old notifications
+ newState = cleanupOldNotifications(newState);
+
+ // Recalculate total count
+ newState = calculateTotalCount(newState);
+
+ return newState;
+}
diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/useAgentNotifications.ts b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/useAgentNotifications.ts
new file mode 100644
index 0000000000..133aaed954
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/AgentNotifications/useAgentNotifications.ts
@@ -0,0 +1,156 @@
+import { useGetV1GetAllExecutions } from "@/app/api/__generated__/endpoints/graphs/graphs";
+import { useGetV2GetMyAgents } from "@/app/api/__generated__/endpoints/store/store";
+import BackendAPI from "@/lib/autogpt-server-api/client";
+import type { GraphExecution } from "@/lib/autogpt-server-api/types";
+import { useCallback, useEffect, useState } from "react";
+import {
+ NotificationState,
+ categorizeExecutions,
+ createAgentInfoMap,
+ handleExecutionUpdate,
+} from "./helpers";
+
+export function useAgentNotifications() {
+ const [api] = useState(() => new BackendAPI());
+ const [notifications, setNotifications] = useState({
+ activeExecutions: [],
+ recentCompletions: [],
+ recentFailures: [],
+ totalCount: 0,
+ });
+ const [isConnected, setIsConnected] = useState(false);
+ const [agentInfoMap, setAgentInfoMap] = useState<
+ Map
+ >(new Map());
+
+ // Get library agents using generated hook
+ const {
+ data: myAgentsResponse,
+ isLoading: isAgentsLoading,
+ error: agentsError,
+ } = useGetV2GetMyAgents({
+ query: {
+ enabled: true,
+ },
+ });
+
+ // Get all executions using generated hook
+ const {
+ data: executionsResponse,
+ isLoading: isExecutionsLoading,
+ error: executionsError,
+ } = useGetV1GetAllExecutions({
+ query: {
+ enabled: true,
+ },
+ });
+
+ // Update agent info map when library agents data changes
+ useEffect(() => {
+ if (myAgentsResponse?.data?.agents) {
+ console.log(
+ "[AgentNotifications] Processing library agents:",
+ myAgentsResponse.data.agents.length,
+ );
+ const agentMap = createAgentInfoMap(myAgentsResponse.data.agents);
+ console.log(
+ "[AgentNotifications] Agent info map created:",
+ agentMap.size,
+ "agents",
+ );
+ setAgentInfoMap(agentMap);
+ }
+ }, [myAgentsResponse]);
+
+ // Handle real-time execution updates
+ const handleExecutionEvent = useCallback(
+ (execution: GraphExecution) => {
+ console.log(
+ "[AgentNotifications] Received graph execution event:",
+ execution,
+ );
+ setNotifications((currentState) =>
+ handleExecutionUpdate(currentState, execution, agentInfoMap),
+ );
+ },
+ [agentInfoMap],
+ );
+
+ // Process initial execution state when data loads
+ useEffect(() => {
+ if (
+ executionsResponse?.data &&
+ !isExecutionsLoading &&
+ agentInfoMap.size > 0
+ ) {
+ console.log(
+ "[AgentNotifications] Processing executions:",
+ executionsResponse.data.length,
+ );
+
+ const newNotifications = categorizeExecutions(
+ executionsResponse.data,
+ agentInfoMap,
+ );
+
+ console.log("[AgentNotifications] Processed notifications:", {
+ active: newNotifications.activeExecutions.length,
+ completed: newNotifications.recentCompletions.length,
+ failed: newNotifications.recentFailures.length,
+ });
+
+ setNotifications(newNotifications);
+ }
+ }, [executionsResponse, isExecutionsLoading, agentInfoMap]);
+
+ // Initialize WebSocket connection for real-time updates
+ useEffect(() => {
+ const connectHandler = api.onWebSocketConnect(() => {
+ console.log(
+ "[AgentNotifications] WebSocket connected - setting up execution subscriptions",
+ );
+ setIsConnected(true);
+
+ // Subscribe to graph executions for all user agents
+ if (myAgentsResponse?.data?.agents) {
+ myAgentsResponse.data.agents.forEach((agent) => {
+ api
+ .subscribeToGraphExecutions(agent.agent_id as any)
+ .catch((error) => {
+ console.error(
+ `[AgentNotifications] Failed to subscribe to graph ${agent.agent_id}:`,
+ error,
+ );
+ });
+ });
+ }
+ });
+
+ const disconnectHandler = api.onWebSocketDisconnect(() => {
+ console.log("[AgentNotifications] WebSocket disconnected");
+ setIsConnected(false);
+ });
+
+ const messageHandler = api.onWebSocketMessage(
+ "graph_execution_event",
+ handleExecutionEvent,
+ );
+
+ console.log("[AgentNotifications] Starting WebSocket connection...");
+ api.connectWebSocket();
+
+ return () => {
+ connectHandler();
+ disconnectHandler();
+ messageHandler();
+ api.disconnectWebSocket();
+ };
+ }, [api, handleExecutionEvent, myAgentsResponse]);
+
+ return {
+ ...notifications,
+ isConnected,
+ isLoading: isAgentsLoading || isExecutionsLoading,
+ error: agentsError || executionsError,
+ };
+}
diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLink.tsx b/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLink.tsx
index cde27c0412..0f795689a3 100644
--- a/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLink.tsx
+++ b/autogpt_platform/frontend/src/components/layout/Navbar/components/NavbarLink.tsx
@@ -18,9 +18,7 @@ interface Props {
export function NavbarLink({ name, href }: Props) {
const pathname = usePathname();
- const parts = pathname.split("/");
- const activeLink = "/" + (parts.length > 2 ? parts[2] : parts[1]);
- const isActive = activeLink === href;
+ const isActive = pathname.includes(href);
return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/autogpt_platform/frontend/src/components/layout/Navbar/useNavbar.ts b/autogpt_platform/frontend/src/components/layout/Navbar/useNavbar.ts
new file mode 100644
index 0000000000..cc537fdb64
--- /dev/null
+++ b/autogpt_platform/frontend/src/components/layout/Navbar/useNavbar.ts
@@ -0,0 +1,26 @@
+import { useGetV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store";
+import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
+
+export function useNavbar() {
+ const { isLoggedIn, isUserLoading } = useSupabase();
+
+ const {
+ data: profileResponse,
+ isLoading: isProfileLoading,
+ error: profileError,
+ } = useGetV2GetUserProfile({
+ query: {
+ enabled: isLoggedIn === true,
+ },
+ });
+
+ const profile = profileResponse?.data || null;
+ const isLoading = isUserLoading || (isLoggedIn && isProfileLoading);
+
+ return {
+ isLoggedIn,
+ profile,
+ isLoading,
+ profileError,
+ };
+}
diff --git a/autogpt_platform/frontend/src/components/styles/colors.ts b/autogpt_platform/frontend/src/components/styles/colors.ts
index 1a6616e3fd..24977ebca8 100644
--- a/autogpt_platform/frontend/src/components/styles/colors.ts
+++ b/autogpt_platform/frontend/src/components/styles/colors.ts
@@ -71,6 +71,30 @@ export const colors = {
800: "#0c5a29",
900: "#09441f",
},
+ purple: {
+ 50: "#f1ebfe",
+ 100: "#d5c0fc",
+ 200: "#c0a1fa",
+ 300: "#a476f8",
+ 400: "#925cf7",
+ 500: "#7733f5",
+ 600: "#6c2edf",
+ 700: "#5424ae",
+ 800: "#411c87",
+ 900: "#321567",
+ },
+ pink: {
+ 50: "#fdedf5",
+ 100: "#f9c6df",
+ 200: "#f6abd0",
+ 300: "#f284bb",
+ 400: "#f06dad",
+ 500: "#ec4899",
+ 600: "#d7428b",
+ 700: "#a8336d",
+ 800: "#822854",
+ 900: "#631e40",
+ },
// Special semantic colors
white: "#fefefe",
diff --git a/autogpt_platform/frontend/src/components/tokens/colors.stories.tsx b/autogpt_platform/frontend/src/components/tokens/colors.stories.tsx
index 3b60d23b16..9eb3ba1ff0 100644
--- a/autogpt_platform/frontend/src/components/tokens/colors.stories.tsx
+++ b/autogpt_platform/frontend/src/components/tokens/colors.stories.tsx
@@ -36,6 +36,8 @@ const colorCategories = Object.entries(colors)
orange: "Warnings, notifications, and secondary call-to-actions",
yellow: "Highlights, cautions, and attention-grabbing elements",
green: "Success states, confirmations, and positive actions",
+ purple: "Brand accents, premium features, and creative elements",
+ pink: "Highlights, special promotions, and playful interactions",
};
return {
@@ -312,6 +314,8 @@ export function AllVariants() {
Success
Error
Warning
+Premium
+Special
// ❌ INCORRECT - Don't use these
❌ Not approved
diff --git a/autogpt_platform/frontend/src/components/ui/popover.tsx b/autogpt_platform/frontend/src/components/ui/popover.tsx
index 45fd7b0487..e89ea7b5e0 100644
--- a/autogpt_platform/frontend/src/components/ui/popover.tsx
+++ b/autogpt_platform/frontend/src/components/ui/popover.tsx
@@ -1,7 +1,7 @@
"use client";
-import * as React from "react";
import * as PopoverPrimitive from "@radix-ui/react-popover";
+import * as React from "react";
import { cn } from "@/lib/utils";
@@ -23,7 +23,7 @@ const PopoverContent = React.forwardRef<
align={align}
sideOffset={sideOffset}
className={cn(
- "z-50 w-72 rounded-md border border-neutral-200 bg-white p-4 text-neutral-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
+ "rounded-medium z-50 w-72 border border-neutral-200 bg-white p-4 text-neutral-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
className,
)}
{...props}
@@ -34,8 +34,8 @@ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
export {
Popover,
- PopoverTrigger,
- PopoverContent,
PopoverAnchor,
+ PopoverContent,
PopoverPortal,
+ PopoverTrigger,
};