diff --git a/SerpentRace_Frontend/src/App.jsx b/SerpentRace_Frontend/src/App.jsx index 96b7afe2..a93f3284 100644 --- a/SerpentRace_Frontend/src/App.jsx +++ b/SerpentRace_Frontend/src/App.jsx @@ -9,6 +9,7 @@ import ResetPassword from "./pages/Auth/ResetPassword" import Landingpage from "./pages/Landing/Landingpage" import Home from "./pages/Landing/Home" import DeckManagerPage from "./pages/Decks/DeckManagerPage" +import DeckCreator from "./pages/DeckCreator/DeckCreator" import CompanyHub from "./pages/Companies/Companies" import About from "./pages/About/About" import ScrollToTop from "./components/ScrollToTop" @@ -53,6 +54,8 @@ function App() { } /> } /> } /> + } /> + } /> } /> } /> diff --git a/SerpentRace_Frontend/src/components/DeckCreator/CardEditor.jsx b/SerpentRace_Frontend/src/components/DeckCreator/CardEditor.jsx new file mode 100644 index 00000000..2f48e046 --- /dev/null +++ b/SerpentRace_Frontend/src/components/DeckCreator/CardEditor.jsx @@ -0,0 +1,224 @@ +// 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" + +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 + } + + switch (type) { + case 'task': + return { + ...baseData, + subType: 'quiz', + question: '', + options: ['', '', '', ''], + correctAnswer: 0, + explanation: '' + } + case 'joker': + return { + ...baseData, + title: '', + description: '', + effect: '', + actionType: 'skip', + usage: 'once' + } + case 'luck': + return { + ...baseData, + event: '', + positiveEffect: '', + negativeEffect: '', + probability: 50, + risk: 'low' + } + default: + return baseData + } + } + + // Kártya adatok inicializálása + useEffect(() => { + if (isCreating && cardType) { + setCardData(getDefaultCardData(cardType)) + } else if (card) { + setCardData({ ...card }) + } else { + setCardData(null) + } + }, [card, isCreating, cardType]) + + const handleSave = () => { + if (!cardData) return + + // Validáció + if (!validateCard(cardData)) return + + onSave(cardData) + } + + const validateCard = (data) => { + if (data.type === 'task') { + if (!data.question && !data.statement) { + alert("❌ Kérdés vagy állítás megadása kötelező!") + return false + } + if (data.subType === 'quiz' && data.options.some(opt => !opt.trim())) { + alert("❌ Minden válaszlehetőséget ki kell tölteni!") + return false + } + } else if (data.type === 'joker') { + if (!data.text || !data.text.trim()) { + alert("❌ Joker kártya szövege nem lehet üres!") + return false + } + } else if (data.type === 'luck') { + if (!data.text || !data.text.trim()) { + alert("❌ Szerencse kártya szövege nem lehet üres!") + return false + } + } + + return true + } + + const updateCardData = (updates) => { + setCardData(prev => prev ? { ...prev, ...updates } : null) + } + + // Ha nincs kiválasztott kártya vagy új kártya létrehozás + if (!cardData) { + return ( +
+
+
🃏
+
+ Válassz ki egy kártyát +
+
+ Klikkelj egy kártyára a bal oldalon a szerkesztéshez,
+ vagy hozz létre egy újat. +
+
+
+ ) + } + + return ( +
+ {/* Header */} +
+
+
+
+ {cardData.type === 'task' && '📋'} + {cardData.type === 'joker' && '🃏'} + {cardData.type === 'luck' && '🎲'} +
+
+

+ {isCreating ? 'Új' : 'Szerkesztés'} {' '} + {cardData.type === 'task' && 'Feladat kártya'} + {cardData.type === 'joker' && 'Joker kártya'} + {cardData.type === 'luck' && 'Szerencse kártya'} +

+
+ {cardData.type === 'task' && 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'} + + )} +
+
+
+ +
+ + + + + +
+
+
+ + {/* Content */} +
+ {showPreview ? ( + /* Preview Mode */ +
+ +
+ ) : ( + /* Edit Mode */ +
+ {cardData.type === 'task' && ( + + )} + + {cardData.type === 'joker' && ( + + )} + + {cardData.type === 'luck' && ( + + )} +
+ )} +
+
+ ) +} \ No newline at end of file diff --git a/SerpentRace_Frontend/src/components/DeckCreator/CardPreview.jsx b/SerpentRace_Frontend/src/components/DeckCreator/CardPreview.jsx new file mode 100644 index 00000000..5835bfe8 --- /dev/null +++ b/SerpentRace_Frontend/src/components/DeckCreator/CardPreview.jsx @@ -0,0 +1,148 @@ +// src/components/DeckCreator/CardPreview.jsx +// Kártya előnézet komponens + +import React from "react" +import { FaQuestionCircle, FaTheaterMasks, FaDice, FaClock, FaStar } from "react-icons/fa" + +export default function CardPreview({ card }) { + if (!card) { + return ( +
+
🃏
+
Nincs kiválasztott kártya az előnézethez
+
+ ) + } + + // Kártya típus specifikus beállítások + const getCardConfig = (card) => { + switch (card.type) { + case 'task': + return { + bgColor: 'var(--color-question)', + icon: FaQuestionCircle, + title: 'FELADAT KÁRTYA', + emoji: '📋' + } + case 'joker': + return { + bgColor: 'var(--color-fun)', + icon: FaTheaterMasks, + title: 'JOKER KÁRTYA', + emoji: '🎭' + } + case 'luck': + return { + bgColor: 'var(--color-luck)', + icon: FaDice, + title: 'SZERENCSE KÁRTYA', + emoji: '🎲' + } + default: + return { + bgColor: 'var(--color-border)', + icon: FaQuestionCircle, + title: 'ISMERETLEN KÁRTYA', + emoji: '❓' + } + } + } + + const config = getCardConfig(card) + + // Kártya tartalom meghatározása + const getCardContent = (card) => { + if (card.type === 'task') { + return card.question || card.statement || 'Feladat leírása...' + } + if (card.type === 'joker' || card.type === 'luck') { + return card.text || 'Kártya szövege...' + } + return 'Kártya tartalma...' + } + + return ( +
+ {/* Kártya container */} +
+ {/* Kártya header */} +
+ {/* Háttér pattern */} +
+
+
+ +
+ + + {config.title} + +
+
+ + {/* Kártya body */} +
+ {/* Főikon */} +
+
{config.emoji}
+
+ + {/* Tartalom */} +
+
100 ? '14px' : '16px' + }} + > + {getCardContent(card)} +
+
+ + {/* Alsó információk */} +
+
+ {/* Idő */} + {card.timeLimit && ( +
+ + {card.timeLimit}s +
+ )} + + {/* Pontok */} + {card.points && ( +
+ + {card.points} pont +
+ )} + + {/* Ha nincs idő/pont info */} + {!card.timeLimit && !card.points && ( +
+ SerpentRace Deck +
+ )} +
+
+
+ + {/* Kártya corner dekoráció */} +
+
+
+
+ ) +} \ No newline at end of file diff --git a/SerpentRace_Frontend/src/components/DeckCreator/CardsList.jsx b/SerpentRace_Frontend/src/components/DeckCreator/CardsList.jsx new file mode 100644 index 00000000..cbbc8ce9 --- /dev/null +++ b/SerpentRace_Frontend/src/components/DeckCreator/CardsList.jsx @@ -0,0 +1,230 @@ +// src/components/DeckCreator/CardsList.jsx +// Bal oldali kártyák listája és új kártya létrehozás + +import React from "react" +import { FaPlus, FaEdit, FaTrash, FaQuestionCircle, FaCheck, FaTimes, FaDice, FaTheaterMasks } from "react-icons/fa" + +const cardTypeIcons = { + task: { icon: FaQuestionCircle, color: "var(--color-question)" }, + joker: { icon: FaTheaterMasks, color: "var(--color-fun)" }, + luck: { icon: FaDice, color: "var(--color-luck)" } +} + +const cardSubTypeLabels = { + quiz: "Quiz", + truefalse: "Igaz/Hamis", + matching: "Párosítás", + text: "Szöveges válasz" +} + +export default function CardsList({ + cards, + selectedCard, + onSelectCard, + onCreateCard, + onDeleteCard, + isCreatingCard, + newCardType +}) { + + const getCardPreview = (card) => { + if (card.type === 'task') { + return card.question || card.statement || 'Új feladat kártya' + } + if (card.type === 'joker') { + return card.text || 'Új joker kártya' + } + if (card.type === 'luck') { + return card.text || 'Új szerencse kártya' + } + return 'Ismeretlen kártya' + } + + const getCardTypeLabel = (card) => { + if (card.type === 'task') { + if (card.subType) { + return cardSubTypeLabels[card.subType] || 'Feladat' + } + return 'Feladat' + } + if (card.type === 'joker') { + return 'Joker' + } + if (card.type === 'luck') { + return 'Szerencse' + } + return 'Ismeretlen' + } + + return ( +
+ {/* Header */} +
+

+ 🃏 Kártyák +

+ + {/* New Card Dropdown */} +
+ + + {/* Dropdown Menu */} +
+ + + + + +
+
+
+ + {/* Cards List */} +
+ {/* Creating Card Indicator */} + {isCreatingCard && ( +
+
+ {newCardType && ( +
+ {React.createElement(cardTypeIcons[newCardType]?.icon || FaQuestionCircle, { + className: "text-[color:var(--color-success)] text-sm" + })} +
+ )} +
+
+ Új {newCardType === 'task' ? 'feladat' : newCardType === 'joker' ? 'joker' : 'szerencse'} kártya +
+
+ Szerkesztés folyamatban... +
+
+
+
+ )} + + {/* Existing Cards */} + {cards.map((card, index) => { + const cardIcon = cardTypeIcons[card.type] || cardTypeIcons.task + const isSelected = selectedCard?.id === card.id + + return ( +
onSelectCard(card)} + className={` + p-4 rounded-xl border cursor-pointer transition-all duration-200 hover:scale-105 group + ${isSelected + ? 'bg-[color:var(--color-success)]/10 border-[color:var(--color-success)] shadow-lg' + : 'bg-[color:var(--color-background)]/50 border-[color:var(--color-surface-selected)] hover:bg-[color:var(--color-background)]/80' + } + `} + > + {/* Card Header */} +
+
+
+ {React.createElement(cardIcon.icon, { + style: { color: cardIcon.color }, + className: "text-lg" + })} +
+ +
+
+ #{index + 1} - {getCardTypeLabel(card)} +
+ {card.timeLimit && ( +
+ ⏱️ {card.timeLimit} másodperc +
+ )} +
+
+ + {/* Action Buttons */} +
+ +
+
+ + {/* Card Content Preview */} +
+
+ {getCardPreview(card)} +
+
+
+ ) + })} + + {/* Empty State */} + {cards.length === 0 && !isCreatingCard && ( +
+
🃏
+
+ Még nincsenek kártyák. +
+ Hozz létre az első kártyát! +
+
+ )} +
+ + {/* Footer Stats */} +
+
+
+ 📊 Összesen: {cards.length} kártya +
+ + {cards.length > 0 && ( +
+ 📋 {cards.filter(c => c.type === 'task').length} + 🃏 {cards.filter(c => c.type === 'joker').length} + 🎲 {cards.filter(c => c.type === 'luck').length} +
+ )} +
+
+
+ ) +} \ No newline at end of file diff --git a/SerpentRace_Frontend/src/components/DeckCreator/DeckHeader.jsx b/SerpentRace_Frontend/src/components/DeckCreator/DeckHeader.jsx new file mode 100644 index 00000000..2aaa8626 --- /dev/null +++ b/SerpentRace_Frontend/src/components/DeckCreator/DeckHeader.jsx @@ -0,0 +1,161 @@ +// src/components/DeckCreator/DeckHeader.jsx +// Deck alapadatok szerkesztése és mentés + +import React from "react" +import { FaSave, FaArrowLeft, FaGlobe, FaLock, FaQuestionCircle, FaDice, FaLaughBeam } from "react-icons/fa" + +const deckTypes = [ + { value: "Question", label: "Kérdés", icon: FaQuestionCircle, color: "var(--color-question)" }, + { value: "Luck", label: "Szerencse", icon: FaDice, color: "var(--color-luck)" }, + { value: "Fun", label: "Szórakozás", icon: FaLaughBeam, color: "var(--color-fun)" } +] + +const privacyOptions = [ + { value: "private", label: "Privát", icon: FaLock }, + { value: "public", label: "Publikus", icon: FaGlobe } +] + +export default function DeckHeader({ deck, onUpdate, onSave, onBack }) { + const currentDeckType = deckTypes.find(type => type.value === deck.type) || deckTypes[0] + const currentPrivacy = privacyOptions.find(option => option.value === deck.privacy) || privacyOptions[0] + + const handleInputChange = (field, value) => { + onUpdate({ [field]: value }) + } + + const cardsCount = deck.cards?.length || 0 + const taskCards = deck.cards?.filter(card => card.type === 'task')?.length || 0 + const jokerCards = deck.cards?.filter(card => card.type === 'joker')?.length || 0 + const luckCards = deck.cards?.filter(card => card.type === 'luck')?.length || 0 + + return ( +
+ {/* Top Row - Title and Actions */} +
+
+ + +

+ 📝 Deck Szerkesztés +

+
+ + +
+ + {/* Main Content Row */} +
+ {/* Deck Basic Info */} +
+ {/* Deck Name */} +
+ + handleInputChange('name', e.target.value)} + className="w-full px-4 py-2 rounded-xl bg-[color:var(--color-background)] border border-[color:var(--color-surface-selected)] text-[color:var(--color-text)] focus:ring-2 focus:ring-[color:var(--color-success)] focus:border-transparent outline-none transition-all duration-200" + placeholder="Add meg a deck nevét..." + /> +
+ + {/* Type and Privacy Row */} +
+ {/* Deck Type */} +
+ + +
+ + {/* Privacy */} +
+ + +
+ + {/* Description */} +
+ + handleInputChange('description', e.target.value)} + className="w-full px-4 py-2 rounded-xl bg-[color:var(--color-background)] border border-[color:var(--color-surface-selected)] text-[color:var(--color-text)] focus:ring-2 focus:ring-[color:var(--color-success)] focus:border-transparent outline-none transition-all duration-200" + placeholder="Rövid leírás..." + /> +
+
+
+ + {/* Stats Panel */} +
+

+ 📊 Statisztikák +

+ +
+
+ Összes kártya: + {cardsCount} +
+ +
+ 📋 Feladat: + {taskCards} +
+ +
+ 🃏 Joker: + {jokerCards} +
+ +
+ 🎲 Szerencse: + {luckCards} +
+
+
+
+
+ ) +} \ No newline at end of file diff --git a/SerpentRace_Frontend/src/components/DeckCreator/JokerCardEditor.jsx b/SerpentRace_Frontend/src/components/DeckCreator/JokerCardEditor.jsx new file mode 100644 index 00000000..c902cade --- /dev/null +++ b/SerpentRace_Frontend/src/components/DeckCreator/JokerCardEditor.jsx @@ -0,0 +1,152 @@ +// src/components/DeckCreator/JokerCardEditor.jsx +// Joker kártya szerkesztő + +import React, { useState, useEffect } from 'react' +import { FaTheaterMasks, FaInfoCircle, FaUsers } from 'react-icons/fa' + +export default function JokerCardEditor({ card, onChange }) { + const [cardData, setCardData] = useState({ + type: 'joker', + text: '' + }) + + useEffect(() => { + if (card) { + setCardData({ + type: 'joker', + text: card.text || '' + }) + } + }, [card]) + + const handleTextChange = (e) => { + const newCardData = { + ...cardData, + text: e.target.value + } + setCardData(newCardData) + + if (onChange) { + onChange(newCardData) + } + } + + // Példa joker kártyák + const exampleCards = [ + "Felelsz vagy mersz? (Az előző játékos kérdez)", + "Csinálj 20 felülést!", + "Mesélj el egy vicces történetet az életedből!", + "Utánozd a kedvenc állatodat 30 másodpercig!", + "Énekelj el egy dalt amit mindenki ismer!", + "Mondj el 5 dolgot amiért hálás vagy!", + "Táncolj 1 percig zene nélkül!" + ] + + const insertExample = (example) => { + setCardData(prev => ({ + ...prev, + text: example + })) + + if (onChange) { + onChange({ + ...cardData, + text: example + }) + } + } + + return ( +
+
+ {/* Header */} +
+ +

+ Joker Kártya Szerkesztő +

+
+ + {/* Info box */} +
+
+ +
+

+ + Joker kártya működése: +

+

+ A joker kártyák interaktív feladatokat tartalmaznak, melyek megtörik a jeget a játékosok között. + Ezek lehetnek fizikai feladatok, kérdések, vagy szórakoztató kihívások. +

+

+ Cél: Szórakozás és játékosok közötti kapcsolat erősítése +

+
+
+
+ + {/* Card text input */} +
+
+ +