edgellm / frontend /src /pages /Community.tsx
wu981526092's picture
Restructure features: separate Assistants and Community from Model Catalog
1c47f1e
raw
history blame
18.7 kB
import { useState, useEffect } from 'react'
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Badge } from '@/components/ui/badge'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import {
Users,
Star,
Heart,
Download,
Copy,
Sparkles,
TrendingUp,
Award
} from 'lucide-react'
// Community template interface
interface CommunityTemplate {
id: string
name: string
description: string
author: string
category: string
tags: string[]
model: string
systemPrompt: string
temperature: number
maxTokens: number
likes: number
downloads: number
isOfficial: boolean
createdAt: string
}
// Get predefined community templates
function getCommunityTemplates(): CommunityTemplate[] {
return [
{
id: 'code-reviewer',
name: 'Code Review Expert',
description: 'Professional code reviewer that provides detailed analysis, suggests improvements, and identifies potential issues.',
author: 'EdgeLLM Team',
category: 'coding',
tags: ['code review', 'programming', 'best practices'],
model: 'Qwen/Qwen3-30B-A3B',
systemPrompt: 'You are a senior software engineer specializing in code review. Analyze the provided code for:\n\n1. **Code Quality**: Structure, readability, maintainability\n2. **Best Practices**: Following language conventions and patterns\n3. **Performance**: Potential optimizations and bottlenecks\n4. **Security**: Common vulnerabilities and security issues\n5. **Testing**: Testability and edge cases\n\nProvide constructive feedback with specific examples and actionable suggestions.',
temperature: 0.3,
maxTokens: 1500,
likes: 245,
downloads: 1200,
isOfficial: true,
createdAt: '2024-01-15'
},
{
id: 'writing-tutor',
name: 'Academic Writing Tutor',
description: 'Helps improve academic writing with structure suggestions, grammar corrections, and clarity enhancements.',
author: 'Academic Guild',
category: 'writing',
tags: ['academic writing', 'essay', 'research'],
model: 'Qwen/Qwen3-30B-A3B',
systemPrompt: 'You are an experienced academic writing tutor. Help users improve their writing by:\n\n1. **Structure & Organization**: Clear thesis, logical flow, proper transitions\n2. **Clarity & Precision**: Eliminate ambiguity, improve word choice\n3. **Academic Style**: Formal tone, appropriate citations, scholarly voice\n4. **Grammar & Mechanics**: Correct errors, improve sentence variety\n5. **Argument Development**: Strengthen evidence, address counterarguments\n\nProvide specific feedback with examples and rewrite suggestions where helpful.',
temperature: 0.4,
maxTokens: 1200,
likes: 189,
downloads: 856,
isOfficial: false,
createdAt: '2024-01-20'
},
{
id: 'data-analyst',
name: 'Data Analysis Assistant',
description: 'Helps analyze data, create visualizations, and explain statistical concepts in simple terms.',
author: 'DataPro',
category: 'analysis',
tags: ['data science', 'statistics', 'visualization'],
model: 'Qwen/Qwen3-30B-A3B',
systemPrompt: 'You are a data analysis expert. Help users understand and analyze data by:\n\n1. **Data Exploration**: Identify patterns, outliers, relationships\n2. **Statistical Analysis**: Apply appropriate tests, interpret results\n3. **Visualization**: Suggest effective charts and graphs\n4. **Insights**: Draw meaningful conclusions from data\n5. **Communication**: Explain complex concepts simply\n\nProvide step-by-step analysis and practical recommendations.',
temperature: 0.2,
maxTokens: 1000,
likes: 156,
downloads: 643,
isOfficial: false,
createdAt: '2024-01-25'
},
{
id: 'creative-writer',
name: 'Creative Writing Coach',
description: 'Inspires creativity and helps develop compelling stories, characters, and narrative techniques.',
author: 'StoryMaster',
category: 'creative',
tags: ['creative writing', 'storytelling', 'fiction'],
model: 'Qwen/Qwen3-30B-A3B',
systemPrompt: 'You are a creative writing coach with expertise in storytelling. Help writers by:\n\n1. **Story Development**: Plot structure, pacing, conflict resolution\n2. **Character Creation**: Compelling personalities, realistic dialogue, character arcs\n3. **World Building**: Consistent settings, atmosphere, details\n4. **Writing Techniques**: Show vs tell, point of view, voice\n5. **Inspiration**: Creative prompts, overcoming writer\'s block\n\nProvide encouraging feedback and concrete suggestions to enhance creativity.',
temperature: 0.8,
maxTokens: 1200,
likes: 312,
downloads: 987,
isOfficial: false,
createdAt: '2024-01-30'
},
{
id: 'interview-prep',
name: 'Interview Preparation Coach',
description: 'Helps prepare for job interviews with practice questions, answer strategies, and confidence building.',
author: 'CareerBoost',
category: 'career',
tags: ['interview', 'job search', 'career'],
model: 'Qwen/Qwen3-30B-A3B',
systemPrompt: 'You are a professional career coach specializing in interview preparation. Help candidates by:\n\n1. **Question Practice**: Common and behavioral interview questions\n2. **Answer Framework**: STAR method, structured responses\n3. **Company Research**: Industry insights, company-specific preparation\n4. **Confidence Building**: Reducing anxiety, improving presentation\n5. **Follow-up**: Thank you notes, next steps\n\nProvide personalized advice and realistic practice scenarios.',
temperature: 0.5,
maxTokens: 1000,
likes: 278,
downloads: 1456,
isOfficial: true,
createdAt: '2024-02-05'
}
]
}
export function Community() {
const [communityTemplates] = useState<CommunityTemplate[]>(getCommunityTemplates())
const [likedTemplates, setLikedTemplates] = useState<string[]>([])
useEffect(() => {
loadLikedTemplates()
}, [])
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))
window.location.href = '/playground'
}
const featuredTemplates = communityTemplates.filter(t => t.isOfficial || t.likes > 200)
const totalLikes = communityTemplates.reduce((sum, t) => sum + t.likes, 0)
const totalDownloads = communityTemplates.reduce((sum, t) => sum + t.downloads, 0)
return (
<div className="min-h-screen bg-background">
{/* Header */}
<div className="border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="max-w-6xl mx-auto p-6">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center">
<Users className="h-5 w-5 text-white" />
</div>
<div>
<h1 className="text-2xl font-bold">Community Templates</h1>
<p className="text-sm text-muted-foreground">
Discover and share AI assistant templates created by the community
</p>
</div>
</div>
<div className="flex items-center gap-4 text-sm text-muted-foreground">
<div className="flex items-center gap-1">
<Heart className="h-4 w-4" />
<span>{totalLikes.toLocaleString()} likes</span>
</div>
<div className="flex items-center gap-1">
<Download className="h-4 w-4" />
<span>{totalDownloads.toLocaleString()} downloads</span>
</div>
</div>
</div>
</div>
</div>
<div className="flex-1 p-6">
<div className="max-w-6xl mx-auto space-y-6">
{/* Info Card */}
<Card className="bg-blue-50 border-blue-200">
<CardContent className="pt-6">
<div className="flex items-start gap-3">
<Sparkles className="h-5 w-5 text-blue-600 mt-0.5" />
<div>
<h3 className="font-medium text-blue-900">Community-Powered AI</h3>
<p className="text-sm text-blue-700 mt-1">
Explore specialized AI assistant templates created by experts and the community.
Click "Use Template" to load any configuration directly into your Playground.
</p>
</div>
</div>
</CardContent>
</Card>
{/* Tabs */}
<Tabs defaultValue="all" className="space-y-6">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="all" className="flex items-center gap-2">
<Users className="h-4 w-4" />
All Templates
</TabsTrigger>
<TabsTrigger value="featured" className="flex items-center gap-2">
<Star className="h-4 w-4" />
Featured
</TabsTrigger>
<TabsTrigger value="liked" className="flex items-center gap-2">
<Heart className="h-4 w-4" />
My Likes
</TabsTrigger>
</TabsList>
{/* All Templates Tab */}
<TabsContent value="all" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{communityTemplates.map((template) => (
<CommunityTemplateCard
key={template.id}
template={template}
isLiked={likedTemplates.includes(template.id)}
onLike={() => toggleLikeTemplate(template.id)}
onUse={() => useTemplate(template)}
/>
))}
</div>
</TabsContent>
{/* Featured Templates Tab */}
<TabsContent value="featured" className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{featuredTemplates.map((template) => (
<CommunityTemplateCard
key={template.id}
template={template}
isLiked={likedTemplates.includes(template.id)}
onLike={() => toggleLikeTemplate(template.id)}
onUse={() => useTemplate(template)}
featured={true}
/>
))}
</div>
</TabsContent>
{/* Liked Templates Tab */}
<TabsContent value="liked" className="space-y-6">
{likedTemplates.length > 0 ? (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{communityTemplates
.filter(t => likedTemplates.includes(t.id))
.map((template) => (
<CommunityTemplateCard
key={template.id}
template={template}
isLiked={true}
onLike={() => toggleLikeTemplate(template.id)}
onUse={() => useTemplate(template)}
/>
))
}
</div>
) : (
<Card className="text-center py-16">
<Heart className="h-16 w-16 mx-auto text-muted-foreground mb-6" />
<h3 className="text-xl font-medium mb-3">No liked templates yet</h3>
<p className="text-muted-foreground mb-6 max-w-md mx-auto">
Explore the community templates and click the heart icon to save your favorites for easy access.
</p>
</Card>
)}
</TabsContent>
</Tabs>
{/* Stats Cards */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<Card>
<CardContent className="pt-6">
<div className="flex items-center justify-between">
<div>
<div className="text-2xl font-bold text-blue-600">
{communityTemplates.length}
</div>
<div className="text-sm text-muted-foreground">Templates</div>
</div>
<Copy className="h-8 w-8 text-blue-600" />
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center justify-between">
<div>
<div className="text-2xl font-bold text-green-600">
{communityTemplates.filter(t => t.isOfficial).length}
</div>
<div className="text-sm text-muted-foreground">Official</div>
</div>
<Award className="h-8 w-8 text-green-600" />
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center justify-between">
<div>
<div className="text-2xl font-bold text-red-500">
{totalLikes.toLocaleString()}
</div>
<div className="text-sm text-muted-foreground">Total Likes</div>
</div>
<Heart className="h-8 w-8 text-red-500" />
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="flex items-center justify-between">
<div>
<div className="text-2xl font-bold text-purple-600">
{totalDownloads.toLocaleString()}
</div>
<div className="text-sm text-muted-foreground">Downloads</div>
</div>
<TrendingUp className="h-8 w-8 text-purple-600" />
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</div>
)
}
// Community Template Card Component
function CommunityTemplateCard({
template,
isLiked,
onLike,
onUse,
featured = false
}: {
template: CommunityTemplate
isLiked: boolean
onLike: () => void
onUse: () => void
featured?: boolean
}) {
return (
<Card className={`hover:shadow-lg transition-all duration-200 hover:-translate-y-1 ${featured ? 'ring-2 ring-yellow-200 shadow-md' : ''}`}>
<CardHeader className="pb-3">
<div className="flex items-start justify-between">
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<CardTitle className="text-base">{template.name}</CardTitle>
{template.isOfficial && (
<Badge variant="default" className="text-xs px-2 py-0 bg-green-600">
<Award className="h-3 w-3 mr-1" />
Official
</Badge>
)}
{featured && (
<Star className="h-4 w-4 text-yellow-500 fill-current" />
)}
</div>
<p className="text-xs text-blue-600 font-medium">by {template.author}</p>
<p className="text-sm text-muted-foreground mt-2 line-clamp-2">
{template.description}
</p>
</div>
<Button
variant="ghost"
size="sm"
onClick={onLike}
className={isLiked ? "text-red-500" : "text-muted-foreground hover:text-red-500"}
title={isLiked ? "Unlike" : "Like"}
>
<Heart className={`h-4 w-4 ${isLiked ? 'fill-current' : ''}`} />
</Button>
</div>
</CardHeader>
<CardContent className="space-y-4">
{/* Tags */}
<div className="flex flex-wrap gap-1">
{template.tags.slice(0, 3).map((tag) => (
<Badge key={tag} variant="secondary" className="text-xs">
{tag}
</Badge>
))}
</div>
{/* Configuration */}
<div className="grid grid-cols-2 gap-2 text-xs">
<div className="bg-gray-50 rounded p-2">
<span className="font-medium text-gray-600">Temperature:</span>
<p className="text-gray-800">{template.temperature}</p>
</div>
<div className="bg-gray-50 rounded p-2">
<span className="font-medium text-gray-600">Max Tokens:</span>
<p className="text-gray-800">{template.maxTokens}</p>
</div>
</div>
{/* Stats & Action */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-4 text-xs text-muted-foreground">
<div className="flex items-center gap-1">
<Heart className="h-3 w-3" />
<span>{template.likes}</span>
</div>
<div className="flex items-center gap-1">
<Download className="h-3 w-3" />
<span>{template.downloads}</span>
</div>
</div>
<Button size="sm" onClick={onUse} className="flex items-center gap-1">
<Copy className="h-3 w-3" />
Use
</Button>
</div>
</CardContent>
</Card>
)
}