-
Notifications
You must be signed in to change notification settings - Fork 4
Fix setter name filter bug and add multiselect autocomplete with route counts #297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
marcodejongh
merged 13 commits into
main
from
claude/fix-settername-filter-bug-011CUp2PnHRCU1oH6h57N2j3
Nov 5, 2025
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
f7112f9
Fix setter name filter bug and add multiselect autocomplete with rout…
claude 92e67ea
Remove unused useEffect import from setter-name-select component
claude bff5b72
Fix module import error - use usePathname to construct API URL instea…
claude 8113d22
Fix TypeScript errors - update settername from string to string[] acr…
claude 4e36ddd
Fix parsedRouteSearchParamsToSearchParams to handle settername string…
claude 523a34f
Use usePathname hook instead of window.location.pathname for better c…
claude 8f9fa05
Add fallback for usePathname to prevent errors in test environments
claude e220fc8
Fix React hooks rules violation - call usePathname unconditionally
claude 09c2ad6
Add usePathname mock to queue-context tests
claude f913097
Add usePathname mock to all beforeEach blocks in queue-context tests
claude a040905
Fix TypeScript error - add type assertion for settername in parsedRou…
claude 6d40e60
Address code review: use URL utilities, implement lazy loading for se…
claude 06a32b7
Show top setters when dropdown is opened without typing
claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
26 changes: 26 additions & 0 deletions
26
app/api/v1/[board_name]/[layout_id]/[size_id]/[set_ids]/[angle]/setters/route.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { getSetterStats, SetterStat } from '@/app/lib/db/queries/climbs/setter-stats'; | ||
| import { BoardRouteParameters, ErrorResponse } from '@/app/lib/types'; | ||
| import { parseBoardRouteParamsWithSlugs } from '@/app/lib/url-utils.server'; | ||
| import { NextResponse } from 'next/server'; | ||
|
|
||
| export async function GET( | ||
| req: Request, | ||
| props: { params: Promise<BoardRouteParameters> }, | ||
| ): Promise<NextResponse<SetterStat[] | ErrorResponse>> { | ||
| const params = await props.params; | ||
|
|
||
| try { | ||
| const parsedParams = await parseBoardRouteParamsWithSlugs(params); | ||
|
|
||
| // Extract search query parameter | ||
| const url = new URL(req.url); | ||
| const searchQuery = url.searchParams.get('search') || undefined; | ||
|
|
||
| const setterStats = await getSetterStats(parsedParams, searchQuery); | ||
|
|
||
| return NextResponse.json(setterStats); | ||
| } catch (error) { | ||
| console.error('Error fetching setter stats:', error); | ||
| return NextResponse.json({ error: 'Failed to fetch setter stats' }, { status: 500 }); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| 'use client'; | ||
|
|
||
| import React, { useState } from 'react'; | ||
| import { Select } from 'antd'; | ||
| import { useUISearchParams } from '../queue-control/ui-searchparams-provider'; | ||
| import { useQueueContext } from '../queue-control/queue-context'; | ||
| import useSWR from 'swr'; | ||
| import { constructSetterStatsUrl } from '@/app/lib/url-utils'; | ||
|
|
||
| interface SetterStat { | ||
| setter_username: string; | ||
| climb_count: number; | ||
| } | ||
|
|
||
| const fetcher = (url: string) => fetch(url).then((res) => res.json()); | ||
|
|
||
| const MIN_SEARCH_LENGTH = 2; // Only search when user has typed at least 2 characters | ||
|
|
||
| const SetterNameSelect = () => { | ||
| const { uiSearchParams, updateFilters } = useUISearchParams(); | ||
| const { parsedParams } = useQueueContext(); | ||
| const [searchValue, setSearchValue] = useState(''); | ||
| const [isOpen, setIsOpen] = useState(false); | ||
|
|
||
| // Fetch top setters when dropdown is open OR when user is searching | ||
| const shouldFetch = isOpen || searchValue.length >= MIN_SEARCH_LENGTH; | ||
| const isSearching = searchValue.length >= MIN_SEARCH_LENGTH; | ||
|
|
||
| // Build API URL - with search query if searching, without if just showing top setters | ||
| const apiUrl = shouldFetch | ||
| ? constructSetterStatsUrl(parsedParams, isSearching ? searchValue : undefined) | ||
| : null; | ||
|
|
||
| // Fetch setter stats from the API | ||
| const { data: setterStats, isLoading } = useSWR<SetterStat[]>( | ||
| apiUrl, | ||
| fetcher, | ||
| { | ||
| revalidateOnFocus: false, | ||
| revalidateOnReconnect: false, | ||
| keepPreviousData: true, | ||
| } | ||
| ); | ||
|
|
||
| // Map setter stats to Select options | ||
| const options = React.useMemo(() => { | ||
| if (!setterStats) return []; | ||
|
|
||
| return setterStats.map(stat => ({ | ||
| value: stat.setter_username, | ||
| label: `${stat.setter_username} (${stat.climb_count})`, | ||
| count: stat.climb_count, | ||
| })); | ||
| }, [setterStats]); | ||
|
|
||
| return ( | ||
| <Select | ||
| mode="multiple" | ||
| placeholder="Select setters..." | ||
| value={uiSearchParams.settername} | ||
| onChange={(value) => updateFilters({ settername: value })} | ||
| onSearch={setSearchValue} | ||
| onDropdownVisibleChange={setIsOpen} | ||
| loading={isLoading} | ||
| showSearch | ||
| filterOption={false} // Server-side filtering | ||
| options={options} | ||
| style={{ width: '100%' }} | ||
| maxTagCount="responsive" | ||
| notFoundContent={ | ||
| isLoading | ||
| ? 'Loading...' | ||
| : !isOpen && searchValue.length === 0 | ||
| ? 'Open dropdown to see setters' | ||
| : 'No setters found' | ||
| } | ||
| /> | ||
| ); | ||
| }; | ||
|
|
||
| export default SetterNameSelect; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean it uses progressive loading? We probably dont want to load all the setters by default. We need progressive load, and if the user types in text, we should filter on that text