diff --git a/app/[board_name]/[layout_id]/[size_id]/[set_ids]/[angle]/list/page.tsx b/app/[board_name]/[layout_id]/[size_id]/[set_ids]/[angle]/list/page.tsx index 45187354..591e91fb 100644 --- a/app/[board_name]/[layout_id]/[size_id]/[set_ids]/[angle]/list/page.tsx +++ b/app/[board_name]/[layout_id]/[size_id]/[set_ids]/[angle]/list/page.tsx @@ -29,7 +29,7 @@ export default async function DynamicResultsPage({ fetchBoardDetails(parsedParams.board_name, parsedParams.layout_id, parsedParams.size_id, parsedParams.set_ids), ]); - if (!fetchedResults || fetchedResults.climbs.length === 0) { + if (!fetchedResults || fetchedResults.climbs?.length === 0) { notFound(); } diff --git a/app/components/search-drawer/basic-search-form.tsx b/app/components/search-drawer/basic-search-form.tsx index 7b0a8b96..81df5e31 100644 --- a/app/components/search-drawer/basic-search-form.tsx +++ b/app/components/search-drawer/basic-search-form.tsx @@ -1,75 +1,103 @@ 'use client'; import React from 'react'; -import { Form, InputNumber, Row, Col, Select, Input } from 'antd'; +import { Form, InputNumber, Row, Col, Select, Input, Switch, Alert, Typography, Slider } from 'antd'; import { TENSION_KILTER_GRADES } from '@/app/lib/board-data'; import { useUISearchParams } from '@/app/components/queue-control/ui-searchparams-provider'; import SearchClimbNameInput from './search-climb-name-input'; +import { useBoardProvider } from '../board-provider/board-provider-context'; + +const { Title } = Typography; const BasicSearchForm: React.FC = () => { const { uiSearchParams, updateFilters } = useUISearchParams(); + const { user } = useBoardProvider(); + const userId = user?.id; const grades = TENSION_KILTER_GRADES; - const handleGradeChange = (type: 'min' | 'max', value: number | undefined) => { - if (type === 'min') { - updateFilters({ minGrade: value }); - } else { - updateFilters({ maxGrade: value }); + const renderLogbookSection = () => { + if (!userId) { + return ( + + + + ); } + + return ( + <> + + updateFilters({ showDone: checked })} + /> + + + + updateFilters({ showAttempted: checked })} + /> + + + + updateFilters({ showNotAttempted: checked })} + /> + + + + updateFilters({ showOnlyLiked: checked })} + /> + + + ); }; return ( -
+ - - - - - - - - - - - - + difficulty_id === uiSearchParams.minGrade)?.difficulty_name, + }, + [uiSearchParams.maxGrade]: { + style: { transform: 'translate(-5px, -30px)' }, + label: grades.find(({ difficulty_id }) => difficulty_id === uiSearchParams.maxGrade)?.difficulty_name, + }, + }} + onChange={(value) => updateFilters({ minGrade: value[0], maxGrade: value[1] })} + /> updateFilters({ minAscents: value || undefined })} + onChange={(value) => updateFilters({ minAscents: value || 10 })} style={{ width: '100%' }} - placeholder="Any" /> @@ -106,30 +134,26 @@ const BasicSearchForm: React.FC = () => { max={3.0} step={0.1} value={uiSearchParams.minRating} - onChange={(value) => updateFilters({ minRating: value || undefined })} + onChange={(value) => updateFilters({ minRating: value || 1 })} style={{ width: '100%' }} - placeholder="Any" /> - - + + updateFilters({ onlyClassics: checked })} + /> updateFilters({ settername: e.target.value })} /> + + + Logbook + + + {renderLogbookSection()} + + + updateFilters({ onlyWithBeta: checked })} + /> + + + + Climb Types + + + + updateFilters({ showBoulders: checked })} + /> + + + + updateFilters({ showRoutes: checked })} + /> + + + + Climb Status + + + + updateFilters({ showEstablished: checked })} + /> + + + + updateFilters({ showProjects: checked })} + /> + + + + updateFilters({ showDrafts: checked })} + /> + + + + Climb Size & Shape + + + + updateFilters({ onlyTall: checked })} + /> + + + + updateFilters({ onlySide: checked })} + /> + ); }; -export default BasicSearchForm; +export default BasicSearchForm; \ No newline at end of file diff --git a/app/components/search-drawer/search-form.tsx b/app/components/search-drawer/search-form.tsx index 5514f2b4..339d3c32 100644 --- a/app/components/search-drawer/search-form.tsx +++ b/app/components/search-drawer/search-form.tsx @@ -23,4 +23,4 @@ const SearchForm: React.FC = ({ boardDetails }) => { ); }; -export default SearchForm; +export default SearchForm; \ No newline at end of file diff --git a/app/lib/data-sync/aurora/shared-sync.ts b/app/lib/data-sync/aurora/shared-sync.ts index 973d5fae..6d8e4ed2 100644 --- a/app/lib/data-sync/aurora/shared-sync.ts +++ b/app/lib/data-sync/aurora/shared-sync.ts @@ -349,7 +349,7 @@ export async function syncSharedData(board: BoardName): Promise { - const results: Record = {}; + let results: Record = {}; await db.transaction( async ( @@ -372,9 +372,10 @@ const upsertAllSharedTableData = async (board: BoardName, syncResults: SyncData) console.log(`Updated ${tableName} with ${data.length} rows`); return [tableName, { synced: data.length }]; }); - const results = Object.fromEntries(await Promise.all(promises)); + + results = Object.fromEntries(await Promise.all(promises)); } catch (error) { - //@ts-expect-error + //@ts-expect-error TODO Fix later console.error('Failed to commit sync database transaction ', error.toString()); tx.rollback(); } diff --git a/app/lib/types.ts b/app/lib/types.ts index 5cfc269e..e9580dd8 100644 --- a/app/lib/types.ts +++ b/app/lib/types.ts @@ -70,19 +70,42 @@ export type HoldsFilter = Partial>; export type SearchRequest = { gradeAccuracy: number; maxGrade: number; - minAscents: number; minGrade: number; minRating: number; + minAscents: number; sortBy: 'ascents' | 'difficulty' | 'name' | 'quality'; sortOrder: 'asc' | 'desc'; name: string; onlyClassics: boolean; settername: string; setternameSuggestion: string; - holdsFilter: HoldsFilter; [key: `hold_${number}`]: HoldFilterValue; // Allow dynamic hold keys directly in the search params + holdsFilter: HoldStateFilter; + holds: string; + mirroredHolds: string; + + // Logbook filters + showDone: boolean; + showAttempted: boolean; + showNotAttempted: boolean; + showOnlyLiked: boolean; + onlyWithBeta: boolean; + + // Climb Type filters + showBoulders: boolean; + showRoutes: boolean; + + // Climb Status filters + showEstablished: boolean; + showProjects: boolean; + showDrafts: boolean; + + // Climb Size & Shape filters + onlyTall: boolean; + onlySide: boolean; }; +// SearchRequestPagination remains the same structure export type SearchRequestPagination = SearchRequest & { page: number; pageSize: number; diff --git a/app/lib/url-utils.ts b/app/lib/url-utils.ts index 8be797ea..175046b7 100644 --- a/app/lib/url-utils.ts +++ b/app/lib/url-utils.ts @@ -90,6 +90,27 @@ export const DEFAULT_SEARCH_PARAMS: SearchRequestPagination = { holdsFilter: {}, page: 0, pageSize: PAGE_LIMIT, + + // New filter defaults + // Logbook defaults - start with everything visible + showDone: true, + showAttempted: true, + showNotAttempted: true, + showOnlyLiked: false, + onlyWithBeta: false, + + // Climb Type defaults - show all types initially + showBoulders: true, + showRoutes: true, + + // Climb Status defaults - show established climbs by default + showEstablished: true, + showProjects: false, + showDrafts: false, + + // Climb Size & Shape defaults - show all climbs initially + onlyTall: false, + onlySide: false, }; export const urlParamsToSearchParams = (urlParams: URLSearchParams): SearchRequestPagination => {