From 386428118f58950c0e2f24a9eca799be58c91bdc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Nov 2025 10:21:50 +0000 Subject: [PATCH 1/4] Add tall climbs only filter to BoardSesh This commit adds a new filter toggle that shows only climbs using holds available exclusively on the largest board size. Changes: - Add tallClimbsOnly boolean to SearchRequest type - Update filter builder with SQL logic to identify climbs extending beyond smaller size boundaries (checking edgeTop, edgeBottom, edgeLeft, edgeRight) - Add isLargestSize flag to BoardDetails to determine board size hierarchy - Add UI toggle in BasicSearchForm (visible only on largest size) - Update URL parameter serialization and analytics tracking - Fix test mocks to include new tallClimbsOnly property The filter helps users identify climbs that require the full height/width of their board configuration. --- .../[set_ids]/[angle]/list/layout-client.tsx | 2 +- .../hooks/use-queue-data-fetching.test.tsx | 3 +- .../queue-control/__tests__/reducer.test.ts | 6 ++- .../ui-searchparams-provider.tsx | 1 + .../search-drawer/basic-search-form.tsx | 20 +++++++- app/components/search-drawer/search-form.tsx | 2 +- app/lib/data/queries.ts | 24 +++++++++- .../db/queries/climbs/create-climb-filters.ts | 47 ++++++++++++++++++- app/lib/types.ts | 2 + app/lib/url-utils.ts | 6 +++ 10 files changed, 104 insertions(+), 9 deletions(-) diff --git a/app/[board_name]/[layout_id]/[size_id]/[set_ids]/[angle]/list/layout-client.tsx b/app/[board_name]/[layout_id]/[size_id]/[set_ids]/[angle]/list/layout-client.tsx index 8afbe60..bc92d0e 100644 --- a/app/[board_name]/[layout_id]/[size_id]/[set_ids]/[angle]/list/layout-client.tsx +++ b/app/[board_name]/[layout_id]/[size_id]/[set_ids]/[angle]/list/layout-client.tsx @@ -44,7 +44,7 @@ const TabsWrapper: React.FC<{ boardDetails: BoardDetails }> = ({ boardDetails }) children: (
- +
diff --git a/app/components/queue-control/__tests__/hooks/use-queue-data-fetching.test.tsx b/app/components/queue-control/__tests__/hooks/use-queue-data-fetching.test.tsx index a589129..7cb3fbf 100644 --- a/app/components/queue-control/__tests__/hooks/use-queue-data-fetching.test.tsx +++ b/app/components/queue-control/__tests__/hooks/use-queue-data-fetching.test.tsx @@ -81,7 +81,8 @@ const mockSearchParams: SearchRequestPagination = { hideAttempted: false, hideCompleted: false, showOnlyAttempted: false, - showOnlyCompleted: false + showOnlyCompleted: false, + tallClimbsOnly: false }; const mockParsedParams: ParsedBoardRouteParameters = { diff --git a/app/components/queue-control/__tests__/reducer.test.ts b/app/components/queue-control/__tests__/reducer.test.ts index 34b11b5..22da6d3 100644 --- a/app/components/queue-control/__tests__/reducer.test.ts +++ b/app/components/queue-control/__tests__/reducer.test.ts @@ -47,7 +47,8 @@ const mockSearchParams: SearchRequestPagination = { hideAttempted: false, hideCompleted: false, showOnlyAttempted: false, - showOnlyCompleted: false + showOnlyCompleted: false, + tallClimbsOnly: false }; const initialState: QueueState = { @@ -292,7 +293,8 @@ describe('queueReducer', () => { hideAttempted: false, hideCompleted: false, showOnlyAttempted: false, - showOnlyCompleted: false + showOnlyCompleted: false, + tallClimbsOnly: false }; const action: QueueAction = { diff --git a/app/components/queue-control/ui-searchparams-provider.tsx b/app/components/queue-control/ui-searchparams-provider.tsx index 9b4d2ef..456fce9 100644 --- a/app/components/queue-control/ui-searchparams-provider.tsx +++ b/app/components/queue-control/ui-searchparams-provider.tsx @@ -41,6 +41,7 @@ export const UISearchParamsProvider: React.FC<{ children: React.ReactNode }> = ( if (uiSearchParams.hideCompleted) activeFilters.push('hideCompleted'); if (uiSearchParams.showOnlyAttempted) activeFilters.push('showOnlyAttempted'); if (uiSearchParams.showOnlyCompleted) activeFilters.push('showOnlyCompleted'); + if (uiSearchParams.tallClimbsOnly) activeFilters.push('tallClimbsOnly'); if (activeFilters.length > 0) { track('Climb Search Performed', { diff --git a/app/components/search-drawer/basic-search-form.tsx b/app/components/search-drawer/basic-search-form.tsx index e648135..4678fec 100644 --- a/app/components/search-drawer/basic-search-form.tsx +++ b/app/components/search-drawer/basic-search-form.tsx @@ -7,15 +7,21 @@ import { useUISearchParams } from '@/app/components/queue-control/ui-searchparam import { useBoardProvider } from '@/app/components/board-provider/board-provider-context'; import SearchClimbNameInput from './search-climb-name-input'; import SetterNameSelect from './setter-name-select'; +import { BoardDetails } from '@/app/lib/types'; const { Title } = Typography; -const BasicSearchForm: React.FC = () => { +interface BasicSearchFormProps { + boardDetails: BoardDetails; +} + +const BasicSearchForm: React.FC = ({ boardDetails }) => { const { uiSearchParams, updateFilters } = useUISearchParams(); const { token, user_id } = useBoardProvider(); const grades = TENSION_KILTER_GRADES; - + const isLoggedIn = token && user_id; + const isLargestSize = boardDetails.isLargestSize ?? false; const handleGradeChange = (type: 'min' | 'max', value: number | undefined) => { if (type === 'min') { @@ -178,6 +184,16 @@ const BasicSearchForm: React.FC = () => { /> + {isLargestSize && ( + + updateFilters({ tallClimbsOnly: checked })} + /> + + )} +