diff --git a/src/components/dashboard/SignInDashboard.tsx b/src/components/dashboard/SignInDashboard.tsx new file mode 100644 index 00000000..f3cda82c --- /dev/null +++ b/src/components/dashboard/SignInDashboard.tsx @@ -0,0 +1,267 @@ +import React from 'react' +import { Flame, Calendar, Trophy, Activity, Share2, Check, Play } from 'lucide-react' +import { useUser } from '@clerk/react' +import { useCommunityStatsContext } from "@site/src/lib/statsProvider" +import { getWeekBuckets, getCurrentStreak } from "@site/src/lib/streakUtils" +import '../../styles/globals.css' + +export default function SignInDashboard(): JSX.Element { + const { user } = useUser() + const displayName = user?.firstName + + const { + getAllTimePRsForContributor, + getAllTimeContributor + } = useCommunityStatsContext() + + const viewerLogin = + user?.externalAccounts?.find( + (account) => account.provider === "github" + )?.username ?? null + + const viewerPRs = viewerLogin + ? getAllTimePRsForContributor(viewerLogin) + : [] + + const weekBuckets = getWeekBuckets(viewerPRs) + const streak = getCurrentStreak(weekBuckets) + + const activeWeeks = weekBuckets.filter( + (week) => week.count > 0 + ).length + + const activity = weekBuckets.slice(-7) + + const viewer = viewerLogin + ? getAllTimeContributor(viewerLogin) + : undefined + + const totalPRs = viewer?.prs ?? 0 + const totalPoints = viewer?.points ?? 0 + + return ( +
+
+
+ + Recode hive + + + + Take a Quick Tour + + + + New here? Explore the platform and discover everything you can do. + +
+ + +
+ +

+ Welcome Back, {displayName} +

+ +
+
+
+
+ + Current streak + + +

+ {streak} {streak === 1 ? "Week" : "Weeks"} +

+
+ + +
+ +

+ Keep your weekly streak going +

+
+ +
+
+
+ + Total points + + +

+ {totalPoints} points +

+
+ + +
+ +

+ Total points earned +

+
+ +
+
+
+ + Active weeks + + +

+ {activeWeeks} +

+
+ + +
+ +

+ Weeks with merged PRs +

+
+ +
+
+
+ + PRs merged + + +

+ {totalPRs} {totalPRs === 1 ? "PR" : "PRs"} +

+
+ + +
+ +

+ Total pull requests merged +

+
+
+ +
+
+
+

+ BUILD IN PUBLIC +

+

+ Turn consistency into proof +

+

+ Complete meaningful learning actions each week, build your consistency, and share each win. +

+
+
+ +
+ + {streak} + + + Week Streak + +
+
+
+
+

+ Your last 7 weeks +

+

+ {streak} {streak === 1 ? "week" : "weeks"} streak +

+
+
+
+
+
+ {activity.map((week, index) => { + const hasActivity = week.count > 0 + const weekLabel = + index === activity.length - 1 + ? "Now" + : `W-${activity.length - 1 - index}` + return ( +
+
+ {hasActivity ? ( + + ) : ( + "0" + )} +
+ + {weekLabel} + + + {week.count}{" "} + {week.count === 1 ? "PR" : "PRs"} + +
+ ) + })} +
+
+
+
+
+
+

+ NEXT ACHIEVEMENT +

+

+ {streak + 1}-week consistency streak +

+
+ +
+
+
+
+ ) +} \ No newline at end of file diff --git a/src/lib/statsProvider.tsx b/src/lib/statsProvider.tsx index b92e5fa3..160e56f4 100644 --- a/src/lib/statsProvider.tsx +++ b/src/lib/statsProvider.tsx @@ -45,6 +45,8 @@ interface ICommunityStatsContext { currentTimeFilter: TimeFilter; setTimeFilter: (filter: TimeFilter) => void; getFilteredPRsForContributor: (username: string) => PRDetails[]; + getAllTimePRsForContributor: (username: string) => PRDetails[]; + getAllTimeContributor: (username: string) => Contributor | undefined; } // Define types for leaderboard data @@ -313,6 +315,50 @@ export function CommunityStatsProvider({ [allContributors, currentTimeFilter], ); + const getAllTimeContributor = useCallback( + (username: string): Contributor | undefined => { + const contributor = allContributors.find( + (c) => c.username.toLowerCase() === username.toLowerCase() + ); + + if (!contributor) return undefined; + + const totalPRs = contributor.allPRDetails.length; + + const totalPoints = contributor.allPRDetails.reduce( + (sum, pr) => sum + pr.points, + 0 + ); + + return { + username: contributor.username, + avatar: contributor.avatar, + profile: contributor.profile, + prs: totalPRs, + points: totalPoints, + prDetails: contributor.allPRDetails, + }; + }, + [allContributors], +); + + const getAllTimePRsForContributor = useCallback( + (username: string): PRDetails[] => { + const contributor = allContributors.find( + (c) => c.username.toLowerCase() === username.toLowerCase() + ); + + if (!contributor) return []; + + return [...contributor.allPRDetails].sort( + (a, b) => + new Date(b.mergedAt).getTime() - + new Date(a.mergedAt).getTime() + ); + }, + [allContributors], +); + // Time filter setter function const setTimeFilter = useCallback((filter: TimeFilter) => { setCurrentTimeFilter(filter); @@ -465,6 +511,8 @@ export function CommunityStatsProvider({ currentTimeFilter, setTimeFilter, getFilteredPRsForContributor, + getAllTimePRsForContributor, + getAllTimeContributor }; return ( diff --git a/src/pages/dashboard/dashboard.css b/src/pages/dashboard/dashboard.css index 874b15ca..785dfabf 100644 --- a/src/pages/dashboard/dashboard.css +++ b/src/pages/dashboard/dashboard.css @@ -792,3 +792,29 @@ border-color: var(--ifm-color-emphasis-300); border-top-color: var(--dashboard-accent); } + +html[data-theme='dark'] .dashboard-main-content .bg-white { + background-color: #18181B !important; + border-color: #374151 !important; + color: #ffffff !important; +} + +html[data-theme='dark'] .dashboard-main-content .text-gray-900 { + color: #ffffff !important; +} + +html[data-theme='dark'] .dashboard-main-content .text-gray-500 { + color: #9ca3af !important; +} + +html[data-theme='dark'] .dashboard-main-content .bg-gray-50 { + background-color: #27272a !important; +} + +html[data-theme='dark'] .dashboard-main-content .border-gray-300 { + border-color: #0f172a !important; +} + +[data-theme='dark'] .dashboard-layout{ + background: black; +} \ No newline at end of file diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index cb478cd3..13bec415 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import Layout from "@theme/Layout"; import Head from "@docusaurus/Head"; import BrowserOnly from "@docusaurus/BrowserOnly"; -import { SignInButton, useAuth } from "@clerk/react"; +import { Show, SignInButton, useAuth } from "@clerk/react"; import { motion } from "framer-motion"; import { useCommunityStatsContext, @@ -34,6 +34,7 @@ import DashboardSidebar from "@site/src/components/dashboard/Sidebar/DashboardSi import "@site/src/components/discussions/discussions.css"; import "./dashboard.css"; import LeaderBoard from "@site/src/components/dashboard/LeaderBoard/leaderboard"; +import SignInDashboard from "@site/src/components/dashboard/SignInDashboard"; import GiveawayPage from "@site/src/components/dashboard/giveaway"; @@ -105,6 +106,7 @@ const DashboardContent: React.FC = () => { const [discussionsLoading, setDiscussionsLoading] = useState(true); const [discussionsError, setDiscussionsError] = useState(null); const [showDashboardMenu, setShowDashboardMenu] = useState(false); + const { isLoaded, isSignedIn } = useAuth(); // Initialize GitHub service with token from Docusaurus config useEffect(() => { @@ -469,45 +471,76 @@ const DashboardContent: React.FC = () => { {activeTab === "home" && ( - -

- Recode Hive Community Dashboard -

-

- Welcome to the Recode Hive community hub! Explore our stats, - engage in discussions, and connect with fellow contributors. -

- -
-

Community At a Glance

-
- } - title="Total Stars" - valueText={githubStarCountText || "937"} - description="Stars across all our public repositories" - /> - } - title="Contributors" - valueText={githubContributorsCountText || "444"} - description="Amazing community members" - /> - } - title="Forks" - valueText={githubForksCountText || "1.03K"} - description="Community contributions" - /> + <> + {isLoaded ? ( + <> + {isSignedIn ? ( + + ) : ( + +

+ Recode Hive Community Dashboard +

+
+

+ Welcome to the Recode Hive community hub! Explore our stats, + engage in discussions, and connect with fellow contributors. +

+

Sign in to view your stats

+ + + + + +
+ +
+

Community At a Glance

+
+ } + title="Total Stars" + valueText={githubStarCountText || "937"} + description="Stars across all our public repositories" + /> + } + title="Contributors" + valueText={githubContributorsCountText || "444"} + description="Amazing community members" + /> + } + title="Forks" + valueText={githubForksCountText || "1.03K"} + description="Community contributions" + /> +
+
+
+ )} + + ): + ( +
+ Loading...
-
-
- )} + ) + } + + ) + } {activeTab === "discuss" && ( {
diff --git a/src/styles/globals.css b/src/styles/globals.css index b2a4a4ba..aea9ee97 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -1,5 +1,10 @@ @import "tailwindcss"; @import "tw-animate-css"; +@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap'); + +@theme { + --font-space: "Space Grotesk", sans-serif; +} /* Support both class-based (.dark) and attribute-based ([data-theme="dark"]) dark modes */ @custom-variant dark (&:is(.dark, [data-theme="dark"]) *);