import React, { useState, useEffect } from "react" import { useParams } from "react-router-dom" import HandleNavigate from "../../utils/HandleNavigate/HandleNavigate" import { FaArrowLeft, FaFilter, FaArrowUp, FaArrowDown, FaSortAlphaDown, FaSortAlphaUp, FaQuestionCircle, FaChevronLeft, FaChevronRight, } from "react-icons/fa" import Navbar from "../../components/Navbar/Navbar" import SearchBox from "../../components/Search/SearchBox" import PopUp from "../../components/PopUp/PopUp" import { getDeckById } from "../../api/deckApi" const Card_display = () => { const { deckId } = useParams() const { goDecks } = HandleNavigate() const [deck, setDeck] = useState(null) const [cards, setCards] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [search, setSearch] = useState("") const [sortBy, setSortBy] = useState("index") const [showSortHelp, setShowSortHelp] = useState(false) const [itemsPerPage, setItemsPerPage] = useState(20) const [currentPage, setCurrentPage] = useState(1) const [flippedCards, setFlippedCards] = useState(new Set()) // Track which cards are flipped // Load deck and parse cards useEffect(() => { let mounted = true const load = async () => { setLoading(true) setError(null) try { const result = await getDeckById(deckId) if (!mounted) return setDeck(result) // Parse cards from JSON if it's a string let parsedCards = [] if (result.cards) { if (typeof result.cards === 'string') { try { parsedCards = JSON.parse(result.cards) } catch (e) { console.error('Failed to parse cards JSON:', e) } } else if (Array.isArray(result.cards)) { parsedCards = result.cards } } setCards(parsedCards) } catch (err) { console.error('Failed to load deck', err) if (!mounted) return setError(err.message || 'Hiba történt a pakli betöltése közben.') } finally { if (mounted) setLoading(false) } } load() return () => { mounted = false } }, [deckId]) // Filter logic let filteredCards = cards.filter((card) => { if (!search) return true const searchLower = search.toLowerCase() // Check text field (or fallback to question/statement for old data) const questionText = card.text || card.question || card.statement || '' const questionMatch = questionText.toLowerCase().includes(searchLower) const answersMatch = Array.isArray(card.options) ? card.options.some(opt => opt && opt.toLowerCase().includes(searchLower)) : Array.isArray(card.answers) ? card.answers.some(a => a && a.toLowerCase().includes(searchLower)) : false return questionMatch || answersMatch }) // Sort logic filteredCards = [...filteredCards].sort((a, b) => { if (sortBy === "index") { // Keep original order return 0 } else if (sortBy === "question-asc") { const aText = a.text || a.question || a.statement || '' const bText = b.text || b.question || b.statement || '' return aText.localeCompare(bText) } else if (sortBy === "question-desc") { const aText = a.text || a.question || a.statement || '' const bText = b.text || b.question || b.statement || '' return bText.localeCompare(aText) } else if (sortBy === "answers-asc") { const aCount = Array.isArray(a.options) ? a.options.length : Array.isArray(a.answers) ? a.answers.length : 0 const bCount = Array.isArray(b.options) ? b.options.length : Array.isArray(b.answers) ? b.answers.length : 0 return aCount - bCount } else if (sortBy === "answers-desc") { const aCount = Array.isArray(a.options) ? a.options.length : Array.isArray(a.answers) ? a.answers.length : 0 const bCount = Array.isArray(b.options) ? b.options.length : Array.isArray(b.answers) ? b.answers.length : 0 return bCount - aCount } return 0 }) // Pagination logic const totalCards = filteredCards.length const totalPages = Math.ceil(totalCards / itemsPerPage) const startIndex = (currentPage - 1) * itemsPerPage const endIndex = startIndex + itemsPerPage const paginatedCards = filteredCards.slice(startIndex, endIndex) // Reset to page 1 when filters or items per page change useEffect(() => { setCurrentPage(1) }, [search, sortBy, itemsPerPage]) const deckTypes = { 0: { label: "Szerencse", color: "var(--color-luck)" }, 1: { label: "Joker", color: "var(--color-fun)" }, 2: { label: "Kérdés", color: "var(--color-question)" }, } // Card subtype Hungarian labels - UPDATED based on actual data const cardSubTypeLabels = { // String types (from DeckCreator) "quiz": "Quiz ", "truefalse": "Igaz/Hamis", "text": "Szöveges válasz", "matching": "Párosítás", "QUESTION": "Kérdés", "LUCK": "Szerencse", "JOKER": "Joker", "joker": "Joker", "luck": "Szerencse", // Backend CardType enum (numeric): "0": "Quiz", // CardType.QUIZ = 0 "1": "Párosítás", // CardType.SENTENCE_PAIRING = 1 "2": "Szöveges válasz", // CardType.OWN_ANSWER = 2 "3": "Igaz/Hamis", // CardType.TRUE_FALSE = 3 "4": "Közelítés", // CardType.CLOSER = 4 0: "Quiz", 1: "Párosítás", 2: "Szöveges válasz", 3: "Igaz/Hamis", 4: "Közelítés" } const currentDeckType = deck ? (deckTypes[deck.type] || { label: "Ismeretlen", color: "var(--color-success)" }) : null const toggleCardFlip = (cardId) => { setFlippedCards(prev => { const newSet = new Set(prev) if (newSet.has(cardId)) { newSet.delete(cardId) } else { newSet.add(cardId) } return newSet }) } return (
{/* Header with back button */}
{deck && (

{deck.name}

{currentDeckType && ( {currentDeckType.label} )}
)}
{/* Loading / Error states */} {loading && (
Betöltés...
)} {error && (
{error}
)} {/* Filters and controls */} {!loading && !error && ( <>
setSearch(e.target.value)} width={300} placeholder="Keresés..." className="w-full sm:w-auto" />
Rendezés:
{showSortHelp && ( setShowSortHelp(false)}>

Rendezési lehetőségek magyarázata

  • Eredeti sorrend – A kártyák eredeti sorrendben jelennek meg
  • Kérdés A→Z – Kérdések ABC sorrendben (A-tól Z-ig)
  • Kérdés Z→A – Kérdések fordított ABC sorrendben (Z-től A-ig)
  • Válaszok száma ↑ – Kevesebb választól a több válasz felé
  • Válaszok száma ↓ – Több választól a kevesebb válasz felé
)} {/* Items per page selector and pagination info */}
Oldalanként:
{totalCards > 0 ? ( <> {startIndex + 1}-{Math.min(endIndex, totalCards)} / {totalCards} kártya {startIndex + 1}-{Math.min(endIndex, totalCards)} / {totalCards} ) : ( <>0 kártya )}
{/* Cards Grid */}
{totalCards === 0 && (
Nincsenek kártyák ebben a pakliban.
)} {paginatedCards.map((card, idx) => { const cardIndex = startIndex + idx + 1 const questionText = card.text || card.question || card.statement || 'Kérdés hiányzik' // Get answers based on card type let answerOptions = [] let correctAnswerIndex = card.correctAnswer // Detect card type - prioritize numeric card.type over subType let detectedType = 'undefined' // First check deck type - if deck is JOKER or LUCK type, cards inherit that if (deck.type === 1) { // Deck type 1 = Joker deck detectedType = 'joker' } else if (deck.type === 0) { // Deck type 0 = Luck deck detectedType = 'luck' } else if (card.type !== undefined && card.type !== null) { // Check by card.type field (numeric CardType enum) const cardType = typeof card.type === 'string' ? card.type.toLowerCase() : card.type if (cardType === 'joker' || card.type === 'JOKER') { detectedType = 'joker' } else if (cardType === 'luck' || card.type === 'LUCK') { detectedType = 'luck' } else if (card.type === 0) { // type 0 = Quiz (multiple choice) detectedType = 'quiz' } else if (card.type === 1) { // type 1 = Matching/Pairing detectedType = 'matching' } else if (card.type === 2) { // type 2 = Text answer detectedType = 'text' } else if (card.type === 3) { // type 3 = True/False detectedType = 'truefalse' } } else if (card.subType) { // Fallback to subType if card.type is missing detectedType = String(card.subType).toLowerCase() } else if (card.leftItems && card.rightItems && card.correctPairs) { // Has leftItems, rightItems AND correctPairs = matching detectedType = 'matching' } else if (card.acceptedAnswers && card.acceptedAnswers.length > 0 && card.acceptedAnswers[0] && card.acceptedAnswers[0].trim()) { // Only detect as text if acceptedAnswers has non-empty values detectedType = 'text' } else if (card.isTrue !== undefined) { detectedType = 'truefalse' } else if (card.options && Array.isArray(card.options) && card.options.some(opt => opt && opt.trim())) { // Has non-empty options - must be multiple choice detectedType = 'multiplechoice' } // Extract consequence info for JOKER and LUCK cards let consequenceText = null if ((detectedType === 'joker' || detectedType === 'luck') && card.consequence) { const consequenceLabels = { 0: 'Lépj előre', 1: 'Lépj hátra', 2: 'Kör kihagyás', 3: 'Extra kör', 5: 'Vissza a starthoz' } const consequenceType = consequenceLabels[card.consequence.type] || 'Ismeretlen hatás' const consequenceValue = card.consequence.value if (consequenceValue && [0, 1].includes(card.consequence.type)) { consequenceText = `${consequenceType} ${consequenceValue} mezőt` } else if (consequenceValue && [2, 3].includes(card.consequence.type)) { consequenceText = `${consequenceType} (${consequenceValue} kör)` } else { consequenceText = consequenceType } } if (detectedType === 'truefalse' || detectedType === '3') { // True/False cards answerOptions = ['Igaz', 'Hamis'] // Parse answer from various sources let isCorrectTrue = false if (card.isTrue !== undefined) { isCorrectTrue = card.isTrue } else if (card.answer !== undefined) { const answerStr = String(card.answer).toLowerCase() isCorrectTrue = answerStr === 'true' || answerStr === '1' || answerStr === 'igaz' } else if (card.correctAnswer !== undefined) { isCorrectTrue = card.correctAnswer === 0 || card.correctAnswer === true || card.correctAnswer === 'true' } correctAnswerIndex = isCorrectTrue ? 0 : 1 } else if (detectedType === 'quiz' || detectedType === 'multiplechoice' || detectedType === '0') { // Quiz/Multiple choice - parse from backend answer array or frontend options if (card.answer && Array.isArray(card.answer) && card.answer.length > 0 && typeof card.answer[0] === 'object') { // Backend format: [{answer: "A", text: "...", correct: boolean}] answerOptions = card.answer.map(opt => opt.text || opt.label || '') const correctOption = card.answer.find(opt => opt.correct) correctAnswerIndex = card.answer.indexOf(correctOption) } else if (card.options && Array.isArray(card.options)) { // Frontend format: ["option1", "option2"] answerOptions = card.options.filter(opt => opt && opt.trim()) correctAnswerIndex = card.correctAnswer } } else if (detectedType === 'matching' || detectedType === '1') { // Matching cards - parse from backend answer array or frontend fields if (card.answer && Array.isArray(card.answer) && card.answer.length > 0 && typeof card.answer[0] === 'object') { // Backend format: [{left: "...", right: "..."}] answerOptions = card.answer.map(pair => `${pair.left} → ${pair.right}`) correctAnswerIndex = -1 // All pairs are correct } else if (card.leftItems && card.rightItems && card.correctPairs) { // Frontend format with leftItems, rightItems, correctPairs const pairs = [] for (const [leftIdx, rightIdx] of Object.entries(card.correctPairs)) { const left = card.leftItems[parseInt(leftIdx)] const right = card.rightItems[parseInt(rightIdx)] if (left && right) { pairs.push(`${left} → ${right}`) } } answerOptions = pairs correctAnswerIndex = -1 // All pairs are correct } } else if (detectedType === 'text' || detectedType === '2') { // OWN_ANSWER - parse from backend answer array or frontend acceptedAnswers if (card.answer) { // Backend format: ["answer1", "answer2"] or single value answerOptions = Array.isArray(card.answer) ? card.answer : [card.answer] correctAnswerIndex = -1 // All answers are correct } else if (card.acceptedAnswers && Array.isArray(card.acceptedAnswers)) { // Frontend format answerOptions = card.acceptedAnswers correctAnswerIndex = -1 // All accepted answers are correct } } else if (card.options && Array.isArray(card.options)) { // Other types with options answerOptions = card.options.filter(opt => opt && opt.trim()) } else if (card.answers && Array.isArray(card.answers)) { // Other card types with answers array answerOptions = card.answers.filter(opt => opt && opt.trim()) } else if (card.acceptedAnswers && Array.isArray(card.acceptedAnswers)) { // Fallback for accepted answers answerOptions = card.acceptedAnswers correctAnswerIndex = -1 } const answerCount = answerOptions.length const cardId = card.id || idx const isFlipped = flippedCards.has(cardId) return (
{detectedType === 'joker' ? ( // Joker card - no flip, just show the task
Kártya #{cardIndex} 🃏 JOKER
🃏
{questionText}
Típus: Joker
) : detectedType === 'luck' ? ( // Luck card - no flip, show text and consequence
Kártya #{cardIndex} 🎲 SZERENCSE
🎲
{questionText}
{consequenceText && (
{consequenceText}
)}
Típus: Szerencse
) : (
toggleCardFlip(cardId) : undefined} > {/* Front side - Question */}
Kártya #{cardIndex} {detectedType !== 'joker' && detectedType !== 'luck' && ( {answerCount} válasz )} {detectedType === 'joker' && ( 🃏 JOKER )} {detectedType === 'luck' && ( 🎲 SZERENCSE )}

{questionText}

{/* Type info only */}
Típus: {cardSubTypeLabels[detectedType] || cardSubTypeLabels[card.subType] || cardSubTypeLabels[card.type] || detectedType || 'Ismeretlen'}
Kattints a megoldáshoz →
{/* Back side - Answer */}
{detectedType === 'joker' || detectedType === 'luck' ? 'Kártya hatás' : 'Megoldás'} {detectedType === 'joker' || detectedType === 'luck' ? (detectedType === 'joker' ? '🃏 JOKER' : '🎲 SZERENCSE') : `${answerCount} válasz`}
{detectedType === 'joker' ? ( // Joker card - just show the task/challenge
🃏
{questionText}
A játékmester dönti el a teljesítést
) : detectedType === 'luck' ? ( // Luck card - show consequence
🎲
{consequenceText && (
{consequenceText}
)}
Azonnal végrehajt
) : answerCount > 0 ? (
Helyes válasz:
{detectedType === 'truefalse' || detectedType === '3' ? ( // True/False - show only the correct answer (() => { // Determine if correct answer is true let isCorrectTrue = false if (card.isTrue !== undefined) { isCorrectTrue = card.isTrue } else if (card.answer !== undefined) { const answerStr = String(card.answer).toLowerCase() isCorrectTrue = answerStr === 'true' || answerStr === '1' || answerStr === 'igaz' } else if (card.correctAnswer !== undefined) { isCorrectTrue = card.correctAnswer === 0 || card.correctAnswer === true || card.correctAnswer === 'true' } return (
✓ {isCorrectTrue ? 'Igaz' : 'Hamis'}
) })() ) : detectedType === 'matching' || detectedType === '1' ? ( // Matching - show all correct pairs
    {answerOptions.map((pair, idx) => (
  • ✓ {pair}
  • ))}
) : (detectedType === 'text' || detectedType === '2') && answerOptions.length > 0 ? ( // Text answers - show all accepted answers
    {answerOptions.map((answer, ansIdx) => (
  • ✓ {answer}
  • ))}
) : (detectedType === 'quiz' || detectedType === 'multiplechoice' || detectedType === '0') && answerOptions.length > 0 ? ( // Quiz/Multiple choice - show all options with correct one highlighted
    {answerOptions.map((option, ansIdx) => (
  • {ansIdx === correctAnswerIndex ? '✓ ' : ''}{String.fromCharCode(65 + ansIdx)}. {option}
  • ))}
) : ( // Other types - show only the correct answer correctAnswerIndex !== undefined && correctAnswerIndex !== -1 && answerOptions[correctAnswerIndex] ? (
✓ {answerOptions[correctAnswerIndex]}
) : (
Nincs megadva helyes válasz
) )}
) : (
Nincs elérhető válasz
)}
Kattints a kérdéshez ←
)}
) })}
{/* Pagination Controls */} {totalPages > 1 && (
{[...Array(totalPages)].map((_, index) => { const pageNum = index + 1 if ( pageNum === 1 || pageNum === totalPages || (pageNum >= currentPage - 1 && pageNum <= currentPage + 1) ) { return ( ) } else if ( pageNum === currentPage - 2 || pageNum === currentPage + 2 ) { return ( ... ) } return null })}
)} )}
) } export default Card_display