feat(opensource): add guide sharing button and dynamic open graph pre…#1460
feat(opensource): add guide sharing button and dynamic open graph pre…#1460Rajal-ui wants to merge 1 commit into
Conversation
|
Hi @Rajal-ui, thanks for contributing to InternHack! 🎉 I have automatically:
Our workflows will now analyze your changes to classify:
Tip Ensure your PR description references the issue it resolves (e.g. Happy coding! 🚀 |
📝 WalkthroughWalkthroughThis PR implements Open Graph image metadata for guide pages and adds a share-to-clipboard feature. GuideListPage gains an optional ChangesOpen Graph Image Propagation & Share Feature
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with 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.
Inline comments:
In `@client/src/module/student/opensource/FirstPRRoadmapPage.tsx`:
- 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.
In `@client/src/module/student/opensource/GuidanceCards.tsx`:
- 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.
- Around line 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.
- Around line 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.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8df66001-d7ef-4198-80c7-b8f6a44440f9
📒 Files selected for processing (10)
client/src/module/student/opensource/CICDGuidePage.tsxclient/src/module/student/opensource/CommTemplatesPage.tsxclient/src/module/student/opensource/FirstPRRoadmapPage.tsxclient/src/module/student/opensource/GSoCProposalPage.tsxclient/src/module/student/opensource/GSoCReposPage.tsxclient/src/module/student/opensource/GitCheatsheetPage.tsxclient/src/module/student/opensource/GuidanceCards.tsxclient/src/module/student/opensource/ProgramTrackerPage.tsxclient/src/module/student/opensource/ReadCodebasePage.tsxclient/src/module/student/opensource/components/GuideListPage.tsx
| 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" |
There was a problem hiding this comment.
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.
| 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.
| 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."); | ||
| }); |
There was a problem hiding this comment.
🧩 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.
| <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"> | ||
| / {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> |
There was a problem hiding this comment.
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="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"> |
There was a problem hiding this comment.
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.
Description
This PR addresses the missing sharing mechanics across our student open-source curriculum guides. It introduces an interactive share utility with instant clipboard copies directly on the main landing grid cards and configures guide route headers to serve context-aware, static Open Graph (
og:image) assets for high-quality link previews on channels like WhatsApp, Twitter, or Discord.Key Changes
GuidanceCards.tsx): Mounted a clean, accessible<Button>container featuring theShare2icon layout within each individual grid cell. Isolated click events usinge.preventDefault()ande.stopPropagation()to safely intercept actions without accidentally triggering parent<Link>navigation paths.GuideListPage.tsx): Extended the base wrapper component to accept and process dynamicogImagefile references mapping to our public directory layer (client/public/og/).FirstPRRoadmapPage.tsx,GSoCProposalPage.tsx,ProgramTrackerPage.tsx,GSoCReposPage.tsx, and all markdown/cheatsheet sub-document panels).FirstPRRoadmapPage.tsxregarding missing properties on the secondaryConfirmDialogsystem and cleared uninitialized references insideProgramTrackerPage.tsx.Related Issue
Fixes #1020
Type of Change
Testing
https://www.internhack.xyz/student/opensource/first-pr) load cleanly into system clipboard memory.<meta property="og:image" content="..." />tags point exactly to their intended public folder asset targets.npx tsc --noEmit) to ensure zero errors were introduced across the edited files.Screenshots / Video
Checklist
.env, credentials, ornode_modulescommittedSummary by CodeRabbit
New Features
Improvements