Skip to content

Conversation

@marcodejongh
Copy link
Owner

  • Add new GraphQL query userProfileStats that returns distinct climb
    counts per grade per board layout using server-side deduplication
  • Add backend resolver that groups ticks by climbUuid to count unique
    climbs rather than total ascents
  • Update profile page to use server-side stats for the Statistics
    Summary Card showing distinct climbs by grade
  • Charts still use frontend data with timeframe filtering

This ensures that if a user climbed the same problem 5 times, it only
counts as 1 climb for that grade, not 5 ascents.

- Add new GraphQL query `userProfileStats` that returns distinct climb
  counts per grade per board layout using server-side deduplication
- Add backend resolver that groups ticks by climbUuid to count unique
  climbs rather than total ascents
- Update profile page to use server-side stats for the Statistics
  Summary Card showing distinct climbs by grade
- Charts still use frontend data with timeframe filtering

This ensures that if a user climbed the same problem 5 times, it only
counts as 1 climb for that grade, not 5 ascents.
@vercel
Copy link

vercel bot commented Jan 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
boardsesh Building Building Preview, Comment Jan 3, 2026 10:50pm

@claude
Copy link

claude bot commented Jan 3, 2026

Claude Review

Ready to merge - Minor issues noted below, but nothing blocking.

Issues

  1. Missing input validation - packages/backend/src/graphql/resolvers/ticks/queries.ts:330: The userId parameter is not validated before being used in database queries. Other public queries like userTicks validate boardType with validateInput, but this query uses userId directly. Consider adding validation to ensure it's a non-empty string or valid UUID format.

  2. Potential N+1 query inefficiency - packages/backend/src/graphql/resolvers/ticks/queries.ts:350-369: The resolver runs 2 separate database queries (one per board type) sequentially. While acceptable for 2 boards, could use Promise.all for parallel execution if performance becomes a concern.

  3. Lost functionality in UI - packages/web/app/crusher/[user_id]/profile-page-content.tsx: The Statistics Summary Card no longer shows flash/send counts. The old implementation tracked totalFlashes and totalSends with icons, but these were removed. If this was intentional, disregard - but if users relied on this data, it should be preserved or the backend should return it.

No Tests

This PR adds a new GraphQL query without corresponding tests. Consider adding tests for:

  • userProfileStats resolver with various user data scenarios
  • Empty user with no ticks
  • User with only attempts (should return 0 distinct climbs)
  • User with duplicate climbs across multiple ascents

Replace JavaScript Set-based deduplication with SQL aggregation:
- Use COUNT(DISTINCT climb_uuid) GROUP BY for grade counts
- More efficient database-level aggregation instead of fetching all rows
@claude
Copy link

claude bot commented Jan 3, 2026

Claude Review

Ready to merge - Minor issues noted below, but nothing blocking.

Issues

  1. Missing input validation - packages/backend/src/graphql/resolvers/ticks/queries.ts:330: The userId parameter is not validated. An empty string or malformed ID would still execute queries.

  2. Potential null pointer on grade parsing - packages/web/app/crusher/[user_id]/profile-page-content.tsx:560-563: Using parseInt(grade) on the server response could return NaN for non-numeric grade strings, causing difficultyMapping[NaN] to return undefined silently.

  3. No tests - New GraphQL query userProfileStats and its resolver have no test coverage for:

    • Empty results case
    • User with no ticks
    • Edge cases like null layoutId handling
    • SQL injection prevention (though Drizzle should handle this)
  4. Unused type import - packages/web/app/crusher/[user_id]/profile-page-content.tsx:34: GqlLayoutStats is imported but never used.

  5. Sequential database queries - packages/backend/src/graphql/resolvers/ticks/queries.ts:349-406: The loop iterates over board types sequentially with two queries each. Could parallelize with Promise.all for better performance.

- Rename "Routes by Angle" to "Ascents by Angle" with distinct counts
- Count distinct climbs per angle using climbUuid+angle as unique key
- Add userId validation in backend resolver (return empty for invalid)
- Parallelize database queries with Promise.all for better performance
- Add NaN check for parseInt when parsing grade difficulty
- Remove unused GqlLayoutStats import
- Add climbUuid to logbook entries for angle chart deduplication
@claude
Copy link

claude bot commented Jan 3, 2026

Claude Review

Ready to merge - Minor issues noted below, but nothing blocking.

Issues

  1. Missing tests for new query resolver - The userProfileStats resolver in packages/backend/src/graphql/resolvers/ticks/queries.ts:323-445 has no corresponding tests. Given the existing test infrastructure in packages/backend/src/__tests__/, this new public query should have test coverage for:

    • Empty userId handling
    • Multi-board aggregation logic
    • Grade count deduplication
  2. Type duplication - GradeCount and LayoutStats types are defined in both packages/shared-schema/src/types.ts and packages/web/app/lib/graphql/operations/ticks.ts:210-225. Consider importing from shared-schema instead to avoid drift.

  3. Potential null handling - In packages/backend/src/graphql/resolvers/ticks/queries.ts:404, when row.difficulty is null, the entry is skipped. However, the frontend charts in profile-page-content.tsx still filter entry.difficulty === null, suggesting the server could return grades that don't map properly. This is defensive but worth noting for data consistency.

@marcodejongh marcodejongh merged commit 57d4001 into main Jan 4, 2026
5 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants