Spaces:
Runtime error
Runtime error
| import React, { useState, useEffect } from 'react'; | |
| import { Compass, Shuffle, BookOpen, Quote, GraduationCap, Languages, FileText, Sparkles, ExternalLink, ArrowLeft, Loader2 } from 'lucide-react'; | |
| import { WikimediaAPI, WIKIMEDIA_PROJECTS } from '../utils/wikimedia-api'; | |
| import { SearchResult } from '../types'; | |
| interface ExploreSectionProps { | |
| onViewArticle?: (title: string, project: string, content: string) => void; | |
| } | |
| const ExploreSection: React.FC<ExploreSectionProps> = ({ onViewArticle }) => { | |
| const [randomArticles, setRandomArticles] = useState<SearchResult[]>([]); | |
| const [loading, setLoading] = useState(false); | |
| const [selectedCategory, setSelectedCategory] = useState('featured'); | |
| const [selectedProject, setSelectedProject] = useState<string | null>(null); | |
| const [projectContent, setProjectContent] = useState<SearchResult[]>([]); | |
| const [loadingProject, setLoadingProject] = useState(false); | |
| const categories = [ | |
| { id: 'featured', name: 'Featured Content', icon: Sparkles }, | |
| { id: 'trending', name: 'Trending Topics', icon: Compass }, | |
| { id: 'random', name: 'Random Discovery', icon: Shuffle }, | |
| ]; | |
| const projectCards = WIKIMEDIA_PROJECTS.map(project => ({ | |
| ...project, | |
| stats: { | |
| articles: Math.floor(Math.random() * 1000000) + 100000, | |
| languages: Math.floor(Math.random() * 100) + 50, | |
| contributors: Math.floor(Math.random() * 50000) + 10000, | |
| } | |
| })); | |
| const getIconComponent = (iconName: string) => { | |
| const icons: { [key: string]: React.ComponentType<any> } = { | |
| Book: BookOpen, | |
| BookOpen: BookOpen, | |
| Quote: Quote, | |
| GraduationCap: GraduationCap, | |
| Languages: Languages, | |
| FileText: FileText, | |
| }; | |
| return icons[iconName] || BookOpen; | |
| }; | |
| const loadRandomContent = async () => { | |
| setLoading(true); | |
| try { | |
| const allArticles: SearchResult[] = []; | |
| // Get random articles from multiple projects | |
| for (const project of WIKIMEDIA_PROJECTS.slice(0, 3)) { | |
| const articles = await WikimediaAPI.getRandomArticles(project.id, 2); | |
| allArticles.push(...articles); | |
| } | |
| // Shuffle the articles | |
| const shuffled = allArticles.sort(() => 0.5 - Math.random()); | |
| setRandomArticles(shuffled.slice(0, 6)); | |
| } catch (error) { | |
| console.error('Failed to load random content:', error); | |
| } finally { | |
| setLoading(false); | |
| } | |
| }; | |
| const handleProjectClick = async (projectId: string) => { | |
| setSelectedProject(projectId); | |
| setLoadingProject(true); | |
| try { | |
| // Get featured/popular content from the selected project | |
| const content = await WikimediaAPI.getRandomArticles(projectId, 12); | |
| setProjectContent(content); | |
| } catch (error) { | |
| console.error('Failed to load project content:', error); | |
| setProjectContent([]); | |
| } finally { | |
| setLoadingProject(false); | |
| } | |
| }; | |
| const handleViewInWikistro = async (article: SearchResult) => { | |
| try { | |
| const content = await WikimediaAPI.getPageContent(article.title, article.project); | |
| if (onViewArticle) { | |
| onViewArticle(article.title, article.project, content); | |
| } | |
| } catch (error) { | |
| console.error('Failed to load article content:', error); | |
| } | |
| }; | |
| useEffect(() => { | |
| if (selectedCategory === 'random') { | |
| loadRandomContent(); | |
| } | |
| }, [selectedCategory]); | |
| const featuredTopics = [ | |
| { | |
| title: 'Artificial Intelligence', | |
| description: 'Explore the fascinating world of AI, machine learning, and neural networks', | |
| image: 'https://images.pexels.com/photos/8386440/pexels-photo-8386440.jpeg?auto=compress&cs=tinysrgb&w=400', | |
| project: 'wikipedia', | |
| readTime: '15 min read' | |
| }, | |
| { | |
| title: 'Climate Change', | |
| description: 'Understanding global warming, its causes, effects, and potential solutions', | |
| image: 'https://images.pexels.com/photos/60013/desert-drought-dehydrated-clay-soil-60013.jpeg?auto=compress&cs=tinysrgb&w=400', | |
| project: 'wikipedia', | |
| readTime: '12 min read' | |
| }, | |
| { | |
| title: 'Quantum Physics', | |
| description: 'Dive into the mysterious world of quantum mechanics and particle physics', | |
| image: 'https://images.pexels.com/photos/998641/pexels-photo-998641.jpeg?auto=compress&cs=tinysrgb&w=400', | |
| project: 'wikipedia', | |
| readTime: '20 min read' | |
| }, | |
| { | |
| title: 'Ancient History', | |
| description: 'Journey through ancient civilizations and their remarkable achievements', | |
| image: 'https://images.pexels.com/photos/161799/egypt-hieroglyphics-text-wall-161799.jpeg?auto=compress&cs=tinysrgb&w=400', | |
| project: 'wikipedia', | |
| readTime: '18 min read' | |
| }, | |
| { | |
| title: 'Space Exploration', | |
| description: 'Discover the latest in space technology and cosmic discoveries', | |
| image: 'https://images.pexels.com/photos/586056/pexels-photo-586056.jpeg?auto=compress&cs=tinysrgb&w=400', | |
| project: 'wikipedia', | |
| readTime: '16 min read' | |
| }, | |
| { | |
| title: 'Renewable Energy', | |
| description: 'Learn about sustainable energy sources and green technologies', | |
| image: 'https://images.pexels.com/photos/9800029/pexels-photo-9800029.jpeg?auto=compress&cs=tinysrgb&w=400', | |
| project: 'wikipedia', | |
| readTime: '14 min read' | |
| } | |
| ]; | |
| // If a project is selected, show project content | |
| if (selectedProject) { | |
| const project = WIKIMEDIA_PROJECTS.find(p => p.id === selectedProject); | |
| const Icon = project ? getIconComponent(project.icon) : BookOpen; | |
| return ( | |
| <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> | |
| <div className="mb-8"> | |
| <button | |
| onClick={() => setSelectedProject(null)} | |
| className="flex items-center space-x-2 text-primary-600 hover:text-primary-700 font-medium mb-4" | |
| > | |
| <ArrowLeft className="w-4 h-4" /> | |
| <span>Back to Explore</span> | |
| </button> | |
| {project && ( | |
| <div className="flex items-center space-x-4 mb-6"> | |
| <div className={`w-16 h-16 ${project.color} rounded-2xl flex items-center justify-center`}> | |
| <Icon className="w-8 h-8 text-white" /> | |
| </div> | |
| <div> | |
| <h1 className="text-3xl font-bold text-gray-900">{project.name}</h1> | |
| <p className="text-gray-600">{project.description}</p> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| {loadingProject ? ( | |
| <div className="flex items-center justify-center py-12"> | |
| <Loader2 className="w-8 h-8 animate-spin text-primary-600" /> | |
| <span className="ml-3 text-gray-600">Loading {project?.name} content...</span> | |
| </div> | |
| ) : ( | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| {projectContent.map((article, index) => ( | |
| <div | |
| key={index} | |
| className="bg-white border border-gray-200 rounded-2xl p-6 hover:shadow-md transition-all duration-200 hover:border-primary-200" | |
| > | |
| <div className="flex items-center space-x-2 mb-3"> | |
| <span className="text-xs font-medium text-secondary-600 bg-secondary-50 px-2 py-1 rounded-full"> | |
| {article.project} | |
| </span> | |
| </div> | |
| <h3 className="text-lg font-semibold text-gray-900 mb-2"> | |
| {article.title} | |
| </h3> | |
| <p className="text-gray-600 text-sm mb-4">{article.snippet}</p> | |
| <div className="flex space-x-2"> | |
| <button | |
| onClick={() => handleViewInWikistro(article)} | |
| className="flex-1 px-3 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors text-sm" | |
| > | |
| View in Wikistro | |
| </button> | |
| <a | |
| href={article.url} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="px-3 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors" | |
| > | |
| <ExternalLink className="w-4 h-4" /> | |
| </a> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> | |
| <div className="mb-8"> | |
| <h1 className="text-3xl font-bold text-gray-900 mb-2">Explore Knowledge</h1> | |
| <p className="text-gray-600">Discover amazing content across Wikimedia projects</p> | |
| </div> | |
| {/* Category Tabs */} | |
| <div className="mb-8"> | |
| <div className="flex space-x-1 bg-gray-100 p-1 rounded-xl"> | |
| {categories.map((category) => { | |
| const Icon = category.icon; | |
| return ( | |
| <button | |
| key={category.id} | |
| onClick={() => setSelectedCategory(category.id)} | |
| className={`flex items-center space-x-2 px-4 py-3 rounded-lg text-sm font-medium transition-all ${ | |
| selectedCategory === category.id | |
| ? 'bg-white text-primary-700 shadow-sm' | |
| : 'text-gray-600 hover:text-gray-900' | |
| }`} | |
| > | |
| <Icon className="w-4 h-4" /> | |
| <span>{category.name}</span> | |
| </button> | |
| ); | |
| })} | |
| </div> | |
| </div> | |
| {/* Wikimedia Projects Overview */} | |
| <div className="mb-12"> | |
| <h2 className="text-2xl font-semibold text-gray-900 mb-6">Wikimedia Projects</h2> | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| {projectCards.map((project) => { | |
| const Icon = getIconComponent(project.icon); | |
| return ( | |
| <div | |
| key={project.id} | |
| onClick={() => handleProjectClick(project.id)} | |
| className="bg-white border border-gray-200 rounded-2xl p-6 hover:shadow-lg transition-all duration-200 hover:border-primary-200 cursor-pointer group" | |
| > | |
| <div className="flex items-center space-x-3 mb-4"> | |
| <div className={`w-12 h-12 ${project.color} rounded-xl flex items-center justify-center group-hover:scale-110 transition-transform`}> | |
| <Icon className="w-6 h-6 text-white" /> | |
| </div> | |
| <div className="flex-1"> | |
| <h3 className="font-semibold text-gray-900 group-hover:text-primary-600 transition-colors">{project.name}</h3> | |
| <p className="text-sm text-gray-600">{project.description}</p> | |
| </div> | |
| <ExternalLink className="w-5 h-5 text-gray-400 group-hover:text-primary-600 transition-colors" /> | |
| </div> | |
| <div className="grid grid-cols-3 gap-4 text-center"> | |
| <div> | |
| <div className="text-lg font-semibold text-gray-900"> | |
| {(project.stats.articles / 1000).toFixed(0)}K | |
| </div> | |
| <div className="text-xs text-gray-600">Articles</div> | |
| </div> | |
| <div> | |
| <div className="text-lg font-semibold text-gray-900"> | |
| {project.stats.languages} | |
| </div> | |
| <div className="text-xs text-gray-600">Languages</div> | |
| </div> | |
| <div> | |
| <div className="text-lg font-semibold text-gray-900"> | |
| {(project.stats.contributors / 1000).toFixed(0)}K | |
| </div> | |
| <div className="text-xs text-gray-600">Contributors</div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| </div> | |
| {/* Content based on selected category */} | |
| {selectedCategory === 'featured' && ( | |
| <div> | |
| <h2 className="text-2xl font-semibold text-gray-900 mb-6">Featured Topics</h2> | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| {featuredTopics.map((topic, index) => ( | |
| <div | |
| key={index} | |
| className="bg-white rounded-2xl overflow-hidden shadow-sm hover:shadow-lg transition-all duration-300 cursor-pointer group" | |
| onClick={() => handleViewInWikistro({ | |
| title: topic.title, | |
| pageid: index, | |
| size: 0, | |
| snippet: topic.description, | |
| timestamp: new Date().toISOString(), | |
| project: topic.project, | |
| url: `https://en.wikipedia.org/wiki/${encodeURIComponent(topic.title)}` | |
| })} | |
| > | |
| <div className="aspect-w-16 aspect-h-9 overflow-hidden"> | |
| <img | |
| src={topic.image} | |
| alt={topic.title} | |
| className="w-full h-48 object-cover group-hover:scale-105 transition-transform duration-300" | |
| /> | |
| </div> | |
| <div className="p-6"> | |
| <div className="flex items-center justify-between mb-3"> | |
| <span className="text-xs font-medium text-primary-600 bg-primary-50 px-2 py-1 rounded-full"> | |
| {topic.project} | |
| </span> | |
| <span className="text-xs text-gray-500">{topic.readTime}</span> | |
| </div> | |
| <h3 className="text-lg font-semibold text-gray-900 mb-2 group-hover:text-primary-600 transition-colors"> | |
| {topic.title} | |
| </h3> | |
| <p className="text-gray-600 text-sm leading-relaxed">{topic.description}</p> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| )} | |
| {selectedCategory === 'random' && ( | |
| <div> | |
| <div className="flex items-center justify-between mb-6"> | |
| <h2 className="text-2xl font-semibold text-gray-900">Random Discovery</h2> | |
| <button | |
| onClick={loadRandomContent} | |
| disabled={loading} | |
| className="flex items-center space-x-2 px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors disabled:opacity-50" | |
| > | |
| <Shuffle className="w-4 h-4" /> | |
| <span>{loading ? 'Loading...' : 'Shuffle'}</span> | |
| </button> | |
| </div> | |
| {loading && ( | |
| <div className="flex items-center justify-center py-12"> | |
| <Loader2 className="w-8 h-8 animate-spin text-primary-600" /> | |
| <span className="ml-3 text-gray-600">Finding random articles...</span> | |
| </div> | |
| )} | |
| {!loading && randomArticles.length > 0 && ( | |
| <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| {randomArticles.map((article, index) => ( | |
| <div | |
| key={index} | |
| className="bg-white border border-gray-200 rounded-2xl p-6 hover:shadow-md transition-all duration-200 hover:border-primary-200" | |
| > | |
| <div className="flex items-center space-x-2 mb-3"> | |
| <span className="text-xs font-medium text-secondary-600 bg-secondary-50 px-2 py-1 rounded-full"> | |
| {article.project} | |
| </span> | |
| </div> | |
| <h3 className="text-lg font-semibold text-gray-900 mb-2"> | |
| {article.title} | |
| </h3> | |
| <p className="text-gray-600 text-sm mb-4">{article.snippet}</p> | |
| <div className="flex space-x-2"> | |
| <button | |
| onClick={() => handleViewInWikistro(article)} | |
| className="flex-1 px-3 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors text-sm" | |
| > | |
| View in Wikistro | |
| </button> | |
| <a | |
| href={article.url} | |
| target="_blank" | |
| rel="noopener noreferrer" | |
| className="px-3 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors" | |
| > | |
| <ExternalLink className="w-4 h-4" /> | |
| </a> | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| )} | |
| {selectedCategory === 'trending' && ( | |
| <div> | |
| <h2 className="text-2xl font-semibold text-gray-900 mb-6">Trending Topics</h2> | |
| <div className="bg-white rounded-2xl p-8 text-center border border-gray-200"> | |
| <Compass className="w-16 h-16 text-gray-300 mx-auto mb-4" /> | |
| <h3 className="text-lg font-medium text-gray-900 mb-2">Coming Soon</h3> | |
| <p className="text-gray-600"> | |
| We're working on bringing you the most trending topics across Wikimedia projects. | |
| </p> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| }; | |
| export default ExploreSection; |