From 1d01571500c13d684b9fbce44b11778c76d2b99f Mon Sep 17 00:00:00 2001 From: marko-kraemer Date: Wed, 6 Aug 2025 12:14:25 -0700 Subject: [PATCH] url query param & share agent marketplace --- frontend/src/app/(dashboard)/agents/page.tsx | 55 ++++++++++++++++++- .../custom-agents-page/marketplace-tab.tsx | 35 +++--------- .../marketplace-agent-preview-dialog.tsx | 19 ++++++- 3 files changed, 79 insertions(+), 30 deletions(-) diff --git a/frontend/src/app/(dashboard)/agents/page.tsx b/frontend/src/app/(dashboard)/agents/page.tsx index fdcf12bf..48cab9e3 100644 --- a/frontend/src/app/(dashboard)/agents/page.tsx +++ b/frontend/src/app/(dashboard)/agents/page.tsx @@ -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(null); const [selectedItem, setSelectedItem] = useState(null); const [showInstallDialog, setShowInstallDialog] = useState(false); + const [showPreviewDialog, setShowPreviewDialog] = useState(false); const [marketplaceFilter, setMarketplaceFilter] = useState<'all' | 'kortix' | 'community' | 'mine'>('all'); const [templatesActioningId, setTemplatesActioningId] = useState(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() {
-
+
)}
@@ -581,6 +624,14 @@ export default function AgentsPage() { open={showNewAgentDialog} onOpenChange={setShowNewAgentDialog} /> + +
); diff --git a/frontend/src/components/agents/custom-agents-page/marketplace-tab.tsx b/frontend/src/components/agents/custom-agents-page/marketplace-tab.tsx index fc14e74f..ecde8d68 100644 --- a/frontend/src/components/agents/custom-agents-page/marketplace-tab.tsx +++ b/frontend/src/components/agents/custom-agents-page/marketplace-tab.tsx @@ -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(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 = ({ )} - - ); }; \ No newline at end of file diff --git a/frontend/src/components/agents/marketplace-agent-preview-dialog.tsx b/frontend/src/components/agents/marketplace-agent-preview-dialog.tsx index 4aac6e75..21a7196b 100644 --- a/frontend/src/components/agents/marketplace-agent-preview-dialog.tsx +++ b/frontend/src/components/agents/marketplace-agent-preview-dialog.tsx @@ -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 { + 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 )} +