|
|
import { useState, useEffect } from 'react' |
|
|
import { useNavigate } from 'react-router-dom' |
|
|
import { Badge } from '@/components/ui/badge' |
|
|
import { Button } from '@/components/ui/button' |
|
|
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card' |
|
|
import { Input } from '@/components/ui/input' |
|
|
import { Brain, MessageSquare, Search, TrendingUp, Crown, Bot, Eye, Star, Heart, Users, Award } from 'lucide-react' |
|
|
|
|
|
|
|
|
interface CommunityTemplate { |
|
|
id: string |
|
|
name: string |
|
|
description: string |
|
|
author: string |
|
|
category: string |
|
|
tags: string[] |
|
|
model: string |
|
|
systemPrompt: string |
|
|
temperature: number |
|
|
maxTokens: number |
|
|
isOfficial: boolean |
|
|
createdAt: string |
|
|
icon: string |
|
|
usageCount: number |
|
|
rating: number |
|
|
} |
|
|
|
|
|
|
|
|
function getCommunityTemplates(): CommunityTemplate[] { |
|
|
return [ |
|
|
{ |
|
|
id: 'code-reviewer', |
|
|
name: 'Code Review Expert', |
|
|
description: 'Professional code reviewer for detailed analysis and suggestions. Helps identify bugs, security issues, and performance optimizations.', |
|
|
author: 'EdgeLLM Team', |
|
|
category: 'Developer Tools', |
|
|
tags: ['code review', 'programming', 'debugging'], |
|
|
model: 'Qwen/Qwen3-30B-A3B', |
|
|
systemPrompt: 'You are a senior software engineer. Analyze code for quality, best practices, performance, and security. Provide constructive feedback with specific examples.', |
|
|
temperature: 0.3, |
|
|
maxTokens: 1000, |
|
|
isOfficial: true, |
|
|
createdAt: '2024-01-15', |
|
|
icon: 'π', |
|
|
usageCount: 1247, |
|
|
rating: 4.8 |
|
|
}, |
|
|
{ |
|
|
id: 'writing-tutor', |
|
|
name: 'Writing Assistant', |
|
|
description: 'Helps improve writing with structure suggestions and clarity enhancements. Perfect for essays, articles, and professional documents.', |
|
|
author: 'Community', |
|
|
category: 'Writing', |
|
|
tags: ['writing', 'editing', 'grammar'], |
|
|
model: 'Qwen/Qwen3-30B-A3B', |
|
|
systemPrompt: 'You are a writing tutor. Help improve writing through structure, clarity, grammar, and style suggestions. Provide specific feedback and examples.', |
|
|
temperature: 0.4, |
|
|
maxTokens: 800, |
|
|
isOfficial: false, |
|
|
createdAt: '2024-01-20', |
|
|
icon: 'π', |
|
|
usageCount: 892, |
|
|
rating: 4.6 |
|
|
}, |
|
|
{ |
|
|
id: 'creative-writer', |
|
|
name: 'Creative Writing Coach', |
|
|
description: 'Inspires creativity and helps develop compelling stories and characters. Great for fiction writers and storytellers.', |
|
|
author: 'Community', |
|
|
category: 'Writing', |
|
|
tags: ['creative', 'storytelling', 'fiction'], |
|
|
model: 'Qwen/Qwen3-30B-A3B', |
|
|
systemPrompt: 'You are a creative writing coach. Help develop stories, characters, and narrative techniques. Provide encouraging feedback and creative suggestions.', |
|
|
temperature: 0.8, |
|
|
maxTokens: 1000, |
|
|
isOfficial: true, |
|
|
createdAt: '2024-01-30', |
|
|
icon: 'β¨', |
|
|
usageCount: 634, |
|
|
rating: 4.7 |
|
|
}, |
|
|
{ |
|
|
id: 'data-analyst', |
|
|
name: 'Data Analysis Expert', |
|
|
description: 'Helps analyze data patterns, create insights, and generate comprehensive reports with statistical analysis.', |
|
|
author: 'EdgeLLM Team', |
|
|
category: 'Data & Analytics', |
|
|
tags: ['data', 'analytics', 'statistics'], |
|
|
model: 'Qwen/Qwen3-30B-A3B', |
|
|
systemPrompt: 'You are a data analysis expert. Help users understand data patterns, create visualizations, and provide statistical insights.', |
|
|
temperature: 0.2, |
|
|
maxTokens: 1200, |
|
|
isOfficial: true, |
|
|
createdAt: '2024-01-18', |
|
|
icon: 'π', |
|
|
usageCount: 567, |
|
|
rating: 4.9 |
|
|
}, |
|
|
{ |
|
|
id: 'language-tutor', |
|
|
name: 'Language Learning Tutor', |
|
|
description: 'Interactive language tutor for vocabulary, grammar, and conversation practice. Supports multiple languages.', |
|
|
author: 'Community', |
|
|
category: 'Education', |
|
|
tags: ['language', 'learning', 'education'], |
|
|
model: 'Qwen/Qwen3-30B-A3B', |
|
|
systemPrompt: 'You are a friendly language tutor. Help users learn languages through interactive exercises, explanations, and conversation practice.', |
|
|
temperature: 0.6, |
|
|
maxTokens: 800, |
|
|
isOfficial: false, |
|
|
createdAt: '2024-01-20', |
|
|
icon: 'π', |
|
|
usageCount: 423, |
|
|
rating: 4.5 |
|
|
}, |
|
|
{ |
|
|
id: 'business-advisor', |
|
|
name: 'Business Strategy Advisor', |
|
|
description: 'Provides strategic business advice, market analysis, and growth recommendations for entrepreneurs and businesses.', |
|
|
author: 'EdgeLLM Team', |
|
|
category: 'Business', |
|
|
tags: ['strategy', 'business', 'consulting'], |
|
|
model: 'Qwen/Qwen3-30B-A3B', |
|
|
systemPrompt: 'You are a business strategy consultant. Provide strategic advice, market insights, and growth recommendations based on business principles.', |
|
|
temperature: 0.4, |
|
|
maxTokens: 1000, |
|
|
isOfficial: true, |
|
|
createdAt: '2024-01-22', |
|
|
icon: 'πΌ', |
|
|
usageCount: 789, |
|
|
rating: 4.7 |
|
|
}, |
|
|
{ |
|
|
id: 'research-assistant', |
|
|
name: 'Research Assistant', |
|
|
description: 'Helps with academic and professional research, source analysis, and comprehensive report writing.', |
|
|
author: 'Community', |
|
|
category: 'Research', |
|
|
tags: ['research', 'academic', 'analysis'], |
|
|
model: 'Qwen/Qwen3-30B-A3B', |
|
|
systemPrompt: 'You are a research assistant. Help users conduct thorough research, analyze sources, and organize findings into comprehensive reports.', |
|
|
temperature: 0.3, |
|
|
maxTokens: 1200, |
|
|
isOfficial: false, |
|
|
createdAt: '2024-01-25', |
|
|
icon: 'π', |
|
|
usageCount: 356, |
|
|
rating: 4.4 |
|
|
}, |
|
|
{ |
|
|
id: 'math-tutor', |
|
|
name: 'Math & Science Tutor', |
|
|
description: 'Expert tutor for mathematics, physics, chemistry, and other STEM subjects with step-by-step explanations.', |
|
|
author: 'EdgeLLM Team', |
|
|
category: 'Education', |
|
|
tags: ['math', 'science', 'tutoring'], |
|
|
model: 'Qwen/Qwen3-30B-A3B', |
|
|
systemPrompt: 'You are a math and science tutor. Provide clear, step-by-step explanations for mathematical and scientific concepts.', |
|
|
temperature: 0.2, |
|
|
maxTokens: 1000, |
|
|
isOfficial: true, |
|
|
createdAt: '2024-01-28', |
|
|
icon: 'π¬', |
|
|
usageCount: 678, |
|
|
rating: 4.8 |
|
|
} |
|
|
] |
|
|
} |
|
|
|
|
|
export function Templates() { |
|
|
const navigate = useNavigate() |
|
|
const [isOnline, setIsOnline] = useState(false) |
|
|
const [communityTemplates] = useState<CommunityTemplate[]>(getCommunityTemplates()) |
|
|
const [likedTemplates, setLikedTemplates] = useState<string[]>([]) |
|
|
const [searchQuery, setSearchQuery] = useState('') |
|
|
const [selectedCategory, setSelectedCategory] = useState<string>('All') |
|
|
|
|
|
useEffect(() => { |
|
|
checkSystemStatus() |
|
|
loadLikedTemplates() |
|
|
}, []) |
|
|
|
|
|
const checkSystemStatus = async () => { |
|
|
try { |
|
|
const baseUrl = `${window.location.protocol}//${window.location.host}` |
|
|
const res = await fetch(`${baseUrl}/models`) |
|
|
setIsOnline(res.ok) |
|
|
} catch (error) { |
|
|
setIsOnline(false) |
|
|
} |
|
|
} |
|
|
|
|
|
const loadLikedTemplates = () => { |
|
|
try { |
|
|
const liked = JSON.parse(localStorage.getItem('likedTemplates') || '[]') |
|
|
setLikedTemplates(liked) |
|
|
} catch (error) { |
|
|
console.error('Failed to load liked templates:', error) |
|
|
} |
|
|
} |
|
|
|
|
|
const toggleLikeTemplate = (templateId: string) => { |
|
|
const updatedLiked = likedTemplates.includes(templateId) |
|
|
? likedTemplates.filter(id => id !== templateId) |
|
|
: [...likedTemplates, templateId] |
|
|
|
|
|
setLikedTemplates(updatedLiked) |
|
|
localStorage.setItem('likedTemplates', JSON.stringify(updatedLiked)) |
|
|
} |
|
|
|
|
|
const useTemplate = (template: CommunityTemplate) => { |
|
|
const assistantConfig = { |
|
|
name: template.name, |
|
|
description: template.description, |
|
|
model: template.model, |
|
|
systemPrompt: template.systemPrompt, |
|
|
temperature: template.temperature, |
|
|
maxTokens: template.maxTokens |
|
|
} |
|
|
|
|
|
localStorage.setItem('loadAssistantConfig', JSON.stringify(assistantConfig)) |
|
|
navigate('/playground') |
|
|
} |
|
|
|
|
|
|
|
|
const filteredTemplates = communityTemplates.filter(template => { |
|
|
const matchesSearch = template.name.toLowerCase().includes(searchQuery.toLowerCase()) || |
|
|
template.description.toLowerCase().includes(searchQuery.toLowerCase()) || |
|
|
template.tags.some(tag => tag.toLowerCase().includes(searchQuery.toLowerCase())) |
|
|
const matchesCategory = selectedCategory === 'All' || template.category === selectedCategory |
|
|
return matchesSearch && matchesCategory |
|
|
}) |
|
|
|
|
|
const featuredTemplates = filteredTemplates.filter(t => t.isOfficial) |
|
|
const trendingTemplates = [...filteredTemplates].sort((a, b) => b.usageCount - a.usageCount).slice(0, 6) |
|
|
const categories = ['All', ...Array.from(new Set(communityTemplates.map(t => t.category)))] |
|
|
|
|
|
return ( |
|
|
<div className="min-h-screen bg-background"> |
|
|
{/* Header */} |
|
|
<div className="sticky top-0 z-40 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"> |
|
|
<div className="max-w-6xl mx-auto px-6 py-4"> |
|
|
<div className="flex items-center justify-between"> |
|
|
<div className="flex items-center gap-4"> |
|
|
<div className="flex items-center gap-2"> |
|
|
<div className="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center"> |
|
|
<Brain className="h-5 w-5 text-white" /> |
|
|
</div> |
|
|
<h1 className="text-xl font-bold text-gray-900">Assistant Templates</h1> |
|
|
<Badge variant={isOnline ? "default" : "secondary"} className="text-xs"> |
|
|
{isOnline ? 'Online' : 'Offline'} |
|
|
</Badge> |
|
|
</div> |
|
|
</div> |
|
|
<Button onClick={() => navigate('/playground')} className="flex items-center gap-2"> |
|
|
<Bot className="h-4 w-4" /> |
|
|
Create Assistant |
|
|
</Button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div className="max-w-6xl mx-auto px-6 py-8"> |
|
|
{/* Hero Section */} |
|
|
<div className="text-center mb-12"> |
|
|
<h1 className="text-4xl font-bold text-gray-900 mb-4"> |
|
|
Explore AI Assistants |
|
|
</h1> |
|
|
<p className="text-lg text-gray-600 max-w-2xl mx-auto mb-8"> |
|
|
Discover, customize and deploy specialized AI assistants. Start with proven templates or build from scratch. |
|
|
</p> |
|
|
|
|
|
{/* Search Bar */} |
|
|
<div className="relative max-w-2xl mx-auto mb-8"> |
|
|
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400 h-5 w-5" /> |
|
|
<Input |
|
|
type="text" |
|
|
placeholder="Search assistants..." |
|
|
value={searchQuery} |
|
|
onChange={(e) => setSearchQuery(e.target.value)} |
|
|
className="pl-12 pr-4 py-3 text-lg rounded-full border-2 border-gray-200 focus:border-blue-500" |
|
|
/> |
|
|
</div> |
|
|
|
|
|
{/* Category Filters */} |
|
|
<div className="flex flex-wrap justify-center gap-2 mb-8"> |
|
|
{categories.map((category) => ( |
|
|
<Button |
|
|
key={category} |
|
|
variant={selectedCategory === category ? "default" : "outline"} |
|
|
size="sm" |
|
|
onClick={() => setSelectedCategory(category)} |
|
|
className="rounded-full" |
|
|
> |
|
|
{category} |
|
|
</Button> |
|
|
))} |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
{/* Featured Section */} |
|
|
{selectedCategory === 'All' && featuredTemplates.length > 0 && ( |
|
|
<div className="mb-12"> |
|
|
<div className="flex items-center gap-2 mb-6"> |
|
|
<Crown className="h-5 w-5 text-yellow-600" /> |
|
|
<h2 className="text-2xl font-bold text-gray-900">Featured</h2> |
|
|
<Badge variant="secondary" className="text-xs"> |
|
|
Curated by EdgeLLM Team |
|
|
</Badge> |
|
|
</div> |
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> |
|
|
{featuredTemplates.slice(0, 3).map((template) => ( |
|
|
<TemplateCard |
|
|
key={template.id} |
|
|
template={template} |
|
|
isLiked={likedTemplates.includes(template.id)} |
|
|
onLike={() => toggleLikeTemplate(template.id)} |
|
|
onUse={() => useTemplate(template)} |
|
|
featured={true} |
|
|
/> |
|
|
))} |
|
|
</div> |
|
|
</div> |
|
|
)} |
|
|
|
|
|
{/* Trending Section */} |
|
|
{selectedCategory === 'All' && ( |
|
|
<div className="mb-12"> |
|
|
<div className="flex items-center gap-2 mb-6"> |
|
|
<TrendingUp className="h-5 w-5 text-green-600" /> |
|
|
<h2 className="text-2xl font-bold text-gray-900">Trending</h2> |
|
|
<Badge variant="secondary" className="text-xs"> |
|
|
Most popular this week |
|
|
</Badge> |
|
|
</div> |
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> |
|
|
{trendingTemplates.slice(0, 6).map((template) => ( |
|
|
<TemplateCard |
|
|
key={template.id} |
|
|
template={template} |
|
|
isLiked={likedTemplates.includes(template.id)} |
|
|
onLike={() => toggleLikeTemplate(template.id)} |
|
|
onUse={() => useTemplate(template)} |
|
|
trending={true} |
|
|
/> |
|
|
))} |
|
|
</div> |
|
|
</div> |
|
|
)} |
|
|
|
|
|
{/* All Templates Section */} |
|
|
<div className="mb-12"> |
|
|
<div className="flex items-center gap-2 mb-6"> |
|
|
<Users className="h-5 w-5 text-blue-600" /> |
|
|
<h2 className="text-2xl font-bold text-gray-900"> |
|
|
{selectedCategory === 'All' ? 'All Templates' : selectedCategory} |
|
|
</h2> |
|
|
<Badge variant="outline" className="text-xs"> |
|
|
{filteredTemplates.length} available |
|
|
</Badge> |
|
|
</div> |
|
|
|
|
|
{filteredTemplates.length > 0 ? ( |
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> |
|
|
{filteredTemplates.map((template) => ( |
|
|
<TemplateCard |
|
|
key={template.id} |
|
|
template={template} |
|
|
isLiked={likedTemplates.includes(template.id)} |
|
|
onLike={() => toggleLikeTemplate(template.id)} |
|
|
onUse={() => useTemplate(template)} |
|
|
/> |
|
|
))} |
|
|
</div> |
|
|
) : ( |
|
|
<Card className="p-12 text-center"> |
|
|
<div className="flex flex-col items-center gap-4"> |
|
|
<Search className="h-12 w-12 text-gray-400" /> |
|
|
<div> |
|
|
<h3 className="text-lg font-medium text-gray-900 mb-2">No templates found</h3> |
|
|
<p className="text-gray-600"> |
|
|
Try adjusting your search or category filters |
|
|
</p> |
|
|
</div> |
|
|
<Button variant="outline" onClick={() => { |
|
|
setSearchQuery('') |
|
|
setSelectedCategory('All') |
|
|
}}> |
|
|
Clear filters |
|
|
</Button> |
|
|
</div> |
|
|
</Card> |
|
|
)} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
) |
|
|
} |
|
|
|
|
|
|
|
|
function TemplateCard({ |
|
|
template, |
|
|
isLiked, |
|
|
onLike, |
|
|
onUse, |
|
|
featured = false, |
|
|
trending = false |
|
|
}: { |
|
|
template: CommunityTemplate |
|
|
isLiked: boolean |
|
|
onLike: () => void |
|
|
onUse: () => void |
|
|
featured?: boolean |
|
|
trending?: boolean |
|
|
}) { |
|
|
return ( |
|
|
<Card className="group hover:shadow-xl transition-all duration-300 hover:-translate-y-1 border-2 hover:border-blue-200 relative"> |
|
|
{featured && ( |
|
|
<div className="absolute -top-2 -right-2 bg-yellow-500 text-white text-xs px-2 py-1 rounded-full font-medium flex items-center gap-1"> |
|
|
<Crown className="h-3 w-3" /> |
|
|
Featured |
|
|
</div> |
|
|
)} |
|
|
{trending && ( |
|
|
<div className="absolute -top-2 -right-2 bg-green-500 text-white text-xs px-2 py-1 rounded-full font-medium flex items-center gap-1"> |
|
|
<TrendingUp className="h-3 w-3" /> |
|
|
Trending |
|
|
</div> |
|
|
)} |
|
|
|
|
|
<CardHeader className="pb-4"> |
|
|
<div className="flex items-start justify-between"> |
|
|
<div className="flex items-center gap-3"> |
|
|
<div className="w-12 h-12 bg-gradient-to-br from-blue-500 to-purple-600 rounded-xl flex items-center justify-center text-2xl"> |
|
|
{template.icon} |
|
|
</div> |
|
|
<div> |
|
|
<CardTitle className="text-lg group-hover:text-blue-600 transition-colors"> |
|
|
{template.name} |
|
|
</CardTitle> |
|
|
<div className="flex items-center gap-2 mt-1"> |
|
|
<p className="text-sm text-blue-600 font-medium">by {template.author}</p> |
|
|
{template.isOfficial && ( |
|
|
<Badge variant="default" className="text-xs px-2 py-0 bg-blue-600"> |
|
|
<Award className="h-3 w-3 mr-1" /> |
|
|
Official |
|
|
</Badge> |
|
|
)} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<Button |
|
|
variant="ghost" |
|
|
size="sm" |
|
|
onClick={onLike} |
|
|
className={`${isLiked ? "text-red-500" : "text-gray-400 hover:text-red-500"} transition-colors`} |
|
|
title={isLiked ? "Unlike" : "Like"} |
|
|
> |
|
|
<Heart className={`h-4 w-4 ${isLiked ? 'fill-current' : ''}`} /> |
|
|
</Button> |
|
|
</div> |
|
|
</CardHeader> |
|
|
|
|
|
<CardContent className="space-y-4"> |
|
|
<p className="text-sm text-gray-600 line-clamp-2 leading-relaxed"> |
|
|
{template.description} |
|
|
</p> |
|
|
|
|
|
{/* Tags */} |
|
|
<div className="flex flex-wrap gap-2"> |
|
|
{template.tags.slice(0, 3).map((tag) => ( |
|
|
<Badge key={tag} variant="secondary" className="text-xs bg-gray-100 hover:bg-gray-200 transition-colors"> |
|
|
{tag} |
|
|
</Badge> |
|
|
))} |
|
|
</div> |
|
|
|
|
|
{/* Stats */} |
|
|
<div className="flex items-center justify-between text-sm text-gray-500"> |
|
|
<div className="flex items-center gap-4"> |
|
|
<div className="flex items-center gap-1"> |
|
|
<Eye className="h-4 w-4" /> |
|
|
<span>{template.usageCount.toLocaleString()}</span> |
|
|
</div> |
|
|
<div className="flex items-center gap-1"> |
|
|
<Star className="h-4 w-4 text-yellow-500 fill-current" /> |
|
|
<span>{template.rating}</span> |
|
|
</div> |
|
|
</div> |
|
|
<Badge variant="outline" className="text-xs"> |
|
|
{template.category} |
|
|
</Badge> |
|
|
</div> |
|
|
|
|
|
{/* Action Button */} |
|
|
<div className="pt-2"> |
|
|
<Button |
|
|
onClick={onUse} |
|
|
className="w-full group-hover:bg-blue-600 group-hover:text-white transition-all" |
|
|
variant="outline" |
|
|
> |
|
|
<MessageSquare className="h-4 w-4 mr-2" /> |
|
|
Build Assistant |
|
|
</Button> |
|
|
</div> |
|
|
</CardContent> |
|
|
</Card> |
|
|
) |
|
|
} |