import React, { useState, useMemo } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Search, SortAsc, SortDesc } from 'lucide-react'; import Layout from '@/components/Layout'; interface Interactive { id: string; title: string; description: string; tags: string[]; path: string; theme: string; dateAdded?: string; // For recency sorting } // This would ideally be generated from the actual components, but for now we'll hardcode const allInteractives: Interactive[] = [ { id: 'binary-number-game', title: 'Binary Number Representation', description: 'Learn how numbers are represented in binary (base-2) format through an interactive guessing game.', tags: ['binary', 'numbers', 'conversion', 'representation'], path: '/binary-number-game', theme: 'Discrete Math', dateAdded: '2024-01-15' }, { id: 'binary-search-trick', title: 'Binary Search Magic Trick', description: 'Perform an amazing magic trick that reveals how binary numbers work. Think of a number and watch the magic happen!', tags: ['binary', 'magic', 'numbers', 'trick', 'data-structures'], path: '/binary-search-trick', theme: 'Data Structures', dateAdded: '2024-02-20' }, { id: 'game-of-sim', title: 'Game of Sim', description: 'A strategic two-player game where you take turns coloring edges between vertices, trying to avoid creating a triangle of your own color.', tags: ['strategy', 'graph-theory', 'two-player', 'logic', 'game-theory'], path: '/game-of-sim', theme: 'Games', dateAdded: '2024-03-10' }, { id: 'ternary-search-trick', title: 'Ternary Search Magic Trick', description: 'Experience the magic of ternary search! Think of a number and watch as the cards reveal it through mathematical calculations.', tags: ['ternary', 'magic', 'numbers', 'trick', 'data-structures', 'search'], path: '/ternary-search-trick', theme: 'Data Structures', dateAdded: '2024-07-20' } ]; type SortField = 'title' | 'dateAdded'; type SortDirection = 'asc' | 'desc'; const InteractiveIndex = () => { const [searchTerm, setSearchTerm] = useState(''); const [selectedTag, setSelectedTag] = useState(''); const [sortField, setSortField] = useState('title'); const [sortDirection, setSortDirection] = useState('asc'); const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 10; // Get all unique tags const allTags = useMemo(() => { const tags = new Set(); allInteractives.forEach(interactive => { interactive.tags.forEach(tag => tags.add(tag)); }); return Array.from(tags).sort(); }, []); // Filter and sort interactives const filteredAndSortedInteractives = useMemo(() => { let filtered = allInteractives.filter(interactive => { const matchesSearch = interactive.title.toLowerCase().includes(searchTerm.toLowerCase()) || interactive.description.toLowerCase().includes(searchTerm.toLowerCase()) || interactive.tags.some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase())); const matchesTag = !selectedTag || interactive.tags.includes(selectedTag); return matchesSearch && matchesTag; }); // Sort filtered.sort((a, b) => { let aValue = a[sortField]; let bValue = b[sortField]; if (sortField === 'title') { aValue = aValue.toLowerCase(); bValue = bValue.toLowerCase(); } if (aValue < bValue) return sortDirection === 'asc' ? -1 : 1; if (aValue > bValue) return sortDirection === 'asc' ? 1 : -1; return 0; }); return filtered; }, [searchTerm, selectedTag, sortField, sortDirection]); // Pagination const totalPages = Math.ceil(filteredAndSortedInteractives.length / itemsPerPage); const startIndex = (currentPage - 1) * itemsPerPage; const paginatedInteractives = filteredAndSortedInteractives.slice(startIndex, startIndex + itemsPerPage); const handleTagClick = (tag: string) => { setSelectedTag(selectedTag === tag ? '' : tag); setCurrentPage(1); }; const handleSortChange = (field: SortField) => { if (sortField === field) { setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc'); } else { setSortField(field); setSortDirection('asc'); } setCurrentPage(1); }; return (

All Interactives

{/* Search and Sort Controls */}
{ setSearchTerm(e.target.value); setCurrentPage(1); }} className="pl-10" />
{/* Tag Filter */} {selectedTag && (
Filtered by: setSelectedTag('')} > {selectedTag} ×
)} {/* Tags */}

Filter by Tags

{allTags.map(tag => ( handleTagClick(tag)} > {tag} ))}
{/* Results Count */}

Showing {filteredAndSortedInteractives.length} of {allInteractives.length} interactives

{/* Interactive List */}
{paginatedInteractives.map(interactive => ( {interactive.title}
{interactive.tags.map(tag => ( handleTagClick(tag)} > {tag} ))}

{interactive.description}

{interactive.theme}
))}
{/* Pagination */} {totalPages > 1 && (
{Array.from({ length: totalPages }, (_, i) => i + 1).map(page => ( ))}
)} {paginatedInteractives.length === 0 && (

No interactives found matching your criteria.

)}
); }; export default InteractiveIndex;