wiki-project / src /components /ExploreSection.tsx
Nagi15's picture
Add codebase
fcb5a67
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;