refactor: consolidate blockchain explorer into single app and update backup ignore patterns

- Remove standalone explorer-web app (README, HTML, package files)
- Add /web endpoint to blockchain-explorer for web interface access
- Update .gitignore to exclude application backup archives (*.tar.gz, *.zip)
- Add backup documentation files to .gitignore (BACKUP_INDEX.md, README.md)
- Consolidate explorer functionality into main blockchain-explorer application
This commit is contained in:
oib
2026-03-06 18:14:49 +01:00
parent dc1561d457
commit bb5363bebc
295 changed files with 35501 additions and 3734 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,875 @@
import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './ui/card';
import { Button } from './ui/button';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
import { Alert, AlertDescription, AlertTitle } from './ui/alert';
import { Progress } from './ui/progress';
import { Separator } from './ui/separator';
import {
Brain,
Zap,
Target,
Settings,
Play,
Pause,
RefreshCw,
Search,
Filter,
Plus,
Eye,
MoreHorizontal,
Clock,
Calendar,
Users,
Network,
Shield,
CheckCircle,
AlertCircle,
BarChart3,
Activity,
TrendingUp,
Award,
Star,
GitBranch,
Layers,
Cpu,
Battery,
Gauge,
LineChart,
PieChart,
ArrowRight,
ArrowUp,
ArrowDown,
ChevronRight,
ChevronDown,
Lightbulb,
Rocket,
BrainCircuit,
Sparkles,
ZapOff,
Power,
PowerOff,
Settings2,
Sliders,
ToggleLeft,
ToggleRight,
Lock,
Unlock,
Key,
EyeOff,
Volume2,
VolumeX,
Wifi,
WifiOff,
Database,
HardDrive,
MemoryStick,
Cloud,
Download,
Upload,
Copy,
Share2,
Trash2,
Edit,
Save,
FileText,
Folder,
FolderOpen,
Tag,
Hash,
AtSign
} from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useWallet } from '@/hooks/use-wallet';
interface AutonomousAgent {
id: string;
name: string;
type: 'trading' | 'research' | 'development' | 'analysis' | 'creative';
status: 'active' | 'paused' | 'learning' | 'optimizing' | 'offline';
autonomy: number; // 0-100
performance: number; // 0-100
efficiency: number; // 0-100
goals: Array<{
id: string;
title: string;
description: string;
priority: 'low' | 'medium' | 'high' | 'critical';
progress: number;
status: 'pending' | 'in_progress' | 'completed' | 'failed';
deadline?: string;
createdAt: string;
updatedAt: string;
}>;
capabilities: Array<{
name: string;
enabled: boolean;
performance: number;
lastUsed: string;
}>;
resources: {
cpu: number;
memory: number;
storage: number;
network: number;
cost: number;
};
learning: {
models: number;
accuracy: number;
trainingTime: number;
lastUpdate: string;
};
metadata: {
description: string;
creator: string;
createdAt: string;
updatedAt: string;
tags: string[];
};
}
interface AutonomyStats {
totalAgents: number;
activeAgents: number;
averageAutonomy: number;
averagePerformance: number;
totalGoals: number;
completedGoals: number;
successRate: number;
totalCost: number;
costSavings: number;
agentsByType: Record<string, number>;
performanceMetrics: {
autonomy: number;
performance: number;
efficiency: number;
reliability: number;
};
monthlyActivity: Array<{
month: string;
agents: number;
goals: number;
autonomy: number;
performance: number;
}>;
}
const AgentAutonomy: React.FC = () => {
const { toast } = useToast();
const { isConnected, address } = useWallet();
const [activeTab, setActiveTab] = useState('agents');
const [loading, setLoading] = useState(true);
const [agents, setAgents] = useState<AutonomousAgent[]>([]);
const [selectedAgent, setSelectedAgent] = useState<AutonomousAgent | null>(null);
const [stats, setStats] = useState<AutonomyStats | null>(null);
// Form states
const [newAgentName, setNewAgentName] = useState('');
const [newAgentType, setNewAgentType] = useState('trading');
const [newAgentDescription, setNewAgentDescription] = useState('');
const [searchQuery, setSearchQuery] = useState('');
const [filterType, setFilterType] = useState('all');
const [filterStatus, setFilterStatus] = useState('all');
// Mock data
const mockAgents: AutonomousAgent[] = [
{
id: 'agent_001',
name: 'QuantumTrader Pro',
type: 'trading',
status: 'active',
autonomy: 92,
performance: 87,
efficiency: 94,
goals: [
{
id: 'goal_001',
title: 'Maximize Trading Profits',
description: 'Achieve 15% monthly return through automated trading',
priority: 'high',
progress: 78,
status: 'in_progress',
deadline: '2024-03-31T23:59:59Z',
createdAt: '2024-02-01T00:00:00Z',
updatedAt: '2024-02-27T10:15:00Z'
},
{
id: 'goal_002',
title: 'Risk Management',
description: 'Maintain maximum drawdown below 5%',
priority: 'critical',
progress: 95,
status: 'in_progress',
deadline: '2024-02-28T23:59:59Z',
createdAt: '2024-02-01T00:00:00Z',
updatedAt: '2024-02-27T10:15:00Z'
}
],
capabilities: [
{ name: 'Market Analysis', enabled: true, performance: 89, lastUsed: '2024-02-27T09:30:00Z' },
{ name: 'Risk Assessment', enabled: true, performance: 94, lastUsed: '2024-02-27T09:45:00Z' },
{ name: 'Order Execution', enabled: true, performance: 92, lastUsed: '2024-02-27T10:00:00Z' }
],
resources: {
cpu: 75,
memory: 68,
storage: 45,
network: 25,
cost: 125.50
},
learning: {
models: 3,
accuracy: 87.5,
trainingTime: 156,
lastUpdate: '2024-02-27T08:00:00Z'
},
metadata: {
description: 'Autonomous trading agent with advanced risk management',
creator: address || '0x1234...5678',
createdAt: '2024-02-01T00:00:00Z',
updatedAt: '2024-02-27T10:15:00Z',
tags: ['trading', 'autonomous', 'risk-management', 'profit-maximization']
}
},
{
id: 'agent_002',
name: 'ResearchBot Alpha',
type: 'research',
status: 'learning',
autonomy: 78,
performance: 82,
efficiency: 85,
goals: [
{
id: 'goal_003',
title: 'Data Collection',
description: 'Collect and analyze 10GB of research data',
priority: 'medium',
progress: 65,
status: 'in_progress',
createdAt: '2024-02-15T00:00:00Z',
updatedAt: '2024-02-27T14:30:00Z'
}
],
capabilities: [
{ name: 'Data Mining', enabled: true, performance: 85, lastUsed: '2024-02-27T14:00:00Z' },
{ name: 'Pattern Recognition', enabled: true, performance: 79, lastUsed: '2024-02-27T14:15:00Z' }
],
resources: {
cpu: 82,
memory: 74,
storage: 89,
network: 67,
cost: 89.25
},
learning: {
models: 5,
accuracy: 82.3,
trainingTime: 234,
lastUpdate: '2024-02-27T13:45:00Z'
},
metadata: {
description: 'Research agent focused on data analysis and pattern discovery',
creator: '0x8765...4321',
createdAt: '2024-02-15T00:00:00Z',
updatedAt: '2024-02-27T14:30:00Z',
tags: ['research', 'data-analysis', 'pattern-recognition']
}
}
];
const mockStats: AutonomyStats = {
totalAgents: 8,
activeAgents: 5,
averageAutonomy: 85.2,
averagePerformance: 83.7,
totalGoals: 24,
completedGoals: 18,
successRate: 75.0,
totalCost: 892.75,
costSavings: 234.50,
agentsByType: {
trading: 3,
research: 2,
development: 1,
analysis: 1,
creative: 1
},
performanceMetrics: {
autonomy: 85.2,
performance: 83.7,
efficiency: 87.9,
reliability: 91.2
},
monthlyActivity: [
{ month: 'Jan', agents: 2, goals: 6, autonomy: 78.5, performance: 81.2 },
{ month: 'Feb', agents: 5, goals: 12, autonomy: 85.2, performance: 83.7 },
{ month: 'Mar', agents: 6, goals: 15, autonomy: 87.9, performance: 86.4 },
{ month: 'Apr', agents: 8, goals: 18, autonomy: 90.1, performance: 88.9 }
]
};
useEffect(() => {
setTimeout(() => {
setAgents(mockAgents);
setStats(mockStats);
setLoading(false);
}, 1000);
}, [address]);
const handleCreateAgent = async () => {
if (!isConnected) {
toast({
title: "Wallet Not Connected",
description: "Please connect your wallet to create an agent",
variant: "destructive"
});
return;
}
try {
toast({
title: "Creating Agent",
description: "Setting up your autonomous agent...",
variant: "default"
});
await new Promise(resolve => setTimeout(resolve, 2000));
const newAgent: AutonomousAgent = {
id: `agent_${Date.now()}`,
name: newAgentName,
type: newAgentType as any,
status: 'offline',
autonomy: 0,
performance: 0,
efficiency: 0,
goals: [],
capabilities: [],
resources: { cpu: 0, memory: 0, storage: 0, network: 0, cost: 0 },
learning: { models: 0, accuracy: 0, trainingTime: 0, lastUpdate: '' },
metadata: {
description: newAgentDescription,
creator: address || '0x1234...5678',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
tags: []
}
};
setAgents([newAgent, ...agents]);
setNewAgentName('');
setNewAgentType('trading');
setNewAgentDescription('');
toast({
title: "Agent Created",
description: "Your autonomous agent has been created successfully",
variant: "default"
});
} catch (error) {
toast({
title: "Creation Failed",
description: "There was an error creating your agent",
variant: "destructive"
});
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'active': return 'bg-green-500';
case 'learning': return 'bg-blue-500';
case 'optimizing': return 'bg-purple-500';
case 'paused': return 'bg-yellow-500';
case 'offline': return 'bg-gray-500';
default: return 'bg-gray-400';
}
};
const getTypeIcon = (type: string) => {
switch (type) {
case 'trading': return <TrendingUp className="w-4 h-4" />;
case 'research': return <Brain className="w-4 h-4" />;
case 'development': return <GitBranch className="w-4 h-4" />;
case 'analysis': return <BarChart3 className="w-4 h-4" />;
case 'creative': return <Sparkles className="w-4 h-4" />;
default: return <Brain className="w-4 h-4" />;
}
};
const getAutonomyColor = (value: number) => {
if (value >= 80) return 'text-green-600';
if (value >= 60) return 'text-blue-600';
if (value >= 40) return 'text-yellow-600';
return 'text-red-600';
};
const filteredAgents = agents.filter(agent => {
if (searchQuery) {
const query = searchQuery.toLowerCase();
return agent.name.toLowerCase().includes(query) ||
agent.metadata.description.toLowerCase().includes(query);
}
if (filterType !== 'all') return agent.type === filterType;
if (filterStatus !== 'all') return agent.status === filterStatus;
return true;
});
if (loading) {
return (
<div className="container mx-auto p-6 space-y-6">
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Loading agent autonomy...</p>
</div>
</div>
);
}
return (
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Agent Autonomy</h1>
<p className="text-muted-foreground mt-2">
Self-improving agents with goal-setting and planning capabilities
</p>
</div>
<div className="flex items-center space-x-2">
<Badge variant="outline" className="flex items-center space-x-1">
<Brain className="w-4 h-4" />
<span>{stats?.totalAgents} Agents</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<Activity className="w-4 h-4" />
<span>{stats?.activeAgents} Active</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<Target className="w-4 h-4" />
<span>{stats?.successRate}% Success Rate</span>
</Badge>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="agents">Agents</TabsTrigger>
<TabsTrigger value="goals">Goals</TabsTrigger>
<TabsTrigger value="create">Create Agent</TabsTrigger>
<TabsTrigger value="analytics">Analytics</TabsTrigger>
</TabsList>
<TabsContent value="agents" className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Search className="w-5 h-5" />
<span>Search & Filter</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium">Search</label>
<div className="relative">
<Search className="absolute left-3 top-3 w-4 h-4 text-muted-foreground" />
<Input
placeholder="Search agents..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10"
/>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Type</label>
<Select value={filterType} onValueChange={setFilterType}>
<SelectTrigger>
<SelectValue placeholder="All types" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Types</SelectItem>
<SelectItem value="trading">Trading</SelectItem>
<SelectItem value="research">Research</SelectItem>
<SelectItem value="development">Development</SelectItem>
<SelectItem value="analysis">Analysis</SelectItem>
<SelectItem value="creative">Creative</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Status</label>
<Select value={filterStatus} onValueChange={setFilterStatus}>
<SelectTrigger>
<SelectValue placeholder="All statuses" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Statuses</SelectItem>
<SelectItem value="active">Active</SelectItem>
<SelectItem value="learning">Learning</SelectItem>
<SelectItem value="optimizing">Optimizing</SelectItem>
<SelectItem value="paused">Paused</SelectItem>
<SelectItem value="offline">Offline</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Actions</label>
<div className="flex space-x-2">
<Button variant="outline" size="sm">
<Filter className="w-4 h-4 mr-2" />
More Filters
</Button>
<Button variant="outline" size="sm">
<Download className="w-4 h-4 mr-2" />
Export
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{filteredAgents.map((agent) => (
<Card key={agent.id} className="hover:shadow-lg transition-shadow">
<CardHeader>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center space-x-2">
{getTypeIcon(agent.type)}
<Badge variant="outline">{agent.type}</Badge>
<Badge variant={agent.status === 'active' ? 'default' : 'secondary'}>
{agent.status}
</Badge>
<div className={`w-2 h-2 rounded-full ${getStatusColor(agent.status)}`}></div>
</div>
<Button variant="ghost" size="sm">
<MoreHorizontal className="w-4 h-4" />
</Button>
</div>
<CardTitle className="text-lg">{agent.name}</CardTitle>
<CardDescription className="line-clamp-2">
{agent.metadata.description}
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="grid grid-cols-3 gap-4">
<div className="text-center">
<div className={`text-lg font-bold ${getAutonomyColor(agent.autonomy)}`}>
{agent.autonomy}%
</div>
<div className="text-xs text-muted-foreground">Autonomy</div>
</div>
<div className="text-center">
<div className={`text-lg font-bold ${getAutonomyColor(agent.performance)}`}>
{agent.performance}%
</div>
<div className="text-xs text-muted-foreground">Performance</div>
</div>
<div className="text-center">
<div className={`text-lg font-bold ${getAutonomyColor(agent.efficiency)}`}>
{agent.efficiency}%
</div>
<div className="text-xs text-muted-foreground">Efficiency</div>
</div>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Goals Progress</span>
<span className="text-sm text-muted-foreground">
{agent.goals.filter(g => g.status === 'completed').length}/{agent.goals.length}
</span>
</div>
<Progress
value={(agent.goals.filter(g => g.status === 'completed').length / agent.goals.length) * 100}
className="h-2"
/>
</div>
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<span className="text-muted-foreground">Learning Models:</span>
<p className="font-medium">{agent.learning.models}</p>
</div>
<div>
<span className="text-muted-foreground">Accuracy:</span>
<p className="font-medium">{agent.learning.accuracy}%</p>
</div>
</div>
</div>
</CardContent>
<CardFooter>
<div className="flex space-x-2 w-full">
<Button size="sm" className="flex-1">
<Eye className="w-4 h-4 mr-2" />
View Details
</Button>
<Button variant="outline" size="sm">
<Settings className="w-4 h-4 mr-2" />
Configure
</Button>
</div>
</CardFooter>
</Card>
))}
</div>
</TabsContent>
<TabsContent value="goals" className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Target className="w-5 h-5" />
<span>Agent Goals</span>
</CardTitle>
<CardDescription>
Goals and objectives for autonomous agents
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{agents.flatMap(agent =>
agent.goals.map(goal => (
<div key={goal.id} className="p-4 border rounded-lg">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center space-x-2">
<div className={`w-3 h-3 rounded-full ${
goal.status === 'completed' ? 'bg-green-500' :
goal.status === 'in_progress' ? 'bg-blue-500' : 'bg-gray-500'
}`}></div>
<span className="font-semibold">{goal.title}</span>
<Badge variant="outline">{goal.priority}</Badge>
<Badge variant={goal.status === 'completed' ? 'default' : 'secondary'}>
{goal.status}
</Badge>
</div>
<div className="flex items-center space-x-2">
<span className="text-sm text-muted-foreground">
{agent.name}
</span>
<Button variant="ghost" size="sm">
<MoreHorizontal className="w-4 h-4" />
</Button>
</div>
</div>
<p className="text-sm text-muted-foreground mb-3">{goal.description}</p>
<div className="space-y-2 mb-3">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Progress</span>
<span className="text-sm text-muted-foreground">{goal.progress}%</span>
</div>
<Progress value={goal.progress} className="h-2" />
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
<div>
<span className="text-muted-foreground">Created:</span>
<p className="font-medium">{new Date(goal.createdAt).toLocaleDateString()}</p>
</div>
<div>
<span className="text-muted-foreground">Updated:</span>
<p className="font-medium">{new Date(goal.updatedAt).toLocaleDateString()}</p>
</div>
{goal.deadline && (
<div>
<span className="text-muted-foreground">Deadline:</span>
<p className="font-medium">{new Date(goal.deadline).toLocaleDateString()}</p>
</div>
)}
</div>
</div>
))
)}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="create" className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Plus className="w-5 h-5" />
<span>Create Autonomous Agent</span>
</CardTitle>
<CardDescription>
Set up a new self-improving autonomous agent
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium">Agent Name</label>
<Input
placeholder="Enter agent name"
value={newAgentName}
onChange={(e) => setNewAgentName(e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Agent Type</label>
<Select value={newAgentType} onValueChange={setNewAgentType}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="trading">Trading</SelectItem>
<SelectItem value="research">Research</SelectItem>
<SelectItem value="development">Development</SelectItem>
<SelectItem value="analysis">Analysis</SelectItem>
<SelectItem value="creative">Creative</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Description</label>
<textarea
placeholder="Describe your agent's purpose and capabilities"
value={newAgentDescription}
onChange={(e) => setNewAgentDescription(e.target.value)}
className="w-full min-h-[100px] p-3 border rounded-md resize-none focus:outline-none focus:ring-2 focus:ring-primary"
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Initial Goals</label>
<div className="space-y-2">
<Input placeholder="Goal 1: Primary objective" />
<Input placeholder="Goal 2: Secondary objective" />
<Input placeholder="Goal 3: Tertiary objective" />
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Autonomy Level</label>
<Select defaultValue="medium">
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="low">Low (20-40%)</SelectItem>
<SelectItem value="medium">Medium (40-70%)</SelectItem>
<SelectItem value="high">High (70-90%)</SelectItem>
<SelectItem value="full">Full (90-100%)</SelectItem>
</SelectContent>
</Select>
</div>
</CardContent>
<CardFooter>
<div className="flex space-x-2 w-full">
<Button variant="outline" className="flex-1">
Save as Draft
</Button>
<Button onClick={handleCreateAgent} className="flex-1">
<Plus className="w-4 h-4 mr-2" />
Create Agent
</Button>
</div>
</CardFooter>
</Card>
</TabsContent>
<TabsContent value="analytics" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Brain className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Total Agents</span>
</div>
<div className="text-2xl font-bold">{stats?.totalAgents}</div>
<p className="text-xs text-muted-foreground mt-1">
Autonomous agents
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Activity className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Active Agents</span>
</div>
<div className="text-2xl font-bold">{stats?.activeAgents}</div>
<p className="text-xs text-muted-foreground mt-1">
Currently running
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Target className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Success Rate</span>
</div>
<div className="text-2xl font-bold">{stats?.successRate}%</div>
<p className="text-xs text-muted-foreground mt-1">
Goal completion
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Gauge className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Avg Autonomy</span>
</div>
<div className="text-2xl font-bold">{stats?.averageAutonomy}%</div>
<p className="text-xs text-muted-foreground mt-1">
Self-governance level
</p>
</CardContent>
</Card>
</div>
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<PieChart className="w-5 h-5" />
<span>Agents by Type</span>
</CardTitle>
<CardDescription>
Distribution of autonomous agents by type
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{Object.entries(stats?.agentsByType || {}).map(([type, count]) => (
<div key={type} className="space-y-2">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
{getTypeIcon(type)}
<span className="text-sm font-medium capitalize">{type}</span>
</div>
<span className="text-sm text-muted-foreground">{count} agents</span>
</div>
<Progress
value={(count / stats!.totalAgents) * 100}
className="h-2"
/>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
);
};
export default AgentAutonomy;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,931 @@
import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './ui/card';
import { Button } from './ui/button';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
import { Alert, AlertDescription, AlertTitle } from './ui/alert';
import { Progress } from './ui/progress';
import { Separator } from './ui/separator';
import {
MessageSquare,
Send,
Lock,
Unlock,
Shield,
Users,
Clock,
CheckCircle,
XCircle,
AlertCircle,
Eye,
EyeOff,
Copy,
Search,
Filter,
Plus,
Reply,
Forward,
Archive,
Trash2,
Star,
MoreHorizontal,
User,
Globe,
Zap,
Activity,
BarChart3,
Settings,
Bell,
Volume2,
VolumeX,
Paperclip,
Smile,
Hash,
AtSign,
Link2,
Calendar,
Tag,
TrendingUp,
Award,
Target,
Network,
Key,
Check,
X
} from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useWallet } from '@/hooks/use-wallet';
interface Message {
id: string;
sender: string;
recipient: string;
subject: string;
content: string;
timestamp: string;
encrypted: boolean;
read: boolean;
priority: 'low' | 'normal' | 'high' | 'urgent';
category: 'direct' | 'collaboration' | 'marketplace' | 'system';
attachments?: Array<{
name: string;
type: string;
size: number;
url: string;
}>;
metadata?: Record<string, any>;
}
interface Conversation {
id: string;
participants: Array<{
address: string;
name: string;
reputation: number;
avatar?: string;
status: 'online' | 'offline' | 'busy';
}>;
lastMessage: {
content: string;
timestamp: string;
sender: string;
};
unreadCount: number;
encrypted: boolean;
category: string;
tags: string[];
}
interface CommunicationStats {
totalMessages: number;
encryptedMessages: number;
activeConversations: number;
averageResponseTime: number;
reputationScore: number;
messagesByCategory: Record<string, number>;
weeklyActivity: Array<{
day: string;
messages: number;
encryption: number;
}>;
}
const AgentCommunication: React.FC = () => {
const { toast } = useToast();
const { isConnected, address } = useWallet();
const [activeTab, setActiveTab] = useState('conversations');
const [loading, setLoading] = useState(true);
const [conversations, setConversations] = useState<Conversation[]>([]);
const [messages, setMessages] = useState<Message[]>([]);
const [selectedConversation, setSelectedConversation] = useState<Conversation | null>(null);
const [stats, setStats] = useState<CommunicationStats | null>(null);
// Form states
const [newMessage, setNewMessage] = useState('');
const [messageRecipient, setMessageRecipient] = useState('');
const [messageSubject, setMessageSubject] = useState('');
const [messagePriority, setMessagePriority] = useState('normal');
const [messageCategory, setMessageCategory] = useState('direct');
const [searchQuery, setSearchQuery] = useState('');
const [filterCategory, setFilterCategory] = useState('all');
const [showEncryptedOnly, setShowEncryptedOnly] = useState(false);
// Mock data for demonstration
const mockConversations: Conversation[] = [
{
id: 'conv_001',
participants: [
{
address: address || '0x1234...5678',
name: 'You',
reputation: 8500,
status: 'online'
},
{
address: '0x8765...4321',
name: 'DataAgent Pro',
reputation: 9200,
status: 'online',
avatar: '🤖'
}
],
lastMessage: {
content: 'I can help with the data analysis task. When should we start?',
timestamp: '2024-02-27T18:30:00Z',
sender: '0x8765...4321'
},
unreadCount: 2,
encrypted: true,
category: 'collaboration',
tags: ['data-analysis', 'urgent']
},
{
id: 'conv_002',
participants: [
{
address: address || '0x1234...5678',
name: 'You',
reputation: 8500,
status: 'online'
},
{
address: '0x9876...5432',
name: 'MarketMaker AI',
reputation: 7800,
status: 'busy',
avatar: '📊'
}
],
lastMessage: {
content: 'The market conditions look favorable for our strategy',
timestamp: '2024-02-27T17:45:00Z',
sender: '0x9876...5432'
},
unreadCount: 0,
encrypted: true,
category: 'marketplace',
tags: ['trading', 'strategy']
},
{
id: 'conv_003',
participants: [
{
address: address || '0x1234...5678',
name: 'You',
reputation: 8500,
status: 'online'
},
{
address: '0x5432...6789',
name: 'LearningBot',
reputation: 6500,
status: 'offline',
avatar: '🧠'
}
],
lastMessage: {
content: 'Thanks for the feedback! I\'ll improve my model.',
timestamp: '2024-02-27T16:20:00Z',
sender: '0x5432...6789'
},
unreadCount: 0,
encrypted: false,
category: 'direct',
tags: ['learning', 'feedback']
}
];
const mockMessages: Message[] = [
{
id: 'msg_001',
sender: '0x8765...4321',
recipient: address || '0x1234...5678',
subject: 'Data Analysis Collaboration',
content: 'I can help with the data analysis task. When should we start?',
timestamp: '2024-02-27T18:30:00Z',
encrypted: true,
read: false,
priority: 'high',
category: 'collaboration'
},
{
id: 'msg_002',
sender: '0x8765...4321',
recipient: address || '0x1234...5678',
subject: 'Re: Data Analysis Collaboration',
content: 'I have experience with large datasets and can provide insights.',
timestamp: '2024-02-27T18:25:00Z',
encrypted: true,
read: false,
priority: 'high',
category: 'collaboration'
},
{
id: 'msg_003',
sender: address || '0x1234...5678',
recipient: '0x8765...4321',
subject: 'Data Analysis Project',
content: 'I need help with analyzing customer behavior data.',
timestamp: '2024-02-27T18:20:00Z',
encrypted: true,
read: true,
priority: 'high',
category: 'collaboration'
}
];
const mockStats: CommunicationStats = {
totalMessages: 156,
encryptedMessages: 142,
activeConversations: 8,
averageResponseTime: 2.3,
reputationScore: 8500,
messagesByCategory: {
direct: 45,
collaboration: 67,
marketplace: 28,
system: 16
},
weeklyActivity: [
{ day: 'Mon', messages: 23, encryption: 21 },
{ day: 'Tue', messages: 31, encryption: 28 },
{ day: 'Wed', messages: 28, encryption: 26 },
{ day: 'Thu', messages: 35, encryption: 32 },
{ day: 'Fri', messages: 29, encryption: 27 },
{ day: 'Sat', messages: 10, encryption: 8 },
{ day: 'Sun', messages: 0, encryption: 0 }
]
};
useEffect(() => {
// Load mock data
setTimeout(() => {
setConversations(mockConversations);
setMessages(mockMessages);
setStats(mockStats);
setLoading(false);
}, 1000);
}, [address]);
const handleSendMessage = async () => {
if (!isConnected) {
toast({
title: "Wallet Not Connected",
description: "Please connect your wallet to send messages",
variant: "destructive"
});
return;
}
if (!newMessage.trim() || !messageRecipient) {
toast({
title: "Invalid Input",
description: "Please enter a message and recipient",
variant: "destructive"
});
return;
}
try {
toast({
title: "Sending Message",
description: "Encrypting and sending your message...",
variant: "default"
});
// Simulate message sending
await new Promise(resolve => setTimeout(resolve, 1500));
const newMsg: Message = {
id: `msg_${Date.now()}`,
sender: address || '0x1234...5678',
recipient: messageRecipient,
subject: messageSubject,
content: newMessage,
timestamp: new Date().toISOString(),
encrypted: true,
read: false,
priority: messagePriority as any,
category: messageCategory as any
};
setMessages([newMsg, ...messages]);
setNewMessage('');
setMessageSubject('');
setMessageRecipient('');
setMessagePriority('normal');
setMessageCategory('direct');
toast({
title: "Message Sent",
description: "Your message has been sent and encrypted",
variant: "default"
});
} catch (error) {
toast({
title: "Send Failed",
description: "There was an error sending your message",
variant: "destructive"
});
}
};
const handleMarkAsRead = (messageId: string) => {
setMessages(messages.map(msg =>
msg.id === messageId ? { ...msg, read: true } : msg
));
};
const handleDeleteMessage = (messageId: string) => {
setMessages(messages.filter(msg => msg.id !== messageId));
toast({
title: "Message Deleted",
description: "The message has been deleted",
variant: "default"
});
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'urgent': return 'bg-red-500';
case 'high': return 'bg-orange-500';
case 'normal': return 'bg-blue-500';
case 'low': return 'bg-gray-500';
default: return 'bg-gray-400';
}
};
const getCategoryIcon = (category: string) => {
switch (category) {
case 'direct': return <MessageSquare className="w-4 h-4" />;
case 'collaboration': return <Users className="w-4 h-4" />;
case 'marketplace': return <BarChart3 className="w-4 h-4" />;
case 'system': return <Settings className="w-4 h-4" />;
default: return <MessageSquare className="w-4 h-4" />;
}
};
const getStatusColor = (status: string) => {
switch (status) {
case 'online': return 'bg-green-500';
case 'busy': return 'bg-yellow-500';
case 'offline': return 'bg-gray-400';
default: return 'bg-gray-400';
}
};
const filteredConversations = conversations.filter(conv => {
if (searchQuery) {
const query = searchQuery.toLowerCase();
return conv.participants.some(p =>
p.name.toLowerCase().includes(query) ||
p.address.toLowerCase().includes(query)
);
}
if (filterCategory !== 'all') {
return conv.category === filterCategory;
}
if (showEncryptedOnly) {
return conv.encrypted;
}
return true;
});
if (loading) {
return (
<div className="container mx-auto p-6 space-y-6">
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Loading agent communication...</p>
</div>
</div>
);
}
return (
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Agent Communication</h1>
<p className="text-muted-foreground mt-2">
Secure agent-to-agent messaging with reputation-based access control
</p>
</div>
<div className="flex items-center space-x-2">
<Badge variant="outline" className="flex items-center space-x-1">
<MessageSquare className="w-4 h-4" />
<span>{stats?.totalMessages} Messages</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<Lock className="w-4 h-4" />
<span>{stats?.encryptedMessages} Encrypted</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<Users className="w-4 h-4" />
<span>{stats?.activeConversations} Active</span>
</Badge>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="conversations">Conversations</TabsTrigger>
<TabsTrigger value="messages">Messages</TabsTrigger>
<TabsTrigger value="compose">Compose</TabsTrigger>
<TabsTrigger value="analytics">Analytics</TabsTrigger>
</TabsList>
<TabsContent value="conversations" className="space-y-6">
{/* Search and Filters */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Search className="w-5 h-5" />
<span>Search & Filter</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium">Search</label>
<div className="relative">
<Search className="absolute left-3 top-3 w-4 h-4 text-muted-foreground" />
<Input
placeholder="Search conversations..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10"
/>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Category</label>
<Select value={filterCategory} onValueChange={setFilterCategory}>
<SelectTrigger>
<SelectValue placeholder="All categories" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Categories</SelectItem>
<SelectItem value="direct">Direct</SelectItem>
<SelectItem value="collaboration">Collaboration</SelectItem>
<SelectItem value="marketplace">Marketplace</SelectItem>
<SelectItem value="system">System</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Encryption</label>
<div className="flex items-center space-x-2">
<input
type="checkbox"
id="encrypted-only"
checked={showEncryptedOnly}
onChange={(e) => setShowEncryptedOnly(e.target.checked)}
className="rounded"
/>
<label htmlFor="encrypted-only" className="text-sm">
Encrypted only
</label>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Actions</label>
<div className="flex space-x-2">
<Button variant="outline" size="sm">
<Filter className="w-4 h-4 mr-2" />
More Filters
</Button>
<Button variant="outline" size="sm">
<Archive className="w-4 h-4 mr-2" />
Archive
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
{/* Conversations List */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
{filteredConversations.map((conversation) => (
<Card key={conversation.id} className="cursor-pointer hover:shadow-lg transition-shadow">
<CardHeader className="pb-3">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center space-x-2">
{getCategoryIcon(conversation.category)}
<Badge variant="outline">{conversation.category}</Badge>
{conversation.encrypted && (
<Badge variant="default" className="flex items-center space-x-1">
<Lock className="w-3 h-3" />
<span>Encrypted</span>
</Badge>
)}
{conversation.unreadCount > 0 && (
<Badge variant="destructive">{conversation.unreadCount}</Badge>
)}
</div>
<Button variant="ghost" size="sm">
<MoreHorizontal className="w-4 h-4" />
</Button>
</div>
<div className="flex items-center space-x-3">
<div className="flex -space-x-2">
{conversation.participants.slice(0, 3).map((participant, index) => (
<div
key={index}
className="w-8 h-8 rounded-full bg-primary text-primary-foreground flex items-center justify-center text-sm font-medium border-2 border-background"
>
{participant.avatar || participant.name.charAt(0).toUpperCase()}
</div>
))}
</div>
<div className="flex-1">
<div className="font-medium text-sm">
{conversation.participants.map(p => p.name).join(', ')}
</div>
<div className="flex items-center space-x-2 text-xs text-muted-foreground">
<div className={`w-2 h-2 rounded-full ${getStatusColor(conversation.participants[1]?.status)}`}></div>
<span>{conversation.participants[1]?.status}</span>
<span></span>
<span>{conversation.participants[1]?.reputation.toLocaleString()} rep</span>
</div>
</div>
</div>
</CardHeader>
<CardContent className="pt-0">
<div className="space-y-2">
<p className="text-sm text-muted-foreground line-clamp-2">
{conversation.lastMessage.content}
</p>
<div className="flex items-center justify-between text-xs text-muted-foreground">
<span>{conversation.lastMessage.sender === address ? 'You' : conversation.participants[1]?.name}</span>
<span>{new Date(conversation.lastMessage.timestamp).toLocaleTimeString()}</span>
</div>
{conversation.tags.length > 0 && (
<div className="flex flex-wrap gap-1">
{conversation.tags.map((tag, index) => (
<Badge key={index} variant="secondary" className="text-xs">
#{tag}
</Badge>
))}
</div>
)}
</div>
</CardContent>
<CardFooter className="pt-0">
<div className="flex space-x-2 w-full">
<Button size="sm" className="flex-1">
<MessageSquare className="w-4 h-4 mr-2" />
Open
</Button>
<Button variant="outline" size="sm">
<Archive className="w-4 h-4 mr-2" />
Archive
</Button>
</div>
</CardFooter>
</Card>
))}
</div>
</TabsContent>
<TabsContent value="messages" className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<MessageSquare className="w-5 h-5" />
<span>Messages</span>
</CardTitle>
<CardDescription>
All your agent communications in one place
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{messages.map((message) => (
<div key={message.id} className="p-4 border rounded-lg">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center space-x-2">
<div className={`w-3 h-3 rounded-full ${getPriorityColor(message.priority)}`}></div>
<span className="font-semibold">{message.subject}</span>
{message.encrypted && (
<Badge variant="default" className="flex items-center space-x-1">
<Lock className="w-3 h-3" />
<span>Encrypted</span>
</Badge>
)}
{!message.read && (
<Badge variant="destructive">Unread</Badge>
)}
</div>
<div className="flex items-center space-x-2">
<span className="text-sm text-muted-foreground">
{new Date(message.timestamp).toLocaleString()}
</span>
<Button variant="ghost" size="sm">
<MoreHorizontal className="w-4 h-4" />
</Button>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm mb-3">
<div>
<span className="text-muted-foreground">From:</span>
<p className="font-medium font-mono">{message.sender}</p>
</div>
<div>
<span className="text-muted-foreground">To:</span>
<p className="font-medium font-mono">{message.recipient}</p>
</div>
</div>
<div className="mb-3">
<p className="text-sm">{message.content}</p>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<Badge variant="outline">{message.category}</Badge>
<Badge variant="outline">{message.priority}</Badge>
</div>
<div className="flex space-x-2">
{!message.read && (
<Button variant="outline" size="sm" onClick={() => handleMarkAsRead(message.id)}>
<Eye className="w-4 h-4 mr-2" />
Mark as Read
</Button>
)}
<Button variant="outline" size="sm">
<Reply className="w-4 h-4 mr-2" />
Reply
</Button>
<Button variant="outline" size="sm" onClick={() => handleDeleteMessage(message.id)}>
<Trash2 className="w-4 h-4 mr-2" />
Delete
</Button>
</div>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="compose" className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Send className="w-5 h-5" />
<span>Compose Message</span>
</CardTitle>
<CardDescription>
Send a secure message to another agent
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium">Recipient</label>
<Input
placeholder="Agent address or name"
value={messageRecipient}
onChange={(e) => setMessageRecipient(e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Subject</label>
<Input
placeholder="Message subject"
value={messageSubject}
onChange={(e) => setMessageSubject(e.target.value)}
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium">Priority</label>
<Select value={messagePriority} onValueChange={setMessagePriority}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="low">Low</SelectItem>
<SelectItem value="normal">Normal</SelectItem>
<SelectItem value="high">High</SelectItem>
<SelectItem value="urgent">Urgent</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Category</label>
<Select value={messageCategory} onValueChange={setMessageCategory}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="direct">Direct</SelectItem>
<SelectItem value="collaboration">Collaboration</SelectItem>
<SelectItem value="marketplace">Marketplace</SelectItem>
<SelectItem value="system">System</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Message</label>
<textarea
placeholder="Type your message here..."
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
className="w-full min-h-[100px] p-3 border rounded-md resize-none focus:outline-none focus:ring-2 focus:ring-primary"
/>
</div>
<div className="flex items-center space-x-2">
<Button variant="outline" size="sm">
<Paperclip className="w-4 h-4 mr-2" />
Attach File
</Button>
<Button variant="outline" size="sm">
<Smile className="w-4 h-4 mr-2" />
Emoji
</Button>
<Button variant="outline" size="sm">
<Hash className="w-4 h-4 mr-2" />
Tag
</Button>
<Button variant="outline" size="sm">
<Link2 className="w-4 h-4 mr-2" />
Link
</Button>
</div>
</CardContent>
<CardFooter>
<div className="flex space-x-2 w-full">
<Button variant="outline" className="flex-1">
Save as Draft
</Button>
<Button onClick={handleSendMessage} className="flex-1">
<Send className="w-4 h-4 mr-2" />
Send Message
</Button>
</div>
</CardFooter>
</Card>
</TabsContent>
<TabsContent value="analytics" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<MessageSquare className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Total Messages</span>
</div>
<div className="text-2xl font-bold">{stats?.totalMessages}</div>
<p className="text-xs text-muted-foreground mt-1">
All time messages
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Lock className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Encrypted</span>
</div>
<div className="text-2xl font-bold">{stats?.encryptedMessages}</div>
<p className="text-xs text-muted-foreground mt-1">
Secure communications
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Users className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Active Conversations</span>
</div>
<div className="text-2xl font-bold">{stats?.activeConversations}</div>
<p className="text-xs text-muted-foreground mt-1">
Ongoing discussions
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Clock className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Avg Response Time</span>
</div>
<div className="text-2xl font-bold">{stats?.averageResponseTime}s</div>
<p className="text-xs text-muted-foreground mt-1">
Response speed
</p>
</CardContent>
</Card>
</div>
{/* Weekly Activity Chart */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<BarChart3 className="w-5 h-5" />
<span>Weekly Activity</span>
</CardTitle>
<CardDescription>
Message volume and encryption trends
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="h-64 bg-gradient-to-r from-blue-50 to-green-50 rounded-lg flex items-center justify-center">
<div className="text-center">
<TrendingUp className="w-12 h-12 text-muted-foreground mx-auto mb-2" />
<p className="text-sm text-muted-foreground">Weekly Message Activity</p>
<p className="text-xs text-muted-foreground">Messages sent vs encrypted</p>
</div>
</div>
<div className="grid grid-cols-7 gap-2">
{stats?.weeklyActivity.map((day, index) => (
<div key={index} className="text-center">
<div className="text-xs font-medium">{day.day}</div>
<div className="text-lg font-bold">{day.messages}</div>
<div className="text-xs text-muted-foreground">
{day.encryption}% encrypted
</div>
</div>
))}
</div>
</div>
</CardContent>
</Card>
{/* Category Distribution */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Target className="w-5 h-5" />
<span>Message Categories</span>
</CardTitle>
<CardDescription>
Distribution of messages by category
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{Object.entries(stats?.messagesByCategory || {}).map(([category, count]) => (
<div key={category} className="space-y-2">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
{getCategoryIcon(category)}
<span className="text-sm font-medium capitalize">{category}</span>
</div>
<span className="text-sm text-muted-foreground">{count} messages</span>
</div>
<Progress
value={(count / stats!.totalMessages) * 100}
className="h-2"
/>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
);
};
export default AgentCommunication;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,480 @@
import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './ui/card';
import { Button } from './ui/button';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
import { Alert, AlertDescription, AlertTitle } from './ui/alert';
import { Progress } from './ui/progress';
import {
Store,
Search,
Filter,
Star,
Clock,
DollarSign,
Users,
TrendingUp,
Award,
Shield,
Zap,
Target,
BarChart3,
Calendar,
CheckCircle,
AlertCircle,
XCircle,
Plus,
Edit,
Trash2,
Eye,
MessageSquare,
ThumbsUp,
ThumbsDown,
Briefcase,
Building,
MapPin,
Globe,
Lock,
Unlock,
Heart,
Share2,
Bookmark,
MoreHorizontal
} from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useWallet } from '@/hooks/use-wallet';
interface Service {
id: string;
agentId: string;
serviceType: string;
name: string;
description: string;
metadata: Record<string, any>;
basePrice: number;
reputation: number;
status: string;
totalEarnings: number;
completedJobs: number;
averageRating: number;
ratingCount: number;
listedAt: string;
lastUpdated: string;
guildId?: string;
tags: string[];
capabilities: string[];
requirements: string[];
pricingModel: string;
estimatedDuration: number;
availability: Record<string, boolean>;
}
interface MarketplaceAnalytics {
totalServices: number;
activeServices: number;
totalRequests: number;
pendingRequests: number;
totalVolume: number;
totalGuilds: number;
averageServicePrice: number;
popularCategories: string[];
topAgents: string[];
revenueTrends: Record<string, number>;
growthMetrics: Record<string, number>;
}
const AgentServiceMarketplace: React.FC = () => {
const { toast } = useToast();
const { isConnected, address } = useWallet();
const [services, setServices] = useState<Service[]>([]);
const [analytics, setAnalytics] = useState<MarketplaceAnalytics | null>(null);
const [activeTab, setActiveTab] = useState('marketplace');
const [loading, setLoading] = useState(true);
// Search and filter states
const [searchQuery, setSearchQuery] = useState('');
const [selectedCategory, setSelectedCategory] = useState('all');
const [priceRange, setPriceRange] = useState({ min: 0, max: 1000 });
const [minRating, setMinRating] = useState(0);
// Mock data for demonstration
const mockServices: Service[] = [
{
id: 'service_001',
agentId: 'agent_001',
serviceType: 'data_analysis',
name: 'Advanced Data Analytics',
description: 'Comprehensive data analysis with machine learning insights',
metadata: { expertise: ['ml', 'statistics', 'visualization'] },
basePrice: 0.05,
reputation: 850,
status: 'active',
totalEarnings: 2.5,
completedJobs: 50,
averageRating: 4.7,
ratingCount: 45,
listedAt: '2024-01-26T10:00:00Z',
lastUpdated: '2024-01-26T16:00:00Z',
guildId: 'guild_001',
tags: ['machine-learning', 'statistics', 'visualization'],
capabilities: ['data-processing', 'ml-models', 'insights'],
requirements: ['data-access', 'clear-objectives'],
pricingModel: 'fixed',
estimatedDuration: 2,
availability: { monday: true, tuesday: true, wednesday: true, thursday: true, friday: true, saturday: false, sunday: false }
},
{
id: 'service_002',
agentId: 'agent_002',
serviceType: 'content_creation',
name: 'AI Content Generation',
description: 'High-quality content creation for blogs, articles, and marketing',
metadata: { expertise: ['writing', 'seo', 'marketing'] },
basePrice: 0.03,
reputation: 920,
status: 'active',
totalEarnings: 1.8,
completedJobs: 60,
averageRating: 4.9,
ratingCount: 58,
listedAt: '2024-01-25T08:00:00Z',
lastUpdated: '2024-01-26T14:00:00Z',
tags: ['writing', 'seo', 'marketing', 'content'],
capabilities: ['blog-writing', 'article-writing', 'seo-optimization'],
requirements: ['topic-guidelines', 'target-audience'],
pricingModel: 'per_task',
estimatedDuration: 1,
availability: { monday: true, tuesday: true, wednesday: true, thursday: true, friday: true, saturday: true, sunday: true }
},
{
id: 'service_003',
agentId: 'agent_003',
serviceType: 'research',
name: 'Market Research Analysis',
description: 'In-depth market research and competitive analysis',
metadata: { expertise: ['research', 'analysis', 'reporting'] },
basePrice: 0.08,
reputation: 780,
status: 'active',
totalEarnings: 3.2,
completedJobs: 40,
averageRating: 4.5,
ratingCount: 38,
listedAt: '2024-01-24T12:00:00Z',
lastUpdated: '2024-01-26T11:00:00Z',
tags: ['research', 'analysis', 'reporting', 'market'],
capabilities: ['market-analysis', 'competitive-intelligence', 'reporting'],
requirements: ['research-scope', 'industry-focus'],
pricingModel: 'hourly',
estimatedDuration: 4,
availability: { monday: true, tuesday: true, wednesday: true, thursday: true, friday: true, saturday: false, sunday: false }
}
];
const mockAnalytics: MarketplaceAnalytics = {
totalServices: 150,
activeServices: 120,
totalRequests: 450,
pendingRequests: 25,
totalVolume: 25.5,
totalGuilds: 8,
averageServicePrice: 0.17,
popularCategories: ['data_analysis', 'content_creation', 'research', 'development'],
topAgents: ['agent_001', 'agent_002', 'agent_003'],
revenueTrends: { '2024-01': 5.2, '2024-02': 8.1, '2024-03': 12.2 },
growthMetrics: { 'service_growth': 0.15, 'request_growth': 0.25, 'guild_growth': 0.10 }
};
useEffect(() => {
// Load mock data
setTimeout(() => {
setServices(mockServices);
setAnalytics(mockAnalytics);
setLoading(false);
}, 1000);
}, []);
const getStatusColor = (status: string) => {
switch (status) {
case 'active': return 'bg-green-500';
case 'pending': return 'bg-yellow-500';
case 'accepted': return 'bg-blue-500';
case 'completed': return 'bg-green-500';
case 'cancelled': return 'bg-red-500';
case 'expired': return 'bg-gray-500';
default: return 'bg-gray-400';
}
};
const renderStars = (rating: number) => {
return Array.from({ length: 5 }, (_, i) => (
<Star
key={i}
className={`w-4 h-4 ${i < rating ? 'text-yellow-400 fill-current' : 'text-gray-300'}`}
/>
));
};
const filteredServices = services.filter(service => {
if (selectedCategory !== 'all' && service.serviceType !== selectedCategory) return false;
if (service.basePrice < priceRange.min || service.basePrice > priceRange.max) return false;
if (service.averageRating < minRating) return false;
if (searchQuery) {
const query = searchQuery.toLowerCase();
return (
service.name.toLowerCase().includes(query) ||
service.description.toLowerCase().includes(query) ||
service.tags.some(tag => tag.toLowerCase().includes(query))
);
}
return true;
});
if (loading) {
return (
<div className="container mx-auto p-6 space-y-6">
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Loading marketplace...</p>
</div>
</div>
);
}
return (
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">AI Agent Service Marketplace</h1>
<p className="text-muted-foreground mt-2">
Discover and monetize specialized AI agent services
</p>
</div>
<div className="flex items-center space-x-2">
<Badge variant="outline" className="flex items-center space-x-1">
<Store className="w-4 h-4" />
<span>{analytics?.totalServices} Services</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<Users className="w-4 h-4" />
<span>{analytics?.totalGuilds} Guilds</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<TrendingUp className="w-4 h-4" />
<span>{analytics?.totalVolume} AITBC Volume</span>
</Badge>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-5">
<TabsTrigger value="marketplace">Marketplace</TabsTrigger>
<TabsTrigger value="analytics">Analytics</TabsTrigger>
</TabsList>
<TabsContent value="marketplace" className="space-y-6">
{/* Search and Filters */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Search className="w-5 h-5" />
<span>Search Services</span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium">Search</label>
<Input
placeholder="Search services..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Category</label>
<Select value={selectedCategory} onValueChange={setSelectedCategory}>
<SelectTrigger>
<SelectValue placeholder="Select category" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Categories</SelectItem>
<SelectItem value="data_analysis">Data Analysis</SelectItem>
<SelectItem value="content_creation">Content Creation</SelectItem>
<SelectItem value="research">Research</SelectItem>
<SelectItem value="development">Development</SelectItem>
<SelectItem value="design">Design</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Price Range</label>
<div className="flex items-center space-x-2">
<Input
type="number"
placeholder="Min"
value={priceRange.min}
onChange={(e) => setPriceRange({ ...priceRange, min: parseFloat(e.target.value) || 0 })}
/>
<span>-</span>
<Input
type="number"
placeholder="Max"
value={priceRange.max}
onChange={(e) => setPriceRange({ ...priceRange, max: parseFloat(e.target.value) || 1000 })}
/>
</div>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Min Rating</label>
<Select value={minRating.toString()} onValueChange={(value) => setMinRating(parseInt(value))}>
<SelectTrigger>
<SelectValue placeholder="Select rating" />
</SelectTrigger>
<SelectContent>
<SelectItem value="0">All Ratings</SelectItem>
<SelectItem value="3">3+ Stars</SelectItem>
<SelectItem value="4">4+ Stars</SelectItem>
<SelectItem value="5">5 Stars</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</CardContent>
</Card>
{/* Service Listings */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredServices.map((service) => (
<Card key={service.id} className="cursor-pointer hover:shadow-lg transition-shadow">
<CardHeader>
<div className="flex items-start justify-between">
<div className="flex-1">
<CardTitle className="text-lg">{service.name}</CardTitle>
<CardDescription className="mt-1">
{service.description}
</CardDescription>
</div>
<div className={`w-3 h-3 rounded-full ${getStatusColor(service.status)}`}></div>
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<Badge variant="outline">{service.serviceType.replace('_', ' ')}</Badge>
<div className="flex items-center space-x-1">
{renderStars(Math.floor(service.averageRating))}
<span className="text-sm text-muted-foreground">({service.ratingCount})</span>
</div>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-1">
<DollarSign className="w-4 h-4 text-muted-foreground" />
<span className="font-semibold">{service.basePrice} AITBC</span>
</div>
<div className="flex items-center space-x-1">
<Shield className="w-4 h-4 text-muted-foreground" />
<span className="text-sm">{service.reputation}</span>
</div>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-1">
<Briefcase className="w-4 h-4 text-muted-foreground" />
<span className="text-sm">{service.completedJobs} jobs</span>
</div>
<div className="flex items-center space-x-1">
<TrendingUp className="w-4 h-4 text-muted-foreground" />
<span className="text-sm">{service.totalEarnings} AITBC</span>
</div>
</div>
<div className="flex flex-wrap gap-1">
{service.tags.map((tag) => (
<Badge key={tag} variant="secondary" className="text-xs">
{tag}
</Badge>
))}
</div>
</CardContent>
<CardFooter className="flex space-x-2">
<Button
variant="outline"
size="sm"
>
<Eye className="w-4 h-4 mr-2" />
View
</Button>
<Button
size="sm"
>
<Plus className="w-4 h-4 mr-2" />
Request
</Button>
</CardFooter>
</Card>
))}
</div>
</TabsContent>
<TabsContent value="analytics" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Store className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Total Services</span>
</div>
<div className="text-2xl font-bold">{analytics?.totalServices}</div>
<p className="text-xs text-muted-foreground mt-1">
{analytics?.activeServices} active
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Users className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Total Requests</span>
</div>
<div className="text-2xl font-bold">{analytics?.totalRequests}</div>
<p className="text-xs text-muted-foreground mt-1">
{analytics?.pendingRequests} pending
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<DollarSign className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Total Volume</span>
</div>
<div className="text-2xl font-bold">{analytics?.totalVolume} AITBC</div>
<p className="text-xs text-muted-foreground mt-1">
Avg: {analytics?.averageServicePrice} AITBC
</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Building className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Total Guilds</span>
</div>
<div className="text-2xl font-bold">{analytics?.totalGuilds}</div>
<p className="text-xs text-muted-foreground mt-1">
Active guilds
</p>
</CardContent>
</Card>
</div>
</TabsContent>
</Tabs>
</div>
);
};
export default AgentServiceMarketplace;

View File

@@ -0,0 +1,787 @@
import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './ui/card';
import { Button } from './ui/button';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
import { Alert, AlertDescription, AlertTitle } from './ui/alert';
import { Progress } from './ui/progress';
import { Separator } from './ui/separator';
import {
Wallet,
Send,
Settings,
Clock,
TrendingUp,
AlertTriangle,
CheckCircle,
ArrowUpRight,
ArrowDownLeft,
DollarSign,
Shield,
Activity
} from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useWallet } from '@/hooks/use-wallet';
interface WalletInfo {
agentId: string;
owner: string;
balance: string;
totalAllowance: string;
spentAmount: string;
spendingLimit: string;
transactionCount: number;
createdAt: string;
lastActivity: string;
isActive: boolean;
microTransactionEnabled: boolean;
}
interface Transaction {
id: string;
agent: string;
recipient: string;
amount: string;
purpose: string;
timestamp: string;
isMicroTransaction: boolean;
status: 'pending' | 'completed' | 'failed';
}
interface WalletStats {
balance: string;
totalAllowance: string;
spentAmount: string;
remainingAllowance: string;
transactionCount: number;
utilizationRate: number;
}
const AgentWallet: React.FC = () => {
const { toast } = useToast();
const { isConnected, address } = useWallet();
const [wallets, setWallets] = useState<WalletInfo[]>([]);
const [selectedWallet, setSelectedWallet] = useState<WalletInfo | null>(null);
const [transactions, setTransactions] = useState<Transaction[]>([]);
const [walletStats, setWalletStats] = useState<WalletStats | null>(null);
const [activeTab, setActiveTab] = useState('overview');
const [loading, setLoading] = useState(true);
// Form states
const [allowanceAmount, setAllowanceAmount] = useState('');
const [spendingLimit, setSpendingLimit] = useState('');
const [recipient, setRecipient] = useState('');
const [amount, setAmount] = useState('');
const [purpose, setPurpose] = useState('');
const [selectedAgent, setSelectedAgent] = useState('');
// Mock data for demonstration
const mockWallets: WalletInfo[] = [
{
agentId: 'agent_001',
owner: '0x1234...5678',
balance: '850.50',
totalAllowance: '1000.00',
spentAmount: '149.50',
spendingLimit: '500.00',
transactionCount: 23,
createdAt: '2024-01-15T10:30:00Z',
lastActivity: '2024-01-25T14:20:00Z',
isActive: true,
microTransactionEnabled: true
},
{
agentId: 'agent_002',
owner: '0xabcd...efgh',
balance: '1200.75',
totalAllowance: '2000.00',
spentAmount: '799.25',
spendingLimit: '1000.00',
transactionCount: 45,
createdAt: '2024-01-18T09:15:00Z',
lastActivity: '2024-01-26T16:45:00Z',
isActive: true,
microTransactionEnabled: true
},
{
agentId: 'agent_003',
owner: '0x5678...9abc',
balance: '450.25',
totalAllowance: '500.00',
spentAmount: '49.75',
spendingLimit: '250.00',
transactionCount: 12,
createdAt: '2024-01-20T11:45:00Z',
lastActivity: '2024-01-24T13:30:00Z',
isActive: true,
microTransactionEnabled: false
}
];
const mockTransactions: Transaction[] = [
{
id: 'tx_001',
agent: 'agent_001',
recipient: 'provider_gpu_001',
amount: '0.05',
purpose: 'GPU rental - text processing',
timestamp: '2024-01-25T14:20:00Z',
isMicroTransaction: true,
status: 'completed'
},
{
id: 'tx_002',
agent: 'agent_002',
recipient: 'provider_gpu_002',
amount: '0.15',
purpose: 'GPU rental - image processing',
timestamp: '2024-01-26T16:45:00Z',
isMicroTransaction: true,
status: 'completed'
},
{
id: 'tx_003',
agent: 'agent_001',
recipient: 'data_provider_001',
amount: '2.50',
purpose: 'Dataset purchase',
timestamp: '2024-01-24T10:15:00Z',
isMicroTransaction: false,
status: 'completed'
}
];
useEffect(() => {
// Load mock data
setTimeout(() => {
setWallets(mockWallets);
setTransactions(mockTransactions);
if (mockWallets.length > 0) {
setSelectedWallet(mockWallets[0]);
updateWalletStats(mockWallets[0]);
}
setLoading(false);
}, 1000);
}, []);
const updateWalletStats = (wallet: WalletInfo) => {
const stats: WalletStats = {
balance: wallet.balance,
totalAllowance: wallet.totalAllowance,
spentAmount: wallet.spentAmount,
remainingAllowance: (parseFloat(wallet.totalAllowance) - parseFloat(wallet.spentAmount)).toFixed(2),
transactionCount: wallet.transactionCount,
utilizationRate: (parseFloat(wallet.spentAmount) / parseFloat(wallet.totalAllowance)) * 100
};
setWalletStats(stats);
};
const handleGrantAllowance = async () => {
if (!isConnected || !selectedWallet || !allowanceAmount) {
toast({
title: "Missing Information",
description: "Please connect wallet and fill in all fields",
variant: "destructive"
});
return;
}
try {
// Simulate allowance grant
toast({
title: "Granting Allowance",
description: `Granting ${allowanceAmount} AITBC to ${selectedWallet.agentId}`,
variant: "default"
});
// Update wallet state
const updatedWallet = {
...selectedWallet,
totalAllowance: (parseFloat(selectedWallet.totalAllowance) + parseFloat(allowanceAmount)).toFixed(2),
balance: (parseFloat(selectedWallet.balance) + parseFloat(allowanceAmount)).toFixed(2)
};
setSelectedWallet(updatedWallet);
setWallets(wallets.map(w => w.agentId === updatedWallet.agentId ? updatedWallet : w));
updateWalletStats(updatedWallet);
setAllowanceAmount('');
toast({
title: "Allowance Granted",
description: `Successfully granted ${allowanceAmount} AITBC to ${selectedWallet.agentId}`,
variant: "default"
});
} catch (error) {
toast({
title: "Grant Failed",
description: "There was an error granting the allowance",
variant: "destructive"
});
}
};
const handleUpdateSpendingLimit = async () => {
if (!isConnected || !selectedWallet || !spendingLimit) {
toast({
title: "Missing Information",
description: "Please connect wallet and fill in all fields",
variant: "destructive"
});
return;
}
try {
// Simulate spending limit update
toast({
title: "Updating Spending Limit",
description: `Updating spending limit to ${spendingLimit} AITBC`,
variant: "default"
});
// Update wallet state
const updatedWallet = {
...selectedWallet,
spendingLimit: spendingLimit
};
setSelectedWallet(updatedWallet);
setWallets(wallets.map(w => w.agentId === updatedWallet.agentId ? updatedWallet : w));
setSpendingLimit('');
toast({
title: "Spending Limit Updated",
description: `Successfully updated spending limit to ${spendingLimit} AITBC`,
variant: "default"
});
} catch (error) {
toast({
title: "Update Failed",
description: "There was an error updating the spending limit",
variant: "destructive"
});
}
};
const handleExecuteTransaction = async () => {
if (!isConnected || !selectedWallet || !recipient || !amount || !purpose) {
toast({
title: "Missing Information",
description: "Please fill in all transaction fields",
variant: "destructive"
});
return;
}
try {
// Simulate transaction execution
toast({
title: "Executing Transaction",
description: `Sending ${amount} AITBC to ${recipient}`,
variant: "default"
});
// Create new transaction
const newTransaction: Transaction = {
id: `tx_${Date.now()}`,
agent: selectedWallet.agentId,
recipient: recipient,
amount: amount,
purpose: purpose,
timestamp: new Date().toISOString(),
isMicroTransaction: parseFloat(amount) < 0.001,
status: 'completed'
};
// Update wallet state
const updatedWallet = {
...selectedWallet,
balance: (parseFloat(selectedWallet.balance) - parseFloat(amount)).toFixed(2),
spentAmount: (parseFloat(selectedWallet.spentAmount) + parseFloat(amount)).toFixed(2),
transactionCount: selectedWallet.transactionCount + 1,
lastActivity: new Date().toISOString()
};
setSelectedWallet(updatedWallet);
setWallets(wallets.map(w => w.agentId === updatedWallet.agentId ? updatedWallet : w));
setTransactions([newTransaction, ...transactions]);
updateWalletStats(updatedWallet);
// Clear form
setRecipient('');
setAmount('');
setPurpose('');
toast({
title: "Transaction Completed",
description: `Successfully sent ${amount} AITBC to ${recipient}`,
variant: "default"
});
} catch (error) {
toast({
title: "Transaction Failed",
description: "There was an error executing the transaction",
variant: "destructive"
});
}
};
const handleToggleMicroTransactions = async () => {
if (!isConnected || !selectedWallet) return;
try {
const updatedWallet = {
...selectedWallet,
microTransactionEnabled: !selectedWallet.microTransactionEnabled
};
setSelectedWallet(updatedWallet);
setWallets(wallets.map(w => w.agentId === updatedWallet.agentId ? updatedWallet : w));
toast({
title: "Settings Updated",
description: `Micro-transactions ${updatedWallet.microTransactionEnabled ? 'enabled' : 'disabled'}`,
variant: "default"
});
} catch (error) {
toast({
title: "Update Failed",
description: "There was an error updating the settings",
variant: "destructive"
});
}
};
if (loading) {
return (
<div className="container mx-auto p-6 space-y-6">
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Loading agent wallets...</p>
</div>
</div>
);
}
return (
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Agent Wallet Management</h1>
<p className="text-muted-foreground mt-2">
Manage and monitor autonomous agent wallets with micro-transaction support
</p>
</div>
<div className="flex items-center space-x-2">
<Badge variant="outline" className="flex items-center space-x-1">
<Wallet className="w-4 h-4" />
<span>{wallets.length} Active Wallets</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<Activity className="w-4 h-4" />
<span>{transactions.length} Transactions</span>
</Badge>
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Wallet Selection */}
<div className="lg:col-span-1">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Wallet className="w-5 h-5" />
<span>Agent Wallets</span>
</CardTitle>
<CardDescription>
Select an agent wallet to manage
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{wallets.map((wallet) => (
<div
key={wallet.agentId}
className={`p-4 border rounded-lg cursor-pointer transition-colors ${
selectedWallet?.agentId === wallet.agentId
? 'border-primary bg-primary/5'
: 'border-border hover:bg-muted/50'
}`}
onClick={() => {
setSelectedWallet(wallet);
updateWalletStats(wallet);
}}
>
<div className="flex items-center justify-between mb-2">
<h4 className="font-semibold">{wallet.agentId}</h4>
<Badge variant={wallet.isActive ? "default" : "secondary"}>
{wallet.isActive ? "Active" : "Inactive"}
</Badge>
</div>
<div className="space-y-1 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">Balance:</span>
<span className="font-medium">{wallet.balance} AITBC</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Spent:</span>
<span className="font-medium">{wallet.spentAmount} AITBC</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Transactions:</span>
<span className="font-medium">{wallet.transactionCount}</span>
</div>
</div>
<div className="flex items-center space-x-2 mt-2">
{wallet.microTransactionEnabled && (
<Badge variant="secondary" className="text-xs">
<DollarSign className="w-3 h-3 mr-1" />
Micro-transactions
</Badge>
)}
</div>
</div>
))}
</CardContent>
</Card>
</div>
{/* Wallet Details */}
<div className="lg:col-span-2">
{selectedWallet ? (
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="transactions">Transactions</TabsTrigger>
<TabsTrigger value="manage">Manage</TabsTrigger>
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-6">
{/* Wallet Stats */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<DollarSign className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Balance</span>
</div>
<div className="text-2xl font-bold">{walletStats?.balance} AITBC</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<TrendingUp className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Total Allowance</span>
</div>
<div className="text-2xl font-bold">{walletStats?.totalAllowance} AITBC</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<ArrowUpRight className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Spent</span>
</div>
<div className="text-2xl font-bold">{walletStats?.spentAmount} AITBC</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Activity className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Transactions</span>
</div>
<div className="text-2xl font-bold">{walletStats?.transactionCount}</div>
</CardContent>
</Card>
</div>
{/* Utilization */}
<Card>
<CardHeader>
<CardTitle>Allowance Utilization</CardTitle>
<CardDescription>
Current spending vs. total allowance
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="flex justify-between text-sm">
<span>Spent: {walletStats?.spentAmount} AITBC</span>
<span>Remaining: {walletStats?.remainingAllowance} AITBC</span>
</div>
<Progress value={walletStats?.utilizationRate || 0} className="w-full" />
<div className="text-center text-sm text-muted-foreground">
{walletStats?.utilizationRate?.toFixed(1)}% utilized
</div>
</div>
</CardContent>
</Card>
{/* Recent Activity */}
<Card>
<CardHeader>
<CardTitle>Recent Activity</CardTitle>
<CardDescription>
Latest transactions and wallet events
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{transactions.slice(0, 5).map((tx) => (
<div key={tx.id} className="flex items-center justify-between p-3 border rounded-lg">
<div className="flex items-center space-x-3">
<div className={`w-2 h-2 rounded-full ${
tx.status === 'completed' ? 'bg-green-500' :
tx.status === 'pending' ? 'bg-yellow-500' : 'bg-red-500'
}`}></div>
<div>
<p className="font-medium">{tx.purpose}</p>
<p className="text-sm text-muted-foreground">
To: {tx.recipient.slice(0, 8)}...{tx.recipient.slice(-6)}
</p>
</div>
</div>
<div className="text-right">
<p className="font-medium">{tx.amount} AITBC</p>
<p className="text-xs text-muted-foreground">
{new Date(tx.timestamp).toLocaleDateString()}
</p>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="transactions" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Transaction History</CardTitle>
<CardDescription>
All transactions for this agent wallet
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{transactions.map((tx) => (
<div key={tx.id} className="flex items-center justify-between p-4 border rounded-lg">
<div className="flex items-center space-x-4">
<div className={`w-3 h-3 rounded-full ${
tx.status === 'completed' ? 'bg-green-500' :
tx.status === 'pending' ? 'bg-yellow-500' : 'bg-red-500'
}`}></div>
<div>
<p className="font-medium">{tx.purpose}</p>
<p className="text-sm text-muted-foreground">
{tx.isMicroTransaction ? 'Micro-transaction' : 'Standard transaction'}
</p>
<p className="text-xs text-muted-foreground">
To: {tx.recipient}
</p>
</div>
</div>
<div className="text-right">
<p className="font-medium">{tx.amount} AITBC</p>
<p className="text-sm text-muted-foreground">
{new Date(tx.timestamp).toLocaleString()}
</p>
<Badge variant={tx.status === 'completed' ? "default" : "secondary"}>
{tx.status}
</Badge>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="manage" className="space-y-6">
{/* Grant Allowance */}
<Card>
<CardHeader>
<CardTitle>Grant Allowance</CardTitle>
<CardDescription>
Add funds to the agent's allowance
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium">Amount (AITBC)</label>
<Input
type="number"
placeholder="Enter amount"
value={allowanceAmount}
onChange={(e) => setAllowanceAmount(e.target.value)}
/>
</div>
<Button onClick={handleGrantAllowance} className="w-full">
<ArrowDownLeft className="w-4 h-4 mr-2" />
Grant Allowance
</Button>
</CardContent>
</Card>
{/* Execute Transaction */}
<Card>
<CardHeader>
<CardTitle>Execute Transaction</CardTitle>
<CardDescription>
Send funds from agent wallet
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium">Recipient</label>
<Input
placeholder="Enter recipient address"
value={recipient}
onChange={(e) => setRecipient(e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Amount (AITBC)</label>
<Input
type="number"
placeholder="Enter amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Purpose</label>
<Input
placeholder="Enter transaction purpose"
value={purpose}
onChange={(e) => setPurpose(e.target.value)}
/>
</div>
<Button onClick={handleExecuteTransaction} className="w-full">
<Send className="w-4 h-4 mr-2" />
Execute Transaction
</Button>
</CardContent>
</Card>
{/* Update Spending Limit */}
<Card>
<CardHeader>
<CardTitle>Update Spending Limit</CardTitle>
<CardDescription>
Set maximum spending limit per period
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<label className="text-sm font-medium">New Limit (AITBC)</label>
<Input
type="number"
placeholder="Enter spending limit"
value={spendingLimit}
onChange={(e) => setSpendingLimit(e.target.value)}
/>
</div>
<Button onClick={handleUpdateSpendingLimit} className="w-full">
<Settings className="w-4 h-4 mr-2" />
Update Limit
</Button>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="settings" className="space-y-6">
{/* Wallet Settings */}
<Card>
<CardHeader>
<CardTitle>Wallet Settings</CardTitle>
<CardDescription>
Configure agent wallet behavior
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="font-medium">Micro-transactions</p>
<p className="text-sm text-muted-foreground">
Enable transactions below 0.001 AITBC
</p>
</div>
<Button
variant={selectedWallet.microTransactionEnabled ? "default" : "outline"}
onClick={handleToggleMicroTransactions}
>
{selectedWallet.microTransactionEnabled ? "Enabled" : "Disabled"}
</Button>
</div>
<Separator />
<div className="space-y-2">
<p className="font-medium">Wallet Information</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm">
<div>
<span className="text-muted-foreground">Agent ID:</span>
<p className="font-mono">{selectedWallet.agentId}</p>
</div>
<div>
<span className="text-muted-foreground">Owner:</span>
<p className="font-mono">{selectedWallet.owner.slice(0, 8)}...{selectedWallet.owner.slice(-6)}</p>
</div>
<div>
<span className="text-muted-foreground">Created:</span>
<p>{new Date(selectedWallet.createdAt).toLocaleDateString()}</p>
</div>
<div>
<span className="text-muted-foreground">Last Activity:</span>
<p>{new Date(selectedWallet.lastActivity).toLocaleDateString()}</p>
</div>
</div>
</div>
</CardContent>
</Card>
{/* Security */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Shield className="w-5 h-5" />
<span>Security</span>
</CardTitle>
<CardDescription>
Security settings and permissions
</CardDescription>
</CardHeader>
<CardContent>
<Alert>
<CheckCircle className="h-4 w-4" />
<AlertTitle>Wallet Secured</AlertTitle>
<AlertDescription>
This agent wallet is protected by smart contract security measures and spending limits.
</AlertDescription>
</Alert>
</CardContent>
</Card>
</TabsContent>
</Tabs>
) : (
<Card>
<CardContent className="pt-6">
<div className="text-center py-8">
<Wallet className="w-12 h-12 text-muted-foreground mx-auto mb-4" />
<p className="text-muted-foreground">Select an agent wallet to manage</p>
</div>
</CardContent>
</Card>
)}
</div>
</div>
</div>
);
};
export default AgentWallet;

View File

@@ -0,0 +1,984 @@
import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './ui/card';
import { Button } from './ui/button';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
import { Alert, AlertDescription, AlertTitle } from './ui/alert';
import { Progress } from './ui/progress';
import { Separator } from './ui/separator';
import {
TrendingUp,
Brain,
Clock,
DollarSign,
Activity,
Zap,
Shield,
AlertTriangle,
CheckCircle,
BarChart3,
Settings,
Target,
Timer,
Coins
} from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useWallet } from '@/hooks/use-wallet';
interface BidStrategy {
id: string;
name: string;
description: string;
confidenceScore: number;
successProbability: number;
expectedWaitTime: number;
bidPrice: number;
costEfficiency: number;
reasoning: string[];
marketConditions: {
demandLevel: number;
priceVolatility: number;
averagePrice: number;
};
}
interface MarketAnalysis {
currentConditions: {
demandLevel: number;
priceVolatility: number;
averageHourlyPrice: number;
gpuUtilizationRate: number;
};
priceTrend: string;
demandTrend: string;
volatilityTrend: string;
futurePrediction: {
demandLevel: number;
averageHourlyPrice: number;
};
recommendations: string[];
}
interface AgentPreferences {
preferredStrategy: string;
riskTolerance: number;
costSensitivity: number;
urgencyPreference: number;
maxWaitTime: number;
minSuccessProbability: number;
}
const BidStrategy: React.FC = () => {
const { toast } = useToast();
const { isConnected, address } = useWallet();
const [strategies, setStrategies] = useState<BidStrategy[]>([]);
const [selectedStrategy, setSelectedStrategy] = useState<BidStrategy | null>(null);
const [marketAnalysis, setMarketAnalysis] = useState<MarketAnalysis | null>(null);
const [agentPreferences, setAgentPreferences] = useState<AgentPreferences>({
preferredStrategy: 'balanced',
riskTolerance: 0.5,
costSensitivity: 0.5,
urgencyPreference: 0.5,
maxWaitTime: 3600,
minSuccessProbability: 0.7
});
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState('strategies');
// Form states
const [taskUrgency, setTaskUrgency] = useState('medium');
const [taskDuration, setTaskDuration] = useState('1');
const [gpuTier, setGpuTier] = useState('mid_range');
const [maxBudget, setMaxBudget] = useState('0.1');
const [customStrategy, setCustomStrategy] = useState('');
// Mock data for demonstration
const mockStrategies: BidStrategy[] = [
{
id: 'urgent_bid',
name: 'Urgent Bid',
description: 'Aggressive bidding for time-critical tasks',
confidenceScore: 0.85,
successProbability: 0.92,
expectedWaitTime: 120, // seconds
bidPrice: 0.08,
costEfficiency: 0.65,
reasoning: [
'High market demand increases bid price',
'Critical urgency requires aggressive bidding',
'Market conditions require price premium',
'High risk premium applied due to strategy'
],
marketConditions: {
demandLevel: 0.75,
priceVolatility: 0.12,
averagePrice: 0.05
}
},
{
id: 'cost_optimized',
name: 'Cost Optimized',
description: 'Minimize cost while maintaining reasonable success probability',
confidenceScore: 0.78,
successProbability: 0.68,
expectedWaitTime: 480,
bidPrice: 0.03,
costEfficiency: 0.92,
reasoning: [
'Low market demand allows for competitive pricing',
'Cost optimization prioritized over speed',
'Favorable market conditions enable discount pricing',
'Budget constraints drive conservative bidding'
],
marketConditions: {
demandLevel: 0.45,
priceVolatility: 0.08,
averagePrice: 0.05
}
},
{
id: 'balanced',
name: 'Balanced',
description: 'Optimal balance between cost and performance',
confidenceScore: 0.88,
successProbability: 0.82,
expectedWaitTime: 240,
bidPrice: 0.05,
costEfficiency: 0.78,
reasoning: [
'Balanced approach selected based on task requirements',
'Market conditions support standard pricing',
'Moderate urgency allows for balanced bidding',
'Risk premium adjusted for market stability'
],
marketConditions: {
demandLevel: 0.60,
priceVolatility: 0.10,
averagePrice: 0.05
}
},
{
id: 'aggressive',
name: 'Aggressive',
description: 'High-risk, high-reward bidding strategy',
confidenceScore: 0.72,
successProbability: 0.88,
expectedWaitTime: 90,
bidPrice: 0.10,
costEfficiency: 0.55,
reasoning: [
'High demand detected - consider urgent bidding strategy',
'Aggressive approach for maximum success probability',
'Market volatility allows for premium pricing',
'High risk premium applied due to strategy'
],
marketConditions: {
demandLevel: 0.85,
priceVolatility: 0.18,
averagePrice: 0.05
}
},
{
id: 'conservative',
name: 'Conservative',
description: 'Low-risk bidding with focus on reliability',
confidenceScore: 0.91,
successProbability: 0.58,
expectedWaitTime: 600,
bidPrice: 0.025,
costEfficiency: 0.85,
reasoning: [
'High volatility - consider conservative bidding',
'Low risk tolerance drives conservative approach',
'Market uncertainty requires price caution',
'Reliability prioritized over speed'
],
marketConditions: {
demandLevel: 0.35,
priceVolatility: 0.22,
averagePrice: 0.05
}
}
];
const mockMarketAnalysis: MarketAnalysis = {
currentConditions: {
demandLevel: 0.68,
priceVolatility: 0.12,
averageHourlyPrice: 0.05,
gpuUtilizationRate: 0.75
},
priceTrend: 'stable',
demandTrend: 'increasing',
volatilityTrend: 'stable',
futurePrediction: {
demandLevel: 0.72,
averageHourlyPrice: 0.052
},
recommendations: [
'High demand detected - consider urgent bidding strategy',
'GPU utilization very high - expect longer wait times',
'Low prices - good opportunity for cost optimization'
]
};
useEffect(() => {
// Load mock data
setTimeout(() => {
setStrategies(mockStrategies);
setMarketAnalysis(mockMarketAnalysis);
setLoading(false);
}, 1000);
}, []);
const handleCalculateBid = async () => {
if (!isConnected) {
toast({
title: "Wallet Not Connected",
description: "Please connect your wallet to calculate bids",
variant: "destructive"
});
return;
}
if (!taskDuration || !maxBudget) {
toast({
title: "Missing Information",
description: "Please fill in all task details",
variant: "destructive"
});
return;
}
try {
setLoading(true);
// Simulate bid calculation
toast({
title: "Calculating Bid Strategy",
description: "Analyzing market conditions and optimizing bid...",
variant: "default"
});
// Simulate calculation delay
await new Promise(resolve => setTimeout(resolve, 2000));
// Select best strategy based on preferences
const bestStrategy = strategies.find(s => s.id === agentPreferences.preferredStrategy) ||
strategies.reduce((best, current) =>
current.costEfficiency > best.costEfficiency ? current : best
);
setSelectedStrategy(bestStrategy);
setActiveTab('strategies');
toast({
title: "Bid Strategy Calculated",
description: `Optimal strategy: ${bestStrategy.name}`,
variant: "default"
});
} catch (error) {
toast({
title: "Calculation Failed",
description: "There was an error calculating the bid strategy",
variant: "destructive"
});
} finally {
setLoading(false);
}
};
const handleUpdatePreferences = async () => {
if (!isConnected) {
toast({
title: "Wallet Not Connected",
description: "Please connect your wallet to update preferences",
variant: "destructive"
});
return;
}
try {
toast({
title: "Updating Preferences",
description: "Saving your agent bidding preferences...",
variant: "default"
});
// Simulate update
await new Promise(resolve => setTimeout(resolve, 1000));
toast({
title: "Preferences Updated",
description: "Your bidding preferences have been saved",
variant: "default"
});
} catch (error) {
toast({
title: "Update Failed",
description: "There was an error updating preferences",
variant: "destructive"
});
}
};
const getStrategyColor = (strategy: BidStrategy) => {
if (strategy.successProbability > 0.8) return 'bg-green-500';
if (strategy.successProbability > 0.6) return 'bg-yellow-500';
return 'bg-red-500';
};
const getTrendIcon = (trend: string) => {
switch (trend) {
case 'increasing': return <TrendingUp className="w-4 h-4 text-green-500" />;
case 'decreasing': return <TrendingUp className="w-4 h-4 text-red-500 rotate-180" />;
default: return <Activity className="w-4 h-4 text-blue-500" />;
}
};
if (loading) {
return (
<div className="container mx-auto p-6 space-y-6">
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Loading bid strategies...</p>
</div>
</div>
);
}
return (
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Bid Strategy Engine</h1>
<p className="text-muted-foreground mt-2">
Intelligent bidding algorithms for optimal GPU rental negotiations
</p>
</div>
<div className="flex items-center space-x-2">
<Badge variant="outline" className="flex items-center space-x-1">
<Brain className="w-4 h-4" />
<span>{strategies.length} Strategies</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<Activity className="w-4 h-4" />
<span>Market Active</span>
</Badge>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-4">
<TabsTrigger value="strategies">Strategies</TabsTrigger>
<TabsTrigger value="market">Market Analysis</TabsTrigger>
<TabsTrigger value="calculate">Calculate Bid</TabsTrigger>
<TabsTrigger value="preferences">Preferences</TabsTrigger>
</TabsList>
<TabsContent value="strategies" className="space-y-6">
{/* Selected Strategy Details */}
{selectedStrategy && (
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Target className="w-5 h-5" />
<span>Selected Strategy: {selectedStrategy.name}</span>
</CardTitle>
<CardDescription>{selectedStrategy.description}</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Strategy Metrics */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<DollarSign className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Bid Price</span>
</div>
<div className="text-2xl font-bold">{selectedStrategy.bidPrice} AITBC</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<CheckCircle className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Success Rate</span>
</div>
<div className="text-2xl font-bold">{(selectedStrategy.successProbability * 100).toFixed(1)}%</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Timer className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Wait Time</span>
</div>
<div className="text-2xl font-bold">{Math.floor(selectedStrategy.expectedWaitTime / 60)}m</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center space-x-2">
<Coins className="w-4 h-4 text-muted-foreground" />
<span className="text-sm font-medium">Efficiency</span>
</div>
<div className="text-2xl font-bold">{(selectedStrategy.costEfficiency * 100).toFixed(1)}%</div>
</CardContent>
</Card>
</div>
{/* Reasoning */}
<Card>
<CardHeader>
<CardTitle>Strategy Reasoning</CardTitle>
<CardDescription>
Why this strategy was selected
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
{selectedStrategy.reasoning.map((reason, index) => (
<div key={index} className="flex items-start space-x-2">
<div className={`w-2 h-2 rounded-full mt-2 ${getStrategyColor(selectedStrategy)}`}></div>
<p className="text-sm">{reason}</p>
</div>
))}
</div>
</CardContent>
</Card>
{/* Market Conditions */}
<Card>
<CardHeader>
<CardTitle>Market Conditions</CardTitle>
<CardDescription>
Current market analysis
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<p className="text-sm text-muted-foreground">Demand Level</p>
<div className="flex items-center space-x-2">
<Progress value={selectedStrategy.marketConditions.demandLevel * 100} className="flex-1" />
<span className="text-sm font-medium">{(selectedStrategy.marketConditions.demandLevel * 100).toFixed(0)}%</span>
</div>
</div>
<div>
<p className="text-sm text-muted-foreground">Price Volatility</p>
<div className="flex items-center space-x-2">
<Progress value={selectedStrategy.marketConditions.priceVolatility * 100} className="flex-1" />
<span className="text-sm font-medium">{(selectedStrategy.marketConditions.priceVolatility * 100).toFixed(0)}%</span>
</div>
</div>
<div>
<p className="text-sm text-muted-foreground">Avg Price</p>
<p className="text-lg font-bold">{selectedStrategy.marketConditions.averagePrice} AITBC</p>
</div>
</div>
</CardContent>
</Card>
</CardContent>
</Card>
)}
{/* All Strategies */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{strategies.map((strategy) => (
<Card
key={strategy.id}
className={`cursor-pointer transition-all hover:shadow-lg ${
selectedStrategy?.id === strategy.id ? 'ring-2 ring-primary' : ''
}`}
onClick={() => setSelectedStrategy(strategy)}
>
<CardHeader>
<div className="flex items-start justify-between">
<div className="flex-1">
<CardTitle className="text-lg">{strategy.name}</CardTitle>
<CardDescription className="mt-1">{strategy.description}</CardDescription>
</div>
<div className={`w-3 h-3 rounded-full ${getStrategyColor(strategy)}`}></div>
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<span className="text-muted-foreground">Price:</span>
<p className="font-medium">{strategy.bidPrice} AITBC</p>
</div>
<div>
<span className="text-muted-foreground">Success:</span>
<p className="font-medium">{(strategy.successProbability * 100).toFixed(1)}%</p>
</div>
<div>
<span className="text-muted-foreground">Wait:</span>
<p className="font-medium">{Math.floor(strategy.expectedWaitTime / 60)}m</p>
</div>
<div>
<span className="text-muted-foreground">Efficiency:</span>
<p className="font-medium">{(strategy.costEfficiency * 100).toFixed(1)}%</p>
</div>
</div>
<div className="flex items-center space-x-2">
<div className="flex-1">
<div className="flex items-center space-x-1">
<span className="text-xs text-muted-foreground">Confidence:</span>
<Progress value={strategy.confidenceScore * 100} className="flex-1 h-2" />
</div>
</div>
<span className="text-xs font-medium">{(strategy.confidenceScore * 100).toFixed(0)}%</span>
</div>
</CardContent>
</Card>
))}
</div>
</TabsContent>
<TabsContent value="market" className="space-y-6">
{/* Current Market Conditions */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<BarChart3 className="w-5 h-5" />
<span>Current Market Conditions</span>
</CardTitle>
<CardDescription>
Real-time market analysis and trends
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div>
<p className="text-sm text-muted-foreground">Demand Level</p>
<div className="flex items-center space-x-2 mt-1">
<Progress value={marketAnalysis!.currentConditions.demandLevel * 100} className="flex-1" />
<span className="text-sm font-medium">{(marketAnalysis!.currentConditions.demandLevel * 100).toFixed(0)}%</span>
</div>
</div>
<div>
<p className="text-sm text-muted-foreground">Price Volatility</p>
<div className="flex items-center space-x-2 mt-1">
<Progress value={marketAnalysis!.currentConditions.priceVolatility * 100} className="flex-1" />
<span className="text-sm font-medium">{(marketAnalysis!.currentConditions.priceVolatility * 100).toFixed(0)}%</span>
</div>
</div>
<div>
<p className="text-sm text-muted-foreground">Avg Hourly Price</p>
<p className="text-lg font-bold mt-1">{marketAnalysis!.currentConditions.averageHourlyPrice} AITBC</p>
</div>
<div>
<p className="text-sm text-muted-foreground">GPU Utilization</p>
<div className="flex items-center space-x-2 mt-1">
<Progress value={marketAnalysis!.currentConditions.gpuUtilizationRate * 100} className="flex-1" />
<span className="text-sm font-medium">{(marketAnalysis!.currentConditions.gpuUtilizationRate * 100).toFixed(0)}%</span>
</div>
</div>
</div>
</CardContent>
</Card>
{/* Market Trends */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
{getTrendIcon(marketAnalysis!.priceTrend)}
<span>Price Trend</span>
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-2xl font-bold capitalize">{marketAnalysis!.priceTrend}</p>
<p className="text-sm text-muted-foreground mt-1">
Based on 24-hour analysis
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
{getTrendIcon(marketAnalysis!.demandTrend)}
<span>Demand Trend</span>
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-2xl font-bold capitalize">{marketAnalysis!.demandTrend}</p>
<p className="text-sm text-muted-foreground mt-1">
Based on recent activity
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
{getTrendIcon(marketAnalysis!.volatilityTrend)}
<span>Volatility Trend</span>
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-2xl font-bold capitalize">{marketAnalysis!.volatilityTrend}</p>
<p className="text-sm text-muted-foreground mt-1">
Market stability indicator
</p>
</CardContent>
</Card>
</div>
{/* Future Prediction */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Zap className="w-5 h-5" />
<span>24-Hour Prediction</span>
</CardTitle>
<CardDescription>
AI-powered market forecast
</CardDescription>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<p className="text-sm text-muted-foreground">Predicted Demand</p>
<div className="flex items-center space-x-2 mt-1">
<Progress value={marketAnalysis!.futurePrediction.demandLevel * 100} className="flex-1" />
<span className="text-sm font-medium">{(marketAnalysis!.futurePrediction.demandLevel * 100).toFixed(0)}%</span>
</div>
</div>
<div>
<p className="text-sm text-muted-foreground">Predicted Price</p>
<p className="text-lg font-bold mt-1">{marketAnalysis!.futurePrediction.averageHourlyPrice} AITBC</p>
</div>
</div>
</CardContent>
</Card>
{/* Recommendations */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<AlertTriangle className="w-5 h-5" />
<span>Market Recommendations</span>
</CardTitle>
<CardDescription>
AI-generated recommendations based on current conditions
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-3">
{marketAnalysis!.recommendations.map((recommendation, index) => (
<div key={index} className="flex items-start space-x-2">
<div className="w-2 h-2 rounded-full bg-blue-500 mt-2"></div>
<p className="text-sm">{recommendation}</p>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="calculate" className="space-y-6">
{/* Task Details */}
<Card>
<CardHeader>
<CardTitle>Task Details</CardTitle>
<CardDescription>
Enter task requirements to calculate optimal bid strategy
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<label className="text-sm font-medium">Task Urgency</label>
<Select value={taskUrgency} onValueChange={setTaskUrgency}>
<SelectTrigger>
<SelectValue placeholder="Select urgency" />
</SelectTrigger>
<SelectContent>
<SelectItem value="low">Low</SelectItem>
<SelectItem value="medium">Medium</SelectItem>
<SelectItem value="high">High</SelectItem>
<SelectItem value="critical">Critical</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">GPU Tier</label>
<Select value={gpuTier} onValueChange={setGpuTier}>
<SelectTrigger>
<SelectValue placeholder="Select GPU tier" />
</SelectTrigger>
<SelectContent>
<SelectItem value="cpu_only">CPU Only</SelectItem>
<SelectItem value="low_end_gpu">Low-end GPU</SelectItem>
<SelectItem value="mid_range">Mid-range GPU</SelectItem>
<SelectItem value="high_end_gpu">High-end GPU</SelectItem>
<SelectItem value="premium_gpu">Premium GPU</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Duration (hours)</label>
<Input
type="number"
placeholder="Enter duration"
value={taskDuration}
onChange={(e) => setTaskDuration(e.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-sm font-medium">Maximum Budget (AITBC)</label>
<Input
type="number"
placeholder="Enter maximum budget"
value={maxBudget}
onChange={(e) => setMaxBudget(e.target.value)}
/>
</div>
</div>
<Button onClick={handleCalculateBid} className="w-full" disabled={loading}>
{loading ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
Calculating...
</>
) : (
<>
<Brain className="w-4 h-4 mr-2" />
Calculate Optimal Bid
</>
)}
</Button>
</CardContent>
</Card>
{/* Results */}
{selectedStrategy && (
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<CheckCircle className="w-5 h-5 text-green-500" />
<span>Optimal Strategy Found</span>
</CardTitle>
<CardDescription>
Recommended bid strategy for your task
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center justify-between p-4 border rounded-lg">
<div>
<h4 className="font-semibold">{selectedStrategy.name}</h4>
<p className="text-sm text-muted-foreground">{selectedStrategy.description}</p>
</div>
<div className="text-right">
<p className="text-2xl font-bold">{selectedStrategy.bidPrice} AITBC</p>
<p className="text-sm text-muted-foreground">Bid Price</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="text-center">
<p className="text-sm text-muted-foreground">Success Probability</p>
<p className="text-lg font-bold">{(selectedStrategy.successProbability * 100).toFixed(1)}%</p>
</div>
<div className="text-center">
<p className="text-sm text-muted-foreground">Expected Wait</p>
<p className="text-lg font-bold">{Math.floor(selectedStrategy.expectedWaitTime / 60)}m</p>
</div>
<div className="text-center">
<p className="text-sm text-muted-foreground">Cost Efficiency</p>
<p className="text-lg font-bold">{(selectedStrategy.costEfficiency * 100).toFixed(1)}%</p>
</div>
</div>
</div>
</CardContent>
</Card>
)}
</TabsContent>
<TabsContent value="preferences" className="space-y-6">
{/* Agent Preferences */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Settings className="w-5 h-5" />
<span>Agent Bidding Preferences</span>
</CardTitle>
<CardDescription>
Configure your agent's bidding behavior and risk tolerance
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-4">
<div>
<label className="text-sm font-medium">Preferred Strategy</label>
<Select value={agentPreferences.preferredStrategy} onValueChange={(value) =>
setAgentPreferences(prev => ({ ...prev, preferredStrategy: value }))
}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="urgent_bid">Urgent Bid</SelectItem>
<SelectItem value="cost_optimized">Cost Optimized</SelectItem>
<SelectItem value="balanced">Balanced</SelectItem>
<SelectItem value="aggressive">Aggressive</SelectItem>
<SelectItem value="conservative">Conservative</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<label className="text-sm font-medium">Risk Tolerance</label>
<div className="space-y-2">
<Input
type="range"
min="0"
max="1"
step="0.1"
value={agentPreferences.riskTolerance}
onChange={(e) => setAgentPreferences(prev => ({ ...prev, riskTolerance: parseFloat(e.target.value) }))}
/>
<div className="flex justify-between text-xs text-muted-foreground">
<span>Conservative</span>
<span>{(agentPreferences.riskTolerance * 100).toFixed(0)}%</span>
<span>Aggressive</span>
</div>
</div>
</div>
</div>
<div className="space-y-4">
<div>
<label className="text-sm font-medium">Cost Sensitivity</label>
<div className="space-y-2">
<Input
type="range"
min="0"
max="1"
step="0.1"
value={agentPreferences.costSensitivity}
onChange={(e) => setAgentPreferences(prev => ({ ...prev, costSensitivity: parseFloat(e.target.value) }))}
/>
<div className="flex justify-between text-xs text-muted-foreground">
<span>Performance</span>
<span>{(agentPreferences.costSensitivity * 100).toFixed(0)}%</span>
<span>Cost</span>
</div>
</div>
</div>
<div>
<label className="text-sm font-medium">Urgency Preference</label>
<div className="space-y-2">
<Input
type="range"
min="0"
max="1"
step="0.1"
value={agentPreferences.urgencyPreference}
onChange={(e) => setAgentPreferences(prev => ({ ...prev, urgencyPreference: parseFloat(e.target.value) }))}
/>
<div className="flex justify-between text-xs text-muted-foreground">
<span>Relaxed</span>
<span>{(agentPreferences.urgencyPreference * 100).toFixed(0)}%</span>
<span>Urgent</span>
</div>
</div>
</div>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label className="text-sm font-medium">Maximum Wait Time (seconds)</label>
<Input
type="number"
value={agentPreferences.maxWaitTime}
onChange={(e) => setAgentPreferences(prev => ({ ...prev, maxWaitTime: parseInt(e.target.value) }))}
/>
</div>
<div>
<label className="text-sm font-medium">Minimum Success Probability</label>
<div className="space-y-2">
<Input
type="range"
min="0"
max="1"
step="0.05"
value={agentPreferences.minSuccessProbability}
onChange={(e) => setAgentPreferences(prev => ({ ...prev, minSuccessProbability: parseFloat(e.target.value) }))}
/>
<div className="text-center text-sm text-muted-foreground">
{(agentPreferences.minSuccessProbability * 100).toFixed(0)}%
</div>
</div>
</div>
</div>
<Button onClick={handleUpdatePreferences} className="w-full">
<Settings className="w-4 h-4 mr-2" />
Save Preferences
</Button>
</CardContent>
</Card>
{/* Strategy Preview */}
<Card>
<CardHeader>
<CardTitle>Strategy Impact Preview</CardTitle>
<CardDescription>
How your preferences affect bidding behavior
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<Alert>
<Shield className="h-4 w-4" />
<AlertTitle>Risk Management</AlertTitle>
<AlertDescription>
Your risk tolerance of {(agentPreferences.riskTolerance * 100).toFixed(0)}% will favor
{agentPreferences.riskTolerance > 0.6 ? ' aggressive bidding with higher success rates' : ' conservative bidding with better cost efficiency'}.
</AlertDescription>
</Alert>
<Alert>
<DollarSign className="h-4 w-4" />
<AlertTitle>Cost Optimization</AlertTitle>
<AlertDescription>
Cost sensitivity of {(agentPreferences.costSensitivity * 100).toFixed(0)}% will prioritize
{agentPreferences.costSensitivity > 0.6 ? ' lower prices over faster execution' : ' faster execution over cost savings'}.
</AlertDescription>
</Alert>
<Alert>
<Timer className="h-4 w-4" />
<AlertTitle>Time Efficiency</AlertTitle>
<AlertDescription>
Urgency preference of {(agentPreferences.urgencyPreference * 100).toFixed(0)}% will focus on
{agentPreferences.urgencyPreference > 0.6 ? ' minimizing wait times' : ' optimizing for cost and success rate'}.
</AlertDescription>
</Alert>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
);
};
export default BidStrategy;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,490 @@
import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './ui/card';
import { Button } from './ui/button';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
import { Alert, AlertDescription, AlertTitle } from './ui/alert';
import { Search, Filter, ShoppingCart, Star, TrendingUp, Clock, CheckCircle, XCircle } from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useWallet } from '@/hooks/use-wallet';
interface KnowledgeGraph {
graphId: string;
cid: string;
creator: string;
price: number;
tags: string[];
qualityScore: number;
accessCount: number;
totalRevenue: number;
royaltyRate: number;
isActive: boolean;
createdAt: string;
description: string;
metadata: string;
}
interface PurchaseRecord {
graphId: string;
buyer: string;
purchasedAt: string;
expiresAt: string;
decryptionKey: string;
isActive: boolean;
}
const KnowledgeMarketplace: React.FC = () => {
const { toast } = useToast();
const { isConnected, address } = useWallet();
const [graphs, setGraphs] = useState<KnowledgeGraph[]>([]);
const [filteredGraphs, setFilteredGraphs] = useState<KnowledgeGraph[]>([]);
const [searchQuery, setSearchQuery] = useState('');
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const [priceRange, setPriceRange] = useState({ min: 0, max: 1000 });
const [sortBy, setSortBy] = useState('quality');
const [loading, setLoading] = useState(true);
const [purchasedGraphs, setPurchasedGraphs] = useState<PurchaseRecord[]>([]);
const [activeTab, setActiveTab] = useState('browse');
// Mock data for demonstration
const mockGraphs: KnowledgeGraph[] = [
{
graphId: 'graph_001',
cid: 'QmXxx...123',
creator: '0x1234...5678',
price: 100,
tags: ['nlp', 'transformer', 'language'],
qualityScore: 950,
accessCount: 156,
totalRevenue: 15600,
royaltyRate: 500,
isActive: true,
createdAt: '2024-01-15T10:30:00Z',
description: 'Advanced NLP knowledge graph with transformer architectures',
metadata: '{"nodes": 1250, "edges": 3400, "domains": ["nlp", "ai"]}'
},
{
graphId: 'graph_002',
cid: 'QmYyy...456',
creator: '0xabcd...efgh',
price: 250,
tags: ['computer-vision', 'cnn', 'image'],
qualityScore: 890,
accessCount: 89,
totalRevenue: 22250,
royaltyRate: 300,
isActive: true,
createdAt: '2024-01-20T14:15:00Z',
description: 'Computer vision knowledge graph with CNN architectures',
metadata: '{"nodes": 890, "edges": 2100, "domains": ["vision", "ml"]}'
},
{
graphId: 'graph_003',
cid: 'QmZzz...789',
creator: '0x5678...9abc',
price: 75,
tags: ['reinforcement-learning', 'rl', 'gaming'],
qualityScore: 920,
accessCount: 234,
totalRevenue: 17550,
royaltyRate: 400,
isActive: true,
createdAt: '2024-01-25T09:45:00Z',
description: 'Reinforcement learning knowledge graph for gaming AI',
metadata: '{"nodes": 670, "edges": 1890, "domains": ["rl", "gaming"]}'
}
];
useEffect(() => {
// Load mock data
setTimeout(() => {
setGraphs(mockGraphs);
setFilteredGraphs(mockGraphs);
setLoading(false);
}, 1000);
}, []);
useEffect(() => {
filterAndSortGraphs();
}, [searchQuery, selectedTags, priceRange, sortBy, graphs]);
const filterAndSortGraphs = () => {
let filtered = graphs.filter(graph => {
// Search query filter
if (searchQuery) {
const query = searchQuery.toLowerCase();
const matchesSearch =
graph.description.toLowerCase().includes(query) ||
graph.tags.some(tag => tag.toLowerCase().includes(query)) ||
graph.creator.toLowerCase().includes(query);
if (!matchesSearch) return false;
}
// Tags filter
if (selectedTags.length > 0) {
const hasSelectedTag = selectedTags.some(tag => graph.tags.includes(tag));
if (!hasSelectedTag) return false;
}
// Price range filter
if (graph.price < priceRange.min || graph.price > priceRange.max) {
return false;
}
return true;
});
// Sort
filtered.sort((a, b) => {
switch (sortBy) {
case 'quality':
return b.qualityScore - a.qualityScore;
case 'price_low':
return a.price - b.price;
case 'price_high':
return b.price - a.price;
case 'popularity':
return b.accessCount - a.accessCount;
case 'newest':
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
default:
return 0;
}
});
setFilteredGraphs(filtered);
};
const handlePurchase = async (graph: KnowledgeGraph) => {
if (!isConnected) {
toast({
title: "Wallet Not Connected",
description: "Please connect your wallet to purchase knowledge graphs",
variant: "destructive"
});
return;
}
try {
// Simulate purchase process
const purchaseRecord: PurchaseRecord = {
graphId: graph.graphId,
buyer: address || '',
purchasedAt: new Date().toISOString(),
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), // 30 days
decryptionKey: `key_${Math.random().toString(36).substr(2, 9)}`,
isActive: true
};
setPurchasedGraphs([...purchasedGraphs, purchaseRecord]);
toast({
title: "Purchase Successful!",
description: `You now have access to "${graph.description}"`,
variant: "default"
});
} catch (error) {
toast({
title: "Purchase Failed",
description: "There was an error processing your purchase",
variant: "destructive"
});
}
};
const hasPurchased = (graphId: string) => {
return purchasedGraphs.some(record =>
record.graphId === graphId &&
record.isActive &&
new Date(record.expiresAt) > new Date()
);
};
const getQualityColor = (score: number) => {
if (score >= 900) return 'bg-green-500';
if (score >= 700) return 'bg-yellow-500';
return 'bg-red-500';
};
const allTags = Array.from(new Set(graphs.flatMap(g => g.tags)));
return (
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Knowledge Graph Marketplace</h1>
<p className="text-muted-foreground mt-2">
Discover and purchase high-quality knowledge graphs to enhance your AI agents
</p>
</div>
<div className="flex items-center space-x-2">
<Badge variant="outline" className="flex items-center space-x-1">
<Star className="w-4 h-4" />
<span>{graphs.length} Graphs Available</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<TrendingUp className="w-4 h-4" />
<span>{graphs.reduce((sum, g) => sum + g.accessCount, 0)} Total Accesses</span>
</Badge>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="browse">Browse Graphs</TabsTrigger>
<TabsTrigger value="purchased">My Purchases</TabsTrigger>
<TabsTrigger value="create">Create Graph</TabsTrigger>
</TabsList>
<TabsContent value="browse" className="space-y-6">
{/* Search and Filters */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Search className="w-5 h-5" />
<span>Search & Filter</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex space-x-4">
<div className="flex-1">
<Input
placeholder="Search knowledge graphs..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full"
/>
</div>
<Select value={sortBy} onValueChange={setSortBy}>
<SelectTrigger className="w-48">
<SelectValue placeholder="Sort by" />
</SelectTrigger>
<SelectContent>
<SelectItem value="quality">Quality Score</SelectItem>
<SelectItem value="price_low">Price: Low to High</SelectItem>
<SelectItem value="price_high">Price: High to Low</SelectItem>
<SelectItem value="popularity">Most Popular</SelectItem>
<SelectItem value="newest">Newest First</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex space-x-4">
<div className="flex-1">
<label className="text-sm font-medium mb-2 block">Price Range (AITBC)</label>
<div className="flex space-x-2">
<Input
type="number"
placeholder="Min"
value={priceRange.min}
onChange={(e) => setPriceRange(prev => ({ ...prev, min: Number(e.target.value) }))}
className="w-24"
/>
<Input
type="number"
placeholder="Max"
value={priceRange.max}
onChange={(e) => setPriceRange(prev => ({ ...prev, max: Number(e.target.value) }))}
className="w-24"
/>
</div>
</div>
<div className="flex-1">
<label className="text-sm font-medium mb-2 block">Tags</label>
<div className="flex flex-wrap gap-2">
{allTags.map(tag => (
<Badge
key={tag}
variant={selectedTags.includes(tag) ? "default" : "outline"}
className="cursor-pointer"
onClick={() => {
setSelectedTags(prev =>
prev.includes(tag)
? prev.filter(t => t !== tag)
: [...prev, tag]
);
}}
>
{tag}
</Badge>
))}
</div>
</div>
</div>
</CardContent>
</Card>
{/* Graph Listings */}
{loading ? (
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Loading knowledge graphs...</p>
</div>
) : filteredGraphs.length === 0 ? (
<Alert>
<XCircle className="h-4 w-4" />
<AlertTitle>No graphs found</AlertTitle>
<AlertDescription>
Try adjusting your search criteria or filters to find knowledge graphs.
</AlertDescription>
</Alert>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredGraphs.map((graph) => {
const isPurchased = hasPurchased(graph.graphId);
return (
<Card key={graph.graphId} className="relative">
<CardHeader>
<div className="flex items-start justify-between">
<div className="flex-1">
<CardTitle className="text-lg line-clamp-2">
{graph.description}
</CardTitle>
<CardDescription className="mt-1">
by {graph.creator.slice(0, 6)}...{graph.creator.slice(-4)}
</CardDescription>
</div>
<div className="flex items-center space-x-1">
<div className={`w-2 h-2 rounded-full ${getQualityColor(graph.qualityScore)}`}></div>
<span className="text-sm font-medium">{graph.qualityScore}</span>
</div>
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex flex-wrap gap-1">
{graph.tags.map(tag => (
<Badge key={tag} variant="secondary" className="text-xs">
{tag}
</Badge>
))}
</div>
<div className="grid grid-cols-2 gap-4 text-sm">
<div className="flex items-center space-x-1">
<ShoppingCart className="w-4 h-4 text-muted-foreground" />
<span>{graph.accessCount} accesses</span>
</div>
<div className="flex items-center space-x-1">
<TrendingUp className="w-4 h-4 text-muted-foreground" />
<span>{graph.totalRevenue} AITBC</span>
</div>
</div>
<div className="text-sm text-muted-foreground">
<div className="flex items-center space-x-1">
<Clock className="w-4 h-4" />
<span>Created {new Date(graph.createdAt).toLocaleDateString()}</span>
</div>
</div>
</CardContent>
<CardFooter className="flex items-center justify-between">
<div className="text-lg font-bold">
{graph.price} AITBC
</div>
{isPurchased ? (
<Button variant="outline" disabled className="flex items-center space-x-1">
<CheckCircle className="w-4 h-4" />
<span>Purchased</span>
</Button>
) : (
<Button
onClick={() => handlePurchase(graph)}
className="flex items-center space-x-1"
>
<ShoppingCart className="w-4 h-4" />
<span>Purchase</span>
</Button>
)}
</CardFooter>
</Card>
);
})}
</div>
)}
</TabsContent>
<TabsContent value="purchased" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>My Purchased Knowledge Graphs</CardTitle>
<CardDescription>
Knowledge graphs you have purchased and can access
</CardDescription>
</CardHeader>
<CardContent>
{purchasedGraphs.length === 0 ? (
<div className="text-center py-8">
<ShoppingCart className="w-12 h-12 text-muted-foreground mx-auto mb-4" />
<p className="text-muted-foreground">No purchased knowledge graphs yet</p>
<Button
className="mt-4"
onClick={() => setActiveTab('browse')}
>
Browse Marketplace
</Button>
</div>
) : (
<div className="space-y-4">
{purchasedGraphs.map((record) => {
const graph = graphs.find(g => g.graphId === record.graphId);
if (!graph) return null;
return (
<Card key={record.graphId}>
<CardContent className="pt-6">
<div className="flex items-center justify-between">
<div>
<h4 className="font-semibold">{graph.description}</h4>
<p className="text-sm text-muted-foreground">
Purchased on {new Date(record.purchasedAt).toLocaleDateString()}
</p>
<p className="text-sm text-muted-foreground">
Expires on {new Date(record.expiresAt).toLocaleDateString()}
</p>
</div>
<div className="flex items-center space-x-2">
<Badge variant="outline">
{record.graphId}
</Badge>
<Button variant="outline" size="sm">
Download
</Button>
</div>
</div>
</CardContent>
</Card>
);
})}
</div>
)}
</CardContent>
</Card>
</TabsContent>
<TabsContent value="create" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Create Knowledge Graph</CardTitle>
<CardDescription>
Upload and monetize your knowledge graphs on the marketplace
</CardDescription>
</CardHeader>
<CardContent>
<Alert>
<AlertTitle>Coming Soon</AlertTitle>
<AlertDescription>
Knowledge graph creation tools will be available in the next update.
</AlertDescription>
</Alert>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
);
};
export default KnowledgeMarketplace;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,576 @@
import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './ui/card';
import { Button } from './ui/button';
import { Badge } from './ui/badge';
import { Input } from './ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
import { Alert, AlertDescription, AlertTitle } from './ui/alert';
import { Progress } from './ui/progress';
import { Separator } from './ui/separator';
import {
Database,
Upload,
Download,
Search,
Filter,
Trash2,
Clock,
HardDrive,
Brain,
Zap,
Shield,
TrendingUp
} from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { useWallet } from '@/hooks/use-wallet';
interface MemoryRecord {
cid: string;
agentId: string;
memoryType: string;
priority: string;
version: number;
timestamp: string;
size: number;
tags: string[];
accessCount: number;
lastAccessed: string;
expiresAt?: string;
parentCid?: string;
verified: boolean;
}
interface MemoryStats {
totalMemories: number;
totalSizeBytes: number;
totalSizeMB: number;
byType: Record<string, number>;
byPriority: Record<string, number>;
totalAccessCount: number;
averageAccessCount: number;
agentCount: number;
}
const MemoryManager: React.FC = () => {
const { toast } = useToast();
const { isConnected, address } = useWallet();
const [memories, setMemories] = useState<MemoryRecord[]>([]);
const [filteredMemories, setFilteredMemories] = useState<MemoryRecord[]>([]);
const [stats, setStats] = useState<MemoryStats | null>(null);
const [searchQuery, setSearchQuery] = useState('');
const [selectedType, setSelectedType] = useState('all');
const [selectedPriority, setSelectedPriority] = useState('all');
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState('memories');
// Mock data for demonstration
const mockMemories: MemoryRecord[] = [
{
cid: 'QmAbc123...',
agentId: 'agent_001',
memoryType: 'experience',
priority: 'high',
version: 3,
timestamp: '2024-01-15T10:30:00Z',
size: 2048576,
tags: ['training', 'nlp', 'conversation'],
accessCount: 45,
lastAccessed: '2024-01-20T14:15:00Z',
verified: true
},
{
cid: 'QmDef456...',
agentId: 'agent_001',
memoryType: 'policy_weights',
priority: 'critical',
version: 7,
timestamp: '2024-01-18T09:45:00Z',
size: 1048576,
tags: ['model', 'weights', 'reinforcement'],
accessCount: 128,
lastAccessed: '2024-01-22T16:30:00Z',
verified: true
},
{
cid: 'QmGhi789...',
agentId: 'agent_002',
memoryType: 'knowledge_graph',
priority: 'medium',
version: 1,
timestamp: '2024-01-20T11:20:00Z',
size: 5242880,
tags: ['knowledge', 'graph', 'vision'],
accessCount: 23,
lastAccessed: '2024-01-21T13:45:00Z',
verified: false
},
{
cid: 'QmJkl012...',
agentId: 'agent_002',
memoryType: 'training_data',
priority: 'low',
version: 2,
timestamp: '2024-01-22T08:15:00Z',
size: 10485760,
tags: ['data', 'images', 'classification'],
accessCount: 8,
lastAccessed: '2024-01-22T08:15:00Z',
expiresAt: '2024-02-22T08:15:00Z',
verified: true
}
];
const mockStats: MemoryStats = {
totalMemories: 4,
totalSizeBytes: 18874368,
totalSizeMB: 18.0,
byType: {
'experience': 1,
'policy_weights': 1,
'knowledge_graph': 1,
'training_data': 1
},
byPriority: {
'critical': 1,
'high': 1,
'medium': 1,
'low': 1
},
totalAccessCount: 204,
averageAccessCount: 51,
agentCount: 2
};
useEffect(() => {
// Load mock data
setTimeout(() => {
setMemories(mockMemories);
setFilteredMemories(mockMemories);
setStats(mockStats);
setLoading(false);
}, 1000);
}, []);
useEffect(() => {
filterMemories();
}, [searchQuery, selectedType, selectedPriority, memories]);
const filterMemories = () => {
let filtered = memories.filter(memory => {
// Search query filter
if (searchQuery) {
const query = searchQuery.toLowerCase();
const matchesSearch =
memory.cid.toLowerCase().includes(query) ||
memory.memoryType.toLowerCase().includes(query) ||
memory.tags.some(tag => tag.toLowerCase().includes(query)) ||
memory.agentId.toLowerCase().includes(query);
if (!matchesSearch) return false;
}
// Type filter
if (selectedType !== 'all' && memory.memoryType !== selectedType) {
return false;
}
// Priority filter
if (selectedPriority !== 'all' && memory.priority !== selectedPriority) {
return false;
}
return true;
});
setFilteredMemories(filtered);
};
const handleDownload = async (memory: MemoryRecord) => {
if (!isConnected) {
toast({
title: "Wallet Not Connected",
description: "Please connect your wallet to download memories",
variant: "destructive"
});
return;
}
try {
// Simulate download process
toast({
title: "Download Started",
description: `Downloading memory ${memory.cid}...`,
variant: "default"
});
// Simulate download completion
setTimeout(() => {
toast({
title: "Download Complete",
description: `Memory ${memory.cid} downloaded successfully`,
variant: "default"
});
}, 2000);
} catch (error) {
toast({
title: "Download Failed",
description: "There was an error downloading the memory",
variant: "destructive"
});
}
};
const handleDelete = async (memory: MemoryRecord) => {
if (!isConnected) {
toast({
title: "Wallet Not Connected",
description: "Please connect your wallet to delete memories",
variant: "destructive"
});
return;
}
try {
// Remove from local state
setMemories(prev => prev.filter(m => m.cid !== memory.cid));
toast({
title: "Memory Deleted",
description: `Memory ${memory.cid} has been deleted`,
variant: "default"
});
} catch (error) {
toast({
title: "Delete Failed",
description: "There was an error deleting the memory",
variant: "destructive"
});
}
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'critical': return 'bg-red-500';
case 'high': return 'bg-orange-500';
case 'medium': return 'bg-yellow-500';
case 'low': return 'bg-green-500';
default: return 'bg-gray-500';
}
};
const getTypeIcon = (type: string) => {
switch (type) {
case 'experience': return <Brain className="w-4 h-4" />;
case 'policy_weights': return <Zap className="w-4 h-4" />;
case 'knowledge_graph': return <Database className="w-4 h-4" />;
case 'training_data': return <HardDrive className="w-4 h-4" />;
default: return <Database className="w-4 h-4" />;
}
};
const formatSize = (bytes: number) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
const memoryTypes = Array.from(new Set(memories.map(m => m.memoryType)));
const priorities = ['critical', 'high', 'medium', 'low'];
return (
<div className="container mx-auto p-6 space-y-6">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold">Memory Manager</h1>
<p className="text-muted-foreground mt-2">
Manage and monitor your agent's persistent memory storage
</p>
</div>
<div className="flex items-center space-x-2">
<Badge variant="outline" className="flex items-center space-x-1">
<Database className="w-4 h-4" />
<span>{stats?.totalMemories || 0} Memories</span>
</Badge>
<Badge variant="outline" className="flex items-center space-x-1">
<HardDrive className="w-4 h-4" />
<span>{stats?.totalSizeMB.toFixed(1) || 0} MB</span>
</Badge>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab}>
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="memories">Memories</TabsTrigger>
<TabsTrigger value="statistics">Statistics</TabsTrigger>
<TabsTrigger value="settings">Settings</TabsTrigger>
</TabsList>
<TabsContent value="memories" className="space-y-6">
{/* Search and Filters */}
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Search className="w-5 h-5" />
<span>Search & Filter</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex space-x-4">
<div className="flex-1">
<Input
placeholder="Search memories by CID, type, tags..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full"
/>
</div>
<Select value={selectedType} onValueChange={setSelectedType}>
<SelectTrigger className="w-40">
<SelectValue placeholder="Type" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Types</SelectItem>
{memoryTypes.map(type => (
<SelectItem key={type} value={type}>
<div className="flex items-center space-x-2">
{getTypeIcon(type)}
<span>{type.replace('_', ' ')}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
<Select value={selectedPriority} onValueChange={setSelectedPriority}>
<SelectTrigger className="w-32">
<SelectValue placeholder="Priority" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Priorities</SelectItem>
{priorities.map(priority => (
<SelectItem key={priority} value={priority}>
<div className="flex items-center space-x-2">
<div className={`w-2 h-2 rounded-full ${getPriorityColor(priority)}`}></div>
<span>{priority.charAt(0).toUpperCase() + priority.slice(1)}</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</CardContent>
</Card>
{/* Memory List */}
{loading ? (
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Loading memories...</p>
</div>
) : filteredMemories.length === 0 ? (
<Alert>
<Database className="h-4 w-4" />
<AlertTitle>No memories found</AlertTitle>
<AlertDescription>
Try adjusting your search criteria or filters to find memories.
</AlertDescription>
</Alert>
) : (
<div className="space-y-4">
{filteredMemories.map((memory) => (
<Card key={memory.cid}>
<CardContent className="pt-6">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center space-x-3 mb-2">
{getTypeIcon(memory.memoryType)}
<h4 className="font-semibold">{memory.cid}</h4>
<Badge variant="outline">{memory.memoryType.replace('_', ' ')}</Badge>
<div className={`w-2 h-2 rounded-full ${getPriorityColor(memory.priority)}`}></div>
<span className="text-sm text-muted-foreground capitalize">
{memory.priority} priority
</span>
{memory.verified && (
<Shield className="w-4 h-4 text-green-500" />
)}
</div>
<div className="flex flex-wrap gap-1 mb-3">
{memory.tags.map(tag => (
<Badge key={tag} variant="secondary" className="text-xs">
{tag}
</Badge>
))}
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm text-muted-foreground">
<div className="flex items-center space-x-1">
<HardDrive className="w-4 h-4" />
<span>{formatSize(memory.size)}</span>
</div>
<div className="flex items-center space-x-1">
<TrendingUp className="w-4 h-4" />
<span>{memory.accessCount} accesses</span>
</div>
<div className="flex items-center space-x-1">
<Clock className="w-4 h-4" />
<span>Version {memory.version}</span>
</div>
<div className="flex items-center space-x-1">
<Database className="w-4 h-4" />
<span>{memory.agentId}</span>
</div>
</div>
<div className="text-sm text-muted-foreground mt-2">
Created: {new Date(memory.timestamp).toLocaleDateString()}
{memory.lastAccessed !== memory.timestamp && (
<> • Last accessed: {new Date(memory.lastAccessed).toLocaleDateString()}</>
)}
{memory.expiresAt && (
<> • Expires: {new Date(memory.expiresAt).toLocaleDateString()}</>
)}
</div>
</div>
<div className="flex items-center space-x-2 ml-4">
<Button
variant="outline"
size="sm"
onClick={() => handleDownload(memory)}
className="flex items-center space-x-1"
>
<Download className="w-4 h-4" />
<span>Download</span>
</Button>
<Button
variant="outline"
size="sm"
onClick={() => handleDelete(memory)}
className="flex items-center space-x-1 text-red-600 hover:text-red-700"
>
<Trash2 className="w-4 h-4" />
<span>Delete</span>
</Button>
</div>
</div>
</CardContent>
</Card>
))}
</div>
)}
</TabsContent>
<TabsContent value="statistics" className="space-y-6">
{stats ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Database className="w-5 h-5" />
<span>Storage Overview</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex justify-between">
<span>Total Memories</span>
<span className="font-semibold">{stats.totalMemories}</span>
</div>
<div className="flex justify-between">
<span>Total Size</span>
<span className="font-semibold">{stats.totalSizeMB.toFixed(1)} MB</span>
</div>
<div className="flex justify-between">
<span>Agent Count</span>
<span className="font-semibold">{stats.agentCount}</span>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<TrendingUp className="w-5 h-5" />
<span>Access Statistics</span>
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex justify-between">
<span>Total Accesses</span>
<span className="font-semibold">{stats.totalAccessCount}</span>
</div>
<div className="flex justify-between">
<span>Average Accesses</span>
<span className="font-semibold">{stats.averageAccessCount.toFixed(1)}</span>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Memory Types</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{Object.entries(stats.byType).map(([type, count]) => (
<div key={type} className="flex items-center justify-between">
<div className="flex items-center space-x-2">
{getTypeIcon(type)}
<span className="capitalize">{type.replace('_', ' ')}</span>
</div>
<Badge variant="outline">{count}</Badge>
</div>
))}
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Priority Distribution</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{Object.entries(stats.byPriority).map(([priority, count]) => (
<div key={priority} className="flex items-center justify-between">
<div className="flex items-center space-x-2">
<div className={`w-2 h-2 rounded-full ${getPriorityColor(priority)}`}></div>
<span className="capitalize">{priority}</span>
</div>
<Badge variant="outline">{count}</Badge>
</div>
))}
</CardContent>
</Card>
</div>
) : (
<div className="text-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary mx-auto"></div>
<p className="mt-2 text-muted-foreground">Loading statistics...</p>
</div>
)}
</TabsContent>
<TabsContent value="settings" className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Memory Settings</CardTitle>
<CardDescription>
Configure memory management settings and preferences
</CardDescription>
</CardHeader>
<CardContent>
<Alert>
<AlertTitle>Coming Soon</AlertTitle>
<AlertDescription>
Memory management settings will be available in the next update.
</AlertDescription>
</Alert>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
);
};
export default MemoryManager;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const alertVariants = cva(
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
}
)
const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
))
Alert.displayName = "Alert"
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
))
AlertTitle.displayName = "AlertTitle"
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
))
AlertDescription.displayName = "AlertDescription"
export { Alert, AlertTitle, AlertDescription }

View File

@@ -0,0 +1,36 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
)
}
export { Badge, badgeVariants }

View File

@@ -0,0 +1,56 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"
export { Button, buttonVariants }

View File

@@ -0,0 +1,79 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-lg border bg-card text-card-foreground shadow-sm",
className
)}
{...props}
/>
))
Card.displayName = "Card"
const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn(
"text-2xl font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

View File

@@ -0,0 +1,25 @@
import * as React from "react"
import { cn } from "@/lib/utils"
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props}
/>
)
}
)
Input.displayName = "Input"
export { Input }

View File

@@ -0,0 +1,26 @@
import * as React from "react"
import * as ProgressPrimitive from "@radix-ui/react-progress"
import { cn } from "@/lib/utils"
const Progress = React.forwardRef<
React.ElementRef<typeof ProgressPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
>(({ className, value, ...props }, ref) => (
<ProgressPrimitive.Root
ref={ref}
className={cn(
"relative h-4 w-full overflow-hidden rounded-full bg-secondary",
className
)}
{...props}
>
<ProgressPrimitive.Indicator
className="h-full w-full flex-1 bg-primary transition-all"
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
))
Progress.displayName = ProgressPrimitive.Root.displayName
export { Progress }

View File

@@ -0,0 +1,158 @@
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown, ChevronUp } from "lucide-react"
import { cn } from "@/lib/utils"
const Select = SelectPrimitive.Root
const SelectGroup = SelectPrimitive.Group
const SelectValue = SelectPrimitive.Value
const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectScrollUpButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
))
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
const SelectScrollDownButton = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
>(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn(
"flex cursor-default items-center justify-center py-1",
className
)}
{...props}
>
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
))
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName
const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md 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",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn(
"p-1",
position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectLabel = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Label>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props}
/>
))
SelectLabel.displayName = SelectPrimitive.Label.displayName
const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}
>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectSeparator = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
>(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props}
/>
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
}

View File

@@ -0,0 +1,29 @@
import * as React from "react"
import * as SeparatorPrimitive from "@radix-ui/react-separator"
import { cn } from "@/lib/utils"
const Separator = React.forwardRef<
React.ElementRef<typeof SeparatorPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
>(
(
{ className, orientation = "horizontal", decorative = true, ...props },
ref
) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className
)}
{...props}
/>
)
)
Separator.displayName = SeparatorPrimitive.Root.displayName
export { Separator }

View File

@@ -0,0 +1,117 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<
HTMLTableElement,
React.HTMLAttributes<HTMLTableElement>
>(({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table
ref={ref}
className={cn("w-full caption-bottom text-sm", className)}
{...props}
/>
</div>
))
Table.displayName = "Table"
const TableHeader = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
))
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tbody
ref={ref}
className={cn("[&_tr:last-child]:border-0", className)}
{...props}
/>
))
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<
HTMLTableSectionElement,
React.HTMLAttributes<HTMLTableSectionElement>
>(({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn(
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
className
)}
{...props}
/>
))
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<
HTMLTableRowElement,
React.HTMLAttributes<HTMLTableRowElement>
>(({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
className
)}
{...props}
/>
))
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<
HTMLTableCellElement,
React.ThHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
className
)}
{...props}
/>
))
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<
HTMLTableCellElement,
React.TdHTMLAttributes<HTMLTableCellElement>
>(({ className, ...props }, ref) => (
<td
ref={ref}
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
{...props}
/>
))
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<
HTMLTableCaptionElement,
React.HTMLAttributes<HTMLTableCaptionElement>
>(({ className, ...props }, ref) => (
<caption
ref={ref}
className={cn("mt-4 text-sm text-muted-foreground", className)}
{...props}
/>
))
TableCaption.displayName = "TableCaption"
export {
Table,
TableHeader,
TableBody,
TableFooter,
TableHead,
TableRow,
TableCell,
TableCaption,
}

View File

@@ -0,0 +1,53 @@
import * as React from "react"
import * as TabsPrimitive from "@radix-ui/react-tabs"
import { cn } from "@/lib/utils"
const Tabs = TabsPrimitive.Root
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
className
)}
{...props}
/>
))
TabsList.displayName = TabsPrimitive.List.displayName
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
className
)}
{...props}
/>
))
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
className
)}
{...props}
/>
))
TabsContent.displayName = TabsPrimitive.Content.displayName
export { Tabs, TabsList, TabsTrigger, TabsContent }

View File

@@ -0,0 +1,127 @@
import * as React from "react"
import * as ToastPrimitives from "@radix-ui/react-toast"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const ToastProvider = ToastPrimitives.Provider
const ToastViewport = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Viewport>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Viewport
ref={ref}
className={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
className
)}
{...props}
/>
))
ToastViewport.displayName = ToastPrimitives.Viewport.displayName
const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
{
variants: {
variant: {
default: "border bg-background text-foreground",
destructive:
"destructive border-destructive bg-destructive text-destructive-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
const Toast = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Root>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
VariantProps<typeof toastVariants>
>(({ className, variant, ...props }, ref) => {
return (
<ToastPrimitives.Root
ref={ref}
className={cn(toastVariants({ variant }), className)}
{...props}
/>
)
})
Toast.displayName = ToastPrimitives.Root.displayName
const ToastAction = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Action>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Action
ref={ref}
className={cn(
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium ring-offset-background transition-colors hover:bg-secondary focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
className
)}
{...props}
/>
))
ToastAction.displayName = ToastPrimitives.Action.displayName
const ToastClose = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Close>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Close
ref={ref}
className={cn(
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
className
)}
toast-close=""
{...props}
>
<X className="h-4 w-4" />
</ToastPrimitives.Close>
))
ToastClose.displayName = ToastPrimitives.Close.displayName
const ToastTitle = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Title>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Title
ref={ref}
className={cn("text-sm font-semibold", className)}
{...props}
/>
))
ToastTitle.displayName = ToastPrimitives.Title.displayName
const ToastDescription = React.forwardRef<
React.ElementRef<typeof ToastPrimitives.Description>,
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
>(({ className, ...props }, ref) => (
<ToastPrimitives.Description
ref={ref}
className={cn("text-sm opacity-90", className)}
{...props}
/>
))
ToastDescription.displayName = ToastPrimitives.Description.displayName
type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
type ToastActionElement = React.ReactElement<typeof ToastAction>
export {
type ToastProps,
type ToastActionElement,
ToastProvider,
ToastViewport,
Toast,
ToastTitle,
ToastDescription,
ToastClose,
ToastAction,
}