Merge pull request #975 from kortix-ai/cursor/optimize-agents-page-switcher-states-1b17

Optimize agents page switcher states
This commit is contained in:
Marko Kraemer 2025-07-17 13:19:53 +02:00 committed by GitHub
commit 7bc0ab6548
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 126 additions and 120 deletions

View File

@ -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}

View File

@ -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>
); );
}; };

View File

@ -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>
); );
}; };

View File

@ -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))',