mirror of https://github.com/kortix-ai/suna.git
url query param & share agent marketplace
This commit is contained in:
parent
2c00ab706b
commit
1d01571500
|
@ -22,7 +22,7 @@ import { MarketplaceTab } from '@/components/agents/custom-agents-page/marketpla
|
|||
import { PublishDialog } from '@/components/agents/custom-agents-page/publish-dialog';
|
||||
import { LoadingSkeleton } from '@/components/agents/custom-agents-page/loading-skeleton';
|
||||
import { NewAgentDialog } from '@/components/agents/new-agent-dialog';
|
||||
|
||||
import { MarketplaceAgentPreviewDialog } from '@/components/agents/marketplace-agent-preview-dialog';
|
||||
|
||||
type ViewMode = 'grid' | 'list';
|
||||
type AgentSortOption = 'name' | 'created_at' | 'updated_at' | 'tools_count';
|
||||
|
@ -78,6 +78,7 @@ export default function AgentsPage() {
|
|||
const [installingItemId, setInstallingItemId] = useState<string | null>(null);
|
||||
const [selectedItem, setSelectedItem] = useState<MarketplaceTemplate | null>(null);
|
||||
const [showInstallDialog, setShowInstallDialog] = useState(false);
|
||||
const [showPreviewDialog, setShowPreviewDialog] = useState(false);
|
||||
const [marketplaceFilter, setMarketplaceFilter] = useState<'all' | 'kortix' | 'community' | 'mine'>('all');
|
||||
|
||||
const [templatesActioningId, setTemplatesActioningId] = useState<string | null>(null);
|
||||
|
@ -239,6 +240,18 @@ export default function AgentsPage() {
|
|||
setMarketplacePage(1);
|
||||
}, [marketplaceSearchQuery, marketplaceSelectedTags, marketplaceSortBy]);
|
||||
|
||||
// Handle shared agent URL parameter
|
||||
useEffect(() => {
|
||||
const agentId = searchParams.get('agent');
|
||||
if (agentId && allMarketplaceItems.length > 0) {
|
||||
const sharedAgent = allMarketplaceItems.find(agent => agent.id === agentId);
|
||||
if (sharedAgent) {
|
||||
setSelectedItem(sharedAgent);
|
||||
setShowPreviewDialog(true);
|
||||
}
|
||||
}
|
||||
}, [searchParams, allMarketplaceItems]);
|
||||
|
||||
const handleDeleteAgent = async (agentId: string) => {
|
||||
try {
|
||||
await deleteAgent(agentId);
|
||||
|
@ -277,6 +290,35 @@ export default function AgentsPage() {
|
|||
setShowInstallDialog(true);
|
||||
};
|
||||
|
||||
const handlePreviewClose = () => {
|
||||
setShowPreviewDialog(false);
|
||||
setSelectedItem(null);
|
||||
|
||||
// Remove agent parameter from URL
|
||||
const currentUrl = new URL(window.location.href);
|
||||
if (currentUrl.searchParams.has('agent')) {
|
||||
currentUrl.searchParams.delete('agent');
|
||||
router.replace(currentUrl.pathname + (currentUrl.searchParams.toString() ? '?' + currentUrl.searchParams.toString() : ''), { scroll: false });
|
||||
}
|
||||
};
|
||||
|
||||
const handlePreviewInstall = (agent: MarketplaceTemplate) => {
|
||||
setShowPreviewDialog(false);
|
||||
setSelectedItem(agent);
|
||||
setShowInstallDialog(true);
|
||||
};
|
||||
|
||||
const handleAgentPreview = (agent: MarketplaceTemplate) => {
|
||||
setSelectedItem(agent);
|
||||
setShowPreviewDialog(true);
|
||||
|
||||
// Update URL with agent parameter for sharing
|
||||
const currentUrl = new URL(window.location.href);
|
||||
currentUrl.searchParams.set('agent', agent.id);
|
||||
currentUrl.searchParams.set('tab', 'marketplace');
|
||||
router.replace(currentUrl.toString(), { scroll: false });
|
||||
};
|
||||
|
||||
const handleInstall = async (
|
||||
item: MarketplaceTemplate,
|
||||
instanceName?: string,
|
||||
|
@ -501,7 +543,7 @@ export default function AgentsPage() {
|
|||
<div className="container mx-auto max-w-7xl px-4 py-8">
|
||||
<AgentsPageHeader />
|
||||
</div>
|
||||
<div className="sticky top-0 z-50 relative">
|
||||
<div className="sticky top-0 z-50">
|
||||
<div className="absolute inset-0 backdrop-blur-md" style={{
|
||||
maskImage: 'linear-gradient(to bottom, black 0%, black 60%, transparent 100%)',
|
||||
WebkitMaskImage: 'linear-gradient(to bottom, black 0%, black 60%, transparent 100%)'
|
||||
|
@ -558,6 +600,7 @@ export default function AgentsPage() {
|
|||
onDeleteTemplate={handleDeleteTemplate}
|
||||
getItemStyling={getItemStyling}
|
||||
currentUserId={user?.id}
|
||||
onAgentPreview={handleAgentPreview}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -581,6 +624,14 @@ export default function AgentsPage() {
|
|||
open={showNewAgentDialog}
|
||||
onOpenChange={setShowNewAgentDialog}
|
||||
/>
|
||||
|
||||
<MarketplaceAgentPreviewDialog
|
||||
agent={selectedItem}
|
||||
isOpen={showPreviewDialog}
|
||||
onClose={handlePreviewClose}
|
||||
onInstall={handlePreviewInstall}
|
||||
isInstalling={installingItemId === selectedItem?.id}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { SearchBar } from './search-bar';
|
||||
import { MarketplaceSectionHeader } from './marketplace-section-header';
|
||||
import { AgentCard } from './agent-card';
|
||||
import { MarketplaceAgentPreviewDialog } from '@/components/agents/marketplace-agent-preview-dialog';
|
||||
|
||||
import type { MarketplaceTemplate } from '@/components/agents/installation/types';
|
||||
|
||||
interface MarketplaceTabProps {
|
||||
|
@ -24,6 +24,7 @@ interface MarketplaceTabProps {
|
|||
onDeleteTemplate?: (item: MarketplaceTemplate, e?: React.MouseEvent) => void;
|
||||
getItemStyling: (item: MarketplaceTemplate) => { avatar: string; color: string };
|
||||
currentUserId?: string;
|
||||
onAgentPreview?: (agent: MarketplaceTemplate) => void;
|
||||
}
|
||||
|
||||
export const MarketplaceTab = ({
|
||||
|
@ -40,25 +41,13 @@ export const MarketplaceTab = ({
|
|||
onInstallClick,
|
||||
onDeleteTemplate,
|
||||
getItemStyling,
|
||||
currentUserId
|
||||
currentUserId,
|
||||
onAgentPreview
|
||||
}: MarketplaceTabProps) => {
|
||||
const [previewAgent, setPreviewAgent] = useState<MarketplaceTemplate | null>(null);
|
||||
const [previewDialogOpen, setPreviewDialogOpen] = useState(false);
|
||||
|
||||
const handleAgentClick = (item: MarketplaceTemplate) => {
|
||||
setPreviewAgent(item);
|
||||
setPreviewDialogOpen(true);
|
||||
};
|
||||
|
||||
const handlePreviewClose = () => {
|
||||
setPreviewDialogOpen(false);
|
||||
setPreviewAgent(null);
|
||||
};
|
||||
|
||||
const handleInstallFromPreview = (agent: MarketplaceTemplate) => {
|
||||
onInstallClick(agent);
|
||||
setPreviewDialogOpen(false);
|
||||
setPreviewAgent(null);
|
||||
if (onAgentPreview) {
|
||||
onAgentPreview(item);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -174,14 +163,6 @@ export const MarketplaceTab = ({
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<MarketplaceAgentPreviewDialog
|
||||
agent={previewAgent}
|
||||
isOpen={previewDialogOpen}
|
||||
onClose={handlePreviewClose}
|
||||
onInstall={handleInstallFromPreview}
|
||||
isInstalling={installingItemId === previewAgent?.id}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -6,7 +6,8 @@ import { Badge } from '@/components/ui/badge';
|
|||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { Bot, Download, Wrench, Plug, Tag, User, Calendar, Loader2 } from 'lucide-react';
|
||||
import { Bot, Download, Wrench, Plug, Tag, User, Calendar, Loader2, Share } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
import type { MarketplaceTemplate } from '@/components/agents/installation/types';
|
||||
import { AGENTPRESS_TOOL_DEFINITIONS } from '@/components/agents/tools';
|
||||
import { useComposioToolkitIcon } from '@/hooks/react-query/composio/use-composio';
|
||||
|
@ -111,6 +112,18 @@ export const MarketplaceAgentPreviewDialog: React.FC<MarketplaceAgentPreviewDial
|
|||
onInstall(agent);
|
||||
};
|
||||
|
||||
const handleShare = () => {
|
||||
const currentUrl = new URL(window.location.href);
|
||||
currentUrl.searchParams.set('agent', agent.id);
|
||||
currentUrl.searchParams.set('tab', 'marketplace');
|
||||
|
||||
navigator.clipboard.writeText(currentUrl.toString()).then(() => {
|
||||
toast.success('Share link copied to clipboard!');
|
||||
}).catch(() => {
|
||||
toast.error('Failed to copy link to clipboard');
|
||||
});
|
||||
};
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
|
@ -270,6 +283,10 @@ export const MarketplaceAgentPreviewDialog: React.FC<MarketplaceAgentPreviewDial
|
|||
</>
|
||||
)}
|
||||
</Button>
|
||||
<Button variant="outline" onClick={handleShare}>
|
||||
<Share className="h-4 w-4" />
|
||||
Share
|
||||
</Button>
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
Close
|
||||
</Button>
|
||||
|
|
Loading…
Reference in New Issue