Skip to content
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
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 73 additions & 0 deletions src/components/BadgeIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as React from "react";

export type BadgeKey =
| "first-challenge"
| "design-master"
| "code-warrior"
| "community-helper"
| "streak-master"
| "all-rounder"; // optional fallback

type Props = { kind: BadgeKey; className?: string };

export const BadgeIcon: React.FC<Props> = ({ kind, className = "h-4 w-4" }) => {
// IMPORTANT: boolean values + proper typing
const common: React.SVGProps<SVGSVGElement> = {
className,
viewBox: "0 0 24 24",
"aria-hidden": true,
focusable: false,
fill: "none",
stroke: "currentColor",
strokeWidth: 1.75,
strokeLinecap: "round",
strokeLinejoin: "round",
};

switch (kind) {
case "first-challenge": // 🥇 medal
return (
<svg {...common}>
<circle cx="12" cy="13" r="4" />
<path d="M9 3l3 4 3-4M9 3h6" />
</svg>
);
case "design-master": // 🎨 palette
return (
<svg {...common}>
<path d="M12 21a9 9 0 1 1 0-18 4 4 0 0 1 0 8h-1a2 2 0 0 0 0 4h1" />
<circle cx="8.5" cy="9" r="1" />
<circle cx="10.5" cy="6.5" r="1" />
<circle cx="14.5" cy="7" r="1" />
<circle cx="16" cy="10" r="1" />
</svg>
);
case "code-warrior": // ⟨/⟩
return (
<svg {...common}>
<path d="M8 7l-4 5 4 5M16 7l4 5-4 5" />
</svg>
);
case "community-helper": // 💬
return (
<svg {...common}>
<path d="M7 17l-3 3V7a4 4 0 0 1 4-4h8a4 4 0 0 1 4 4v6a4 4 0 0 1-4 4H7z" />
<path d="M8.5 10.5h7" />
</svg>
);
case "streak-master": // 🔥
return (
<svg {...common}>
<path d="M12 3s2 3 2 5a3 3 0 0 1-6 0c0-2 2-5 2-5" />
<path d="M8 13a4 4 0 1 0 8 0c0-1.5-.6-2.3-1.6-3.4" />
</svg>
);
default: // all-rounder fallback
return (
<svg {...common}>
<circle cx="12" cy="12" r="8" />
<path d="M9 12h6M12 9v6" />
</svg>
);
}
};
51 changes: 28 additions & 23 deletions src/components/Profile/Profile.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import StreakCalendar from "./StreakCalendar";
import { Award, BookOpen, Star, TrendingUp, Calendar, Target } from 'lucide-react';
import { BookOpen, Star, TrendingUp, Calendar, Target } from 'lucide-react';
import { BadgeIcon, type BadgeKey } from "../BadgeIcon";

export const Profile: React.FC = () => {
const completedChallenges = [
Expand Down Expand Up @@ -40,13 +41,13 @@ export const Profile: React.FC = () => {
{ label: 'Avg Rating', value: '4.6', icon: Star, color: 'text-yellow-600 bg-yellow-100 dark:text-yellow-400 dark:bg-yellow-900' }
];

const achievements = [
{ title: 'First Challenge', description: 'Completed your first challenge', earned: true },
{ title: 'Design Master', description: 'Completed 10 design challenges', earned: true },
{ title: 'Code Warrior', description: 'Completed 10 development challenges', earned: true },
{ title: 'Community Helper', description: 'Provided feedback on 25 submissions', earned: false },
{ title: 'Streak Master', description: 'Completed challenges for 7 days straight', earned: false },
{ title: 'All Rounder', description: 'Completed challenges in all domains', earned: false }
const achievements: Array<{ key: BadgeKey; title: string; description: string; earned: boolean }> = [
{ key: 'first-challenge', title: 'First Challenge', description: 'Completed your first challenge', earned: true },
{ key: 'design-master', title: 'Design Master', description: 'Completed 10 design challenges', earned: true },
{ key: 'code-warrior', title: 'Code Warrior', description: 'Completed 10 development challenges', earned: true },
{ key: 'community-helper', title: 'Community Helper', description: 'Provided feedback on 25 submissions', earned: false },
{ key: 'streak-master', title: 'Streak Master', description: 'Completed challenges for 7 days straight', earned: false },
{ key: 'all-rounder', title: 'All Rounder', description: 'Completed challenges in all domains', earned: false },
];

return (
Expand Down Expand Up @@ -149,27 +150,31 @@ export const Profile: React.FC = () => {
</div>
<p className="text-sm text-gray-600 dark:text-gray-400">250 XP needed for Level 4: Advanced</p>
</div>

{/* 30-Day Streak */}
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">
30-Day Streak
</h3>
<StreakCalendar storageKey="cit_activeDates" />
</div>
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">30-Day Streak</h3>
<StreakCalendar storageKey="cit_activeDates" />
</div>

{/* Achievements */}
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-4">Achievements</h3>
<div className="space-y-3">
{achievements.map((achievement, index) => (
<div key={index} className={`flex items-center space-x-3 p-3 rounded-lg ${
achievement.earned ? 'bg-green-50 dark:bg-green-950 border border-green-200 dark:border-green-800' : 'bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600'
}`}>
<div className={`w-8 h-8 rounded-full flex items-center justify-center ${
achievement.earned ? 'bg-green-500 text-white' : 'bg-gray-300 dark:bg-gray-600 text-gray-500 dark:text-gray-400'
}`}>
<Award className="h-4 w-4" />
<div
key={index}
className={`flex items-start gap-2 p-3 rounded-lg ${
achievement.earned
? 'bg-green-50 dark:bg-green-950 border border-green-200 dark:border-green-800'
: 'bg-gray-50 dark:bg-gray-700 border border-gray-200 dark:border-gray-600'
}`}
>
{/* Icon inherits current text color for light/dark */}
<div className={`${achievement.earned ? 'text-green-900 dark:text-green-300' : 'text-gray-700 dark:text-gray-300'}`}>
<BadgeIcon kind={achievement.key} className="h-4 w-4 mt-0.5" />
</div>

<div className="flex-1">
<p className={`text-sm font-medium ${achievement.earned ? 'text-green-900 dark:text-green-300' : 'text-gray-700 dark:text-gray-300'}`}>
{achievement.title}
Expand Down Expand Up @@ -200,8 +205,8 @@ export const Profile: React.FC = () => {
<span className="text-sm text-gray-500 dark:text-gray-400">{item.completed}</span>
</div>
<div className="w-full bg-gray-200 dark:bg-gray-700 rounded-full h-2">
<div
className={`${item.color} h-2 rounded-full`}
<div
className={`${item.color} h-2 rounded-full`}
style={{ width: `${(item.completed / 10) * 100}%` }}
></div>
</div>
Expand Down