05a1ad4017
Reviewed-on: #103
320 lines
11 KiB
React
320 lines
11 KiB
React
// src/components/DeckCreator/CardEditor.jsx
|
||
// Jobb oldali kártya szerkesztő
|
||
|
||
import React, { useState, useEffect } from "react"
|
||
import { FaSave, FaTimes, FaEye } from "react-icons/fa"
|
||
import TaskCardEditor from "./TaskCardEditor.jsx"
|
||
import JokerCardEditor from "./JokerCardEditor.jsx"
|
||
import LuckCardEditor from "./LuckCardEditor.jsx"
|
||
import CardPreview from "./CardPreview.jsx"
|
||
import { notifySuccess, notifyError,notifyWarning } from "../../components/Toastify/toastifyServices"
|
||
|
||
|
||
export default function CardEditor({ card, isCreating, cardType, onSave, onCancel }) {
|
||
const [cardData, setCardData] = useState(null)
|
||
const [showPreview, setShowPreview] = useState(false)
|
||
|
||
// Alapértelmezett kártya adatok
|
||
const getDefaultCardData = (type) => {
|
||
const baseData = {
|
||
id: null,
|
||
type: type,
|
||
points: 10,
|
||
timeLimit: 30,
|
||
consequence: { type: 0, value: 1 }
|
||
}
|
||
|
||
switch (type) {
|
||
case 'QUESTION':
|
||
return {
|
||
...baseData,
|
||
subType: 'quiz',
|
||
question: '',
|
||
options: ['', '', '', ''],
|
||
correctAnswer: 0,
|
||
explanation: '',
|
||
acceptedAnswers: [''],
|
||
wrongConsequence: { type: 1, value: 1 }
|
||
}
|
||
case 'PAIRING':
|
||
case 'MATCHING':
|
||
return {
|
||
...baseData,
|
||
type: 'QUESTION',
|
||
subType: 'matching',
|
||
taskDescription: '',
|
||
leftItems: ['', ''],
|
||
rightItems: ['', ''],
|
||
correctPairs: { 0: 0, 1: 1 },
|
||
wrongConsequence: { type: 1, value: 1 }
|
||
}
|
||
case 'JOKER':
|
||
return {
|
||
...baseData,
|
||
title: '',
|
||
description: '',
|
||
effect: '',
|
||
actionType: 'skip',
|
||
usage: 'once',
|
||
wrongConsequence: { type: 1, value: 1 }
|
||
}
|
||
case 'LUCK':
|
||
return {
|
||
...baseData,
|
||
event: '',
|
||
positiveEffect: '',
|
||
negativeEffect: '',
|
||
probability: 50,
|
||
risk: 'low'
|
||
}
|
||
default:
|
||
return baseData
|
||
}
|
||
}
|
||
|
||
// Kártya adatok inicializálása
|
||
useEffect(() => {
|
||
try {
|
||
if (isCreating && cardType) {
|
||
const defaultData = getDefaultCardData(cardType)
|
||
setCardData(defaultData)
|
||
} else if (card) {
|
||
setCardData({ ...card })
|
||
} else {
|
||
setCardData(null)
|
||
}
|
||
} catch (error) {
|
||
console.error('Kártya inicializálási hiba:', error)
|
||
setCardData(null)
|
||
}
|
||
}, [card, isCreating, cardType])
|
||
|
||
const validateCard = (data) => {
|
||
try {
|
||
if (!data || !data.type) {
|
||
notifyError("Érvénytelen kártya adatok!")
|
||
return false
|
||
}
|
||
|
||
if (data.type === 'QUESTION') {
|
||
// Quiz típus validálás
|
||
if (data.subType === 'quiz') {
|
||
if (!data.text || !data.text.trim()) {
|
||
notifyError("Kérdés megadása kötelező!")
|
||
return false
|
||
}
|
||
if (data.options && data.options.some(opt => !opt.trim())) {
|
||
notifyError("Minden válaszlehetőséget ki kell tölteni!")
|
||
return false
|
||
}
|
||
}
|
||
// Igaz/Hamis típus validálás
|
||
else if (data.subType === 'truefalse') {
|
||
if (!data.text || !data.text.trim()) {
|
||
notifyError("Állítás megadása kötelező!")
|
||
return false
|
||
}
|
||
if (data.isTrue === undefined || data.isTrue === null) {
|
||
notifyError("Válaszd ki, hogy az állítás igaz vagy hamis!")
|
||
return false
|
||
}
|
||
}
|
||
// Párosítás típus validálás
|
||
else if (data.subType === 'matching') {
|
||
if (!data.text || !data.text.trim()) {
|
||
notifyError("Feladat leírása kötelező!")
|
||
return false
|
||
}
|
||
if (!data.leftItems || data.leftItems.length === 0) {
|
||
notifyError("Legalább egy párosítást meg kell adni!")
|
||
return false
|
||
}
|
||
if (data.leftItems.some(item => !item.trim()) || data.rightItems.some(item => !item.trim())) {
|
||
notifyError("Minden párosítási elemet ki kell tölteni!")
|
||
return false
|
||
}
|
||
}
|
||
// Szöveges válasz típus validálás
|
||
else if (data.subType === 'text') {
|
||
if (!data.text || !data.text.trim()) {
|
||
notifyError("Kérdés megadása kötelező!")
|
||
return false
|
||
}
|
||
if (!data.acceptedAnswers || data.acceptedAnswers.length === 0 || data.acceptedAnswers.every(ans => !ans.trim())) {
|
||
notifyError("Legalább egy elfogadott választ meg kell adni!")
|
||
return false
|
||
}
|
||
}
|
||
// Általános validálás (ha nincs subType megadva)
|
||
else {
|
||
if (!data.text || !data.text.trim()) {
|
||
notifyError("Kérdés vagy állítás megadása kötelező!")
|
||
return false
|
||
}
|
||
}
|
||
} else if (data.type === 'JOKER') {
|
||
if (!data.text || !data.text.trim()) {
|
||
notifyError("Joker kártya szövege nem lehet üres!")
|
||
return false
|
||
}
|
||
} else if (data.type === 'LUCK') {
|
||
if (!data.text || !data.text.trim()) {
|
||
notifyError("Szerencse kártya szövege nem lehet üres!")
|
||
return false
|
||
}
|
||
}
|
||
|
||
return true
|
||
} catch (error) {
|
||
console.error('Validálási hiba:', error)
|
||
notifyError("Hiba történt a kártya ellenőrzése során")
|
||
return false
|
||
}
|
||
}
|
||
|
||
const updateCardData = (updates) => {
|
||
setCardData(prev => prev ? { ...prev, ...updates } : null)
|
||
}
|
||
|
||
const handleSave = () => {
|
||
if (!cardData) {
|
||
notifyError("Nincs mentendő kártya adat!")
|
||
return
|
||
}
|
||
|
||
if (!validateCard(cardData)) return
|
||
|
||
onSave(cardData)
|
||
}
|
||
|
||
// Ha nincs kiválasztott kártya vagy új kártya létrehozás
|
||
if (!cardData) {
|
||
return null
|
||
}
|
||
|
||
return (
|
||
<div className="flex-1 flex flex-col">
|
||
{/* Type Mismatch Warning */}
|
||
{cardData?.type && cardType && cardData.type !== cardType && !isCreating && (
|
||
<div className="bg-[color:var(--color-error)]/10 border-l-4 border-[color:var(--color-error)] px-6 py-4">
|
||
<div className="flex items-center gap-3">
|
||
<div className="text-[color:var(--color-error)] text-xl">⚠️</div>
|
||
<div>
|
||
<div className="text-[color:var(--color-error)] font-semibold">
|
||
Figyelmeztetés: Nem megfelelő kártya típus
|
||
</div>
|
||
<div className="text-[color:var(--color-error)]/80 text-sm">
|
||
{`Ez egy ${
|
||
cardData.type === 'QUESTION' ? 'Feladat' :
|
||
cardData.type === 'JOKER' ? 'Joker' : 'Szerencse'
|
||
} kártya, de a pakli típusa ${
|
||
cardType === 'QUESTION' ? 'Feladat' :
|
||
cardType === 'JOKER' ? 'Joker' : 'Szerencse'
|
||
}.`}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Header */}
|
||
<div className="bg-[color:var(--color-surface)] border-b border-[color:var(--color-surface-selected)] px-4 sm:px-6 py-3 sm:py-4">
|
||
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
|
||
<div className="flex items-center gap-3">
|
||
<div className="text-xl sm:text-2xl">
|
||
{cardData.type === 'QUESTION' && '📋'}
|
||
{cardData.type === 'JOKER' && '🃏'}
|
||
{cardData.type === 'LUCK' && '🎲'}
|
||
</div>
|
||
<div>
|
||
<h2 className="text-lg sm:text-xl font-bold text-[color:var(--color-text)]">
|
||
{isCreating ? 'Új' : 'Szerkesztés'} {' '}
|
||
{(isCreating ? cardType : cardData.type) === 'QUESTION' && 'Feladat kártya'}
|
||
{(isCreating ? cardType : cardData.type) === 'JOKER' && 'Joker kártya'}
|
||
{(isCreating ? cardType : cardData.type) === 'LUCK' && 'Szerencse kártya'}
|
||
</h2>
|
||
<div className="text-[color:var(--color-text-muted)] text-xs sm:text-sm">
|
||
{cardData.type === 'QUESTION' && cardData.subType && (
|
||
<>
|
||
{cardData.subType === 'quiz' && 'Quiz (A/B/C/D)'}
|
||
{cardData.subType === 'truefalse' && 'Igaz/Hamis'}
|
||
{cardData.subType === 'matching' && 'Párosítás'}
|
||
{cardData.subType === 'text' && 'Szöveges válasz'}
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-2 sm:gap-3 flex-wrap w-full sm:w-auto">
|
||
<button
|
||
onClick={() => setShowPreview(!showPreview)}
|
||
className={`
|
||
flex items-center gap-1 sm:gap-2 px-3 sm:px-4 py-2 rounded-lg sm:rounded-xl font-medium transition-all duration-200 text-sm
|
||
${showPreview
|
||
? 'bg-[color:var(--color-success)] text-[color:var(--color-text-inverse)]'
|
||
: 'bg-[color:var(--color-background)] text-[color:var(--color-text)] hover:bg-[color:var(--color-surface-selected)]'
|
||
}
|
||
`}
|
||
>
|
||
<FaEye className="text-sm" />
|
||
<span className="hidden sm:inline">Előnézet</span>
|
||
</button>
|
||
|
||
<button
|
||
onClick={() => {
|
||
notifyWarning('Kártya készítés megszakítva')
|
||
onCancel()
|
||
}}
|
||
className="flex items-center gap-1 sm:gap-2 px-3 sm:px-4 py-2 rounded-lg sm:rounded-xl bg-[color:var(--color-background)] hover:bg-[color:var(--color-surface-selected)] text-[color:var(--color-text)] transition-all duration-200 text-sm"
|
||
>
|
||
<FaTimes className="text-sm" />
|
||
<span className="hidden sm:inline">Mégse</span>
|
||
</button>
|
||
|
||
<button
|
||
onClick={handleSave}
|
||
className="flex items-center gap-1 sm:gap-2 px-4 sm:px-6 py-2 rounded-lg sm:rounded-xl bg-[color:var(--color-success)] hover:bg-[color:var(--color-success)]/80 text-[color:var(--color-text-inverse)] font-semibold transition-all duration-200 hover:scale-105 shadow-lg text-sm"
|
||
>
|
||
<FaSave className="text-sm" />
|
||
Mentés
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Content */}
|
||
<div className="flex-1 overflow-y-auto">
|
||
{showPreview ? (
|
||
<div className="h-full bg-[color:var(--color-background)] flex items-center justify-center p-4 sm:p-6">
|
||
<CardPreview card={cardData} />
|
||
</div>
|
||
) : (
|
||
<div className="p-4 sm:p-6">
|
||
{cardData.type === 'QUESTION' && (
|
||
<TaskCardEditor
|
||
card={cardData}
|
||
onChange={updateCardData}
|
||
/>
|
||
)}
|
||
|
||
{cardData.type === 'JOKER' && (
|
||
<JokerCardEditor
|
||
card={cardData}
|
||
onChange={updateCardData}
|
||
/>
|
||
)}
|
||
|
||
{cardData.type === 'LUCK' && (
|
||
<LuckCardEditor
|
||
card={cardData}
|
||
onChange={updateCardData}
|
||
/>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|