import { useState, useEffect } from 'react' import { useQuery } from '@tanstack/react-query' import { cardsApi } from '@/api/cards' 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 PackCardsManagerProps { currentCardIds: string[] onCardsChange: (addIds: string[], removeIds: string[]) => void disabled?: boolean } export function PackCardsManager({ currentCardIds, onCardsChange, disabled = false, }: PackCardsManagerProps) { const [search, setSearch] = useState('') const [selectedCards, setSelectedCards] = useState>(new Set()) const [removedCards, setRemovedCards] = useState>(new Set()) // Load all cards with search const { data: cardsData, isLoading } = useQuery({ queryKey: ['cards', 1, 100, search], // Limit to 100 (backend max) queryFn: () => cardsApi.getCards({ page: 1, limit: 100, search }), enabled: !disabled, }) // Initialize selected cards from currentCardIds when it changes useEffect(() => { const initialSelected = new Set( currentCardIds.filter((id) => !removedCards.has(id.toString())) ) setSelectedCards(initialSelected) // Reset removed cards when currentCardIds changes (e.g., when opening dialog) setRemovedCards(new Set()) }, [currentCardIds.join(',')]) // Use join to detect array changes // Calculate which cards to add/remove when selection changes useEffect(() => { const currentlySelected = Array.from(selectedCards) const removed = Array.from(removedCards) // Cards to add: selected but not in currentCardIds and not removed const toAdd = currentlySelected.filter( (id) => !currentCardIds.includes(id) && !removed.includes(id) ) // Cards to remove: in removedCards const toRemove = removed.filter((id) => currentCardIds.includes(id)) if (toAdd.length > 0 || toRemove.length > 0) { onCardsChange(toAdd, toRemove) } else if (selectedCards.size > 0 || removedCards.size > 0) { // Also notify if selection was cleared onCardsChange([], []) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedCards.size, removedCards.size, currentCardIds.join(',')]) const handleToggleCard = (cardId: string | number) => { if (disabled) return const cardIdStr = String(cardId) const isCurrentlySelected = selectedCards.has(cardIdStr) && !removedCards.has(cardIdStr) const isInCurrentPack = currentCardIds.includes(cardIdStr) if (isCurrentlySelected) { // Deselect card const newSelected = new Set(selectedCards) newSelected.delete(cardIdStr) setSelectedCards(newSelected) // If it was in current pack, mark as removed if (isInCurrentPack) { setRemovedCards((prev) => new Set([...prev, cardIdStr])) } } else { // Select card const newSelected = new Set(selectedCards) newSelected.add(cardIdStr) setSelectedCards(newSelected) // If it was marked as removed, unmark it if (removedCards.has(cardIdStr)) { setRemovedCards((prev) => { const newRemoved = new Set(prev) newRemoved.delete(cardIdStr) return newRemoved }) } } } const isCardSelected = (cardId: string | number) => { const cardIdStr = String(cardId) return selectedCards.has(cardIdStr) && !removedCards.has(cardIdStr) } const isCardInCurrentPack = (cardId: string | number) => { return currentCardIds.includes(String(cardId)) } const allCards = cardsData?.items || [] const filteredCards = search ? allCards.filter( (card) => card.original?.toLowerCase().includes(search.toLowerCase()) || card.translation?.toLowerCase().includes(search.toLowerCase()) || card.mnemo?.toLowerCase().includes(search.toLowerCase()) ) : allCards return (
setSearch(e.target.value)} className="max-w-sm" disabled={disabled} />

Selected cards: {selectedCards.size - removedCards.size} / {allCards.length}

{isLoading ? (
Loading cards...
) : (
ID Original Translation Mnemo Status {filteredCards.length === 0 ? ( No cards found ) : ( filteredCards.map((card) => { const cardIdStr = card.id ? String(card.id) : `temp-${card.original}` const selected = isCardSelected(cardIdStr) const inCurrentPack = isCardInCurrentPack(cardIdStr) const newlyRemoved = removedCards.has(cardIdStr) return ( handleToggleCard(cardIdStr)} > {selected ? ( ) : (
)} {card.id || '-'} {card.original} {card.translation} {card.mnemo} {newlyRemoved ? ( Removed ) : selected && inCurrentPack ? ( In Pack ) : selected ? ( Selected ) : inCurrentPack ? ( In Pack ) : null} ) }) )}
)}
) }