diff --git a/frontend/src/pages/LobbyPage.tsx b/frontend/src/pages/LobbyPage.tsx index 729031a..0bc2726 100644 --- a/frontend/src/pages/LobbyPage.tsx +++ b/frontend/src/pages/LobbyPage.tsx @@ -125,6 +125,14 @@ export function LobbyPage() { const [searchQuery, setSearchQuery] = useState('') const [generateSearchQuery, setGenerateSearchQuery] = useState('') + // Games list filters + const [filterProposer, setFilterProposer] = useState('all') + const [filterChallenges, setFilterChallenges] = useState<'all' | 'with' | 'without'>('all') + + // Generation filters + const [generateFilterProposer, setGenerateFilterProposer] = useState('all') + const [generateFilterChallenges, setGenerateFilterChallenges] = useState<'all' | 'with' | 'without'>('all') + // Settings modal const [showSettings, setShowSettings] = useState(false) @@ -563,10 +571,6 @@ export function LobbyPage() { ) } - const selectAllGamesForGeneration = () => { - setSelectedGamesForGeneration(approvedGames.map(g => g.id)) - } - const clearGameSelection = () => { setSelectedGamesForGeneration([]) } @@ -644,6 +648,22 @@ export function LobbyPage() { const approvedGames = games.filter(g => g.status === 'approved') const totalChallenges = approvedGames.reduce((sum, g) => sum + g.challenges_count, 0) + // Get unique proposers for generation filter (from approved games) + const uniqueProposers = approvedGames.reduce((acc, game) => { + if (game.proposed_by && !acc.some(u => u.id === game.proposed_by?.id)) { + acc.push(game.proposed_by) + } + return acc + }, [] as { id: number; nickname: string }[]) + + // Get unique proposers for games list filter (from all games) + const allGamesProposers = games.reduce((acc, game) => { + if (game.proposed_by && !acc.some(u => u.id === game.proposed_by?.id)) { + acc.push(game.proposed_by) + } + return acc + }, [] as { id: number; nickname: string }[]) + const getStatusBadge = (status: string) => { switch (status) { case 'approved': @@ -1422,6 +1442,8 @@ export function LobbyPage() { setShowGenerateSelection(false) clearGameSelection() setGenerateSearchQuery('') + setGenerateFilterProposer('all') + setGenerateFilterChallenges('all') }} variant="secondary" size="sm" @@ -1455,7 +1477,7 @@ export function LobbyPage() { {/* Game selection */} {showGenerateSelection && (
- {/* Search in generation */} + {/* Search */}
)}
-
-
-
- {(() => { - const filteredGames = generateSearchQuery - ? fuzzyFilter(approvedGames, generateSearchQuery, (g) => g.title + ' ' + (g.genre || '')) - : approvedGames + {(() => { + // Compute filtered games + let filteredGames = approvedGames - return filteredGames.length === 0 ? ( -

- Ничего не найдено по запросу "{generateSearchQuery}" -

- ) : ( - filteredGames.map((game) => { - const isSelected = selectedGamesForGeneration.includes(game.id) - const challengeCount = gameChallenges[game.id]?.length ?? game.challenges_count - return ( - - ) - }) - ) - })()} -
+ // Apply proposer filter + if (generateFilterProposer !== 'all') { + filteredGames = filteredGames.filter(g => g.proposed_by?.id === generateFilterProposer) + } + + // Apply challenges filter + if (generateFilterChallenges === 'with') { + filteredGames = filteredGames.filter(g => { + const count = gameChallenges[g.id]?.length ?? g.challenges_count + return count > 0 + }) + } else if (generateFilterChallenges === 'without') { + filteredGames = filteredGames.filter(g => { + const count = gameChallenges[g.id]?.length ?? g.challenges_count + return count === 0 + }) + } + + // Apply search filter + if (generateSearchQuery) { + filteredGames = fuzzyFilter(filteredGames, generateSearchQuery, (g) => g.title + ' ' + (g.genre || '')) + } + + return ( + <> +
+ + +
+
+ {filteredGames.length === 0 ? ( +

+ Ничего не найдено +

+ ) : ( + filteredGames.map((game) => { + const isSelected = selectedGamesForGeneration.includes(game.id) + const challengeCount = gameChallenges[game.id]?.length ?? game.challenges_count + return ( + + ) + }) + )} +
+ + ) + })()}
)} @@ -1702,24 +1777,47 @@ export function LobbyPage() { )} - {/* Search */} -
- - setSearchQuery(e.target.value)} - className="input w-full pl-10 pr-10" - /> - {searchQuery && ( - + )} +
+
+ + +
{/* Add game form */} @@ -1763,26 +1861,47 @@ export function LobbyPage() { {/* Games */} {(() => { - const baseGames = isOrganizer + let filteredGames = isOrganizer ? games.filter(g => g.status !== 'pending') : games.filter(g => g.status === 'approved' || (g.status === 'pending' && g.proposed_by?.id === user?.id)) - const visibleGames = searchQuery - ? fuzzyFilter(baseGames, searchQuery, (g) => g.title + ' ' + (g.genre || '')) - : baseGames + // Apply proposer filter + if (filterProposer !== 'all') { + filteredGames = filteredGames.filter(g => g.proposed_by?.id === filterProposer) + } - return visibleGames.length === 0 ? ( + // Apply challenges filter + if (filterChallenges === 'with') { + filteredGames = filteredGames.filter(g => { + const count = gameChallenges[g.id]?.length ?? g.challenges_count + return count > 0 + }) + } else if (filterChallenges === 'without') { + filteredGames = filteredGames.filter(g => { + const count = gameChallenges[g.id]?.length ?? g.challenges_count + return count === 0 + }) + } + + // Apply search filter + if (searchQuery) { + filteredGames = fuzzyFilter(filteredGames, searchQuery, (g) => g.title + ' ' + (g.genre || '')) + } + + const hasFilters = searchQuery || filterProposer !== 'all' || filterChallenges !== 'all' + + return filteredGames.length === 0 ? (
- {searchQuery ? ( + {hasFilters ? ( ) : ( )}

- {searchQuery - ? `Ничего не найдено по запросу "${searchQuery}"` + {hasFilters + ? 'Ничего не найдено по заданным фильтрам' : isOrganizer ? 'Пока нет игр. Добавьте игры, чтобы начать!' : 'Пока нет одобренных игр. Предложите свою!'} @@ -1790,7 +1909,7 @@ export function LobbyPage() {

) : (
- {visibleGames.map((game) => renderGameCard(game, false))} + {filteredGames.map((game) => renderGameCard(game, false))}
) })()}