feat (opensource): add AI- powered "For You" repo recommendations#1814
feat (opensource): add AI- powered "For You" repo recommendations#1814dhaanisi wants to merge 11 commits into
Conversation
… add database utility scripts
…sitory filtering and tracking
|
Hi @dhaanisi, 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! 🚀 |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR implements Gemini-powered personalized repository recommendations alongside "good first issues" filtering and viewed-repo tracking. The client persists viewed IDs in localStorage, queries the new /opensource/recommended endpoint with refresh control, and displays recommendations as an inline horizontal list. The server caches recommendations per user with TTL, generates them via Gemini API using skills/projects/ATS data, and supports forward-compatible search and filtering. ChangesRepository recommendations and advanced filtering
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (3 warnings)
✅ Passed checks (2 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: 6
🧹 Nitpick comments (4)
client/src/module/student/opensource/RepoCard.tsx (2)
137-140: ⚡ Quick winReplace arbitrary Tailwind font-size token with a canonical scale class.
Line 139 uses
text-[10px], which violates the Tailwind class rule for this repo. Use a scale class (text-xs/ project-defined canonical token) instead.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/RepoCard.tsx` around lines 137 - 140, In RepoCard (component rendering the repo prop and conditional "matchReason" block) replace the arbitrary Tailwind class text-[10px] with the canonical scale token (e.g. text-xs or the project-defined token) on the span that contains the Wand2 icon and match reason; update the class list on that span to remove text-[10px] and use text-xs (or the project's canonical font-size token) so it conforms to the repo's Tailwind class rules.Source: Coding guidelines
18-32: ⚡ Quick winConsolidate good-first label detection into one shared helper.
Line 18-32 duplicates logic that also exists in
client/src/module/student/opensource/RepoDiscoveryPage.tsx(Line 50-70). This can drift and produce inconsistent “good first” behavior across list cards vs detail/filter flows.As per coding guidelines, “Apply DRY principle: no duplicate helpers, shared animation variants per file.”
🤖 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/RepoCard.tsx` around lines 18 - 32, The GOOD_FIRST_LABELS constant and hasGoodFirstIssues logic are duplicated; extract them into a single shared helper (e.g., export a constant GOOD_FIRST_LABELS and a function isGoodFirstIssue(repo) that accepts the same repo union type) and replace the local GOOD_FIRST_LABELS and hasGoodFirstIssues in RepoCard and the duplicate in RepoDiscoveryPage with imports of the shared helper; ensure the helper returns a boolean using the same case-insensitive includes logic and keep the function name (isGoodFirstIssue or hasGoodFirstIssues) consistent across both components so callers (RepoCard and RepoDiscoveryPage) use the common implementation.Source: Coding guidelines
client/src/module/student/opensource/RepoDiscoveryPage.tsx (2)
742-753: ⚡ Quick winUse the shared
Buttoncomponent for newly added controls.Lines 742-753, 770-781, and 971-979 introduce new native
<button>elements. This diverges from the shared Button API already used in this file and makes variants/styles harder to keep consistent.As per coding guidelines, “Use the reusable
Buttoncomponent fromclient/src/components/ui/button.tsxfor all new buttons.”Also applies to: 770-781, 971-979
🤖 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/RepoDiscoveryPage.tsx` around lines 742 - 753, Replace the native <button> controls introduced around the viewed filter with the shared Button component: use Button (from the project's UI button) instead of the raw element for the controls that call updateFilter and rely on viewedOnly/recentlyViewed, and do the same for the other two native buttons at the other locations; map the current attributes (type="button", onClick={() => updateFilter("viewed", viewedOnly ? "" : "true")}) to Button props (onClick, variant/size/className) so the visual conditional classes driven by viewedOnly are applied via Button's styling API while preserving the label "Viewed" and the count from recentlyViewed.length; ensure behavior and accessibility remain identical and update any imports to include Button.Source: Coding guidelines
985-985: ⚡ Quick winReplace arbitrary width tokens with canonical Tailwind scale classes.
Line 985 uses
min-w-[300px] w-[300px] sm:min-w-[350px] sm:w-[350px]. These bracket sizes violate the project’s Tailwind class policy.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/RepoDiscoveryPage.tsx` at line 985, The div rendered for each repo (the element with key={repo.id} in RepoDiscoveryPage.tsx) uses arbitrary bracketed widths (min-w-[300px] w-[300px] sm:min-w-[350px] sm:w-[350px]); replace those with canonical Tailwind scale classes instead (e.g., use min-w-80 w-80 and sm:min-w-96 sm:w-96 or another appropriate w-*/min-w-* pair from the Tailwind scale) by editing the className on that div.Source: Coding guidelines
🤖 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/RepoDiscoveryPage.tsx`:
- Around line 226-233: The state initializer for viewedIds trusts JSON.parse to
return a number[] which can crash later when using viewedIds.join; update the
initializer for viewedIds (and any setter usage around the "viewed_repos" key)
to validate the parsed payload: parse localStorage.getItem("viewed_repos"),
confirm it's an array of numbers (e.g., Array.isArray(parsed) and every item is
typeof "number"), and only then return it; otherwise return [] and overwrite
localStorage with a safe [] when necessary so functions using viewedIds (and
code that calls setViewedIds or reads viewed_repos) always receive a number[].
In `@server/src/module/opensource/opensource.service.ts`:
- Around line 34-35: The global recommendationCache Map stores entries with
expiresAt but never removes expired keys, causing unbounded growth; add a
periodic cleanup and opportunistic pruning: implement a cleanup routine that
iterates recommendationCache and deletes entries with expiresAt <= Date.now()
(run on a setInterval, e.g., every minute) and also remove expired entries on
cache reads/writes where recommendationCache is accessed; reference the
recommendationCache variable and update the functions that read/write it (the
cache lookup/get and set paths in opensource.service.ts) to perform the
opportunistic prune, and apply the same pattern to the other cache Maps
mentioned in the review.
- Around line 559-567: The parsed AI payload assigned to matches must be
validated before mapping into recommended: ensure matches is an array and each
entry has a numeric id and a string matchReason; skip or normalize any entries
that don't match that shape. Update the code around the matches declaration and
the recommended mapping (the matches variable and the mapping that uses
candidateRepos.find) to first validate/filter/normalize items (e.g., typeof
match.id === 'number' and typeof match.matchReason === 'string' or coerce
toString) and only then map to candidate repos, so no non-string matchReason
values are forwarded to the client.
- Around line 554-557: Wrap the Gemini call in a timeout/abort guard so
generateContent cannot hang: create an AbortController with a short timeout
(setTimeout that calls controller.abort()), pass controller.signal into
model.generateContent(prompt, { signal }) if the API supports it, or otherwise
use Promise.race between model.generateContent(prompt) and a timeout promise
that rejects; catch the abort/timeout error around the call where
GoogleGenerativeAI, getGenerativeModel and model.generateContent are used, clear
the timeout, and on timeout/abort return an empty array ([]) instead of letting
the request hang or throw.
In `@server/test-db-real.js`:
- Around line 3-15: The DB-check catch block logs the error but still allows a
zero exit; update the catch in main() to set a non-zero exit status by assigning
process.exitCode = 1 (instead of calling process.exit directly) after logging
the error so prisma.$disconnect() in the finally block still runs, ensuring the
process exits with failure when the DB check fails.
In `@server/test-db.js`:
- Around line 3-15: The catch block in async function main() currently only logs
the error so the script can exit with success; update it to log the error,
ensure the DB client is disconnected, and then exit with a non-zero status so
health checks fail: in the catch for main() capture the error, call
console.error with the error, await prisma.$disconnect() (or ensure
prisma.$disconnect() runs before exiting), and then call process.exit(1); keep
the existing finally block behavior or remove duplicate disconnects to avoid
double-disconnect errors.
---
Nitpick comments:
In `@client/src/module/student/opensource/RepoCard.tsx`:
- Around line 137-140: In RepoCard (component rendering the repo prop and
conditional "matchReason" block) replace the arbitrary Tailwind class
text-[10px] with the canonical scale token (e.g. text-xs or the project-defined
token) on the span that contains the Wand2 icon and match reason; update the
class list on that span to remove text-[10px] and use text-xs (or the project's
canonical font-size token) so it conforms to the repo's Tailwind class rules.
- Around line 18-32: The GOOD_FIRST_LABELS constant and hasGoodFirstIssues logic
are duplicated; extract them into a single shared helper (e.g., export a
constant GOOD_FIRST_LABELS and a function isGoodFirstIssue(repo) that accepts
the same repo union type) and replace the local GOOD_FIRST_LABELS and
hasGoodFirstIssues in RepoCard and the duplicate in RepoDiscoveryPage with
imports of the shared helper; ensure the helper returns a boolean using the same
case-insensitive includes logic and keep the function name (isGoodFirstIssue or
hasGoodFirstIssues) consistent across both components so callers (RepoCard and
RepoDiscoveryPage) use the common implementation.
In `@client/src/module/student/opensource/RepoDiscoveryPage.tsx`:
- Around line 742-753: Replace the native <button> controls introduced around
the viewed filter with the shared Button component: use Button (from the
project's UI button) instead of the raw element for the controls that call
updateFilter and rely on viewedOnly/recentlyViewed, and do the same for the
other two native buttons at the other locations; map the current attributes
(type="button", onClick={() => updateFilter("viewed", viewedOnly ? "" :
"true")}) to Button props (onClick, variant/size/className) so the visual
conditional classes driven by viewedOnly are applied via Button's styling API
while preserving the label "Viewed" and the count from recentlyViewed.length;
ensure behavior and accessibility remain identical and update any imports to
include Button.
- Line 985: The div rendered for each repo (the element with key={repo.id} in
RepoDiscoveryPage.tsx) uses arbitrary bracketed widths (min-w-[300px] w-[300px]
sm:min-w-[350px] sm:w-[350px]); replace those with canonical Tailwind scale
classes instead (e.g., use min-w-80 w-80 and sm:min-w-96 sm:w-96 or another
appropriate w-*/min-w-* pair from the Tailwind scale) by editing the className
on that div.
🪄 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: 99ad5210-c06b-417e-b767-13a9a6127890
⛔ Files ignored due to path filters (4)
server/test-db-real.d.ts.mapis excluded by!**/*.mapserver/test-db-real.js.mapis excluded by!**/*.mapserver/test-db.d.ts.mapis excluded by!**/*.mapserver/test-db.js.mapis excluded by!**/*.map
📒 Files selected for processing (14)
client/src/lib/query-keys.tsclient/src/lib/types/opensource.types.tsclient/src/module/recruiter/applications/ApplicationDetail.tsxclient/src/module/student/opensource/GSoCReposPage.tsxclient/src/module/student/opensource/RepoCard.tsxclient/src/module/student/opensource/RepoDiscoveryPage.tsxserver/src/module/opensource/opensource.controller.tsserver/src/module/opensource/opensource.routes.tsserver/src/module/opensource/opensource.service.tsserver/src/module/opensource/opensource.validation.tsserver/test-db-real.d.tsserver/test-db-real.jsserver/test-db.d.tsserver/test-db.js
💤 Files with no reviewable changes (2)
- client/src/module/student/opensource/GSoCReposPage.tsx
- client/src/module/recruiter/applications/ApplicationDetail.tsx
| const [viewedIds, setViewedIds] = useState<number[]>(() => { | ||
| try { | ||
| const stored = localStorage.getItem("viewed_repos"); | ||
| return stored ? JSON.parse(stored) : []; | ||
| } catch { | ||
| return []; | ||
| } | ||
| }); |
There was a problem hiding this comment.
Validate viewed_repos payload before storing it in state.
Line 226-233 trusts JSON.parse output as number[]. If storage contains an object/string, Line 358/363 (viewedIds.join) will throw and break page rendering.
🔧 Proposed fix
const [viewedIds, setViewedIds] = useState<number[]>(() => {
try {
const stored = localStorage.getItem("viewed_repos");
- return stored ? JSON.parse(stored) : [];
+ if (!stored) return [];
+ const parsed = JSON.parse(stored);
+ return Array.isArray(parsed) && parsed.every((id) => Number.isInteger(id) && id > 0)
+ ? parsed
+ : [];
} catch {
return [];
}
});Also applies to: 358-364
🤖 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/RepoDiscoveryPage.tsx` around lines 226
- 233, The state initializer for viewedIds trusts JSON.parse to return a
number[] which can crash later when using viewedIds.join; update the initializer
for viewedIds (and any setter usage around the "viewed_repos" key) to validate
the parsed payload: parse localStorage.getItem("viewed_repos"), confirm it's an
array of numbers (e.g., Array.isArray(parsed) and every item is typeof
"number"), and only then return it; otherwise return [] and overwrite
localStorage with a safe [] when necessary so functions using viewedIds (and
code that calls setViewedIds or reads viewed_repos) always receive a number[].
| const recommendationCache = new Map<number, { repos: any[]; expiresAt: number }>(); | ||
|
|
There was a problem hiding this comment.
Prune expired recommendation cache entries to prevent unbounded memory growth.
Line 34 introduces a global Map cache, but expired entries are never removed unless the same user is requested again. Over time this can grow with user cardinality and retain stale objects indefinitely.
🔧 Proposed fix
const recommendationCache = new Map<number, { repos: any[]; expiresAt: number }>();
+
+const pruneExpiredRecommendationCache = (now: number) => {
+ for (const [key, value] of recommendationCache.entries()) {
+ if (value.expiresAt <= now) recommendationCache.delete(key);
+ }
+};
async getRecommendedRepos(userId: number, viewedIds: number[] = [], forceRefresh = false) {
const now = Date.now();
+ pruneExpiredRecommendationCache(now);
if (!forceRefresh) {
const cached = recommendationCache.get(userId);Also applies to: 474-479, 568-571
🤖 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 `@server/src/module/opensource/opensource.service.ts` around lines 34 - 35, The
global recommendationCache Map stores entries with expiresAt but never removes
expired keys, causing unbounded growth; add a periodic cleanup and opportunistic
pruning: implement a cleanup routine that iterates recommendationCache and
deletes entries with expiresAt <= Date.now() (run on a setInterval, e.g., every
minute) and also remove expired entries on cache reads/writes where
recommendationCache is accessed; reference the recommendationCache variable and
update the functions that read/write it (the cache lookup/get and set paths in
opensource.service.ts) to perform the opportunistic prune, and apply the same
pattern to the other cache Maps mentioned in the review.
| const genAI = new GoogleGenerativeAI(apiKey); | ||
| const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash-lite" }); | ||
| const result = await model.generateContent(prompt); | ||
| const text = result.response.text(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="server/src/module/opensource/opensource.service.ts"
echo "== lines around 520-590 =="
nl -ba "$FILE" | sed -n '520,590p'
echo
echo "== search for timeout/abort around Gemini calls =="
rg -n "GoogleGenerativeAI|getGenerativeModel|generateContent|AbortController|timeout|setTimeout" "$FILE"Repository: Sachinchaurasiya360/InternHack
Length of output: 162
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="server/src/module/opensource/opensource.service.ts"
echo "== ensure file exists =="
ls -la "$FILE"
echo
echo "== lines around 520-590 (line-numbered via awk) =="
awk 'NR>=520 && NR<=590 {printf "%6d\t%s\n", NR, $0}' "$FILE"
echo
echo "== search for timeout/abort around Gemini calls =="
rg -n "GoogleGenerativeAI|getGenerativeModel|generateContent|AbortController|timeout|setTimeout|Promise\.race|withTimeout" "$FILE" || trueRepository: Sachinchaurasiya360/InternHack
Length of output: 3485
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="server/src/module/opensource/opensource.service.ts"
echo "== lines around Gemini call (670-730) =="
awk 'NR>=670 && NR<=730 {printf "%6d\t%s\n", NR, $0}' "$FILE"
echo
echo "== search for any other Gemini/timeout helpers in the file =="
rg -n "AbortController|setTimeout|withTimeout|Promise\.race|timeout|signal:" "$FILE" || true
echo
echo "== show full Gemini prompt/call block (best-effort) =="
awk 'NR>=660 && NR<=710 {print}' "$FILE"Repository: Sachinchaurasiya360/InternHack
Length of output: 3749
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="server/src/module/opensource/opensource.service.ts"
echo "== count generateContent calls =="
rg -n "generateContent\(" "$FILE" || true
echo
echo "== count getGenerativeModel calls =="
rg -n "getGenerativeModel\(" "$FILE" || true
echo
echo "== search for any wrapper functions possibly named with timeout =="
rg -n "timeout|AbortController|Promise\.race|withTimeout|signal:" "$FILE" || trueRepository: Sachinchaurasiya360/InternHack
Length of output: 362
Add a timeout/abort guard around the Gemini recommendation call
model.generateContent(prompt) is awaited without any timeout or abort signal, so a hung upstream model call can tie up the endpoint (server/src/module/opensource/opensource.service.ts: ~692-695). Add a timeout/AbortController wrapper and fall back to [] on timeout.
🤖 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 `@server/src/module/opensource/opensource.service.ts` around lines 554 - 557,
Wrap the Gemini call in a timeout/abort guard so generateContent cannot hang:
create an AbortController with a short timeout (setTimeout that calls
controller.abort()), pass controller.signal into model.generateContent(prompt, {
signal }) if the API supports it, or otherwise use Promise.race between
model.generateContent(prompt) and a timeout promise that rejects; catch the
abort/timeout error around the call where GoogleGenerativeAI, getGenerativeModel
and model.generateContent are used, clear the timeout, and on timeout/abort
return an empty array ([]) instead of letting the request hang or throw.
| const matches = JSON.parse(cleanText) as { id: number; matchReason: string }[]; | ||
|
|
||
| const recommended = matches | ||
| .map((match) => { | ||
| const repo = candidateRepos.find((candidate) => candidate.id === match.id); | ||
| return repo ? { ...repo, matchReason: match.matchReason } : null; | ||
| }) | ||
| .filter((repo): repo is NonNullable<typeof repo> => Boolean(repo)); | ||
|
|
There was a problem hiding this comment.
Validate parsed AI payload shape before building recommended.
Line 559 trusts JSON output structure from the model. If matchReason is non-string/object, that value is forwarded and can break the client render path when displayed.
🔧 Proposed fix
-const matches = JSON.parse(cleanText) as { id: number; matchReason: string }[];
+const parsed = JSON.parse(cleanText);
+const matches = Array.isArray(parsed)
+ ? parsed.filter(
+ (m): m is { id: number; matchReason: string } =>
+ Number.isInteger(m?.id) && typeof m?.matchReason === "string" && m.matchReason.trim().length > 0,
+ )
+ : [];🤖 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 `@server/src/module/opensource/opensource.service.ts` around lines 559 - 567,
The parsed AI payload assigned to matches must be validated before mapping into
recommended: ensure matches is an array and each entry has a numeric id and a
string matchReason; skip or normalize any entries that don't match that shape.
Update the code around the matches declaration and the recommended mapping (the
matches variable and the mapping that uses candidateRepos.find) to first
validate/filter/normalize items (e.g., typeof match.id === 'number' and typeof
match.matchReason === 'string' or coerce toString) and only then map to
candidate repos, so no non-string matchReason values are forwarded to the
client.
| try { | ||
| const users = await prisma.user.findMany({ take: 1 }); | ||
| console.log("DB connection successful, users:", users.length); | ||
| } | ||
| catch (e) { | ||
| console.error("DB connection error:", e); | ||
| } | ||
| finally { | ||
| await prisma.$disconnect(); | ||
| } | ||
| } | ||
| main(); | ||
| //# sourceMappingURL=test-db-real.js.map No newline at end of file |
There was a problem hiding this comment.
Set a non-zero exit status on DB check failure.
At Line 8 the error is logged but not signaled as failure, so this script can report a broken DB connection while still exiting successfully.
Proposed fix
async function main() {
try {
const users = await prisma.user.findMany({ take: 1 });
console.log("DB connection successful, users:", users.length);
}
catch (e) {
console.error("DB connection error:", e);
+ process.exitCode = 1;
}
finally {
await prisma.$disconnect();
}
}
-main();
+main().catch((e) => {
+ console.error("Unexpected failure during DB check:", e);
+ process.exit(1);
+});📝 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.
| try { | |
| const users = await prisma.user.findMany({ take: 1 }); | |
| console.log("DB connection successful, users:", users.length); | |
| } | |
| catch (e) { | |
| console.error("DB connection error:", e); | |
| } | |
| finally { | |
| await prisma.$disconnect(); | |
| } | |
| } | |
| main(); | |
| //# sourceMappingURL=test-db-real.js.map | |
| try { | |
| const users = await prisma.user.findMany({ take: 1 }); | |
| console.log("DB connection successful, users:", users.length); | |
| } | |
| catch (e) { | |
| console.error("DB connection error:", e); | |
| process.exitCode = 1; | |
| } | |
| finally { | |
| await prisma.$disconnect(); | |
| } | |
| } | |
| main().catch((e) => { | |
| console.error("Unexpected failure during DB check:", e); | |
| process.exit(1); | |
| }); | |
| //# sourceMappingURL=test-db-real.js.map |
🤖 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 `@server/test-db-real.js` around lines 3 - 15, The DB-check catch block logs
the error but still allows a zero exit; update the catch in main() to set a
non-zero exit status by assigning process.exitCode = 1 (instead of calling
process.exit directly) after logging the error so prisma.$disconnect() in the
finally block still runs, ensuring the process exits with failure when the DB
check fails.
| async function main() { | ||
| try { | ||
| const users = await prisma.user.findMany({ take: 1 }); | ||
| console.log("DB connection successful, users:", users.length); | ||
| } | ||
| catch (e) { | ||
| console.error("DB connection error:", e); | ||
| } | ||
| finally { | ||
| await prisma.$disconnect(); | ||
| } | ||
| } | ||
| main(); |
There was a problem hiding this comment.
Fail the script explicitly when the DB check fails.
At Line 9 the script logs the error but can still exit as success, which makes health checks unreliable.
Proposed fix
async function main() {
try {
const users = await prisma.user.findMany({ take: 1 });
console.log("DB connection successful, users:", users.length);
}
catch (e) {
console.error("DB connection error:", e);
+ process.exitCode = 1;
}
finally {
await prisma.$disconnect();
}
}
-main();
+main().catch((e) => {
+ console.error("Unexpected failure during DB check:", e);
+ process.exit(1);
+});📝 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.
| async function main() { | |
| try { | |
| const users = await prisma.user.findMany({ take: 1 }); | |
| console.log("DB connection successful, users:", users.length); | |
| } | |
| catch (e) { | |
| console.error("DB connection error:", e); | |
| } | |
| finally { | |
| await prisma.$disconnect(); | |
| } | |
| } | |
| main(); | |
| async function main() { | |
| try { | |
| const users = await prisma.user.findMany({ take: 1 }); | |
| console.log("DB connection successful, users:", users.length); | |
| } | |
| catch (e) { | |
| console.error("DB connection error:", e); | |
| process.exitCode = 1; | |
| } | |
| finally { | |
| await prisma.$disconnect(); | |
| } | |
| } | |
| main().catch((e) => { | |
| console.error("Unexpected failure during DB check:", e); | |
| process.exit(1); | |
| }); |
🤖 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 `@server/test-db.js` around lines 3 - 15, The catch block in async function
main() currently only logs the error so the script can exit with success; update
it to log the error, ensure the DB client is disconnected, and then exit with a
non-zero status so health checks fail: in the catch for main() capture the
error, call console.error with the error, await prisma.$disconnect() (or ensure
prisma.$disconnect() runs before exiting), and then call process.exit(1); keep
the existing finally block behavior or remove duplicate disconnects to avoid
double-disconnect errors.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
client/src/module/student/opensource/RepoDiscoveryPage.tsx (1)
365-371:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMissing
hacktoberfestOnlyin useMemo dependency array.Line 365 uses
hacktoberfestOnlybut the dependency array at line 371 doesn't include it. This causes a stale closure: toggling the Hacktoberfest filter won't trigger a re-fetch becausequeryParamswon't update.- }, [search, selectedDomain, selectedDifficulty, selectedLanguage, languageMode, inferredLanguages, sortKey, trendingOnly, goodFirstOnly, page]); + }, [search, selectedDomain, selectedDifficulty, selectedLanguage, languageMode, inferredLanguages, sortKey, trendingOnly, goodFirstOnly, hacktoberfestOnly, page]);🤖 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/RepoDiscoveryPage.tsx` around lines 365 - 371, The useMemo that builds queryParams references hacktoberfestOnly but the dependency array on the useMemo does not include it, causing stale closures; update the dependency array for the memo (the useMemo that returns params in RepoDiscoveryPage, which currently lists search, selectedDomain, selectedDifficulty, selectedLanguage, languageMode, inferredLanguages, sortKey, trendingOnly, goodFirstOnly, page) to also include hacktoberfestOnly so toggling the Hacktoberfest filter will recompute queryParams and trigger the expected update.
🤖 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/RepoDiscoveryPage.tsx`:
- Around line 1385-1441: Remove the dead RecommendedSection component entirely:
delete the function RecommendedSection and its JSX; ensure you don't leave any
unused imports related to it. If any references remain, replace them to use the
existing inline recommendations implementation (which uses
queryKeys.opensource.recommended()) rather than queryKeys.opensource.list({
recommended: "true" }). Also ensure no code expects the old response shape ({
repos }) or calls RepoCard without the required bookmarked and onToggleBookmark
props — confirm the inline implementation supplies the server's { recommended }
payload and passes bookmarked and onToggleBookmark to RepoCard.
- Around line 438-443: Remove the orphaned useMemo block that redeclares
displayedRepos; keep the single valid declaration (const displayedRepos =
showSaved ? (bookmarkedData ?? []) : (data?.repos ?? [])); delete the
useMemo(...) that references non-existent repos and recentlyViewed (and any
import/variable references only used by that removed block) so there is only one
displayedRepos identifier (and no duplicate const) relying on bookmarkedData,
viewedOnly/viewed flag, recentlyViewed, and data?.repos as intended.
- Line 390: Remove the stray dependency array "}, [search, selectedDomain,
selectedDifficulty, selectedLanguage, languageMode, inferredLanguages, sortKey,
trendingOnly, hacktoberfestOnly, page]);" that is orphaned in
RepoDiscoveryPage.tsx; locate the stray closing brace/array and delete it so the
existing queryParams useMemo (the one that already has its correct dependency
array) remains intact and correctly closed, ensuring no extra closing tokens
remain that break the ESLint/parser.
---
Outside diff comments:
In `@client/src/module/student/opensource/RepoDiscoveryPage.tsx`:
- Around line 365-371: The useMemo that builds queryParams references
hacktoberfestOnly but the dependency array on the useMemo does not include it,
causing stale closures; update the dependency array for the memo (the useMemo
that returns params in RepoDiscoveryPage, which currently lists search,
selectedDomain, selectedDifficulty, selectedLanguage, languageMode,
inferredLanguages, sortKey, trendingOnly, goodFirstOnly, page) to also include
hacktoberfestOnly so toggling the Hacktoberfest filter will recompute
queryParams and trigger the expected update.
🪄 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: c52ff463-b0a1-4f2b-a243-2a64027f5077
📒 Files selected for processing (7)
client/src/lib/query-keys.tsclient/src/lib/types/opensource.types.tsclient/src/module/student/opensource/RepoCard.tsxclient/src/module/student/opensource/RepoDiscoveryPage.tsxserver/src/module/opensource/opensource.controller.tsserver/src/module/opensource/opensource.service.tsserver/src/module/opensource/opensource.validation.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- server/src/module/opensource/opensource.validation.ts
- client/src/lib/query-keys.ts
- server/src/module/opensource/opensource.controller.ts
- client/src/module/student/opensource/RepoCard.tsx
- server/src/module/opensource/opensource.service.ts
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
client/src/module/student/opensource/RepoDiscoveryPage.tsx (1)
365-371:⚠️ Potential issue | 🟠 Major | ⚡ Quick winMissing
hacktoberfestOnlyin useMemo dependency array.Line 365 uses
hacktoberfestOnlybut the dependency array at line 371 doesn't include it. This causes a stale closure: toggling the Hacktoberfest filter won't trigger a re-fetch becausequeryParamswon't update.- }, [search, selectedDomain, selectedDifficulty, selectedLanguage, languageMode, inferredLanguages, sortKey, trendingOnly, goodFirstOnly, page]); + }, [search, selectedDomain, selectedDifficulty, selectedLanguage, languageMode, inferredLanguages, sortKey, trendingOnly, goodFirstOnly, hacktoberfestOnly, page]);🤖 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/RepoDiscoveryPage.tsx` around lines 365 - 371, The useMemo that builds queryParams references hacktoberfestOnly but the dependency array on the useMemo does not include it, causing stale closures; update the dependency array for the memo (the useMemo that returns params in RepoDiscoveryPage, which currently lists search, selectedDomain, selectedDifficulty, selectedLanguage, languageMode, inferredLanguages, sortKey, trendingOnly, goodFirstOnly, page) to also include hacktoberfestOnly so toggling the Hacktoberfest filter will recompute queryParams and trigger the expected update.
🤖 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/RepoDiscoveryPage.tsx`:
- Around line 1385-1441: Remove the dead RecommendedSection component entirely:
delete the function RecommendedSection and its JSX; ensure you don't leave any
unused imports related to it. If any references remain, replace them to use the
existing inline recommendations implementation (which uses
queryKeys.opensource.recommended()) rather than queryKeys.opensource.list({
recommended: "true" }). Also ensure no code expects the old response shape ({
repos }) or calls RepoCard without the required bookmarked and onToggleBookmark
props — confirm the inline implementation supplies the server's { recommended }
payload and passes bookmarked and onToggleBookmark to RepoCard.
- Around line 438-443: Remove the orphaned useMemo block that redeclares
displayedRepos; keep the single valid declaration (const displayedRepos =
showSaved ? (bookmarkedData ?? []) : (data?.repos ?? [])); delete the
useMemo(...) that references non-existent repos and recentlyViewed (and any
import/variable references only used by that removed block) so there is only one
displayedRepos identifier (and no duplicate const) relying on bookmarkedData,
viewedOnly/viewed flag, recentlyViewed, and data?.repos as intended.
- Line 390: Remove the stray dependency array "}, [search, selectedDomain,
selectedDifficulty, selectedLanguage, languageMode, inferredLanguages, sortKey,
trendingOnly, hacktoberfestOnly, page]);" that is orphaned in
RepoDiscoveryPage.tsx; locate the stray closing brace/array and delete it so the
existing queryParams useMemo (the one that already has its correct dependency
array) remains intact and correctly closed, ensuring no extra closing tokens
remain that break the ESLint/parser.
---
Outside diff comments:
In `@client/src/module/student/opensource/RepoDiscoveryPage.tsx`:
- Around line 365-371: The useMemo that builds queryParams references
hacktoberfestOnly but the dependency array on the useMemo does not include it,
causing stale closures; update the dependency array for the memo (the useMemo
that returns params in RepoDiscoveryPage, which currently lists search,
selectedDomain, selectedDifficulty, selectedLanguage, languageMode,
inferredLanguages, sortKey, trendingOnly, goodFirstOnly, page) to also include
hacktoberfestOnly so toggling the Hacktoberfest filter will recompute
queryParams and trigger the expected update.
🪄 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: c52ff463-b0a1-4f2b-a243-2a64027f5077
📒 Files selected for processing (7)
client/src/lib/query-keys.tsclient/src/lib/types/opensource.types.tsclient/src/module/student/opensource/RepoCard.tsxclient/src/module/student/opensource/RepoDiscoveryPage.tsxserver/src/module/opensource/opensource.controller.tsserver/src/module/opensource/opensource.service.tsserver/src/module/opensource/opensource.validation.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- server/src/module/opensource/opensource.validation.ts
- client/src/lib/query-keys.ts
- server/src/module/opensource/opensource.controller.ts
- client/src/module/student/opensource/RepoCard.tsx
- server/src/module/opensource/opensource.service.ts
🛑 Comments failed to post (3)
client/src/module/student/opensource/RepoDiscoveryPage.tsx (3)
390-390:
⚠️ Potential issue | 🔴 Critical | ⚡ Quick winCritical: Orphaned dependency array causes build failure.
Line 390 contains a stray dependency array
}, [search, selectedDomain, ...])that doesn't belong to any function or hook. This is causing the ESLint parsing error reported in CI. This line must be removed—thequeryParamsuseMemo already has its dependency array at line 371.- }, [search, selectedDomain, selectedDifficulty, selectedLanguage, languageMode, inferredLanguages, sortKey, trendingOnly, hacktoberfestOnly, page]);📝 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.🧰 Tools
🪛 GitHub Actions: CI / 3_Lint Client.txt
[error] 390-390: ESLint parsing error: Declaration or statement expected.
🪛 GitHub Actions: CI / Lint Client
[error] 390-390: ESLint Parsing error: Declaration or statement expected.
🤖 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/RepoDiscoveryPage.tsx` at line 390, Remove the stray dependency array "}, [search, selectedDomain, selectedDifficulty, selectedLanguage, languageMode, inferredLanguages, sortKey, trendingOnly, hacktoberfestOnly, page]);" that is orphaned in RepoDiscoveryPage.tsx; locate the stray closing brace/array and delete it so the existing queryParams useMemo (the one that already has its correct dependency array) remains intact and correctly closed, ensuring no extra closing tokens remain that break the ESLint/parser.Source: Pipeline failures
438-443:
⚠️ Potential issue | 🔴 Critical | ⚡ Quick winConflicting
displayedReposdefinitions will cause a build error.There are two definitions of
displayedRepos:
- Lines 438-442: A
useMemoreferencing undefinedreposvariable andrecentlyViewed- Line 443: A direct assignment using
data?.reposThe first definition references
reposwhich doesn't exist in scope, and having duplicateconstdeclarations for the same identifier is invalid. Remove the orphaneduseMemoblock (lines 438-442).- const displayedRepos = useMemo(() => { - if (showSaved) return bookmarkedData || []; - if (viewedOnly) return recentlyViewed; - return repos; - }, [repos, showSaved, viewedOnly, recentlyViewed, bookmarkedData]); const displayedRepos = showSaved ? (bookmarkedData ?? []) : (data?.repos ?? []);📝 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.const displayedRepos = showSaved ? (bookmarkedData ?? []) : (data?.repos ?? []);🤖 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/RepoDiscoveryPage.tsx` around lines 438 - 443, Remove the orphaned useMemo block that redeclares displayedRepos; keep the single valid declaration (const displayedRepos = showSaved ? (bookmarkedData ?? []) : (data?.repos ?? [])); delete the useMemo(...) that references non-existent repos and recentlyViewed (and any import/variable references only used by that removed block) so there is only one displayedRepos identifier (and no duplicate const) relying on bookmarkedData, viewedOnly/viewed flag, recentlyViewed, and data?.repos as intended.
1385-1441: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Orphaned
RecommendedSectioncomponent should be removed.This component is defined but never used—the inline recommendations UI at lines 997-1032 already handles this functionality. Additionally, this component has contract mismatches:
- Uses
queryKeys.opensource.list({ recommended: "true" })instead ofqueryKeys.opensource.recommended()- Expects response shape
{ repos }but server returns{ recommended }(per context snippet 3)- Missing
bookmarkedandonToggleBookmarkprops onRepoCardSince the inline implementation is correct and this component is dead code, remove it entirely.
🤖 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/RepoDiscoveryPage.tsx` around lines 1385 - 1441, Remove the dead RecommendedSection component entirely: delete the function RecommendedSection and its JSX; ensure you don't leave any unused imports related to it. If any references remain, replace them to use the existing inline recommendations implementation (which uses queryKeys.opensource.recommended()) rather than queryKeys.opensource.list({ recommended: "true" }). Also ensure no code expects the old response shape ({ repos }) or calls RepoCard without the required bookmarked and onToggleBookmark props — confirm the inline implementation supplies the server's { recommended } payload and passes bookmarked and onToggleBookmark to RepoCard.
…ops across multiple modules
…reorder GSoCOrgCard definition in GSoCReposPage
…sitory display logic
ba5aa74 to
6bc07dd
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
client/src/module/recruiter/applications/ApplicationDetail.tsx (1)
159-179:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd error handling for fetchDetail calls.
fetchDetail()is called withoutawaitor error handling at lines 162 and 174. SincefetchDetailcan throw (line 121), these calls may produce unhandled promise rejections if the fetch fails.🛡️ Recommended fix
Either await with error handling or add
.catch():const handleAdvance = async () => { try { await api.patch(`/recruiter/applications/${applicationId}/advance`); - fetchDetail(); + fetchDetail().catch(() => { + // Fetch failure after mutation is non-critical; mutation already succeeded + }); toast.success("Application advanced"); } catch { toast.error("Failed to advance"); } };Apply the same pattern to
handleStatusChangeat line 174.🤖 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/recruiter/applications/ApplicationDetail.tsx` around lines 159 - 179, handleAdvance and handleStatusChange call fetchDetail() without awaiting or handling errors; update both functions (handleAdvance and handleStatusChange) to await fetchDetail() inside the existing try blocks (or attach .catch) so any rejection is caught by the surrounding catch handler—i.e., replace the fire-and-forget fetchDetail() calls with await fetchDetail() (or fetchDetail().catch(err => { throw err })) so failures from fetchDetail() don't produce unhandled promise rejections.
🧹 Nitpick comments (3)
client/src/module/student/opensource/RepoDiscoveryPage.tsx (3)
357-369: ⚡ Quick winConsider adding
staleTimeto avoid redundant recommendation fetches.The main repo list query uses
staleTime: 5 * 60 * 1000, butrecommendedDatahas no staleTime. Since recommendations are cached server-side for 6 hours (per PR objectives), adding a client-side staleTime would reduce unnecessary network requests and improve UX.♻️ Suggested change
const { data: recommendedData, isFetching: isFetchingRec } = useQuery({ queryKey: [...queryKeys.opensource.recommended(recommendationRefreshToken), viewedIds.join(",")], queryFn: async () => { const res = await api.get<{ recommended: RecommendedRepo[] }>("/opensource/recommended", { params: { forceRefresh: recommendationRefreshToken > 0 ? "true" : undefined, viewedIds: viewedIds.join(","), }, }); return res.data; }, enabled: !!user, + staleTime: 5 * 60 * 1000, });🤖 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/RepoDiscoveryPage.tsx` around lines 357 - 369, Add a client-side staleTime to the useQuery call that fetches recommendedData to avoid redundant network requests; update the useQuery for recommendedData (the call using queryKey [...queryKeys.opensource.recommended(recommendationRefreshToken), viewedIds.join(",")]) to include a staleTime matching the server-side cache (e.g., 6 hours) and optionally adjust refetch behavior (refetchOnWindowFocus/refetchOnReconnect) so fetches only occur when recommendationRefreshToken changes or after the stale window.
61-62: ⚡ Quick winConsider using the
Buttoncomponent instead of raw class strings.Per coding guidelines, all buttons should use the reusable
Buttoncomponent fromclient/src/components/ui/button.tsx. TheghostBtnClsconstant is applied to the Refresh button at line 974. Consider using<Button variant="ghost" size="sm">instead.♻️ Suggested refactor
-const ghostBtnCls = - "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md text-xs font-bold border border-stone-200 dark:border-white/10 text-stone-600 dark:text-stone-300 hover:border-stone-400 dark:hover:border-white/25 bg-white dark:bg-stone-900 disabled:opacity-60 disabled:cursor-not-allowed transition-colors";Then at line 970-978:
- <button - type="button" - onClick={handleRefreshRecs} - disabled={isFetchingRec} - className={ghostBtnCls} - > + <Button + variant="ghost" + size="sm" + onClick={handleRefreshRecs} + disabled={isFetchingRec} + > <RefreshCw className={`w-3.5 h-3.5 ${isFetchingRec ? "animate-spin" : ""}`} /> Refresh - </button> + </Button>🤖 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/RepoDiscoveryPage.tsx` around lines 61 - 62, The file defines a raw class string ghostBtnCls and applies it to the Refresh button in RepoDiscoveryPage.tsx; replace that usage with the shared Button component from client/src/components/ui/button.tsx (e.g. <Button variant="ghost" size="sm">) keeping the same props (disabled, onClick) and children (icon + label), and remove the ghostBtnCls constant if it’s only used for this button so styling comes from the Button variant instead.Source: Coding guidelines
50-59: ConfirmGOOD_FIRST_LABELSare identical across client and server; dedupe for maintainability.
GOOD_FIRST_LABELSmatches exactly (same 8 strings) in:
client/src/module/student/opensource/RepoDiscoveryPage.tsxclient/src/module/student/opensource/RepoCard.tsxserver/src/module/opensource/opensource.service.tsSince the constant is duplicated (including twice in client), extract to a shared source to avoid future drift.
🤖 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/RepoDiscoveryPage.tsx` around lines 50 - 59, The GOOD_FIRST_LABELS array is duplicated; extract it to a single exported constant (e.g., export const GOOD_FIRST_LABELS = [...]) in a shared module and update all usages to import that constant instead of defining their own arrays: replace the local GOOD_FIRST_LABELS in RepoDiscoveryPage and RepoCard and the server opensource.service with an import from the new shared module so client and server reference the same source of truth and avoid future drift.
🤖 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/roadmap/RoadmapCanvasPage.tsx`:
- Line 499: The level state in RoadmapCanvasPage (useState variable level /
setter setLevel) includes "ALL_LEVELS" but the <select> options and its onChange
handler only handle BEGINNER, INTERMEDIATE, ADVANCED, causing "ALL_LEVELS" from
the backend (initial value set during initialization) to not be selectable or
displayed; either add an "ALL_LEVELS" option to the dropdown and update the
onChange casting to include "ALL_LEVELS", or remove "ALL_LEVELS" from the
useState type and any initialization logic that sets it (so level is only
"BEGINNER" | "INTERMEDIATE" | "ADVANCED"); update the code paths that
read/assign level (including the initialization where level is set and the
select onChange handler) to use the chosen approach consistently.
---
Outside diff comments:
In `@client/src/module/recruiter/applications/ApplicationDetail.tsx`:
- Around line 159-179: handleAdvance and handleStatusChange call fetchDetail()
without awaiting or handling errors; update both functions (handleAdvance and
handleStatusChange) to await fetchDetail() inside the existing try blocks (or
attach .catch) so any rejection is caught by the surrounding catch handler—i.e.,
replace the fire-and-forget fetchDetail() calls with await fetchDetail() (or
fetchDetail().catch(err => { throw err })) so failures from fetchDetail() don't
produce unhandled promise rejections.
---
Nitpick comments:
In `@client/src/module/student/opensource/RepoDiscoveryPage.tsx`:
- Around line 357-369: Add a client-side staleTime to the useQuery call that
fetches recommendedData to avoid redundant network requests; update the useQuery
for recommendedData (the call using queryKey
[...queryKeys.opensource.recommended(recommendationRefreshToken),
viewedIds.join(",")]) to include a staleTime matching the server-side cache
(e.g., 6 hours) and optionally adjust refetch behavior
(refetchOnWindowFocus/refetchOnReconnect) so fetches only occur when
recommendationRefreshToken changes or after the stale window.
- Around line 61-62: The file defines a raw class string ghostBtnCls and applies
it to the Refresh button in RepoDiscoveryPage.tsx; replace that usage with the
shared Button component from client/src/components/ui/button.tsx (e.g. <Button
variant="ghost" size="sm">) keeping the same props (disabled, onClick) and
children (icon + label), and remove the ghostBtnCls constant if it’s only used
for this button so styling comes from the Button variant instead.
- Around line 50-59: The GOOD_FIRST_LABELS array is duplicated; extract it to a
single exported constant (e.g., export const GOOD_FIRST_LABELS = [...]) in a
shared module and update all usages to import that constant instead of defining
their own arrays: replace the local GOOD_FIRST_LABELS in RepoDiscoveryPage and
RepoCard and the server opensource.service with an import from the new shared
module so client and server reference the same source of truth and avoid future
drift.
🪄 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: 08457736-bfea-46d9-9290-cf9c664442c3
📒 Files selected for processing (10)
client/src/components/OssContributionHeatmap.tsxclient/src/components/ui/ConfirmDialog.tsxclient/src/module/recruiter/applications/ApplicationDetail.tsxclient/src/module/recruiter/applications/CandidateImportPage.tsxclient/src/module/student/applications/MyApplicationsPage.tsxclient/src/module/student/applications/questions/interviewQuestions.tsclient/src/module/student/companies/CompanyDetailPage.tsxclient/src/module/student/opensource/GSoCReposPage.tsxclient/src/module/student/opensource/RepoDiscoveryPage.tsxclient/src/module/student/roadmap/RoadmapCanvasPage.tsx
✅ Files skipped from review due to trivial changes (3)
- client/src/components/ui/ConfirmDialog.tsx
- client/src/module/recruiter/applications/CandidateImportPage.tsx
- client/src/module/student/applications/MyApplicationsPage.tsx
| const [title, setTitle] = useState(""); | ||
| const [shortDescription, setShortDescription] = useState(""); | ||
| const [level, setLevel] = useState<"BEGINNER" | "INTERMEDIATE" | "ADVANCED">( | ||
| const [level, setLevel] = useState<"BEGINNER" | "INTERMEDIATE" | "ADVANCED" | "ALL_LEVELS">( |
There was a problem hiding this comment.
Level type includes "ALL_LEVELS" but UI select does not.
The level state type at line 499 includes "ALL_LEVELS", but the select dropdown at lines 1610-1620 only offers BEGINNER, INTERMEDIATE, and ADVANCED. Additionally, the onChange handler at lines 1603-1606 casts to only those three values.
If the backend returns level: "ALL_LEVELS" (initialized at line 589), the select will not display it as a selected option, and the user cannot select it.
Either:
- Add an
ALL_LEVELSoption to the select if it's a valid user choice, or - Remove
"ALL_LEVELS"from the type union if it's not user-selectable via this UI
💡 Possible fix if ALL_LEVELS should be selectable
<select
value={level}
- onChange={(e) =>
- setLevel(
- e.target.value as "BEGINNER" | "INTERMEDIATE" | "ADVANCED",
- )
- }
+ onChange={(e) =>
+ setLevel(
+ e.target.value as "BEGINNER" | "INTERMEDIATE" | "ADVANCED" | "ALL_LEVELS",
+ )
+ }
className="w-full rounded-xl border border-stone-700 bg-stone-900 px-4 py-3 text-stone-50 outline-none focus:border-lime-500"
>
<option value="BEGINNER">
Beginner
</option>
<option value="INTERMEDIATE">
Intermediate
</option>
<option value="ADVANCED">
Advanced
</option>
+
+ <option value="ALL_LEVELS">
+ All Levels
+ </option>
</select>📝 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.
| const [level, setLevel] = useState<"BEGINNER" | "INTERMEDIATE" | "ADVANCED" | "ALL_LEVELS">( | |
| <select | |
| value={level} | |
| onChange={(e) => | |
| setLevel( | |
| e.target.value as "BEGINNER" | "INTERMEDIATE" | "ADVANCED" | "ALL_LEVELS", | |
| ) | |
| } | |
| className="w-full rounded-xl border border-stone-700 bg-stone-900 px-4 py-3 text-stone-50 outline-none focus:border-lime-500" | |
| > | |
| <option value="BEGINNER"> | |
| Beginner | |
| </option> | |
| <option value="INTERMEDIATE"> | |
| Intermediate | |
| </option> | |
| <option value="ADVANCED"> | |
| Advanced | |
| </option> | |
| <option value="ALL_LEVELS"> | |
| All Levels | |
| </option> | |
| </select> |
🤖 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/roadmap/RoadmapCanvasPage.tsx` at line 499, The
level state in RoadmapCanvasPage (useState variable level / setter setLevel)
includes "ALL_LEVELS" but the <select> options and its onChange handler only
handle BEGINNER, INTERMEDIATE, ADVANCED, causing "ALL_LEVELS" from the backend
(initial value set during initialization) to not be selectable or displayed;
either add an "ALL_LEVELS" option to the dropdown and update the onChange
casting to include "ALL_LEVELS", or remove "ALL_LEVELS" from the useState type
and any initialization logic that sets it (so level is only "BEGINNER" |
"INTERMEDIATE" | "ADVANCED"); update the code paths that read/assign level
(including the initialization where level is set and the select onChange
handler) to use the chosen approach consistently.
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
server/src/module/opensource/opensource.service.ts (2)
672-680:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPass
tagsinto Gemini if recommendations are supposed to match on tags.The candidate payload only includes
language,techStack,domain,trending, andissueTypes. The PR objective explicitly says matching should consider repositorytags, so the model currently has no way to use that signal.🔧 Minimal fix
${JSON.stringify( candidateRepos.map((repo) => ({ id: repo.id, name: repo.name, language: repo.language, techStack: repo.techStack, + tags: repo.tags, domain: repo.domain, trending: repo.trending, issueTypes: repo.issueTypes, })), )}🤖 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 `@server/src/module/opensource/opensource.service.ts` around lines 672 - 680, The candidate mapping in opensource.service.ts (the candidateRepos.map block) omits repository tags, so include tags in the payload passed to Gemini by adding tags: repo.tags (or repo?.tags) to the object returned by candidateRepos.map; also verify the repo type/interface used by the function (e.g., the repo model or DTO referenced where candidateRepos is created) exposes a tags field and update that type if necessary so Gemini receives the tag signal for matching.
647-651:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftDon’t cache a
viewedIds-dependent result underuserIdalone.The miss path excludes
viewedIdsbefore ranking, but the cached value is keyed only byuserId. A request from one device/session can therefore shrink another session’s recommendation pool for the full 6-hour TTL. It also hard-excludes viewed repos instead of de-ranking them as described in the PR objective.Also applies to: 706-709
🤖 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 `@server/src/module/opensource/opensource.service.ts` around lines 647 - 651, The current prisma.opensourceRepo.findMany call excludes repos by viewedIds but the cached result is keyed only by userId, causing one session's viewedIds to affect others; fix by removing the where: viewedIds exclusion in prisma.opensourceRepo.findMany so the DB returns the full candidate list (update the candidateRepos query), then de-rank viewed repos in-memory by using viewedIds to push those items to the end of the list (or reduce their score) before returning; and change caching so you either (A) cache only the base candidate list under userId and apply viewedIds-based de-ranking per-request without writing a viewed-dependent value to that same key, or (B) include a short deterministic signature/hash of viewedIds in the cache key (instead of caching solely by userId) to avoid cross-session pollution (referencing candidateRepos, viewedIds, userId, and prisma.opensourceRepo.findMany).
♻️ Duplicate comments (1)
client/src/module/student/roadmap/RoadmapCanvasPage.tsx (1)
501-501:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLevel type includes
"ALL_LEVELS"but UI select does not.This is the same issue flagged in the previous review. The
levelstate type includes"ALL_LEVELS", but the select dropdown (lines 1610-1622) only offersBEGINNER,INTERMEDIATE, andADVANCED, and theonChangehandler (lines 1605-1609) only casts to those three values.If the backend returns
level: "ALL_LEVELS"(set at line 591), the select will not display it as a selected option, and the user cannot select it.Either add an
"ALL_LEVELS"option to the select and update theonChangecast, or remove"ALL_LEVELS"from the type union if it's not user-selectable via this UI.🤖 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/roadmap/RoadmapCanvasPage.tsx` at line 501, The union type for the React state variable level (useState<"BEGINNER" | "INTERMEDIATE" | "ADVANCED" | "ALL_LEVELS">) is out of sync with the select dropdown and its onChange handler (which only handles BEGINNER, INTERMEDIATE, ADVANCED) and with the backend possibly returning "ALL_LEVELS"; either add an "ALL_LEVELS" option to the select options list and include it in the onChange cast so setLevel(...) can accept it, or remove "ALL_LEVELS" from the useState union (and any backend mapping code that sets level to "ALL_LEVELS") so the UI and state type match; locate and update the level state declaration, the select element/options, and the onChange handler that calls setLevel to keep the values consistent.
🤖 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 `@server/src/module/opensource/opensource.service.ts`:
- Around line 456-457: The octStart/novStart values are hard-coded to 2026;
update the calculation to derive the Hacktoberfest window from the current year
instead. Replace the fixed Date.UTC(2026, ...) uses for octStart and novStart
with a year variable computed from new Date().getUTCFullYear() (or similar
UTC-aware current-year logic) so octStart = new Date(Date.UTC(year, 9, 1)) and
novStart = new Date(Date.UTC(year, 10, 1)); modify the occurrences of octStart
and novStart in the opensource.service.ts logic accordingly.
- Around line 129-131: The file contains a duplicate declaration of the variable
trimmedSearch (const trimmedSearch = search?.trim();) in the same scope inside
the OpenSourceService logic; remove the redundant declaration so only one const
trimmedSearch exists and ensure any subsequent code (calls, checks, or uses)
reference that single variable (trimmedSearch) in the same function/method
(e.g., the method where trimmedSearch is declared in opensource.service.ts).
---
Outside diff comments:
In `@server/src/module/opensource/opensource.service.ts`:
- Around line 672-680: The candidate mapping in opensource.service.ts (the
candidateRepos.map block) omits repository tags, so include tags in the payload
passed to Gemini by adding tags: repo.tags (or repo?.tags) to the object
returned by candidateRepos.map; also verify the repo type/interface used by the
function (e.g., the repo model or DTO referenced where candidateRepos is
created) exposes a tags field and update that type if necessary so Gemini
receives the tag signal for matching.
- Around line 647-651: The current prisma.opensourceRepo.findMany call excludes
repos by viewedIds but the cached result is keyed only by userId, causing one
session's viewedIds to affect others; fix by removing the where: viewedIds
exclusion in prisma.opensourceRepo.findMany so the DB returns the full candidate
list (update the candidateRepos query), then de-rank viewed repos in-memory by
using viewedIds to push those items to the end of the list (or reduce their
score) before returning; and change caching so you either (A) cache only the
base candidate list under userId and apply viewedIds-based de-ranking
per-request without writing a viewed-dependent value to that same key, or (B)
include a short deterministic signature/hash of viewedIds in the cache key
(instead of caching solely by userId) to avoid cross-session pollution
(referencing candidateRepos, viewedIds, userId, and
prisma.opensourceRepo.findMany).
---
Duplicate comments:
In `@client/src/module/student/roadmap/RoadmapCanvasPage.tsx`:
- Line 501: The union type for the React state variable level
(useState<"BEGINNER" | "INTERMEDIATE" | "ADVANCED" | "ALL_LEVELS">) is out of
sync with the select dropdown and its onChange handler (which only handles
BEGINNER, INTERMEDIATE, ADVANCED) and with the backend possibly returning
"ALL_LEVELS"; either add an "ALL_LEVELS" option to the select options list and
include it in the onChange cast so setLevel(...) can accept it, or remove
"ALL_LEVELS" from the useState union (and any backend mapping code that sets
level to "ALL_LEVELS") so the UI and state type match; locate and update the
level state declaration, the select element/options, and the onChange handler
that calls setLevel to keep the values consistent.
🪄 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: c96541e9-44aa-42be-8f39-fb89a7921334
📒 Files selected for processing (10)
client/src/lib/query-keys.tsclient/src/lib/types/opensource.types.tsclient/src/module/recruiter/applications/ApplicationDetail.tsxclient/src/module/student/applications/MyApplicationsPage.tsxclient/src/module/student/opensource/RepoCard.tsxclient/src/module/student/opensource/RepoDiscoveryPage.tsxclient/src/module/student/roadmap/RoadmapCanvasPage.tsxserver/src/module/opensource/opensource.controller.tsserver/src/module/opensource/opensource.service.tsserver/src/module/opensource/opensource.validation.ts
✅ Files skipped from review due to trivial changes (2)
- server/src/module/opensource/opensource.validation.ts
- client/src/module/student/applications/MyApplicationsPage.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
- server/src/module/opensource/opensource.controller.ts
- client/src/lib/types/opensource.types.ts
- client/src/lib/query-keys.ts
- client/src/module/student/opensource/RepoCard.tsx
- client/src/module/student/opensource/RepoDiscoveryPage.tsx
- client/src/module/recruiter/applications/ApplicationDetail.tsx
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
server/src/module/opensource/opensource.service.ts (2)
672-680:⚠️ Potential issue | 🟠 Major | ⚡ Quick winPass
tagsinto Gemini if recommendations are supposed to match on tags.The candidate payload only includes
language,techStack,domain,trending, andissueTypes. The PR objective explicitly says matching should consider repositorytags, so the model currently has no way to use that signal.🔧 Minimal fix
${JSON.stringify( candidateRepos.map((repo) => ({ id: repo.id, name: repo.name, language: repo.language, techStack: repo.techStack, + tags: repo.tags, domain: repo.domain, trending: repo.trending, issueTypes: repo.issueTypes, })), )}🤖 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 `@server/src/module/opensource/opensource.service.ts` around lines 672 - 680, The candidate mapping in opensource.service.ts (the candidateRepos.map block) omits repository tags, so include tags in the payload passed to Gemini by adding tags: repo.tags (or repo?.tags) to the object returned by candidateRepos.map; also verify the repo type/interface used by the function (e.g., the repo model or DTO referenced where candidateRepos is created) exposes a tags field and update that type if necessary so Gemini receives the tag signal for matching.
647-651:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftDon’t cache a
viewedIds-dependent result underuserIdalone.The miss path excludes
viewedIdsbefore ranking, but the cached value is keyed only byuserId. A request from one device/session can therefore shrink another session’s recommendation pool for the full 6-hour TTL. It also hard-excludes viewed repos instead of de-ranking them as described in the PR objective.Also applies to: 706-709
🤖 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 `@server/src/module/opensource/opensource.service.ts` around lines 647 - 651, The current prisma.opensourceRepo.findMany call excludes repos by viewedIds but the cached result is keyed only by userId, causing one session's viewedIds to affect others; fix by removing the where: viewedIds exclusion in prisma.opensourceRepo.findMany so the DB returns the full candidate list (update the candidateRepos query), then de-rank viewed repos in-memory by using viewedIds to push those items to the end of the list (or reduce their score) before returning; and change caching so you either (A) cache only the base candidate list under userId and apply viewedIds-based de-ranking per-request without writing a viewed-dependent value to that same key, or (B) include a short deterministic signature/hash of viewedIds in the cache key (instead of caching solely by userId) to avoid cross-session pollution (referencing candidateRepos, viewedIds, userId, and prisma.opensourceRepo.findMany).
♻️ Duplicate comments (1)
client/src/module/student/roadmap/RoadmapCanvasPage.tsx (1)
501-501:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLevel type includes
"ALL_LEVELS"but UI select does not.This is the same issue flagged in the previous review. The
levelstate type includes"ALL_LEVELS", but the select dropdown (lines 1610-1622) only offersBEGINNER,INTERMEDIATE, andADVANCED, and theonChangehandler (lines 1605-1609) only casts to those three values.If the backend returns
level: "ALL_LEVELS"(set at line 591), the select will not display it as a selected option, and the user cannot select it.Either add an
"ALL_LEVELS"option to the select and update theonChangecast, or remove"ALL_LEVELS"from the type union if it's not user-selectable via this UI.🤖 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/roadmap/RoadmapCanvasPage.tsx` at line 501, The union type for the React state variable level (useState<"BEGINNER" | "INTERMEDIATE" | "ADVANCED" | "ALL_LEVELS">) is out of sync with the select dropdown and its onChange handler (which only handles BEGINNER, INTERMEDIATE, ADVANCED) and with the backend possibly returning "ALL_LEVELS"; either add an "ALL_LEVELS" option to the select options list and include it in the onChange cast so setLevel(...) can accept it, or remove "ALL_LEVELS" from the useState union (and any backend mapping code that sets level to "ALL_LEVELS") so the UI and state type match; locate and update the level state declaration, the select element/options, and the onChange handler that calls setLevel to keep the values consistent.
🤖 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 `@server/src/module/opensource/opensource.service.ts`:
- Around line 456-457: The octStart/novStart values are hard-coded to 2026;
update the calculation to derive the Hacktoberfest window from the current year
instead. Replace the fixed Date.UTC(2026, ...) uses for octStart and novStart
with a year variable computed from new Date().getUTCFullYear() (or similar
UTC-aware current-year logic) so octStart = new Date(Date.UTC(year, 9, 1)) and
novStart = new Date(Date.UTC(year, 10, 1)); modify the occurrences of octStart
and novStart in the opensource.service.ts logic accordingly.
- Around line 129-131: The file contains a duplicate declaration of the variable
trimmedSearch (const trimmedSearch = search?.trim();) in the same scope inside
the OpenSourceService logic; remove the redundant declaration so only one const
trimmedSearch exists and ensure any subsequent code (calls, checks, or uses)
reference that single variable (trimmedSearch) in the same function/method
(e.g., the method where trimmedSearch is declared in opensource.service.ts).
---
Outside diff comments:
In `@server/src/module/opensource/opensource.service.ts`:
- Around line 672-680: The candidate mapping in opensource.service.ts (the
candidateRepos.map block) omits repository tags, so include tags in the payload
passed to Gemini by adding tags: repo.tags (or repo?.tags) to the object
returned by candidateRepos.map; also verify the repo type/interface used by the
function (e.g., the repo model or DTO referenced where candidateRepos is
created) exposes a tags field and update that type if necessary so Gemini
receives the tag signal for matching.
- Around line 647-651: The current prisma.opensourceRepo.findMany call excludes
repos by viewedIds but the cached result is keyed only by userId, causing one
session's viewedIds to affect others; fix by removing the where: viewedIds
exclusion in prisma.opensourceRepo.findMany so the DB returns the full candidate
list (update the candidateRepos query), then de-rank viewed repos in-memory by
using viewedIds to push those items to the end of the list (or reduce their
score) before returning; and change caching so you either (A) cache only the
base candidate list under userId and apply viewedIds-based de-ranking
per-request without writing a viewed-dependent value to that same key, or (B)
include a short deterministic signature/hash of viewedIds in the cache key
(instead of caching solely by userId) to avoid cross-session pollution
(referencing candidateRepos, viewedIds, userId, and
prisma.opensourceRepo.findMany).
---
Duplicate comments:
In `@client/src/module/student/roadmap/RoadmapCanvasPage.tsx`:
- Line 501: The union type for the React state variable level
(useState<"BEGINNER" | "INTERMEDIATE" | "ADVANCED" | "ALL_LEVELS">) is out of
sync with the select dropdown and its onChange handler (which only handles
BEGINNER, INTERMEDIATE, ADVANCED) and with the backend possibly returning
"ALL_LEVELS"; either add an "ALL_LEVELS" option to the select options list and
include it in the onChange cast so setLevel(...) can accept it, or remove
"ALL_LEVELS" from the useState union (and any backend mapping code that sets
level to "ALL_LEVELS") so the UI and state type match; locate and update the
level state declaration, the select element/options, and the onChange handler
that calls setLevel to keep the values consistent.
🪄 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: c96541e9-44aa-42be-8f39-fb89a7921334
📒 Files selected for processing (10)
client/src/lib/query-keys.tsclient/src/lib/types/opensource.types.tsclient/src/module/recruiter/applications/ApplicationDetail.tsxclient/src/module/student/applications/MyApplicationsPage.tsxclient/src/module/student/opensource/RepoCard.tsxclient/src/module/student/opensource/RepoDiscoveryPage.tsxclient/src/module/student/roadmap/RoadmapCanvasPage.tsxserver/src/module/opensource/opensource.controller.tsserver/src/module/opensource/opensource.service.tsserver/src/module/opensource/opensource.validation.ts
✅ Files skipped from review due to trivial changes (2)
- server/src/module/opensource/opensource.validation.ts
- client/src/module/student/applications/MyApplicationsPage.tsx
🚧 Files skipped from review as they are similar to previous changes (6)
- server/src/module/opensource/opensource.controller.ts
- client/src/lib/types/opensource.types.ts
- client/src/lib/query-keys.ts
- client/src/module/student/opensource/RepoCard.tsx
- client/src/module/student/opensource/RepoDiscoveryPage.tsx
- client/src/module/recruiter/applications/ApplicationDetail.tsx
🛑 Comments failed to post (2)
server/src/module/opensource/opensource.service.ts (2)
129-131:
⚠️ Potential issue | 🔴 Critical🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE="server/src/module/opensource/opensource.service.ts" echo "== duplicate declaration site ==" awk 'NR>=122 && NR<=134 {printf "%6d\t%s\n", NR, $0}' "$FILE" echo echo "== all matching declarations in file ==" rg -n 'const trimmedSearch = search\?\.trim\(\);' "$FILE"Repository: Sachinchaurasiya360/InternHack
Length of output: 778
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE="server/src/module/opensource/opensource.service.ts" echo "== context around line 129 ==" nl -ba "$FILE" | sed -n '105,160p'Repository: Sachinchaurasiya360/InternHack
Length of output: 165
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE="server/src/module/opensource/opensource.service.ts" echo "== context around line 129 (120-145) ==" awk -v start=115 -v end=150 '{if(NR>=start && NR<=end){printf "%6d\t%s\n", NR, $0}}' "$FILE" echo echo "== other occurrence around line 260 (245-275) ==" awk -v start=235 -v end=280 '{if(NR>=start && NR<=end){printf "%6d\t%s\n", NR, $0}}' "$FILE"Repository: Sachinchaurasiya360/InternHack
Length of output: 3273
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE="server/src/module/opensource/opensource.service.ts" echo "== Cache key / viewedIds dependencies (search) ==" rg -n "viewedIds|cache|recommend|getRecommendedRepos" "$FILE" echo echo "== Hard-coded Hacktoberfest date bounds ==" rg -n "Date\.UTC\(" "$FILE" echo echo "== Gemini prompt candidate payload construction ==" rg -n "getRecommendedRepos|GoogleGenerativeAI|gemini|prompt|candidates|candidate|techStack|issueTypes|domain|trending|tags" "$FILE"Repository: Sachinchaurasiya360/InternHack
Length of output: 3196
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE="server/src/module/opensource/opensource.service.ts" echo "== getHacktoberfestProgress context (420-560) ==" awk 'NR>=410 && NR<=560 {printf "%6d\t%s\n", NR, $0}' "$FILE" echo echo "== getRecommendedRepos context (600-740) ==" awk 'NR>=590 && NR<=740 {printf "%6d\t%s\n", NR, $0}' "$FILE" echo echo "== getRecommendedRepos: occurrences of 'tags' in nearby area ==" rg -n "getRecommendedRepos|tags" "$FILE" | sed -n '580,740p'Repository: Sachinchaurasiya360/InternHack
Length of output: 10934
Remove the duplicate
trimmedSearchdeclaration inserver/src/module/opensource/opensource.service.ts(lines 129–131).
const trimmedSearch = search?.trim();is declared twice in the same scope, which will fail TypeScript compilation.🤖 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 `@server/src/module/opensource/opensource.service.ts` around lines 129 - 131, The file contains a duplicate declaration of the variable trimmedSearch (const trimmedSearch = search?.trim();) in the same scope inside the OpenSourceService logic; remove the redundant declaration so only one const trimmedSearch exists and ensure any subsequent code (calls, checks, or uses) reference that single variable (trimmedSearch) in the same function/method (e.g., the method where trimmedSearch is declared in opensource.service.ts).
456-457:
⚠️ Potential issue | 🟠 Major | ⚡ Quick winDerive the Hacktoberfest window instead of hard-coding 2026.
Date.UTC(2026, 9, 1)andDate.UTC(2026, 10, 1)lock this endpoint to October 2026. Starting January 2027, it will still report against October 1, 2026 through November 1, 2026.🔧 Suggested direction
- const octStart = new Date(Date.UTC(2026, 9, 1)); - const novStart = new Date(Date.UTC(2026, 10, 1)); + const year = new Date().getUTCFullYear(); + const octStart = new Date(Date.UTC(year, 9, 1)); + const novStart = new Date(Date.UTC(year, 10, 1));🤖 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 `@server/src/module/opensource/opensource.service.ts` around lines 456 - 457, The octStart/novStart values are hard-coded to 2026; update the calculation to derive the Hacktoberfest window from the current year instead. Replace the fixed Date.UTC(2026, ...) uses for octStart and novStart with a year variable computed from new Date().getUTCFullYear() (or similar UTC-aware current-year logic) so octStart = new Date(Date.UTC(year, 9, 1)) and novStart = new Date(Date.UTC(year, 10, 1)); modify the occurrences of octStart and novStart in the opensource.service.ts logic accordingly.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
client/src/module/recruiter/applications/ApplicationDetail.tsx (1)
105-118:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winRemove duplicated
fetchDetail/catchblocks (compile blocker).Line 106 and Line 116 introduce nested duplicate declarations, which makes this file fail parsing/build.
Proposed fix
- const fetchDetail = useCallback(async (signal?: AbortSignal) => { -const fetchDetail = useCallback(async (signal?: AbortSignal) => { + const fetchDetail = useCallback(async (signal?: AbortSignal) => { try { const res = await api.get( `/recruiter/applications/${applicationId}`, { signal } ); setApplication(res.data.application); return res.data.application; - } catch (err: any) { - if (err.name !== "CanceledError" && err.name !== "AbortError") { - } catch (err) { - if (err instanceof Error && err.name !== "CanceledError" && err.name !== "AbortError") { + } catch (err) { + if ( + err instanceof Error && + err.name !== "CanceledError" && + err.name !== "AbortError" + ) { console.error(err); } throw err; } }, [applicationId]);🤖 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/recruiter/applications/ApplicationDetail.tsx` around lines 105 - 118, There are duplicate nested declarations of the fetchDetail function and duplicate catch blocks causing a parse error; locate the fetchDetail useCallback (function name fetchDetail) and remove the inner duplicated declaration and its extra catch so only one async function remains that calls api.get(`/recruiter/applications/${applicationId}`, { signal }), calls setApplication(res.data.application), and has a single catch that checks err instanceof Error (or err.name) and logs non-abort errors (e.g., console.error(err)); ensure the function signature remains useCallback(async (signal?: AbortSignal) => { ... }, [applicationId]) and no duplicated blocks remain.Source: Linters/SAST tools
🤖 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/applications/MyApplicationsPage.tsx`:
- Around line 300-315: The filteredExternal useMemo is missing status filtering
so external tabs behave inconsistently; update the computation in
filteredExternal (and its dependency array) to apply the same statusFilter logic
used for internal applications: first filter externalApplications by
statusFilter (e.g., compare a.status to statusFilter when statusFilter !==
"all"), then apply the debouncedSearch filter and the sortOption handling in the
useMemo, and add statusFilter to the dependencies so counts and tab behavior
stay consistent.
---
Outside diff comments:
In `@client/src/module/recruiter/applications/ApplicationDetail.tsx`:
- Around line 105-118: There are duplicate nested declarations of the
fetchDetail function and duplicate catch blocks causing a parse error; locate
the fetchDetail useCallback (function name fetchDetail) and remove the inner
duplicated declaration and its extra catch so only one async function remains
that calls api.get(`/recruiter/applications/${applicationId}`, { signal }),
calls setApplication(res.data.application), and has a single catch that checks
err instanceof Error (or err.name) and logs non-abort errors (e.g.,
console.error(err)); ensure the function signature remains useCallback(async
(signal?: AbortSignal) => { ... }, [applicationId]) and no duplicated blocks
remain.
🪄 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: 784847c2-4cec-4e44-b376-eccba0110045
📒 Files selected for processing (9)
client/src/module/recruiter/applications/ApplicationDetail.tsxclient/src/module/student/applications/MyApplicationsPage.tsxclient/src/module/student/dsa/components/AiHintPanel.tsxclient/src/module/student/opensource/RepoCard.tsxclient/src/module/student/opensource/RepoDiscoveryPage.tsxclient/src/module/student/profile/components/ProfilePageHeader.tsxclient/src/module/student/roadmap/RoadmapDashboardPage.tsxclient/src/module/student/roadmap/RoadmapsLandingPage.tsxserver/src/module/opensource/opensource.service.ts
💤 Files with no reviewable changes (1)
- server/src/module/opensource/opensource.service.ts
✅ Files skipped from review due to trivial changes (2)
- client/src/module/student/profile/components/ProfilePageHeader.tsx
- client/src/module/student/dsa/components/AiHintPanel.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- client/src/module/student/opensource/RepoCard.tsx
- client/src/module/student/opensource/RepoDiscoveryPage.tsx
| const filteredExternal = useMemo(() => { | ||
| let base = !debouncedSearch.trim() | ||
| const base = !debouncedSearch.trim() | ||
| ? externalApplications | ||
| : externalApplications.filter( | ||
| (a) => | ||
| a.adminJob.role?.toLowerCase().includes(debouncedSearch.toLowerCase()) || | ||
| a.adminJob.company?.toLowerCase().includes(debouncedSearch.toLowerCase()) | ||
| ); | ||
|
|
||
| if (statusFilter !== "ALL") { | ||
| base = base.filter((a) => a.status === statusFilter); | ||
| } | ||
|
|
||
| return [...base].sort((a, b) => { | ||
| if (sortOption === "newest") return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); | ||
| if (sortOption === "oldest") return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); | ||
| if (sortOption === "company") return (a.adminJob.company ?? "").localeCompare(b.adminJob.company ?? ""); | ||
| return 0; | ||
| }); | ||
| }, [externalApplications, debouncedSearch, sortOption, statusFilter]); | ||
| }, [externalApplications, debouncedSearch, sortOption]); |
There was a problem hiding this comment.
Reapply status filtering for external applications to keep tab behavior consistent.
Line 301–315 dropped statusFilter from filteredExternal, so status tabs filter internal cards but not external cards. This causes inconsistent results and counts.
Proposed fix
const filteredExternal = useMemo(() => {
- const base = !debouncedSearch.trim()
+ let base = !debouncedSearch.trim()
? externalApplications
: externalApplications.filter(
(a) =>
a.adminJob.role?.toLowerCase().includes(debouncedSearch.toLowerCase()) ||
a.adminJob.company?.toLowerCase().includes(debouncedSearch.toLowerCase())
);
+
+ if (statusFilter !== "ALL") {
+ base = base.filter((a) => a.status === statusFilter);
+ }
return [...base].sort((a, b) => {
if (sortOption === "newest") return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
if (sortOption === "oldest") return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
if (sortOption === "company") return (a.adminJob.company ?? "").localeCompare(b.adminJob.company ?? "");
return 0;
});
- }, [externalApplications, debouncedSearch, sortOption]);
+ }, [externalApplications, debouncedSearch, sortOption, statusFilter]);📝 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.
| const filteredExternal = useMemo(() => { | |
| let base = !debouncedSearch.trim() | |
| const base = !debouncedSearch.trim() | |
| ? externalApplications | |
| : externalApplications.filter( | |
| (a) => | |
| a.adminJob.role?.toLowerCase().includes(debouncedSearch.toLowerCase()) || | |
| a.adminJob.company?.toLowerCase().includes(debouncedSearch.toLowerCase()) | |
| ); | |
| if (statusFilter !== "ALL") { | |
| base = base.filter((a) => a.status === statusFilter); | |
| } | |
| return [...base].sort((a, b) => { | |
| if (sortOption === "newest") return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); | |
| if (sortOption === "oldest") return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); | |
| if (sortOption === "company") return (a.adminJob.company ?? "").localeCompare(b.adminJob.company ?? ""); | |
| return 0; | |
| }); | |
| }, [externalApplications, debouncedSearch, sortOption, statusFilter]); | |
| }, [externalApplications, debouncedSearch, sortOption]); | |
| const filteredExternal = useMemo(() => { | |
| let base = !debouncedSearch.trim() | |
| ? externalApplications | |
| : externalApplications.filter( | |
| (a) => | |
| a.adminJob.role?.toLowerCase().includes(debouncedSearch.toLowerCase()) || | |
| a.adminJob.company?.toLowerCase().includes(debouncedSearch.toLowerCase()) | |
| ); | |
| if (statusFilter !== "ALL") { | |
| base = base.filter((a) => a.status === statusFilter); | |
| } | |
| return [...base].sort((a, b) => { | |
| if (sortOption === "newest") return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); | |
| if (sortOption === "oldest") return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); | |
| if (sortOption === "company") return (a.adminJob.company ?? "").localeCompare(b.adminJob.company ?? ""); | |
| return 0; | |
| }); | |
| }, [externalApplications, debouncedSearch, sortOption, statusFilter]); |
🤖 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/applications/MyApplicationsPage.tsx` around lines
300 - 315, The filteredExternal useMemo is missing status filtering so external
tabs behave inconsistently; update the computation in filteredExternal (and its
dependency array) to apply the same statusFilter logic used for internal
applications: first filter externalApplications by statusFilter (e.g., compare
a.status to statusFilter when statusFilter !== "all"), then apply the
debouncedSearch filter and the sortOption handling in the useMemo, and add
statusFilter to the dependencies so counts and tab behavior stay consistent.
|
Hey @Sachinchaurasiya360! Can I get an update on this PR? I have solved the merge conflicts and build errors. |
ReviewStatus: Changes requested Critical: Committed build artifactsThe PR includes compiled output files that must not be tracked in git:
Remove these from the commit ( Regression: Status filter broken for external applications
// Removed — this breaks filtering external applications by status
- if (statusFilter !== "ALL") {
- base = base.filter((a) => a.status === statusFilter);
- }When a user selects e.g. "REJECTED" from the status chips, external applications no longer filter. Revert this change — it is unrelated to the recommendation feature and introduces a bug. Screenshots requiredThis adds a "Recommended for you" carousel and match-reason chips to the repo discovery page. Please attach screenshots of both light and dark mode. UI style violation
Unrelated changesThis PR mixes the recommendations feature with unrelated fixes across No CI build/lint/test ranOnly label automation checks passed — the full CI suite did not run. Fix the above issues to get a clean CI run before re-requesting review. |
Closes #949
Problem
The current repository discovery page presents a generic, unfiltered list of projects. Users with React skills see the same list as users with Rust skills, leading to a less engaging experience. Open source discovery lacks a "Recommendation for you" layer, which is a core feature for user retention and matching.
What has changed
This PR introduces a personalised, AI-powered "For you" section at the top of the Repo Discovery page, powered by Gemini.
Backend/API (
/api/opensource/recommended)trendingor havegoodFirstIssuesCount > 0.Frontend UI (
RepoDiscoveryPage&RepoCard)Impact
Priority: P1 This personalisation layer directly addresses discovery retention. By matching students with repositories aligned to their active tech stack and highlighting beginner-friendly issues, we significantly lower the barrier to open-source contributions.
Summary by CodeRabbit
New Features
Improvements