diff --git a/client/src/module/student/opensource/GSoCReposPage.tsx b/client/src/module/student/opensource/GSoCReposPage.tsx index 75516ba31..830362f0c 100644 --- a/client/src/module/student/opensource/GSoCReposPage.tsx +++ b/client/src/module/student/opensource/GSoCReposPage.tsx @@ -626,7 +626,7 @@ export default function GSoCReposPage() { - 2016-2026 years + 2016-{new Date().getFullYear()} years diff --git a/client/src/module/student/opensource/ProgramTrackerPage.tsx b/client/src/module/student/opensource/ProgramTrackerPage.tsx index f14207747..a2685ff37 100644 --- a/client/src/module/student/opensource/ProgramTrackerPage.tsx +++ b/client/src/module/student/opensource/ProgramTrackerPage.tsx @@ -6,6 +6,13 @@ import { } from "lucide-react"; import { Button } from "../../../components/ui/button"; +function nextDate(month: number, day: number, hour = 23, minute = 59): string { + const now = new Date(); + const d = new Date(Date.UTC(now.getFullYear(), month - 1, day, hour, minute, 0)); + if (d <= now) d.setUTCFullYear(d.getUTCFullYear() + 1); + return d.toISOString(); +} + // ─── Data ────────────────────────────────────────────────────── interface Program { id: number; @@ -83,7 +90,7 @@ const PROGRAMS: Program[] = [ "Write a detailed proposal (problem statement, timeline, milestones)", "Submit via the GSoC portal before the deadline", ], - applicationDeadline: "2026-04-19T23:59:00Z", + applicationDeadline: nextDate(4, 19), }, { id: 2, @@ -129,7 +136,7 @@ const PROGRAMS: Program[] = [ "Complete any take-home tasks if requested", "Wait for mentor selection notification", ], - applicationDeadline: "2026-05-15T23:59:00Z", + applicationDeadline: nextDate(5, 15), }, { id: 3, @@ -230,8 +237,8 @@ const PROGRAMS: Program[] = [ "Make contributions to 1–2 projects during the contribution period", "Submit a final application with your contribution summary", ], - applicationStart: "2026-02-06T16:00:00Z", - applicationDeadline: "2026-02-13T16:00:00Z", + applicationStart: nextDate(2, 6, 16, 0), + applicationDeadline: nextDate(2, 13, 16, 0), }, { id: 5, diff --git a/client/src/module/student/opensource/SuggestRepoModal.tsx b/client/src/module/student/opensource/SuggestRepoModal.tsx index 142e0701c..fb1bdc0c5 100644 --- a/client/src/module/student/opensource/SuggestRepoModal.tsx +++ b/client/src/module/student/opensource/SuggestRepoModal.tsx @@ -276,10 +276,14 @@ export function SuggestRepoModal({ open, onClose }: SuggestRepoModalProps) { aria-invalid={!!urlError} aria-describedby={urlError ? "suggest-url-error" : undefined} /> - {urlError && ( + {urlError ? (

{urlError}

+ ) : ( +

+ Format: https://github.com/owner/repo — we'll auto-fill the rest +

)} diff --git a/client/src/module/student/opensource/_shared/repo-utils.ts b/client/src/module/student/opensource/_shared/repo-utils.ts index da8ff6dfd..47483e080 100644 --- a/client/src/module/student/opensource/_shared/repo-utils.ts +++ b/client/src/module/student/opensource/_shared/repo-utils.ts @@ -29,6 +29,19 @@ export function difficultyBadge(d: OpenSourceRepo["difficulty"]) { export function parseGithubRepoUrl(raw: string): { owner: string; name: string } | null { const trimmed = raw.trim(); if (!trimmed) return null; + + // Support SSH format: git@github.com:owner/repo.git + const sshMatch = trimmed.match(/^git@github\.com:(.+)$/); + if (sshMatch) { + const segments = sshMatch[1].split("/").filter(Boolean); + if (segments.length < 2) return null; + const [owner, rawName] = segments; + const name = rawName.replace(/\.git$/i, ""); + const nameRe = /^[A-Za-z0-9._-]+$/; + if (!nameRe.test(owner) || !nameRe.test(name)) return null; + return { owner, name }; + } + let url: URL; try { url = new URL(trimmed);