Skip to content

feat: add user reputation dashboard with XP, skills radar, and contri…#169

Open
legend4tech wants to merge 1 commit intoboundlessfi:mainfrom
legend4tech:feature/user-reputation-dashboard
Open

feat: add user reputation dashboard with XP, skills radar, and contri…#169
legend4tech wants to merge 1 commit intoboundlessfi:mainfrom
legend4tech:feature/user-reputation-dashboard

Conversation

@legend4tech
Copy link
Copy Markdown
Contributor

@legend4tech legend4tech commented Mar 29, 2026

Committed and pushed successfully. Here’s a brief PR description you can copy:

Feature: User Reputation Dashboard

Overview

Adds a visual reputation dashboard to user profiles showcasing XP, technical skills, and contribution activity.

Changes

  • New Types: Extended types/reputation.ts with SkillLevel, ContributionHistory, and UserReputationMetrics
  • Skill Radar Chart: Recharts-based radar visualization for technical skills (0-100 scale)
  • Contribution Heatmap: GitHub-style activity heatmap with streak tracking
  • XP Display: Level progression widget with XP thresholds
  • Profile Update: Added "Reputation Dashboard" tab to profile page

Testing

  • Build passes with no TypeScript errors
  • All existing tests pass
  • Responsive layout tested across screen sizes
  • Empty states handled gracefully

Closes #132

Summary by CodeRabbit

Release Notes

  • New Features
    • Added Reputation Dashboard tab to user profiles displaying XP, level progression, and visual progress indicators
    • Added interactive radar chart visualizing contributor skills and expertise levels
    • Added contribution activity heatmap showing yearly contribution patterns and statistics

…bution heatmap

- Extend reputation types with XP, skills, and contribution history fields
- Create skill radar chart component using Recharts
- Create GitHub-style contribution heatmap with streak tracking
- Add XP display widget with level progression
- Add reputation dashboard tab to profile page
- Handle empty states gracefully for all components

Co-authored-by: Qwen-Coder <[email protected]>
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 29, 2026

@legend4tech is attempting to deploy a commit to the Threadflow Team on Vercel.

A member of the Team first needs to authorize it.

@drips-wave
Copy link
Copy Markdown

drips-wave bot commented Mar 29, 2026

@legend4tech Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

This PR introduces a new Reputation Dashboard feature to user profiles, consisting of four new UI components (XpDisplay, SkillRadarChart, ContributionHeatmap, ReputationDashboard), a new dashboard tab integrated into the profile page, and extended reputation types to support the new metrics structure.

Changes

Cohort / File(s) Summary
Reputation Types & Data
types/reputation.ts
Added five new exported interfaces: SkillLevel, ContributionDay, ContributionHistory, UserReputationMetrics, and ContributorReputationWithMetrics (which extends ContributorReputation). Standardized quote formatting in existing type literals.
Dashboard Components
components/profile/xp-display.tsx, components/profile/skill-radar-chart.tsx, components/profile/contribution-heatmap.tsx, components/profile/reputation-dashboard.tsx
Created four new client components: XpDisplay (renders XP with level and progress bar using a 1000-XP-per-level calculation), SkillRadarChart (Recharts-based radar visualization with normalized skill levels and empty-state handling), ContributionHeatmap (GitHub-style year-long activity heatmap with date-based contribution counts and color-intensity mapping), and ReputationDashboard (wrapper component composing the three above with extracted metrics).
Profile Page Integration
app/profile/[userId]/page.tsx
Added a new "Reputation Dashboard" tab to the existing tabbed interface alongside other profile tabs, importing and rendering the ReputationDashboard component with cast reputation data.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • Benjtalkshow
  • 0xdevcollins

Poem

🐰 A dashboard blooms with charts so bright,
XP glows and skills take flight,
Heatmaps dance in verdant hue,
Reputation shines—a shiny new view! ⭐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a user reputation dashboard with XP, skills radar, and contribution heatmap visualization to user profiles.
Linked Issues check ✅ Passed The PR successfully implements all core requirements from issue #132: XP display [XpDisplay], skills radar chart [SkillRadarChart], contribution heatmap [ContributionHeatmap], extended reputation types, and dashboard integration into the profile page.
Out of Scope Changes check ✅ Passed All changes directly support the reputation dashboard feature; no unrelated modifications detected. Type formatting updates in types/reputation.ts are supporting changes for the feature's data structures.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/profile/`[userId]/page.tsx:
- Around line 235-239: The code force-casts reputation to
ContributorReputationWithMetrics when rendering <ReputationDashboard>, which can
crash if the API returned ContributorReputation without metrics; instead, check
for reputation.metrics before casting and handle the missing-metrics case
gracefully—either render a fallback/placeholder UI or pass default metrics
(e.g., zeros) so ReputationDashboard always receives the expected shape; update
the prop handling in ReputationDashboard usage (or its prop type) to accept
ContributorReputation | ContributorReputationWithMetrics and guard/use default
values when metrics is undefined.

In `@components/profile/contribution-heatmap.tsx`:
- Line 12: Remove the unused import and parameter to fix lint warnings: delete
the unused named import isSameDay from the top import list and remove the unused
map callback parameter index (or rename it to _ if needed) in the component's
map/iteration where index is defined so both symbols are no longer unused;
ensure any logic relying on isSameDay is preserved or replaced with the used
utilities (format, subDays, startOfDay) and run the linter to confirm warnings
are resolved.
- Around line 151-169: The current month labels use the static MONTHS array and
can misalign with the actual 365-day heatmap; instead compute monthLabels from
the rendered weeks array: iterate over weeks (use each week’s first real day or
first non-null day), derive its month name/index, and only emit a label when the
month changes or at the first/last week; replace the static MONTHS usage in the
render block with the computed monthLabels so labels align with week columns
(refer to symbols weeks, monthLabels, and MONTHS in contribution-heatmap.tsx).

In `@components/profile/xp-display.tsx`:
- Around line 14-19: getLevelFromXp currently computes level/progress directly
from xp which allows negative xp to produce invalid results; clamp or normalize
xp to be non-negative at the top of getLevelFromXp (e.g., const safeXp =
Math.max(0, xp)) and then use safeXp for computing level, currentLevelXp,
nextLevelXp and progress so level, currentLevelXp and progress are always valid.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4102b51e-7690-4e95-8068-26ace6d84f11

📥 Commits

Reviewing files that changed from the base of the PR and between 5cc6ee3 and 39bf57e.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • app/profile/[userId]/page.tsx
  • components/profile/contribution-heatmap.tsx
  • components/profile/reputation-dashboard.tsx
  • components/profile/skill-radar-chart.tsx
  • components/profile/xp-display.tsx
  • types/reputation.ts

Comment on lines +235 to +239
<TabsContent value="dashboard" className="mt-6">
<h2 className="text-xl font-bold mb-4">Reputation Dashboard</h2>
<ReputationDashboard
reputation={reputation as ContributorReputationWithMetrics}
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Unsafe cast can crash the Dashboard tab at runtime.

At Line 238, reputation is force-cast to ContributorReputationWithMetrics, but API data is typed as ContributorReputation (no guaranteed metrics). Opening this tab can throw when metrics.xp is accessed.

💡 Suggested fix (guard + graceful partial-data handling)
             <TabsContent value="dashboard" className="mt-6">
               <h2 className="text-xl font-bold mb-4">Reputation Dashboard</h2>
-              <ReputationDashboard
-                reputation={reputation as ContributorReputationWithMetrics}
-              />
+              {"metrics" in reputation && reputation.metrics ? (
+                <ReputationDashboard
+                  reputation={reputation as ContributorReputationWithMetrics}
+                />
+              ) : (
+                <div className="p-8 border rounded-lg text-center text-muted-foreground bg-secondary/5">
+                  Reputation metrics are not available yet.
+                </div>
+              )}
             </TabsContent>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<TabsContent value="dashboard" className="mt-6">
<h2 className="text-xl font-bold mb-4">Reputation Dashboard</h2>
<ReputationDashboard
reputation={reputation as ContributorReputationWithMetrics}
/>
<TabsContent value="dashboard" className="mt-6">
<h2 className="text-xl font-bold mb-4">Reputation Dashboard</h2>
{"metrics" in reputation && reputation.metrics ? (
<ReputationDashboard
reputation={reputation as ContributorReputationWithMetrics}
/>
) : (
<div className="p-8 border rounded-lg text-center text-muted-foreground bg-secondary/5">
Reputation metrics are not available yet.
</div>
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/profile/`[userId]/page.tsx around lines 235 - 239, The code force-casts
reputation to ContributorReputationWithMetrics when rendering
<ReputationDashboard>, which can crash if the API returned ContributorReputation
without metrics; instead, check for reputation.metrics before casting and handle
the missing-metrics case gracefully—either render a fallback/placeholder UI or
pass default metrics (e.g., zeros) so ReputationDashboard always receives the
expected shape; update the prop handling in ReputationDashboard usage (or its
prop type) to accept ContributorReputation | ContributorReputationWithMetrics
and guard/use default values when metrics is undefined.

TooltipTrigger,
} from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { format, subDays, startOfDay, isSameDay } from "date-fns";
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Address lint warnings for unused symbols.

Line 12 imports isSameDay but never uses it, and Line 86 defines index but does not use it. This is currently reported by CI lint warnings.

🧹 Minimal cleanup patch
-import { format, subDays, startOfDay, isSameDay } from "date-fns";
+import { format, subDays, startOfDay } from "date-fns";
...
-  daysData.forEach((day, index) => {
+  daysData.forEach((day) => {

Also applies to: 86-86

🧰 Tools
🪛 GitHub Actions: CI - Build & Lint Check

[warning] 12-12: ESLint (@typescript-eslint/no-unused-vars): 'isSameDay' is defined but never used.

🪛 GitHub Check: build-and-lint (24.x)

[warning] 12-12:
'isSameDay' is defined but never used

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/profile/contribution-heatmap.tsx` at line 12, Remove the unused
import and parameter to fix lint warnings: delete the unused named import
isSameDay from the top import list and remove the unused map callback parameter
index (or rename it to _ if needed) in the component's map/iteration where index
is defined so both symbols are no longer unused; ensure any logic relying on
isSameDay is preserved or replaced with the used utilities (format, subDays,
startOfDay) and run the linter to confirm warnings are resolved.

Comment on lines +151 to +169
<div className="flex gap-[3px] ml-6 mb-1">
{MONTHS.map((month, index) => {
// Show month label at the start of each month
const shouldShow =
index === 0 ||
(index > 0 && index % 2 === 0) ||
index === MONTHS.length - 1;
if (!shouldShow) {
return <div key={month} className="w-3" />;
}
return (
<div
key={month}
className="text-xs text-muted-foreground w-8"
>
{month}
</div>
);
})}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Month labels are static and can be chronologically incorrect.

At Line 151–169, labels are hard-coded (Jan..Dec) instead of derived from the rendered week columns. This can show wrong month boundaries for the actual 365-day window.

🗓️ Suggested direction (derive labels from weeks)
-            <div className="flex gap-[3px] ml-6 mb-1">
-              {MONTHS.map((month, index) => {
-                // Show month label at the start of each month
-                const shouldShow =
-                  index === 0 ||
-                  (index > 0 && index % 2 === 0) ||
-                  index === MONTHS.length - 1;
-                if (!shouldShow) {
-                  return <div key={month} className="w-3" />;
-                }
-                return (
-                  <div
-                    key={month}
-                    className="text-xs text-muted-foreground w-8"
-                  >
-                    {month}
-                  </div>
-                );
-              })}
-            </div>
+            <div className="flex gap-[3px] ml-6 mb-1">
+              {monthLabels.map((label, weekIndex) => (
+                <div key={`month-${weekIndex}`} className="text-xs text-muted-foreground w-3">
+                  {label}
+                </div>
+              ))}
+            </div>

And compute monthLabels from weeks (based on each week’s first real day) before render.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/profile/contribution-heatmap.tsx` around lines 151 - 169, The
current month labels use the static MONTHS array and can misalign with the
actual 365-day heatmap; instead compute monthLabels from the rendered weeks
array: iterate over weeks (use each week’s first real day or first non-null
day), derive its month name/index, and only emit a label when the month changes
or at the first/last week; replace the static MONTHS usage in the render block
with the computed monthLabels so labels align with week columns (refer to
symbols weeks, monthLabels, and MONTHS in contribution-heatmap.tsx).

Comment on lines +14 to +19
const getLevelFromXp = (xp: number) => {
const level = Math.floor(xp / 1000) + 1;
const currentLevelXp = xp % 1000;
const nextLevelXp = 1000;
const progress = (currentLevelXp / nextLevelXp) * 100;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clamp invalid XP input to avoid broken progress math.

At Line 15–19, negative xp can produce invalid level/progress output. Add a non-negative guard before calculations.

🔧 Suggested defensive patch
 const getLevelFromXp = (xp: number) => {
-  const level = Math.floor(xp / 1000) + 1;
-  const currentLevelXp = xp % 1000;
+  const safeXp = Math.max(0, xp);
+  const level = Math.floor(safeXp / 1000) + 1;
+  const currentLevelXp = safeXp % 1000;
   const nextLevelXp = 1000;
   const progress = (currentLevelXp / nextLevelXp) * 100;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/profile/xp-display.tsx` around lines 14 - 19, getLevelFromXp
currently computes level/progress directly from xp which allows negative xp to
produce invalid results; clamp or normalize xp to be non-negative at the top of
getLevelFromXp (e.g., const safeXp = Math.max(0, xp)) and then use safeXp for
computing level, currentLevelXp, nextLevelXp and progress so level,
currentLevelXp and progress are always valid.

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.

Contributor Reputation Dashboards

1 participant