Skip to content

Add search toggles #137

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
224 changes: 165 additions & 59 deletions app/components/search-drawer/basic-search-form.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Form.Item>
<Alert
message="Sign in to access your logbook"
description="Login to your account to search for your favourite climbs, climbs you've done, or attempted."
type="info"
showIcon
/>
</Form.Item>
);
}

return (
<>
<Form.Item label="Climbs I have Done" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.showDone}
onChange={(checked) => updateFilters({ showDone: checked })}
/>
</Form.Item>

<Form.Item label="Climbs I have Attempted" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.showAttempted}
onChange={(checked) => updateFilters({ showAttempted: checked })}
/>
</Form.Item>

<Form.Item label="Climbs I have Not Attempted" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.showNotAttempted}
onChange={(checked) => updateFilters({ showNotAttempted: checked })}
/>
</Form.Item>

<Form.Item label="Climbs I Liked" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.showOnlyLiked}
onChange={(checked) => updateFilters({ showOnlyLiked: checked })}
/>
</Form.Item>
</>
);
};

return (
<Form labelCol={{ span: 8 }} wrapperCol={{ span: 16 }}>
<Form layout="horizontal" labelAlign="left" labelCol={{ span: 14 }} wrapperCol={{ span: 10 }}>
<Form.Item label="Climb Name">
<SearchClimbNameInput />
</Form.Item>

<Form.Item label="Grade Range">
<Row gutter={8}>
<Col span={12}>
<Form.Item label="Min" noStyle>
<Select
value={uiSearchParams.minGrade || 0}
defaultValue={0}
onChange={(value) => handleGradeChange('min', value)}
style={{ width: '100%' }}
>
<Select.Option value={0}>Any</Select.Option>
{grades.map((grade) => (
<Select.Option key={grade.difficulty_id} value={grade.difficulty_id}>
{grade.difficulty_name}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="Max" noStyle>
<Select
value={uiSearchParams.maxGrade || 0}
defaultValue={0}
onChange={(value) => handleGradeChange('max', value)}
style={{ width: '100%' }}
>
<Select.Option value={0}>Any</Select.Option>
{grades.map((grade) => (
<Select.Option key={grade.difficulty_id} value={grade.difficulty_id}>
{grade.difficulty_name}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
</Row>
<Slider
range
min={grades[0].difficulty_id}
max={grades[grades.length - 1].difficulty_id}
value={[uiSearchParams.minGrade, uiSearchParams.maxGrade]}
marks={{
[uiSearchParams.minGrade]: {
style: { transform: 'translate(-5px, 0px)' },
label: grades.find(({ difficulty_id }) => 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] })}
/>
</Form.Item>

<Form.Item label="Min Ascents">
<InputNumber
min={1}
value={uiSearchParams.minAscents}
onChange={(value) => updateFilters({ minAscents: value || undefined })}
onChange={(value) => updateFilters({ minAscents: value || 10 })}
style={{ width: '100%' }}
placeholder="Any"
/>
</Form.Item>

Expand Down Expand Up @@ -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"
/>
</Form.Item>

<Form.Item label="Classics Only">
<Select
value={uiSearchParams.onlyClassics}
onChange={(value) => updateFilters({ onlyClassics: value })}
style={{ width: '100%' }}
>
<Select.Option value="0">No</Select.Option>
<Select.Option value="1">Yes</Select.Option>
</Select>
<Form.Item label="Classics Only" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.onlyClassics}
onChange={(checked) => updateFilters({ onlyClassics: checked })}
/>
</Form.Item>

<Form.Item label="Grade Accuracy">
<Select
value={uiSearchParams.gradeAccuracy}
onChange={(value) => updateFilters({ gradeAccuracy: value || undefined })}
onChange={(value) => updateFilters({ gradeAccuracy: value })}
style={{ width: '100%' }}
>
<Select.Option value={undefined}>Any</Select.Option>
<Select.Option value={1}>Any</Select.Option>
<Select.Option value={0.2}>Somewhat Accurate (&lt;0.2)</Select.Option>
<Select.Option value={0.1}>Very Accurate (&lt;0.1)</Select.Option>
<Select.Option value={0.05}>Extremely Accurate (&lt;0.05)</Select.Option>
Expand All @@ -139,8 +163,90 @@ const BasicSearchForm: React.FC = () => {
<Form.Item label="Setter Name">
<Input value={uiSearchParams.settername} onChange={(e) => updateFilters({ settername: e.target.value })} />
</Form.Item>

<Form.Item>
<Title level={5}>Logbook</Title>
</Form.Item>

{renderLogbookSection()}

<Form.Item label="Only Climbs with Beta Videos" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.onlyWithBeta}
onChange={(checked) => updateFilters({ onlyWithBeta: checked })}
/>
</Form.Item>

<Form.Item>
<Title level={5}>Climb Types</Title>
</Form.Item>

<Form.Item label="Boulders" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.showBoulders}
onChange={(checked) => updateFilters({ showBoulders: checked })}
/>
</Form.Item>

<Form.Item label="Routes" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.showRoutes}
onChange={(checked) => updateFilters({ showRoutes: checked })}
/>
</Form.Item>

<Form.Item>
<Title level={5}>Climb Status</Title>
</Form.Item>

<Form.Item label="Established" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.showEstablished}
onChange={(checked) => updateFilters({ showEstablished: checked })}
/>
</Form.Item>

<Form.Item label="Open Projects" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.showProjects}
onChange={(checked) => updateFilters({ showProjects: checked })}
/>
</Form.Item>

<Form.Item label="Drafts" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.showDrafts}
onChange={(checked) => updateFilters({ showDrafts: checked })}
/>
</Form.Item>

<Form.Item>
<Title level={5}>Climb Size & Shape</Title>
</Form.Item>

<Form.Item label="Only Tall Climbs" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.onlyTall}
onChange={(checked) => updateFilters({ onlyTall: checked })}
/>
</Form.Item>

<Form.Item label="Only Side Climbs" valuePropName="checked">
<Switch
style={{ float: 'right' }}
checked={uiSearchParams.onlySide}
onChange={(checked) => updateFilters({ onlySide: checked })}
/>
</Form.Item>
</Form>
);
};

export default BasicSearchForm;
export default BasicSearchForm;
2 changes: 1 addition & 1 deletion app/components/search-drawer/search-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ const SearchForm: React.FC<SearchFormProps> = ({ boardDetails }) => {
);
};

export default SearchForm;
export default SearchForm;
7 changes: 4 additions & 3 deletions app/lib/data-sync/aurora/shared-sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ export async function syncSharedData(board: BoardName): Promise<Record<string, {
}

const upsertAllSharedTableData = async (board: BoardName, syncResults: SyncData) => {
const results: Record<string, { synced: number }> = {};
let results: Record<string, { synced: number }> = {};

await db.transaction(
async (
Expand All @@ -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();
}
Expand Down
27 changes: 25 additions & 2 deletions app/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,19 +70,42 @@ export type HoldsFilter = Partial<Record<HoldFilterKey, HoldFilterValue>>;
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;
Expand Down
21 changes: 21 additions & 0 deletions app/lib/url-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down