mirror of https://github.com/kortix-ai/suna.git
Merge pull request #975 from kortix-ai/cursor/optimize-agents-page-switcher-states-1b17
Optimize agents page switcher states
This commit is contained in:
commit
7bc0ab6548
|
@ -505,7 +505,8 @@ export default function AgentsPage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="container mx-auto max-w-7xl px-4 py-2">
|
<div className="container mx-auto max-w-7xl px-4 py-2">
|
||||||
<div className="w-full">
|
{/* Fixed height container to prevent layout shifts on tab change */}
|
||||||
|
<div className="w-full min-h-[calc(100vh-300px)]">
|
||||||
{activeTab === "my-agents" && (
|
{activeTab === "my-agents" && (
|
||||||
<MyAgentsTab
|
<MyAgentsTab
|
||||||
agentsSearchQuery={agentsSearchQuery}
|
agentsSearchQuery={agentsSearchQuery}
|
||||||
|
|
|
@ -36,7 +36,7 @@ export const MarketplaceTab = ({
|
||||||
getItemStyling
|
getItemStyling
|
||||||
}: MarketplaceTabProps) => {
|
}: MarketplaceTabProps) => {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 mt-8">
|
<div className="space-y-6 mt-8 flex flex-col min-h-full">
|
||||||
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between">
|
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
placeholder="Search agents..."
|
placeholder="Search agents..."
|
||||||
|
@ -55,90 +55,92 @@ export const MarketplaceTab = ({
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{marketplaceLoading ? (
|
<div className="flex-1">
|
||||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
{marketplaceLoading ? (
|
||||||
{Array.from({ length: 8 }).map((_, i) => (
|
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
<div key={i} className="bg-card rounded-2xl overflow-hidden shadow-sm">
|
{Array.from({ length: 8 }).map((_, i) => (
|
||||||
<Skeleton className="h-48" />
|
<div key={i} className="bg-card rounded-2xl overflow-hidden shadow-sm">
|
||||||
<div className="p-6 space-y-3">
|
<Skeleton className="h-48" />
|
||||||
<Skeleton className="h-5 rounded" />
|
<div className="p-6 space-y-3">
|
||||||
<div className="space-y-2">
|
<Skeleton className="h-5 rounded" />
|
||||||
<Skeleton className="h-4 rounded" />
|
<div className="space-y-2">
|
||||||
<Skeleton className="h-4 rounded w-3/4" />
|
<Skeleton className="h-4 rounded" />
|
||||||
|
<Skeleton className="h-4 rounded w-3/4" />
|
||||||
|
</div>
|
||||||
|
<Skeleton className="h-10 rounded-full" />
|
||||||
</div>
|
</div>
|
||||||
<Skeleton className="h-10 rounded-full" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
) : allMarketplaceItems.length === 0 ? (
|
||||||
) : allMarketplaceItems.length === 0 ? (
|
<div className="text-center py-12">
|
||||||
<div className="text-center py-12">
|
<p className="text-muted-foreground">
|
||||||
<p className="text-muted-foreground">
|
{marketplaceSearchQuery
|
||||||
{marketplaceSearchQuery
|
? "No templates found matching your criteria. Try adjusting your search or filters."
|
||||||
? "No templates found matching your criteria. Try adjusting your search or filters."
|
: "No agent templates are currently available in the marketplace."}
|
||||||
: "No agent templates are currently available in the marketplace."}
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
) : (
|
<div className="space-y-12">
|
||||||
<div className="space-y-12">
|
{marketplaceFilter === 'all' ? (
|
||||||
{marketplaceFilter === 'all' ? (
|
<>
|
||||||
<>
|
{kortixTeamItems.length > 0 && (
|
||||||
{kortixTeamItems.length > 0 && (
|
<div className="space-y-6">
|
||||||
<div className="space-y-6">
|
<MarketplaceSectionHeader
|
||||||
<MarketplaceSectionHeader
|
title="Verified by Kortix"
|
||||||
title="Verified by Kortix"
|
subtitle="Official agents, maintained and supported"
|
||||||
subtitle="Official agents, maintained and supported"
|
/>
|
||||||
|
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
|
{kortixTeamItems.map((item) => (
|
||||||
|
<AgentCard
|
||||||
|
key={item.id}
|
||||||
|
mode="marketplace"
|
||||||
|
data={item}
|
||||||
|
styling={getItemStyling(item)}
|
||||||
|
isActioning={installingItemId === item.id}
|
||||||
|
onPrimaryAction={onInstallClick}
|
||||||
|
onClick={() => onInstallClick(item)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{communityItems.length > 0 && (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
|
{communityItems.map((item) => (
|
||||||
|
<AgentCard
|
||||||
|
key={item.id}
|
||||||
|
mode="marketplace"
|
||||||
|
data={item}
|
||||||
|
styling={getItemStyling(item)}
|
||||||
|
isActioning={installingItemId === item.id}
|
||||||
|
onPrimaryAction={onInstallClick}
|
||||||
|
onClick={() => onInstallClick(item)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||||
|
{allMarketplaceItems.map((item) => (
|
||||||
|
<AgentCard
|
||||||
|
key={item.id}
|
||||||
|
mode="marketplace"
|
||||||
|
data={item}
|
||||||
|
styling={getItemStyling(item)}
|
||||||
|
isActioning={installingItemId === item.id}
|
||||||
|
onPrimaryAction={onInstallClick}
|
||||||
|
onClick={() => onInstallClick(item)}
|
||||||
/>
|
/>
|
||||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
))}
|
||||||
{kortixTeamItems.map((item) => (
|
</div>
|
||||||
<AgentCard
|
)}
|
||||||
key={item.id}
|
</div>
|
||||||
mode="marketplace"
|
)}
|
||||||
data={item}
|
</div>
|
||||||
styling={getItemStyling(item)}
|
|
||||||
isActioning={installingItemId === item.id}
|
|
||||||
onPrimaryAction={onInstallClick}
|
|
||||||
onClick={() => onInstallClick(item)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{communityItems.length > 0 && (
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
|
||||||
{communityItems.map((item) => (
|
|
||||||
<AgentCard
|
|
||||||
key={item.id}
|
|
||||||
mode="marketplace"
|
|
||||||
data={item}
|
|
||||||
styling={getItemStyling(item)}
|
|
||||||
isActioning={installingItemId === item.id}
|
|
||||||
onPrimaryAction={onInstallClick}
|
|
||||||
onClick={() => onInstallClick(item)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
|
||||||
{allMarketplaceItems.map((item) => (
|
|
||||||
<AgentCard
|
|
||||||
key={item.id}
|
|
||||||
mode="marketplace"
|
|
||||||
data={item}
|
|
||||||
styling={getItemStyling(item)}
|
|
||||||
isActioning={installingItemId === item.id}
|
|
||||||
onPrimaryAction={onInstallClick}
|
|
||||||
onClick={() => onInstallClick(item)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
|
@ -155,7 +155,7 @@ export const MyAgentsTab = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 mt-8">
|
<div className="space-y-6 mt-8 flex flex-col min-h-full">
|
||||||
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between mb-6">
|
<div className="flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between mb-6">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
placeholder="Search your agents..."
|
placeholder="Search your agents..."
|
||||||
|
@ -203,40 +203,42 @@ export const MyAgentsTab = ({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{agentFilter === 'templates' ? (
|
<div className="flex-1">
|
||||||
renderTemplates()
|
{agentFilter === 'templates' ? (
|
||||||
) : (
|
renderTemplates()
|
||||||
<>
|
) : (
|
||||||
{agentsLoading ? (
|
<>
|
||||||
<LoadingState viewMode={viewMode} />
|
{agentsLoading ? (
|
||||||
) : filteredAgents.length === 0 ? (
|
<LoadingState viewMode={viewMode} />
|
||||||
<EmptyState
|
) : filteredAgents.length === 0 ? (
|
||||||
hasAgents={(agentsPagination?.total || 0) > 0}
|
<EmptyState
|
||||||
onCreateAgent={onCreateAgent}
|
hasAgents={(agentsPagination?.total || 0) > 0}
|
||||||
onClearFilters={handleClearFilters}
|
onCreateAgent={onCreateAgent}
|
||||||
/>
|
onClearFilters={handleClearFilters}
|
||||||
) : (
|
/>
|
||||||
<AgentsGrid
|
) : (
|
||||||
agents={filteredAgents}
|
<AgentsGrid
|
||||||
onEditAgent={onEditAgent}
|
agents={filteredAgents}
|
||||||
onDeleteAgent={onDeleteAgent}
|
onEditAgent={onEditAgent}
|
||||||
onToggleDefault={onToggleDefault}
|
onDeleteAgent={onDeleteAgent}
|
||||||
deleteAgentMutation={deleteAgentMutation}
|
onToggleDefault={onToggleDefault}
|
||||||
onPublish={onPublishAgent}
|
deleteAgentMutation={deleteAgentMutation}
|
||||||
publishingId={publishingAgentId}
|
onPublish={onPublishAgent}
|
||||||
/>
|
publishingId={publishingAgentId}
|
||||||
)}
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{agentsPagination && agentsPagination.pages > 1 && (
|
{agentsPagination && agentsPagination.pages > 1 && (
|
||||||
<Pagination
|
<Pagination
|
||||||
currentPage={agentsPagination.page}
|
currentPage={agentsPagination.page}
|
||||||
totalPages={agentsPagination.pages}
|
totalPages={agentsPagination.pages}
|
||||||
onPageChange={setAgentsPage}
|
onPageChange={setAgentsPage}
|
||||||
isLoading={agentsLoading}
|
isLoading={agentsLoading}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
|
@ -34,11 +34,12 @@ const TabButton = ({ value, isActive, onClick, children }: TabButtonProps) => {
|
||||||
<button
|
<button
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex items-center justify-center gap-2 rounded-2xl px-4 py-2.5 text-sm font-medium transition-all duration-300",
|
"relative flex items-center justify-center gap-2 rounded-2xl px-4 py-2.5 text-sm font-medium transition-all duration-300 ease-out",
|
||||||
isDark ? "hover:bg-white/5" : "hover:bg-muted/80",
|
// Only apply hover background when not active - subtle and elegant
|
||||||
|
!isActive && (isDark ? "hover:bg-white/8" : "hover:bg-muted/60"),
|
||||||
isActive
|
isActive
|
||||||
? isDark ? "text-white" : "text-foreground bg-background border border-border/50"
|
? isDark ? "text-white" : "text-foreground bg-background border border-border/50"
|
||||||
: isDark ? "text-white/50 hover:text-white/70" : "text-muted-foreground hover:text-foreground"
|
: isDark ? "text-white/60 hover:text-white/85" : "text-muted-foreground hover:text-foreground"
|
||||||
)}
|
)}
|
||||||
style={isActive && isDark ? {
|
style={isActive && isDark ? {
|
||||||
background: 'linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.08))',
|
background: 'linear-gradient(135deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0.08))',
|
||||||
|
|
Loading…
Reference in New Issue