From 7d7013ad8a35b0c41082185b5e1acb8e591a7f10 Mon Sep 17 00:00:00 2001 From: Rajal-ui Date: Fri, 5 Jun 2026 21:10:22 +0530 Subject: [PATCH] feat(opensource): add guide sharing button and dynamic open graph preview images --- .../student/opensource/CICDGuidePage.tsx | 1 + .../student/opensource/CommTemplatesPage.tsx | 1 + .../student/opensource/FirstPRRoadmapPage.tsx | 5 ++- .../student/opensource/GSoCProposalPage.tsx | 1 + .../student/opensource/GSoCReposPage.tsx | 1 + .../student/opensource/GitCheatsheetPage.tsx | 1 + .../student/opensource/GuidanceCards.tsx | 44 +++++++++++++++---- .../student/opensource/ProgramTrackerPage.tsx | 9 ++++ .../student/opensource/ReadCodebasePage.tsx | 1 + .../opensource/components/GuideListPage.tsx | 3 ++ 10 files changed, 57 insertions(+), 10 deletions(-) diff --git a/client/src/module/student/opensource/CICDGuidePage.tsx b/client/src/module/student/opensource/CICDGuidePage.tsx index 2a7baa27e..1d84f565d 100644 --- a/client/src/module/student/opensource/CICDGuidePage.tsx +++ b/client/src/module/student/opensource/CICDGuidePage.tsx @@ -14,6 +14,7 @@ export default function CICDGuidePage() { seoTitle="CI/CD Basics for Open Source Contributors" seoDescription="Learn to fix CI/CD failures in open source: lint errors, test failures, type check errors, and build errors. Read CI logs and debug pipeline issues like a pro." seoKeywords="CI/CD open source, fix CI failure, lint errors, test failure, GitHub Actions, build errors" + ogImage="/og/og-cicd.png" icon={Settings} iconColor="text-cyan-500" /> diff --git a/client/src/module/student/opensource/CommTemplatesPage.tsx b/client/src/module/student/opensource/CommTemplatesPage.tsx index 06a821d92..03681f257 100644 --- a/client/src/module/student/opensource/CommTemplatesPage.tsx +++ b/client/src/module/student/opensource/CommTemplatesPage.tsx @@ -14,6 +14,7 @@ export default function CommTemplatesPage() { seoTitle="Open Source Communication Templates" seoDescription="Copy-paste templates for claiming issues, writing PR descriptions, responding to code reviews, filing bug reports, and proposing features in open source projects." seoKeywords="open source communication, PR description template, bug report template, issue comment, code review response" + ogImage="/og/og-communication.png" icon={MessageSquare} iconColor="text-violet-500" /> diff --git a/client/src/module/student/opensource/FirstPRRoadmapPage.tsx b/client/src/module/student/opensource/FirstPRRoadmapPage.tsx index 4b5d8f1bb..11f795c52 100644 --- a/client/src/module/student/opensource/FirstPRRoadmapPage.tsx +++ b/client/src/module/student/opensource/FirstPRRoadmapPage.tsx @@ -155,6 +155,7 @@ export default function FirstPRRoadmapPage() { description="Step-by-step roadmap to making your first pull request on GitHub. Learn git workflow, finding issues, and contributing to open source projects." keywords="first pull request, open source contribution, GitHub beginner, git workflow, contribute to open source" canonicalUrl={canonicalUrl("/student/opensource/first-pr")} + ogImage="/og/og-first-pr.png" /> {/* Atmospheric background */} @@ -282,11 +283,11 @@ export default function FirstPRRoadmapPage() { setShowResetConfirm(false)} title="Reset progress?" description="This will clear all completed steps. Your server-side progress will be reset." confirmLabel="Reset" - variant="danger" + confirmVariant="danger" onConfirm={() => { const toReset = Array.from(completed); setCompleted(new Set()); diff --git a/client/src/module/student/opensource/GSoCProposalPage.tsx b/client/src/module/student/opensource/GSoCProposalPage.tsx index e4915aa29..f32fddb7c 100644 --- a/client/src/module/student/opensource/GSoCProposalPage.tsx +++ b/client/src/module/student/opensource/GSoCProposalPage.tsx @@ -78,6 +78,7 @@ export default function GSoCProposalPage() { description="Learn how to write a winning Google Summer of Code proposal. Covers project selection, timeline planning, and proposal structure." keywords="GSoC proposal guide, Google Summer of Code, GSoC tips, open source proposal, GSoC application" canonicalUrl={canonicalUrl("/student/opensource/gsoc-proposal")} + ogImage="/og/og-gsoc-proposal.png" /> {/* Atmospheric background */} diff --git a/client/src/module/student/opensource/GSoCReposPage.tsx b/client/src/module/student/opensource/GSoCReposPage.tsx index 830362f0c..b9d25ed49 100644 --- a/client/src/module/student/opensource/GSoCReposPage.tsx +++ b/client/src/module/student/opensource/GSoCReposPage.tsx @@ -592,6 +592,7 @@ export default function GSoCReposPage() { description="Browse Google Summer of Code organizations, past projects, technologies, topics, and contributor links." keywords="GSoC, Google Summer of Code, open source organizations, student open source" canonicalUrl={canonicalUrl("/student/opensource/gsoc")} + ogImage="/og/og-gsoc.png" />
diff --git a/client/src/module/student/opensource/GitCheatsheetPage.tsx b/client/src/module/student/opensource/GitCheatsheetPage.tsx index 8ba451f33..040d313c8 100644 --- a/client/src/module/student/opensource/GitCheatsheetPage.tsx +++ b/client/src/module/student/opensource/GitCheatsheetPage.tsx @@ -14,6 +14,7 @@ export default function GitCheatsheetPage() { seoTitle="Git for Open Source - Interactive Cheatsheet" seoDescription="Master the Git workflow for open source: fork, clone, branch, commit, rebase, resolve conflicts, and submit pull requests. With copy-paste commands and real examples." seoKeywords="git open source, fork clone workflow, git cheatsheet, git rebase, merge conflicts, pull request git" + ogImage="/og/og-git-guide.png" icon={GitBranch} iconColor="text-orange-500" /> diff --git a/client/src/module/student/opensource/GuidanceCards.tsx b/client/src/module/student/opensource/GuidanceCards.tsx index feabe7ad3..a347f140d 100644 --- a/client/src/module/student/opensource/GuidanceCards.tsx +++ b/client/src/module/student/opensource/GuidanceCards.tsx @@ -1,12 +1,15 @@ -import React from "react"; +import React, { useCallback } from "react"; import { Link } from "react-router"; import { motion } from "framer-motion"; import { GitPullRequest, Trophy, Award, GraduationCap, - BookOpen, GitBranch, MessageSquare, Settings, + BookOpen, GitBranch, MessageSquare, Settings, Share2, type LucideIcon, } from "lucide-react"; import { GuideSearch } from "./GuideSearch"; +import { SITE_URL } from "../../../lib/seo.utils"; +import toast from "../../../components/ui/toast"; +import { Button } from "../../../components/ui/button"; interface GuidanceCard { to: string; @@ -27,6 +30,18 @@ const GUIDANCE_CARDS: GuidanceCard[] = [ ]; export const GuidanceCards = React.memo(function GuidanceCards() { + const handleShare = useCallback((e: React.MouseEvent, path: string) => { + e.preventDefault(); + e.stopPropagation(); + + const url = `${SITE_URL}${path}`; + void navigator.clipboard.writeText(url).then(() => { + toast.success("Link copied to clipboard!"); + }).catch(() => { + toast.error("Failed to copy link."); + }); + }, []); + return (
@@ -45,24 +60,37 @@ export const GuidanceCards = React.memo(function GuidanceCards() { initial={{ opacity: 0, y: 16 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: 0.05 + Math.min(i, 6) * 0.04, duration: 0.35 }} + className="group relative" >
- + / {String(i + 1).padStart(2, "0")} -
- +
+ +
+ +
-

+

{card.title}

-

+

{card.desc}

diff --git a/client/src/module/student/opensource/ProgramTrackerPage.tsx b/client/src/module/student/opensource/ProgramTrackerPage.tsx index a2685ff37..8e147eefb 100644 --- a/client/src/module/student/opensource/ProgramTrackerPage.tsx +++ b/client/src/module/student/opensource/ProgramTrackerPage.tsx @@ -5,6 +5,8 @@ import { Globe, DollarSign, Calendar, Users, CheckCircle2, X, Filter, CalendarPlus, } from "lucide-react"; import { Button } from "../../../components/ui/button"; +import { SEO } from "../../../components/SEO"; +import { canonicalUrl } from "../../../lib/seo.utils"; function nextDate(month: number, day: number, hour = 23, minute = 59): string { const now = new Date(); @@ -1012,6 +1014,13 @@ export default function ProgramTrackerPage() { return (
+ {/* Hero */}
diff --git a/client/src/module/student/opensource/ReadCodebasePage.tsx b/client/src/module/student/opensource/ReadCodebasePage.tsx index 1eb97f767..58ade2ae6 100644 --- a/client/src/module/student/opensource/ReadCodebasePage.tsx +++ b/client/src/module/student/opensource/ReadCodebasePage.tsx @@ -14,6 +14,7 @@ export default function ReadCodebasePage() { seoTitle="How to Read a Codebase - Open Source Guide" seoDescription="Learn how to navigate and understand any unfamiliar codebase systematically. Trace features, read tests, understand data models, and use debugging as a reading tool." seoKeywords="read codebase, understand code, navigate code, code reading, open source contribution" + ogImage="/og/og-read-codebase.png" icon={BookOpen} iconColor="text-emerald-500" /> diff --git a/client/src/module/student/opensource/components/GuideListPage.tsx b/client/src/module/student/opensource/components/GuideListPage.tsx index cf77ee89a..89151a879 100644 --- a/client/src/module/student/opensource/components/GuideListPage.tsx +++ b/client/src/module/student/opensource/components/GuideListPage.tsx @@ -19,6 +19,7 @@ interface Props { seoTitle: string; seoDescription: string; seoKeywords: string; + ogImage?: string; icon: LucideIcon; iconColor: string; // e.g. "text-emerald-500" } @@ -26,6 +27,7 @@ interface Props { export default function GuideListPage({ steps, storageKey, basePath, title, titleAccent, subtitle, seoTitle, seoDescription, seoKeywords, icon: Icon, iconColor, + ogImage, }: Props) { const [completed, setCompleted] = useState>(() => { try { @@ -62,6 +64,7 @@ export default function GuideListPage({ description={seoDescription} keywords={seoKeywords} canonicalUrl={canonicalUrl(basePath)} + ogImage={ogImage} /> {/* Atmospheric background */}