diff --git a/frontend/frontend/package-lock.json b/frontend/frontend/package-lock.json index f351f21..ff7df1a 100644 --- a/frontend/frontend/package-lock.json +++ b/frontend/frontend/package-lock.json @@ -13,6 +13,7 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@fontsource-variable/geist": "^5.2.8", + "@tanstack/react-virtual": "^3.13.23", "axios": "^1.13.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -2551,6 +2552,33 @@ "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, + "node_modules/@tanstack/react-virtual": { + "version": "3.13.23", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.23.tgz", + "integrity": "sha512-XnMRnHQ23piOVj2bzJqHrRrLg4r+F86fuBcwteKfbIjJrtGxb4z7tIvPVAe4B+4UVwo9G4Giuz5fmapcrnZ0OQ==", + "license": "MIT", + "dependencies": { + "@tanstack/virtual-core": "3.13.23" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.23", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.23.tgz", + "integrity": "sha512-zSz2Z2HNyLjCplANTDyl3BcdQJc2k1+yyFoKhNRmCr7V7dY8o8q5m8uFTI1/Pg1kL+Hgrz6u3Xo6eFUB7l66cg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@ts-morph/common": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz", diff --git a/frontend/frontend/package.json b/frontend/frontend/package.json index db4d7ac..b6e2d0a 100644 --- a/frontend/frontend/package.json +++ b/frontend/frontend/package.json @@ -15,6 +15,7 @@ "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", "@fontsource-variable/geist": "^5.2.8", + "@tanstack/react-virtual": "^3.13.23", "axios": "^1.13.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/frontend/frontend/src/components/questionbank/GameSetupView.tsx b/frontend/frontend/src/components/questionbank/GameSetupView.tsx index 938bf03..e1b30b9 100644 --- a/frontend/frontend/src/components/questionbank/GameSetupView.tsx +++ b/frontend/frontend/src/components/questionbank/GameSetupView.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef, useMemo } from "react"; +import { useState, useEffect, useRef, useMemo, useCallback } from "react"; import { Link, useNavigate } from "react-router-dom"; import { DndContext, @@ -17,12 +17,12 @@ import { verticalListSortingStrategy, } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; +import { useVirtualizer } from "@tanstack/react-virtual"; import { questionsAPI, gamesAPI, teamsAPI } from "../../services/api"; import AdminNavbar from "../common/AdminNavbar"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; -import { ScrollArea } from "@/components/ui/scroll-area"; import { Dialog, DialogContent, @@ -54,6 +54,7 @@ import { interface Question { id: number; question_content: string; + answer?: string; type: string; category?: string; [key: string]: unknown; @@ -65,6 +66,10 @@ interface Template { total_questions: number; } +type PoolRow = + | { kind: "category"; category: string; count: number; selectedCount: number } + | { kind: "question"; question: Question; isSelected: boolean }; + /* ─── Sortable row inside the game queue ───────────────────────────── */ function SortableQuestionRow({ question, @@ -118,7 +123,9 @@ function SortableQuestionRow({ )} - {question.question_content} + {question.type !== "text" && question.answer + ? question.answer + : question.question_content} - {!isCollapsed && ( -
- {qs.map((q) => { - const isSelected = - selectedQuestions.includes( - q.id, - ); - return ( - - ); - })} + { + row.selectedCount + } + + )} + + {row.count} + +
- )} - - ); - }) - )} - - + ); + } + const { question, isSelected } = + row; + return ( +
+ +
+ ); + })} + + )} + {/* RIGHT: Game Queue */} @@ -766,8 +860,7 @@ export default function GameSetupView() { {selectedQuestions.length > 0 && ( {selectedQuestions.length} question - {selectedQuestions.length !== 1 && - "s"} + {selectedQuestions.length !== 1 && "s"} )} @@ -783,7 +876,10 @@ export default function GameSetupView() { )} - +
{selectedQuestions.length === 0 ? (
@@ -824,7 +920,7 @@ export default function GameSetupView() { )} - +
@@ -834,7 +930,10 @@ export default function GameSetupView() {

Teams

{teams.length > 0 && ( - + {teams.length} )}