UNPKG

@gambito-corp/mbs-library

Version:

Librería de componentes React reutilizables - Sistema de diseño modular y escalable

242 lines (217 loc) 8.98 kB
import React, { useState, useEffect, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import TabBar from '../molecules/TabBar'; import SearchBar from '../molecules/SearchBar'; import FlashcardSlider from '../molecules/FlashcardSlider'; import ActionButtons from '../molecules/ActionButtons'; import { useApi } from '../../hooks/useApi'; const FlashcardList = ({ flashcards = [], categories = [], activeTabOverride, onStartGame, onCreateFlashcard, onCreateCategory, onEditFlashcard, onDeleteFlashcard, canEdit = false, canDelete = false, className = '', ...props }) => { const [activeTab, setActiveTab] = useState('sin-categoria'); const [selectedIds, setSelectedIds] = useState([]); const [searchValues, setSearchValues] = useState({}); const [gameLoading, setGameLoading] = useState(false); // ← AGREGAR loading state const api = useApi(); const navigate = useNavigate(); useEffect(() => { if (activeTabOverride) { setActiveTab(activeTabOverride); // Reset después de un momento para permitir cambios manuales setTimeout(() => { // No resetear aquí, dejar que el padre lo maneje }, 1000); } }, [activeTabOverride]); const tabs = useMemo(() => { // ✅ FILTRAR CATEGORÍAS VÁLIDAS const validCategories = categories.filter(category => category && category.id !== undefined && category.nombre !== undefined ); const sinCategoriaCount = flashcards.filter(f => !f.category || f.category === 'Sin Categoría').length; const baseTabs = [ { id: 'sin-categoria', name: 'Sin Categoría', count: sinCategoriaCount } ]; const categoryTabs = validCategories.map(category => ({ id: category.id, name: category.nombre, count: flashcards.filter(f => f.category === category.nombre).length })); return [...baseTabs, ...categoryTabs]; }, [flashcards, categories]); const filteredFlashcards = useMemo(() => { let filtered = flashcards; // Filtrar por categoría if (activeTab === 'sin-categoria') { filtered = flashcards.filter(f => !f.category || f.category === 'Sin Categoría'); } else { const activeCategory = categories.find(c => c.id === activeTab); if (activeCategory) { filtered = flashcards.filter(f => f.category === activeCategory.nombre); } } // Filtrar por búsqueda const searchValue = searchValues[activeTab] || ''; if (searchValue.trim()) { filtered = filtered.filter(f => f.pregunta.toLowerCase().includes(searchValue.toLowerCase()) || f.respuesta.toLowerCase().includes(searchValue.toLowerCase()) ); } return filtered; }, [flashcards, categories, activeTab, searchValues]); const allUniqueIds = useMemo(() => { return [...new Set(flashcards.map(f => f.id))]; }, [flashcards]); const categoryUniqueIds = useMemo(() => { return [...new Set(filteredFlashcards.map(f => f.id))]; }, [filteredFlashcards]); const handleTabChange = (tabId) => { setActiveTab(tabId); }; const handleSearchChange = (e) => { setSearchValues(prev => ({ ...prev, [activeTab]: e.target.value })); }; const handleClearSearch = () => { setSearchValues(prev => ({ ...prev, [activeTab]: '' })); }; const handleSelectFlashcard = (flashcardId) => { setSelectedIds(prev => { const newSet = new Set(prev); if (newSet.has(flashcardId)) { newSet.delete(flashcardId); } else { newSet.add(flashcardId); } return Array.from(newSet); }); }; const handleSelectAll = () => { setSelectedIds(prev => { const currentSet = new Set(prev); allUniqueIds.forEach(id => currentSet.add(id)); return Array.from(currentSet); }); }; const handleDeselectAll = () => { setSelectedIds([]); }; const handleSelectCategory = () => { setSelectedIds(prev => { const currentSet = new Set(prev); categoryUniqueIds.forEach(id => currentSet.add(id)); return Array.from(currentSet); }); }; const handleDeselectCategory = () => { setSelectedIds(prev => { const categorySet = new Set(categoryUniqueIds); return prev.filter(id => !categorySet.has(id)); }); }; const selectedInCategory = selectedIds.filter(id => categoryUniqueIds.includes(id)).length; const allSelected = selectedIds.length === allUniqueIds.length && allUniqueIds.length > 0; const categoryAllSelected = selectedInCategory === categoryUniqueIds.length && categoryUniqueIds.length > 0; const handleStartGame = async () => { if (selectedIds.length === 0) { console.error('❌ No hay flashcards seleccionadas'); return; } setGameLoading(true); try { const result = await api.post('/api/flashcard/start-game', { flashcard_ids: selectedIds, total_selected: selectedIds.length }); console.log('✅ Respuesta del juego:', result); if (result.success) { console.log('🎮 Juego iniciado exitosamente:', result.data); if (onStartGame) { onStartGame(result.data); } navigate('/game'); } else { console.error('❌ Error al iniciar juego:', result.error); // alert('Error al iniciar el juego: ' + result.error); } } catch (error) { console.error('💥 Error de conexión al iniciar juego:', error); // alert('Error de conexión. Inténtalo de nuevo.'); } finally { setGameLoading(false); } }; return ( <div className={`bg-white rounded-lg shadow-sm border border-gray-200 ${className}`} {...props}> {/* Slider de Tabs */} <TabBar tabs={tabs} activeTab={activeTab} onTabChange={handleTabChange} showCounts={true} className="px-6 pt-6" /> {/* Barra de búsqueda */} <SearchBar searchValue={searchValues[activeTab] || ''} onSearchChange={handleSearchChange} onClearSearch={handleClearSearch} selectedCount={selectedInCategory} totalCount={categoryUniqueIds.length} placeholder={`Buscar en ${tabs.find(t => t.id === activeTab)?.name || 'esta categoría'}...`} className="mx-6 my-4" /> {/* Slider de Flashcards */} <FlashcardSlider flashcards={filteredFlashcards} selectedIds={selectedIds} onSelectFlashcard={handleSelectFlashcard} onEditFlashcard={onEditFlashcard} onDeleteFlashcard={onDeleteFlashcard} canEdit={canEdit} canDelete={canDelete} className="h-48" /> {/* Botones de acción */} <ActionButtons selectedCount={selectedIds.length} totalCount={allUniqueIds.length} categorySelectedCount={selectedInCategory} categoryTotalCount={categoryUniqueIds.length} allSelected={allSelected} categoryAllSelected={categoryAllSelected} onStartGame={handleStartGame} onSelectAll={handleSelectAll} onDeselectAll={handleDeselectAll} onSelectCategory={handleSelectCategory} onDeselectCategory={handleDeselectCategory} onCreateFlashcard={onCreateFlashcard} onCreateCategory={onCreateCategory} /> </div> ); }; export default FlashcardList;