Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Praashh/buildml/llms.txt

Use this file to discover all available pages before exploring further.

Every Buildml user has a profile page that consolidates their progress across all problem sets. It shows how many problems they have solved at each difficulty tier, a GitHub-style activity heatmap for the trailing year, and three headline stat cards (Total Solved, Streak, and Achievements). Profiles are shareable — you can view any other user’s profile by passing their ID in the URL.

Profile URLs

URLBehaviour
/profileShows the currently signed-in user’s own profile. Redirects to / if no session exists.
/profile?userId={id}Shows the profile of the user with the given ID. Useful for sharing progress with teammates.
The ProfilePage server component reads searchParams.userId at request time. If a userId param is present it is used as the targetUserId; otherwise it falls back to session?.user?.id. If neither is available the visitor is redirected to the home page.
The /profile route is covered by Next.js middleware authentication. Users who are not signed in cannot access their own profile — they are redirected to sign in first.

user.getProfile Return Shape

Profile data is loaded via the user.getProfile tRPC procedure (public procedure, accepts { userId?: string }). The response contains the following fields:
user
object
required
Basic identity information for the profile owner.
user.id
string
required
The user’s unique CUID from the database.
user.name
string | null
required
The user’s display name sourced from their Google account.
user.image
string | null
required
URL to the user’s Google profile picture. Displayed as a rounded avatar at the top of the profile.
difficultyCounts
object
required
Number of unique problems solved (status PASS) per difficulty tier. A problem solved multiple times is counted only once.
difficultyCounts.Easy
number
required
Count of unique Easy problems passed.
difficultyCounts.Medium
number
required
Count of unique Medium problems passed.
difficultyCounts.Hard
number
required
Count of unique Hard problems passed.
totalCounts
object
required
Total number of problems available in the database per difficulty tier, used to render the progress bar denominators.
totalCounts.Easy
number
required
Total Easy problems in the system.
totalCounts.Medium
number
required
Total Medium problems in the system.
totalCounts.Hard
number
required
Total Hard problems in the system.
solvedCount
number
required
Total unique problems solved across all difficulties. Derived from solvedProblemIds.size — a Set built while iterating accepted submissions.
history
Record<string, number>
required
A date-keyed map of submission activity used to render the heatmap. Keys are YYYY-MM-DD strings; values are the count of PASS submissions on that date.Example:
{
  "2024-11-03": 2,
  "2024-11-07": 1,
  "2025-01-14": 5
}

Stat Cards

The profile renders three stat cards in a responsive grid:
StatSource
Total SolvedsolvedCount from getProfile — unique problems with a PASS submission
StreakHardcoded placeholder (12 days) — not yet driven by live data
AchievementsHardcoded placeholder (7 badges) — not yet driven by live data
A Level badge is also shown next to the user’s name. It is calculated as Math.floor(solvedCount / 10) + 1, so a user who has solved 0–9 problems is Level 1, 10–19 is Level 2, and so on.

Difficulty Progress Bars

The profile renders a Difficulty Breakdown panel showing a labelled progress bar for each tier. The fill percentage is calculated as:
const getPercentage = (count: number, total: number) =>
  total > 0 ? Math.min((count / total) * 100, 100) : 0;
Each bar displays {count} / {total} in the header and animates on load via a CSS transition-all duration-1000.

Activity Heatmap

The Heatmap component (src/app/profile/_components/Heatmap.tsx) renders a GitHub-style contribution grid for the trailing 52–53 weeks. It takes the history map from getProfile as its only prop:
interface HeatmapProps {
  data: Record<string, number>; // YYYY-MM-DD → submission count
}

Colour scale

Submissions on a given dayCell colour
0bg-[var(--line)] (empty/grey)
1bg-primary/30 (faint)
2–4bg-primary/60 (medium)
5+bg-primary (full accent)
Each cell has a title attribute showing "N submissions on <date>" on hover. The grid is scrollable horizontally on narrow viewports and is labelled with abbreviated month names (Jan–Dec) along the bottom.

Server-Side Prefetching

Like the leaderboard, the profile page prefetches data on the server before streaming HTML to the client:
void api.user.getProfile.prefetch({ userId: targetUserId });
The ProfileClient component then uses useSuspenseQuery to hydrate from the server cache instantly, with no loading state visible on first render.

Sharing Profiles

Each profile page includes a Share Profile button. On devices that support the Web Share API, it invokes navigator.share with a pre-composed message. On desktop or unsupported browsers it copies the shareable URL (/profile?userId={id}) to the clipboard and shows a toast confirmation.

Build docs developers (and LLMs) love