Skip to content

Commit c84508b

Browse files
marcodejonghclaude
andauthored
Fix heatmap personal progress filtering (#266)
* Add personal progress filters to climb search Implements filtering by climbs already attempted or completed by the logged-in user. ## New Features - Hide Attempted: Filter out climbs the user has attempted - Hide Completed: Filter out climbs the user has completed - Only Attempted: Show only climbs the user has attempted - Only Completed: Show only climbs the user has completed ## Implementation Details - Added new boolean properties to SearchRequest type - Enhanced search form UI with toggle switches (only visible when logged in) - Updated backend queries to join ascents/bids tables when filters are active - Modified API route to handle user authentication headers - Updated data fetching to include auth headers when available - Added URL parameter persistence and analytics tracking - Fixed test files to include new required properties ## Database Integration - Uses EXISTS subqueries for optimal performance - Supports both Kilter and Tension board types - Only applies filters when user is authenticated Addresses issue #110 - good first issue for filtering climbs by user progress. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Improve personal progress filters UI layout Updates the search form to use the improved UI pattern from PR #137: - Changed form layout to horizontal with left-aligned labels (14/10 span) - Replaced "Classics Only" dropdown with Switch component - Added Typography.Title for "Personal Progress" section heading - Updated Alert message for better clarity when not logged in - Aligned all switches to the right with consistent styling - Used valuePropName="checked" for proper Switch integration The UI now matches the cleaner, more organized design pattern established in the search toggles PR. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> * Fix heatmap personal progress filtering Updates heatmap to properly respect personal progress filters by adding header-based authentication support. ## Changes - Updated heatmap API route to check for x-auth-token and x-user-id headers - Added fallback to session-based auth for backward compatibility - Modified use-heatmap.tsx hook to include auth headers when user is logged in - Added token and user_id to useEffect dependencies for proper re-fetching ## Behavior - When personal progress filters are active, heatmap API checks headers first - Falls back to session auth if no headers provided - Heatmap now properly filters holds based on user's climb history - Maintains existing functionality for non-authenticated users The heatmap will now correctly hide/show holds from climbs the user has attempted or completed when using the personal progress filters. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]> --------- Co-authored-by: Claude <[email protected]>
1 parent 5163860 commit c84508b

File tree

2 files changed

+42
-4
lines changed
  • app

2 files changed

+42
-4
lines changed

app/api/v1/[board_name]/[layout_id]/[size_id]/[set_ids]/[angle]/heatmap/route.ts

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,35 @@ export async function GET(
3333
const parsedParams = await parseBoardRouteParamsWithSlugs(params);
3434
const searchParams: SearchRequestPagination = urlParamsToSearchParams(query);
3535

36-
const cookieStore = await cookies();
37-
const session = await getSession(cookieStore, parsedParams.board_name);
36+
// Extract user authentication - try headers first, then fall back to session
37+
let userId: number | undefined;
38+
39+
// Check for header-based authentication first (for consistency with search API)
40+
const personalProgressFiltersEnabled =
41+
searchParams.hideAttempted ||
42+
searchParams.hideCompleted ||
43+
searchParams.showOnlyAttempted ||
44+
searchParams.showOnlyCompleted;
45+
46+
if (personalProgressFiltersEnabled) {
47+
const userIdHeader = req.headers.get('x-user-id');
48+
const tokenHeader = req.headers.get('x-auth-token');
49+
50+
// Only use userId if both user ID and token are provided (basic auth check)
51+
if (userIdHeader && tokenHeader && userIdHeader !== 'null') {
52+
userId = parseInt(userIdHeader, 10);
53+
}
54+
}
55+
56+
// Fall back to session-based authentication if no header auth
57+
if (!userId) {
58+
const cookieStore = await cookies();
59+
const session = await getSession(cookieStore, parsedParams.board_name);
60+
userId = session.userId;
61+
}
62+
3863
// Get the heatmap data using the query function
39-
const holdStats = await getHoldHeatmapData(parsedParams, searchParams, session.userId);
64+
const holdStats = await getHoldHeatmapData(parsedParams, searchParams, userId);
4065

4166
// Return response
4267
return NextResponse.json({

app/components/search-drawer/use-heatmap.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
22
import { BoardName, SearchRequestPagination } from '@/app/lib/types';
33
import { HeatmapData } from '../board-renderer/types';
44
import { searchParamsToUrlParams } from '@/app/lib/url-utils';
5+
import { useBoardProvider } from '../board-provider/board-provider-context';
56

67
interface UseHeatmapDataProps {
78
boardName: BoardName;
@@ -16,13 +17,25 @@ export default function useHeatmapData({ boardName, layoutId, sizeId, setIds, an
1617
const [heatmapData, setHeatmapData] = useState<HeatmapData[]>([]);
1718
const [loading, setLoading] = useState(true);
1819
const [error, setError] = useState<Error | null>(null);
20+
const { token, user_id } = useBoardProvider();
1921

2022
useEffect(() => {
2123
const fetchHeatmapData = async () => {
2224
try {
2325
setLoading(true);
26+
27+
// Prepare headers
28+
const headers: Record<string, string> = {};
29+
30+
// Add authentication headers if available
31+
if (token && user_id) {
32+
headers['x-auth-token'] = token;
33+
headers['x-user-id'] = user_id.toString();
34+
}
35+
2436
const response = await fetch(
2537
`/api/v1/${boardName}/${layoutId}/${sizeId}/${setIds}/${angle}/heatmap?${searchParamsToUrlParams(filters).toString()}`,
38+
{ headers }
2639
);
2740

2841
if (!response.ok) {
@@ -41,7 +54,7 @@ export default function useHeatmapData({ boardName, layoutId, sizeId, setIds, an
4154
};
4255

4356
fetchHeatmapData();
44-
}, [boardName, layoutId, sizeId, setIds, angle, filters]);
57+
}, [boardName, layoutId, sizeId, setIds, angle, filters, token, user_id]);
4558

4659
return { data: heatmapData, loading, error };
4760
}

0 commit comments

Comments
 (0)