import { useState, useEffect } from 'react' import { useQuery } from '@tanstack/react-query' import { testsApi } from '@/api/tests' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Badge } from '@/components/ui/badge' import { Search, Check } from 'lucide-react' interface PackTestsManagerProps { currentTestIds: string[] onTestsChange: (addIds: string[], removeIds: string[]) => void disabled?: boolean } export function PackTestsManager({ currentTestIds, onTestsChange, disabled = false, }: PackTestsManagerProps) { const [search, setSearch] = useState('') const [selectedTests, setSelectedTests] = useState>(new Set()) const [removedTests, setRemovedTests] = useState>(new Set()) // Load all tests with search const { data: testsData, isLoading } = useQuery({ queryKey: ['tests', 1, 100, search], // Limit to 100 (backend max) queryFn: () => testsApi.getTests({ page: 1, limit: 100, search }), enabled: !disabled, }) // Initialize selected tests from currentTestIds when it changes useEffect(() => { const initialSelected = new Set( currentTestIds.filter((id) => !removedTests.has(id.toString())) ) setSelectedTests(initialSelected) // Reset removed tests when currentTestIds changes (e.g., when opening dialog) setRemovedTests(new Set()) }, [currentTestIds.join(',')]) // Use join to detect array changes // Calculate which tests to add/remove when selection changes useEffect(() => { const currentlySelected = Array.from(selectedTests) const removed = Array.from(removedTests) // Tests to add: selected but not in currentTestIds and not removed const toAdd = currentlySelected.filter( (id) => !currentTestIds.includes(id) && !removed.includes(id) ) // Tests to remove: in removedTests const toRemove = removed.filter((id) => currentTestIds.includes(id)) if (toAdd.length > 0 || toRemove.length > 0) { onTestsChange(toAdd, toRemove) } else if (selectedTests.size > 0 || removedTests.size > 0) { // Also notify if selection was cleared onTestsChange([], []) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedTests.size, removedTests.size, currentTestIds.join(',')]) const handleToggleTest = (testId: string) => { if (disabled || !testId) return const testIdStr = String(testId) const isCurrentlySelected = selectedTests.has(testIdStr) && !removedTests.has(testIdStr) const isInCurrentPack = currentTestIds.includes(testIdStr) if (isCurrentlySelected) { // Deselect test const newSelected = new Set(selectedTests) newSelected.delete(testIdStr) setSelectedTests(newSelected) // If it was in current pack, mark as removed if (isInCurrentPack) { setRemovedTests((prev) => new Set([...prev, testIdStr])) } } else { // Select test const newSelected = new Set(selectedTests) newSelected.add(testIdStr) setSelectedTests(newSelected) // If it was marked as removed, unmark it if (removedTests.has(testIdStr)) { setRemovedTests((prev) => { const newRemoved = new Set(prev) newRemoved.delete(testIdStr) return newRemoved }) } } } const isTestSelected = (testId: string | number) => { const testIdStr = String(testId) return selectedTests.has(testIdStr) && !removedTests.has(testIdStr) } const isTestInCurrentPack = (testId: string | number) => { return currentTestIds.includes(String(testId)) } const allTests = testsData?.items || [] const filteredTests = search ? allTests.filter( (test) => test.name?.toLowerCase().includes(search.toLowerCase()) || test.id?.toLowerCase().includes(search.toLowerCase()) ) : allTests return (
setSearch(e.target.value)} className="max-w-sm" disabled={disabled} />

Selected tests: {selectedTests.size - removedTests.size} / {allTests.length}

{isLoading ? (
Loading tests...
) : (
ID Name Questions Version Status {filteredTests.length === 0 ? ( No tests found ) : ( filteredTests .filter((test) => test.id) // Only show tests with IDs .map((test) => { const testIdStr = String(test.id!) const selected = isTestSelected(testIdStr) const inCurrentPack = isTestInCurrentPack(testIdStr) const newlyRemoved = removedTests.has(testIdStr) return ( handleToggleTest(testIdStr)} > {selected ? ( ) : (
)} {test.id || '-'} {test.name} {typeof test.questions === 'number' ? test.questions : test.questions?.length || 0} {test.version || 'N/A'} {newlyRemoved ? ( Removed ) : selected && inCurrentPack ? ( In Pack ) : selected ? ( Selected ) : inCurrentPack ? ( In Pack ) : null} ) }) )}
)}
) }