diff --git a/SerpentRace_Frontend/src/App.jsx b/SerpentRace_Frontend/src/App.jsx
index ed9188d4..f22180fa 100644
--- a/SerpentRace_Frontend/src/App.jsx
+++ b/SerpentRace_Frontend/src/App.jsx
@@ -15,10 +15,12 @@ import About from "./pages/About/About"
import ScrollToTop from "./components/ScrollToTop"
import GameScreen from "./pages/Game/GameScreen"
import Reports from "./pages/Report/Reports"
-import Lobby from "./pages/Lobby/Lobby"
+import Lobby from "./pages/Game/Lobby"
import ProfileCard from "./components/Userdetails/Userdetails"
import { ToastConfig } from "./components/Toastify/toastifyServices" // ✅ fontos: named import, nem default!
import VerifyEmailPage from "./pages/Auth/VerifyEmailPage"
+import ChooseDeck from "./pages/Game/ChooseDeck"
+import PlayerSetup from "./pages/Game/PlayerSetup"
function App() {
const [isMobile, setIsMobile] = useState(false)
@@ -68,6 +70,8 @@ function App() {
} />
{/* } /> */}
} />
+ } />
+ } />
diff --git a/SerpentRace_Frontend/src/components/Landingpage/PlayMenu.jsx b/SerpentRace_Frontend/src/components/Landingpage/PlayMenu.jsx
index 72cbe2eb..4a26513e 100644
--- a/SerpentRace_Frontend/src/components/Landingpage/PlayMenu.jsx
+++ b/SerpentRace_Frontend/src/components/Landingpage/PlayMenu.jsx
@@ -1,4 +1,5 @@
import React, { useState } from "react"
+import { useNavigate } from "react-router-dom"
import LogoCard from "../../assets/pictures/LogoCard.jsx"
import logoImg from "../../assets/pictures/Logo.png" // <-- EZT ADD HOZZÁ
import ButtonDark from "../Buttons/ButtonDark.jsx"
@@ -12,6 +13,7 @@ const PlayMenu = ({ onJoinGame, onCreateGame, user, setUser }) => {
// gyors username kiolvasás (ha a parent objektum user={ { name: ... } } küldi)
const username = user?.name ?? null
+ const navigate = useNavigate()
const handleJoin = () => {
if (!joinCode.trim()) {
@@ -23,7 +25,22 @@ const PlayMenu = ({ onJoinGame, onCreateGame, user, setUser }) => {
}
const handleCreate = () => {
- onCreateGame()
+ // determine the name we will pass: logged in username or guestName
+ const nameToSend = username ?? guestName?.trim()
+
+ if (!nameToSend) {
+ setGuestError("Adj meg egy nevet, vagy jelentkezz be!")
+ return
+ }
+
+ // if parent provided a setter, set guest as current user (optional)
+ if (!username && setUser) {
+ setUser({ name: nameToSend })
+ }
+
+ // Do NOT call onCreateGame here to avoid any alert side-effects from parent.
+ // Just navigate to choose deck and pass username via location.state
+ navigate("/choosedeck", { state: { username: nameToSend } })
}
// egyszerű segéd a kezdobetűk kinyerésére
diff --git a/SerpentRace_Frontend/src/pages/Game/ChooseDeck.jsx b/SerpentRace_Frontend/src/pages/Game/ChooseDeck.jsx
new file mode 100644
index 00000000..8aa4a9dd
--- /dev/null
+++ b/SerpentRace_Frontend/src/pages/Game/ChooseDeck.jsx
@@ -0,0 +1,362 @@
+import React, { useEffect, useState } from "react"
+import { useNavigate, useLocation } from "react-router-dom"
+import Navbar from "../../components/Navbar/Navbar.jsx"
+import Background from "../../assets/backgrounds/Background.jsx"
+import Footer from "../../components/Footer/Footer.jsx"
+import useRequireAuth from "../../hooks/useRequireAuth.jsx"
+import ButtonGreen from "../../components/Buttons/ButtonGreen.jsx"
+import {
+ FaFilter,
+ FaCalendarAlt,
+ FaArrowUp,
+ FaArrowDown,
+ FaSortAlphaDown,
+ FaSortAlphaUp,
+ FaQuestionCircle,
+ FaCheckCircle,
+ FaCircle,
+} from "react-icons/fa"
+import SearchBox from "../../components/Search/SearchBox.jsx"
+import PopUp from "../../components/PopUp/PopUp.jsx"
+import { motion } from "framer-motion"
+
+const deckTypes = [
+ { label: "Luck", color: "var(--color-luck)" },
+ { label: "Question", color: "var(--color-question)" },
+ { label: "Joker", color: "var(--color-fun)" },
+]
+
+const origins = ["Mind", "Vállalati", "Saját"]
+
+const sortOptions = [
+ { value: "date-asc", label: "📅↑" },
+ { value: "date-desc", label: "📅↓" },
+ { value: "abc-asc", label: "A→Z" },
+ { value: "abc-desc", label: "Z→A" },
+]
+
+const ChooseDeck = () => {
+ const location = useLocation()
+ const locationUsername = location.state?.username ?? null
+
+ // always call hook (hooks must be called unconditionally) and use as fallback
+ const [authUsername] = useRequireAuth({ key: "username", redirectTo: "/" })
+
+ // prefer passed username (from navigate state) over authenticated username
+ const username = locationUsername ?? authUsername
+
+ const navigate = useNavigate()
+
+ const [selectedType, setSelectedType] = useState("All")
+ const [selectedOrigin, setSelectedOrigin] = useState("Mind")
+ const [sortBy, setSortBy] = useState("date-desc")
+ const [search, setSearch] = useState("")
+ const [showSortHelp, setShowSortHelp] = useState(false)
+ const [allDecks, setAllDecks] = useState([])
+ const [loading, setLoading] = useState(false)
+ const [selectedDeckIds, setSelectedDeckIds] = useState([])
+
+ // Load all decks once
+ useEffect(() => {
+ let mounted = true
+ const load = async () => {
+ setLoading(true)
+ try {
+ const result = await import("../../api/deckApi.js").then((m) => m.getDecksPage(0, 99))
+ if (!mounted) return
+
+ console.log("Loaded decks:", result)
+
+ const mapped = (result.decks || []).map((d) => ({
+ id: d.id,
+ name: d.name,
+ type: d.type === 2 ? "Question" : d.type === 1 ? "Joker" : "Luck",
+ created: d.creationdate ? new Date(d.creationdate).toLocaleDateString() : "",
+ origin: d.ctype === 2 ? "Vállalati" : d.ctype === 0 ? "Mind" : "Saját",
+ raw: d,
+ }))
+
+ console.log("Mapped decks:", mapped)
+ setAllDecks(mapped)
+ } catch (err) {
+ console.error("Failed to load decks", err)
+ } finally {
+ setLoading(false)
+ }
+ }
+ load()
+ return () => {
+ mounted = false
+ }
+ }, [])
+
+ // Filter logic
+ let filteredDecks = allDecks.filter((deck) => {
+ const typeMatch = selectedType === "All" || deck.type === selectedType
+ const originMatch = selectedOrigin === "Mind" || deck.origin === selectedOrigin
+ const searchMatch = !search || deck.name.toLowerCase().includes(search.toLowerCase())
+ return typeMatch && originMatch && searchMatch
+ })
+
+ // Sort logic
+ filteredDecks = [...filteredDecks].sort((a, b) => {
+ if (sortBy === "date-asc") {
+ return a.created.localeCompare(b.created)
+ } else if (sortBy === "date-desc") {
+ return b.created.localeCompare(a.created)
+ } else if (sortBy === "abc-asc") {
+ return a.name.localeCompare(b.name)
+ } else if (sortBy === "abc-desc") {
+ return b.name.localeCompare(a.name)
+ }
+ return 0
+ })
+
+ // Toggle deck selection
+ const toggleDeckSelection = (deckId) => {
+ setSelectedDeckIds((prev) => {
+ if (prev.includes(deckId)) {
+ return prev.filter((id) => id !== deckId)
+ } else {
+ return [...prev, deckId]
+ }
+ })
+ }
+
+ // Handle continue button
+ const handleContinue = () => {
+ if (selectedDeckIds.length === 0) {
+ alert("Kérlek válassz ki legalább egy paklit!")
+ return
+ }
+ console.log("Kiválasztott pakli ID-k:", selectedDeckIds)
+ navigate("/playersetup", { state: { deckIds: selectedDeckIds } })
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {/* Title */}
+
+ Válassz Paklikat a Játékhoz
+
+
+
+ Válaszd ki azokat a paklikat, amelyekkel játszani szeretnél. Több paklit is kiválaszthatsz
+ egyszerre.
+
+
+ {/* Filters */}
+
+
+ setSearch(e.target.value)}
+ width={240}
+ placeholder="Keresés..."
+ className="mr-4"
+ />
+
+ Típus:
+
+ {deckTypes.map((type) => (
+
+ ))}
+ Eredet:
+
+
+ Rendezés:
+
+
+
+
+
+
+ {showSortHelp && (
+ setShowSortHelp(false)}>
+ Rendezési lehetőségek
+
+ -
+ 📅↑ – Dátum szerint növekvő
+
+ -
+ 📅↓ – Dátum szerint csökkenő
+
+ -
+ A→Z – Név szerint növekvő
+
+ -
+ Z→A – Név szerint csökkenő
+
+
+
+
+ )}
+
+ {/* Selection Info */}
+
+
+ Kiválasztva: {selectedDeckIds.length} pakli
+
+
+
+ {/* Decks Grid */}
+
+ {loading && (
+
Betöltés...
+ )}
+ {!loading && filteredDecks.length === 0 && (
+
+ Nincsenek elérhető paklik.
+
+ )}
+ {!loading &&
+ filteredDecks.map((deck) => {
+ const deckType = deckTypes.find((t) => t.label === deck.type)
+ const borderColor = deckType ? deckType.color : "var(--color-success)"
+ const isSelected = selectedDeckIds.includes(deck.id)
+
+ return (
+
toggleDeckSelection(deck.id)}
+ >
+ {/* Selection Indicator */}
+
+ {isSelected ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {deck.type === "Luck" ? "Szerencse" : deck.type === "Question" ? "Kérdés" : "Joker"}
+
+
+ {deck.name}
+
+
+
+ Létrehozva: {deck.created}
+
+
+ )
+ })}
+
+
+ {/* Continue Button */}
+
+
+
+
+
+
+
+
+ )
+}
+
+export default ChooseDeck
diff --git a/SerpentRace_Frontend/src/pages/Lobby/Lobby.jsx b/SerpentRace_Frontend/src/pages/Game/Lobby.jsx
similarity index 96%
rename from SerpentRace_Frontend/src/pages/Lobby/Lobby.jsx
rename to SerpentRace_Frontend/src/pages/Game/Lobby.jsx
index 00760124..a1d6368c 100644
--- a/SerpentRace_Frontend/src/pages/Lobby/Lobby.jsx
+++ b/SerpentRace_Frontend/src/pages/Game/Lobby.jsx
@@ -1,8 +1,8 @@
import React, { useEffect, useRef, useState } from "react"
import { useNavigate, useLocation } from "react-router-dom"
-import Navbar from "../../components/Navbar/Navbar"
+import Navbar from "../../components/Navbar/Navbar.jsx"
import Background from "../../assets/backgrounds/Background.jsx"
-import useRequireAuth from "../../hooks/useRequireAuth"
+import useRequireAuth from "../../hooks/useRequireAuth.jsx"
const Lobby = () => {
const [visible, setVisible] = useState(false)
@@ -10,7 +10,6 @@ const Lobby = () => {
const navigate = useNavigate()
const location = useLocation()
-
const [user, setUser] = useRequireAuth()
useEffect(() => {
diff --git a/SerpentRace_Frontend/src/pages/Game/PlayerSetup.jsx b/SerpentRace_Frontend/src/pages/Game/PlayerSetup.jsx
new file mode 100644
index 00000000..47d4b6d8
--- /dev/null
+++ b/SerpentRace_Frontend/src/pages/Game/PlayerSetup.jsx
@@ -0,0 +1,135 @@
+import React, { useState } from "react"
+import { useNavigate, useLocation } from "react-router-dom"
+import Navbar from "../../components/Navbar/Navbar.jsx"
+import Background from "../../assets/backgrounds/Background.jsx"
+import Footer from "../../components/Footer/Footer.jsx"
+import useRequireAuth from "../../hooks/useRequireAuth.jsx"
+import ButtonGreen from "../../components/Buttons/ButtonGreen.jsx"
+import { motion } from "framer-motion"
+
+const PlayerSetup = () => {
+ const [username] = useRequireAuth({ key: "username", redirectTo: "/" })
+ const navigate = useNavigate()
+ const location = useLocation()
+
+ const deckIds = location.state?.deckIds || []
+
+ const [maxPlayers, setMaxPlayers] = useState(4)
+ const [isPublic, setIsPublic] = useState(true)
+
+ const handleCreateLobby = () => {
+ const privacyFlag = isPublic ? 0 : 1
+ console.log({
+ deckIds,
+ playerCount: maxPlayers,
+ privacy: privacyFlag,
+ })
+ // Itt küldd el az API-nak a lobby létrehozását
+ // navigate("/game-lobby", { state: { lobbyId: response.lobbyId } })
+ }
+
+ if (deckIds.length === 0) {
+ navigate("/choosedeck")
+ return null
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ Lobby Beállítások
+
+
+
+ {deckIds.length} pakli kiválasztva. Add meg a játék részleteit.
+
+
+
+ {/* Max Players */}
+
+
+ setMaxPlayers(parseInt(e.target.value) || 2)}
+ className="w-full px-4 py-2 rounded-lg bg-[color:var(--color-card)] text-[color:var(--color-text)] border border-[color:var(--color-surface)] focus:ring-2 focus:ring-[color:var(--color-success)] outline-none"
+ />
+
+
+ {/* Public/Private */}
+
+
+
+
+
+
+
+
+
+ {/* Action Buttons */}
+
+ navigate("/choosedeck")}
+ width="w-auto px-8"
+ className="bg-gray-600 hover:bg-gray-700"
+ />
+
+
+
+
+
+
+
+ )
+}
+
+export default PlayerSetup