From a9a481ecd726d3425a23d54e03a45f343f2b9d6a Mon Sep 17 00:00:00 2001 From: yachikadev Date: Mon, 10 Aug 2026 20:50:29 +0530 Subject: [PATCH 1/2] feat(dashboard): fetch stats and pass to ShareProfileButton --- src/components/DashboardHeader.tsx | 44 ++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/src/components/DashboardHeader.tsx b/src/components/DashboardHeader.tsx index 2d222a106..64ebb8e47 100644 --- a/src/components/DashboardHeader.tsx +++ b/src/components/DashboardHeader.tsx @@ -40,6 +40,11 @@ interface CacheEntry { statusText: string; } +interface ShareStats { +streak: number | null; +mergeRate: number | null; +goalPercent: number | null; +} const clientCache = new Map(); const pendingRequests = new Map>(); @@ -212,6 +217,12 @@ export default function DashboardHeader() { const [isNightOwl, setIsNightOwl] = useState(false); const [isEarlyBird, setIsEarlyBird] = useState(false); +const [shareStats, setShareStats] = useState({ + streak: null, + mergeRate: null, + goalPercent: null, +}); + useEffect(() => { const computeCurrentGreeting = () => { const currentHour = new Date().getHours(); @@ -291,6 +302,33 @@ export default function DashboardHeader() { evaluateCodingDistributionMilestones(); }, [session]); + useEffect(() => { + if (!session?.githubLogin) return; + + async function loadShareStats() { + try { + const [streakRes, prsRes, goalsRes] = await Promise.all([ + fetch("/api/metrics/streak"), + fetch("/api/metrics/prs"), + fetch("/api/goals"), + ]); + + const streakData = streakRes.ok ? await streakRes.json() : null; + const prsData = prsRes.ok ? await prsRes.json() : null; + const goalsData = goalsRes.ok ? await goalsRes.json() : null; + + setShareStats({ + streak: streakData?.current_streak ?? null, + mergeRate: prsData?.merge_rate ?? null, + goalPercent: goalsData?.[0]?.progress_percent ?? null, + }); + } catch (err) { + console.error("Failed to load share stats:", err); + } + } + + loadShareStats(); + }, [session]); const [menuOpen, setMenuOpen] = useState(false); @@ -393,8 +431,8 @@ export default function DashboardHeader() {
{isPublic === true && session?.githubLogin && ( - - )} + + )}
@@ -490,7 +528,7 @@ export default function DashboardHeader() {
{isPublic === true && session?.githubLogin && ( - + )}
)} From f3654700b309b7991c260bdf1459254614b636f1 Mon Sep 17 00:00:00 2001 From: yachikadev Date: Mon, 10 Aug 2026 20:50:45 +0530 Subject: [PATCH 2/2] feat(share): add Twitter/X and LinkedIn share buttons --- src/components/ShareProfileButton.tsx | 104 +++++++++++++++++++------- 1 file changed, 79 insertions(+), 25 deletions(-) diff --git a/src/components/ShareProfileButton.tsx b/src/components/ShareProfileButton.tsx index 89ae48a96..8f7d3bd62 100644 --- a/src/components/ShareProfileButton.tsx +++ b/src/components/ShareProfileButton.tsx @@ -5,49 +5,103 @@ import { Link2, Check } from "lucide-react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; +interface ShareStats { + streak: number | null; + mergeRate: number | null; + goalPercent: number | null; +} + interface ShareProfileButtonProps { githubLogin: string; + stats?: ShareStats; } export default function ShareProfileButton({ githubLogin, + stats, }: ShareProfileButtonProps) { const [copied, setCopied] = useState(false); + const baseUrl = + process.env.NEXT_PUBLIC_APP_URL || + "https://devtrack-silk-kappa.vercel.app"; + const profileUrl = `${baseUrl}/u/${githubLogin}`; + const handleCopy = async () => { try { - const baseUrl = - process.env.NEXT_PUBLIC_APP_URL || - "https://devtrack-silk-kappa.vercel.app"; - - const profileUrl = `${baseUrl}/u/${githubLogin}`; - await navigator.clipboard.writeText(profileUrl); - setCopied(true); toast.success("Link copied!"); - - setTimeout(() => { - setCopied(false); - }, 2000); + setTimeout(() => setCopied(false), 2000); } catch { toast.error("Failed to copy link"); } }; + const buildTweetText = () => { + const lines = ["🔥 My DevTrack stats:"]; + if (stats?.streak !== null && stats?.streak !== undefined) + lines.push(`📈 ${stats.streak} day streak`); + if (stats?.mergeRate !== null && stats?.mergeRate !== undefined) + lines.push(`✅ ${stats.mergeRate}% merge rate`); + if (stats?.goalPercent !== null && stats?.goalPercent !== undefined) + lines.push(`🎯 Weekly goal ${stats.goalPercent}% complete`); + lines.push("Track your own coding pulse 👇"); + lines.push(profileUrl); + lines.push("#GitHub #DevTrack #GSSoC"); + return lines.join("\n"); + }; + + const handleTwitterShare = () => { + const tweet = encodeURIComponent(buildTweetText()); + window.open(`https://twitter.com/intent/tweet?text=${tweet}`, "_blank"); + }; + + const handleLinkedInShare = () => { + const url = encodeURIComponent(profileUrl); + window.open( + `https://www.linkedin.com/sharing/share-offsite/?url=${url}`, + "_blank" + ); + }; + return ( - +
+ + + + + +
); -} +} \ No newline at end of file