Skip to content
Merged
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 client/src/module/student/opensource/CICDGuidePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
/>
Expand Down
1 change: 1 addition & 0 deletions client/src/module/student/opensource/CommTemplatesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
/>
Expand Down
5 changes: 3 additions & 2 deletions client/src/module/student/opensource/FirstPRRoadmapPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add ogImage to the loading state SEO component for consistency.

The loading skeleton's SEO component (lines 100-105) is missing the ogImage prop that was added here on line 158. Both SEO components should have identical metadata to ensure consistent Open Graph previews regardless of loading state.

🔧 Proposed fix

Add the same ogImage prop to the SEO component in the loading state:

       <SEO
         title="First Pull Request Guide - Open Source for Beginners"
         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"
       />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ogImage="/og/og-first-pr.png"
<SEO
title="First Pull Request Guide - Open Source for Beginners"
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"
/>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/module/student/opensource/FirstPRRoadmapPage.tsx` at line 158, The
loading-state SEO component in FirstPRRoadmapPage is missing the ogImage prop;
update the SEO invocation used for the loading skeleton (the same props used in
the main render) to include ogImage="/og/og-first-pr.png" so both SEO components
have identical Open Graph metadata.

/>

{/* Atmospheric background */}
Expand Down Expand Up @@ -282,11 +283,11 @@ export default function FirstPRRoadmapPage() {

<ConfirmDialog
open={showResetConfirm}
onOpenChange={setShowResetConfirm}
onCancel={() => 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());
Expand Down
1 change: 1 addition & 0 deletions client/src/module/student/opensource/GSoCProposalPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 */}
Expand Down
1 change: 1 addition & 0 deletions client/src/module/student/opensource/GSoCReposPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
/>

<div className="mx-auto max-w-6xl px-4 py-8 sm:px-8">
Expand Down
1 change: 1 addition & 0 deletions client/src/module/student/opensource/GitCheatsheetPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
/>
Expand Down
44 changes: 36 additions & 8 deletions client/src/module/student/opensource/GuidanceCards.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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.");
});
Comment on lines +33 to +42
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and show the relevant portion of the file
FILE="client/src/module/student/opensource/GuidanceCards.tsx"
if [ ! -f "$FILE" ]; then
  echo "Missing file: $FILE"
  exit 1
fi

echo "---- file: $FILE (lines 1-120) ----"
nl -ba "$FILE" | sed -n '1,120p'

echo "---- file: $FILE (lines 20-80) ----"
nl -ba "$FILE" | sed -n '20,80p'

Repository: Sachinchaurasiya360/InternHack

Length of output: 221


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="client/src/module/student/opensource/GuidanceCards.tsx"
if [ ! -f "$FILE" ]; then
  echo "Missing file: $FILE"
  exit 1
fi

echo "---- file: $FILE (lines 1-140) ----"
cat -n "$FILE" | sed -n '1,140p'

echo "---- file: $FILE (lines 30-80) ----"
cat -n "$FILE" | sed -n '30,80p'

Repository: Sachinchaurasiya360/InternHack

Length of output: 8486


Guard navigator.clipboard.writeText support before calling it
At line 38, navigator.clipboard.writeText(url) is called without checking availability; if navigator.clipboard is undefined, it can throw synchronously and the .catch(...) toast won’t run. Add a capability guard with a fallback toast.

Minimal guard
- const url = `${SITE_URL}${path}`;
- void navigator.clipboard.writeText(url).then(() => {
+ const url = `${SITE_URL}${path}`;
+ if (!navigator.clipboard?.writeText) {
+   toast.error("Clipboard is not supported in this browser.");
+   return;
+ }
+ void navigator.clipboard.writeText(url).then(() => {
    toast.success("Link copied to clipboard!");
  }).catch(() => {
    toast.error("Failed to copy link.");
  });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/module/student/opensource/GuidanceCards.tsx` around lines 33 - 42,
The handleShare handler calls navigator.clipboard.writeText(url) without
guarding for navigator.clipboard support; update the handleShare function to
first check that navigator and navigator.clipboard and
navigator.clipboard.writeText exist, and if not immediately show
toast.error("Clipboard not supported" or similar) and return; if supported, call
writeText inside a Promise/try-catch (preserve the existing .then/.catch or
await with try/catch) so synchronous exceptions are caught and the
success/failure toasts still fire. Reference: handleShare (useCallback) and the
use of navigator.clipboard.writeText(url) to find where to add the guard and
error fallback.

}, []);

return (
<div className="mb-10">
<div className="flex items-center gap-2 mb-3">
Expand All @@ -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"
>
<Link
to={card.to}
className="group relative flex flex-col gap-3 p-4 h-full bg-white dark:bg-stone-900 border-r border-b border-stone-200 dark:border-white/10 no-underline hover:bg-stone-900 dark:hover:bg-stone-50 transition-colors"
className="flex flex-col gap-3 p-4 h-full bg-white dark:bg-stone-900 border-r border-b border-stone-200 dark:border-white/10 no-underline hover:bg-stone-50 dark:hover:bg-stone-800 transition-colors"
>
<div className="flex items-center justify-between">
<span className="text-[10px] font-mono uppercase tracking-widest text-stone-500 dark:text-stone-400 group-hover:text-lime-400">
<span className="text-[10px] font-mono uppercase tracking-widest text-stone-500 dark:text-stone-400 group-hover:text-lime-500 transition-colors">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Replace arbitrary Tailwind text size with canonical scale class.

At Line 70, text-[10px] violates the Tailwind class rule. Please switch to a standard size token (for example text-xs) and adjust tracking/opacity if needed for visual parity.

As per coding guidelines, do not use arbitrary bracket sizes like text-[17px]; use standard scale classes instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/module/student/opensource/GuidanceCards.tsx` at line 70, In the
GuidanceCards.tsx span where the className contains the arbitrary token
"text-[10px]" (the span with className="... text-[10px] font-mono uppercase
tracking-widest ..."), replace that arbitrary size with a canonical Tailwind
size such as "text-xs" and, if needed, tweak adjacent utilities like
tracking-widest or opacity/color classes (e.g., adjust tracking or
group-hover:text-lime-500) to match the original visual appearance; update only
the className on that span inside the GuidanceCards component to remove
bracketed sizing and use the standard scale token.

/ {String(i + 1).padStart(2, "0")}
</span>
<div className="w-8 h-8 rounded-md bg-stone-100 dark:bg-white/5 group-hover:bg-white/10 dark:group-hover:bg-stone-900/10 flex items-center justify-center transition-colors">
<card.icon className="w-4 h-4 text-stone-700 dark:text-stone-300 group-hover:text-lime-400" aria-hidden />
<div className="flex items-center gap-2">
<Button
variant="ghost"
mode="icon"
size="sm"
className="h-8 w-8 rounded-md opacity-0 group-hover:opacity-100 transition-all hover:bg-lime-400/10 hover:text-lime-600 dark:hover:text-lime-400"
onClick={(e) => handleShare(e, card.to)}
title="Share Guide"
>
<Share2 className="w-3.5 h-3.5" />
</Button>
Comment on lines 65 to +83
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid nesting Button inside Link (interactive-in-interactive).

At Line 65-83, the share Button is rendered inside a Link. This is invalid interactive nesting and can produce inconsistent click/focus behavior for keyboard and assistive-tech users.

Suggested structure change
- <Link to={card.to} className="...">
-   ...
-   <Button onClick={(e) => handleShare(e, card.to)} ... />
-   ...
- </Link>
+ <div className="relative">
+   <Link to={card.to} className="...">
+     ...
+   </Link>
+   <Button
+     variant="ghost"
+     mode="icon"
+     size="sm"
+     className="absolute top-3 right-3 h-8 w-8 rounded-md ..."
+     onClick={(e) => handleShare(e, card.to)}
+     title="Share Guide"
+   >
+     <Share2 className="w-3.5 h-3.5" />
+   </Button>
+ </div>

As per coding guidelines, interactive UI should use accessible patterns and this change currently introduces a non-compliant nested interaction model.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/module/student/opensource/GuidanceCards.tsx` around lines 65 - 83,
The share Button is nested inside a Link (interactive-in-interactive); move the
Button out of the Link container in GuidanceCards.tsx so it is a sibling (or
positioned absolutely) instead of a child, keep the onClick handler
(handleShare) and title/aria-label on the Button, and update styles/layout so
the visual appearance is unchanged; ensure the Button's onClick calls
e.stopPropagation() / e.preventDefault() as needed so clicking Share does not
trigger Link navigation and that the Button remains keyboard-focusable and
accessible.

<div className="w-8 h-8 rounded-md bg-stone-100 dark:bg-white/5 group-hover:bg-lime-400/10 flex items-center justify-center transition-colors">
<card.icon className="w-4 h-4 text-stone-700 dark:text-stone-300 group-hover:text-lime-600 dark:group-hover:text-lime-400" aria-hidden />
</div>
</div>
</div>
<div className="flex flex-col gap-1">
<p className="text-sm font-bold tracking-tight text-stone-900 dark:text-stone-50 group-hover:text-stone-50 dark:group-hover:text-stone-900">
<p className="text-sm font-bold tracking-tight text-stone-900 dark:text-stone-50">
{card.title}
</p>
<p className="text-xs text-stone-600 dark:text-stone-400 group-hover:text-stone-300 dark:group-hover:text-stone-700 line-clamp-2">
<p className="text-xs text-stone-600 dark:text-stone-400 line-clamp-2 leading-relaxed">
{card.desc}
</p>
</div>
Expand Down
9 changes: 9 additions & 0 deletions client/src/module/student/opensource/ProgramTrackerPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -1012,6 +1014,13 @@ export default function ProgramTrackerPage() {

return (
<div className="max-w-5xl mx-auto px-4 py-8">
<SEO
title="Open Source Program Tracker - Deadlines & Stipends"
description="Track deadlines, eligibility, and stipends for GSoC, LFX, MLH Fellowship, Outreachy, and 20+ other open source programs."
keywords="GSoC tracker, LFX mentorship, open source internships, Outreachy deadline, paid open source"
canonicalUrl={canonicalUrl("/student/opensource/programs")}
ogImage="/og/og-programs.png"
/>
{/* Hero */}
<section className="relative overflow-hidden rounded-2xl bg-gradient-to-br from-emerald-50 via-teal-50 to-cyan-50 border border-emerald-100 mb-8 p-8">
<div className="absolute top-0 right-0 w-56 h-56 bg-gradient-to-bl from-emerald-200/30 to-transparent rounded-bl-full pointer-events-none" />
Expand Down
1 change: 1 addition & 0 deletions client/src/module/student/opensource/ReadCodebasePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ interface Props {
seoTitle: string;
seoDescription: string;
seoKeywords: string;
ogImage?: string;
icon: LucideIcon;
iconColor: string; // e.g. "text-emerald-500"
}

export default function GuideListPage({
steps, storageKey, basePath, title, titleAccent, subtitle,
seoTitle, seoDescription, seoKeywords, icon: Icon, iconColor,
ogImage,
}: Props) {
const [completed, setCompleted] = useState<Set<string>>(() => {
try {
Expand Down Expand Up @@ -62,6 +64,7 @@ export default function GuideListPage({
description={seoDescription}
keywords={seoKeywords}
canonicalUrl={canonicalUrl(basePath)}
ogImage={ogImage}
/>

{/* Atmospheric background */}
Expand Down
Loading