Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 2 additions & 3 deletions src/components/dashboard/LeaderBoard/PRListModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import React, { useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { FaTimes, FaExternalLinkAlt, FaGithub } from "react-icons/fa";
import { useColorMode } from "@docusaurus/theme-common";
import { useSafeColorMode } from "@site/src/utils/useSafeColorMode";
import { useCommunityStatsContext } from "../../../lib/statsProvider";

interface PRDetails {
Expand Down Expand Up @@ -34,8 +34,7 @@ export default function PRListModal({
isOpen,
onClose,
}: PRListModalProps): JSX.Element | null {
const { colorMode } = useColorMode();
const isDark = colorMode === "dark";
const { isDark } = useSafeColorMode();

// Get filtered PRs from context
const { getFilteredPRsForContributor, currentTimeFilter } =
Expand Down
74 changes: 72 additions & 2 deletions src/components/dashboard/LeaderBoard/leaderboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@

.contributors-header {
display: grid;
grid-template-columns: 0.5fr 0.5fr 2fr 1fr 1fr;
grid-template-columns: 0.5fr 0.5fr 2fr 1fr 1fr 1.5fr;
padding: 16px 24px;
font-weight: bold;
font-size: 14px;
Expand All @@ -569,7 +569,7 @@

.contributor-row {
display: grid;
grid-template-columns: 0.5fr 0.5fr 2fr 1fr 1fr;
grid-template-columns: 0.5fr 0.5fr 2fr 1fr 1fr 1.5fr;
align-items: center;
padding: 16px 24px;
transition: background-color 0.2s ease;
Expand Down Expand Up @@ -615,6 +615,76 @@
padding: 8px;
}

/* Badge display styles */
.contributor-badges {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}

.contributor-badge-icon {
width: 44px;
height: 44px;
object-fit: contain;
border-radius: 8px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
cursor: pointer;
background: rgba(255, 255, 255, 0.1);
padding: 2px;
}

.contributor-badge-icon:hover {
transform: scale(1.15);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
z-index: 10;
position: relative;
}

.username-with-badges {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
}

/* Badges column styling */
.contributor-cell.badges-cell {
display: flex;
align-items: center;
justify-content: flex-start;
padding: 8px;
}

/* Badge display in top performer cards */
.top-performer-card .contributor-badges {
margin-top: 8px;
justify-content: center;
}

.top-performer-card .contributor-badge-icon {
width: 36px;
height: 36px;
}

/* Badge display in podium cards */
.podium-card .details {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}

.podium-card .contributor-badges {
margin-top: 4px;
justify-content: center;
}

.podium-card .contributor-badge-icon {
width: 32px;
height: 32px;
}

/* Position the badge inside the cell */
.rank-badge {
position: absolute;
Expand Down
83 changes: 78 additions & 5 deletions src/components/dashboard/LeaderBoard/leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { motion } from "framer-motion";
import { FaStar, FaCode, FaUsers, FaGithub, FaSearch } from "react-icons/fa";
import { ChevronRight, ChevronLeft } from "lucide-react";
import { useColorMode } from "@docusaurus/theme-common";
import { useSafeColorMode } from "@site/src/utils/useSafeColorMode";
import { useCommunityStatsContext } from "@site/src/lib/statsProvider";
import PRListModal from "./PRListModal";
import { mockContributors } from "./mockData";
Expand All @@ -29,14 +29,78 @@
points: number;
prs: number;
prDetails?: PRDetails[];
badges?: string[]; // Array of badge image paths
}

interface Stats {

Check warning on line 35 in src/components/dashboard/LeaderBoard/leaderboard.tsx

View workflow job for this annotation

GitHub Actions / lint

'Stats' is defined but never used
flooredTotalPRs: number;
totalContributors: number;
flooredTotalPoints: number;
}

// Badge configuration - maps badge numbers to achievement criteria
const BADGE_CONFIG = [
{ image: "/badges/1.png", name: "First Contribution", criteria: (prs: number) => prs >= 1 },
{ image: "/badges/2.png", name: "Bronze Contributor", criteria: (prs: number) => prs >= 5 },
{ image: "/badges/3.png", name: "Silver Contributor", criteria: (prs: number) => prs >= 10 },
{ image: "/badges/4.png", name: "Gold Contributor", criteria: (prs: number) => prs >= 25 },
{ image: "/badges/5.png", name: "Platinum Contributor", criteria: (prs: number) => prs >= 50 },
{ image: "/badges/6.png", name: "Diamond Contributor", criteria: (prs: number) => prs >= 100 },
{ image: "/badges/7.png", name: "Points Master", criteria: (_: number, points: number) => points >= 500 },
{ image: "/badges/8.png", name: "Elite Contributor", criteria: (prs: number) => prs >= 200 },
{ image: "/badges/9.png", name: "Legendary Contributor", criteria: (prs: number) => prs >= 500 },
{ image: "/badges/10.png", name: "Hall of Fame", criteria: (prs: number, points: number) => prs >= 1000 || points >= 5000 },
];

/**
* Determines which badges a contributor should have based on their stats
*/
function getContributorBadges(contributor: Contributor, rank: number): string[] {
const badges: string[] = [];

// Special rank-based badges
if (rank === 1) {
badges.push("/badges/10.png"); // Hall of Fame for #1
} else if (rank === 2) {
badges.push("/badges/9.png"); // Legendary for #2
} else if (rank === 3) {
badges.push("/badges/8.png"); // Elite for #3
}

// Achievement-based badges
BADGE_CONFIG.forEach((badge) => {
if (badge.criteria(contributor.prs, contributor.points)) {
// Avoid duplicates
if (!badges.includes(badge.image)) {
badges.push(badge.image);
}
}
});

return badges;
}

/**
* Badge display component
*/
function ContributorBadges({ badges }: { badges: string[] }) {
if (!badges || badges.length === 0) return null;

return (
<div className="contributor-badges">
{badges.map((badge, index) => (
<img
key={index}
src={badge}
alt={`Badge ${index + 1}`}
className="contributor-badge-icon"
title={BADGE_CONFIG.find(b => b.image === badge)?.name || "Achievement Badge"}
/>
))}
</div>
);
}

function Badge({
count,
label,
Expand Down Expand Up @@ -84,7 +148,7 @@
);
}

function TopPerformerCard({

Check warning on line 151 in src/components/dashboard/LeaderBoard/leaderboard.tsx

View workflow job for this annotation

GitHub Actions / lint

'TopPerformerCard' is defined but never used
contributor,
rank,
onPRClick,
Expand All @@ -93,9 +157,9 @@
rank: number;
onPRClick: (contributor: Contributor) => void;
}) {
const { colorMode } = useColorMode();
const isDark = colorMode === "dark";
const { isDark } = useSafeColorMode();
const rankClass = rank === 1 ? "top-1" : rank === 2 ? "top-2" : "top-3";
const badges = getContributorBadges(contributor, rank);

return (
<div className={`top-performer-card ${isDark ? "dark" : "light"}`}>
Expand All @@ -116,6 +180,7 @@
>
{contributor.username}
</a>
<ContributorBadges badges={badges} />
<div className="badges-container">
<Badge
count={contributor.prs}
Expand Down Expand Up @@ -146,8 +211,7 @@
setTimeFilter,
} = useCommunityStatsContext();

const { colorMode } = useColorMode();
const isDark = colorMode === "dark";
const { isDark } = useSafeColorMode();

const [searchQuery, setSearchQuery] = useState("");
const [currentPage, setCurrentPage] = useState(1);
Expand Down Expand Up @@ -377,6 +441,7 @@
/>
<div className="details">
<p className="username">{filteredContributors[1].username}</p>
<ContributorBadges badges={getContributorBadges(filteredContributors[1], 2)} />
<div className="stats">
<button
className="prs"
Expand All @@ -402,6 +467,7 @@
/>
<div className="details">
<p className="username">{filteredContributors[0].username}</p>
<ContributorBadges badges={getContributorBadges(filteredContributors[0], 1)} />
<div className="stats">
<button
className="prs"
Expand All @@ -427,6 +493,7 @@
/>
<div className="details">
<p className="username">{filteredContributors[2].username}</p>
<ContributorBadges badges={getContributorBadges(filteredContributors[2], 3)} />
<div className="stats">
<button
className="prs"
Expand Down Expand Up @@ -574,6 +641,7 @@
<div className="contributor-cell username-cell">User</div>
<div className="contributor-cell prs-cell">PRs</div>
<div className="contributor-cell points-cell">Points</div>
<div className="contributor-cell badges-cell">Badges</div>
</div>
{currentItems.map((contributor, index) => (
<motion.div
Expand Down Expand Up @@ -629,6 +697,11 @@
color={{ background: "#ede9fe", color: "#7c3aed" }}
/>
</div>
<div className="contributor-cell badges-cell">
<ContributorBadges
badges={getContributorBadges(contributor, indexOfFirst + index + 1)}
/>
</div>
</motion.div>
))}

Expand Down
5 changes: 2 additions & 3 deletions src/components/faqs/faqs.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from "react";
import { FiChevronDown } from "react-icons/fi";
import { motion } from "framer-motion";
import { useColorMode } from "@docusaurus/theme-common"; // Docusaurus theme detection
import { useSafeColorMode } from "../../utils/useSafeColorMode";

const faqData = [
{
Expand Down Expand Up @@ -44,8 +44,7 @@ const faqData = [

const FAQs: React.FC = () => {
const [activeIndex, setActiveIndex] = useState<number | null>(null);
const { colorMode } = useColorMode();
const isDark = colorMode === "dark";
const { colorMode, isDark } = useSafeColorMode();

const toggleAccordion = (index: number) => {
setActiveIndex(activeIndex === index ? null : index);
Expand Down
5 changes: 2 additions & 3 deletions src/components/ourProjects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ChevronRight } from "lucide-react";
import { useState } from "react";
import { motion } from "framer-motion";
import React from "react";
import { useColorMode } from "@docusaurus/theme-common";
import { useSafeColorMode } from "../utils/useSafeColorMode";
// Mobile-specific overrides for very small screens (<768px and <320px)
import "./ourProjects.mobile.css";
// Import projects data and types
Expand Down Expand Up @@ -51,8 +51,7 @@ export interface OurProjectsProps {
const OurProjects: React.FC<OurProjectsProps> = ({
OurProjectsData: legacyData,
}) => {
const { colorMode } = useColorMode(); // light or dark
const isDark = colorMode === "dark";
const { colorMode, isDark } = useSafeColorMode();

// Use JSON data by default, fallback to legacy props for backward compatibility
// Convert legacy data to new format if needed
Expand Down
5 changes: 2 additions & 3 deletions src/components/testimonials/TestimonialCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import { motion } from "framer-motion";
import { Avatar, AvatarFallback, AvatarImage } from "../ui/avatar";
import { useColorMode } from "@docusaurus/theme-common";
import { useSafeColorMode } from "../../utils/useSafeColorMode";

interface TestimonialCardProps {
name: string;
Expand All @@ -20,8 +20,7 @@ const TestimonialCard: React.FC<TestimonialCardProps> = ({
avatar,
link,
}) => {
const { colorMode } = useColorMode();
const isDark = colorMode === "dark";
const { colorMode, isDark } = useSafeColorMode();

// Function to format the link display
const formatLinkDisplay = (url: string) => {
Expand Down
5 changes: 2 additions & 3 deletions src/components/topmate/TopMateCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import { motion } from "framer-motion";
import { ArrowUpRight, Clock } from "lucide-react";
import { useColorMode } from "@docusaurus/theme-common";
import { useSafeColorMode } from "../../utils/useSafeColorMode";

interface TopMateCardProps {
title: string;
Expand All @@ -20,8 +20,7 @@ const TopMateCard: React.FC<TopMateCardProps> = ({
username,
setShowTopmate,
}) => {
const { colorMode } = useColorMode();
const isDark = colorMode === "dark";
const { colorMode, isDark } = useSafeColorMode();

return (
<motion.div
Expand Down
4 changes: 2 additions & 2 deletions src/components/topmate/TopMateSection.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from "react";
import TopMateCard from "./TopMateCard";
import { useColorMode } from "@docusaurus/theme-common";
import { useSafeColorMode } from "../../utils/useSafeColorMode";

const TopMateSection = ({ setShowTopmate }) => {
const { colorMode } = useColorMode(); // Get current theme: 'light' or 'dark'
const { colorMode } = useSafeColorMode(); // Get current theme: 'light' or 'dark'

const profileData = {
title: "1:1 Mentorship Call",
Expand Down
18 changes: 16 additions & 2 deletions src/theme/ColorModeToggle/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import React from "react";
import { useColorMode } from "@docusaurus/theme-common";
import { useSafeColorMode } from "@site/src/utils/useSafeColorMode";
import ExecutionEnvironment from "@docusaurus/ExecutionEnvironment";

export default function ColorModeToggle(): JSX.Element {
const { colorMode, setColorMode } = useColorMode();
const { colorMode } = useSafeColorMode();

// Safe setColorMode that works with DOM
const setColorMode = (mode: "light" | "dark") => {
if (!ExecutionEnvironment.canUseDOM) return;
document.documentElement.setAttribute("data-theme", mode);
// Also trigger Docusaurus's internal theme change if available
try {
const { setColorMode: docusaurusSetColorMode } = require("@docusaurus/theme-common");
docusaurusSetColorMode(mode);
} catch (e) {
// Fallback: just set the DOM attribute
}
};

const toggleColorMode = () => {
const newMode = colorMode === "dark" ? "light" : "dark";
Expand Down
Loading
Loading