Skip to content

Commit a6beec3

Browse files
Merge pull request #7 from Sachinchaurasiya360/fix/mvp-fix
fix issues
2 parents fa43332 + 1072de2 commit a6beec3

9 files changed

Lines changed: 180 additions & 4 deletions

File tree

client/src/components/ContributionGraphs.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ interface LeetCodeData {
1717
calendar: Activity[];
1818
}
1919

20+
function emptyYearCalendar(): Activity[] {
21+
const now = new Date();
22+
const start = new Date(now.getFullYear(), 0, 1);
23+
const entries: Activity[] = [];
24+
for (let d = new Date(start); d <= now; d.setDate(d.getDate() + 1)) {
25+
entries.push({ date: d.toISOString().split("T")[0]!, count: 0, level: 0 });
26+
}
27+
return entries;
28+
}
29+
2030
interface Props {
2131
githubUsername?: string;
2232
leetcodeUsername?: string;
@@ -122,7 +132,7 @@ export default function ContributionGraphs({ githubUsername, leetcodeUsername, s
122132
<>
123133
<div className="overflow-x-auto pb-2">
124134
<ActivityCalendar
125-
data={leetcodeData.calendar}
135+
data={leetcodeData.calendar.length > 0 ? leetcodeData.calendar : emptyYearCalendar()}
126136
colorScheme="light"
127137
fontSize={11}
128138
blockSize={10}

client/src/module/student/profile/StudentProfilePage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ export default function StudentProfilePage() {
189189
collegeTimerRef.current = setTimeout(async () => {
190190
setCollegeLoading(true);
191191
try {
192-
const res = await fetch(`https://universities.hipolabs.com/search?name=${encodeURIComponent(query)}`);
193-
const data: Array<{ name: string; country: string; "state-province": string | null }> = await res.json();
192+
const res = await api.get<Array<{ name: string; country: string; "state-province": string | null }>>(`/universities/search`, { params: { name: query } });
193+
const data = res.data;
194194
// Deduplicate by name and take first 8
195195
const seen = new Set<string>();
196196
const suggestions: CollegeSuggestion[] = [];
@@ -1013,7 +1013,7 @@ export default function StudentProfilePage() {
10131013
<motion.div custom={7} variants={fadeInUp} initial="hidden" animate="visible">
10141014
<ContributionGraphs
10151015
githubUsername={form.githubUrl ? form.githubUrl.split("github.com/").pop()?.replace(/\/$/,"") : undefined}
1016-
leetcodeUsername={form.leetcodeUrl ? form.leetcodeUrl.split("leetcode.com/").pop()?.replace(/\/$/,"") : undefined}
1016+
leetcodeUsername={form.leetcodeUrl ? form.leetcodeUrl.split("leetcode.com/").pop()?.replace(/^\/?u\//, "").replace(/\/$/,"") : undefined}
10171017
showPrompts
10181018
/>
10191019
</motion.div>

server/package-lock.json

Lines changed: 82 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

server/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"helmet": "^8.1.0",
4646
"isomorphic-dompurify": "^3.0.0",
4747
"jsonwebtoken": "^9.0.3",
48+
"morgan": "^1.10.1",
4849
"multer": "^2.0.2",
4950
"node-cron": "^4.2.1",
5051
"openai": "^6.27.0",
@@ -63,6 +64,7 @@
6364
"@types/dompurify": "^3.0.5",
6465
"@types/express": "^5.0.6",
6566
"@types/jsonwebtoken": "^9.0.10",
67+
"@types/morgan": "^1.9.10",
6668
"@types/multer": "^2.0.0",
6769
"@types/node-cron": "^3.0.11",
6870
"@types/pdf-parse": "^1.1.5",

server/src/database/prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ model user {
172172
linkedinUrl String?
173173
githubUrl String?
174174
portfolioUrl String?
175+
leetcodeUrl String?
175176
location String?
176177
jobStatus String?
177178
projects Json @default("[]")

server/src/index.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import cookieParser from "cookie-parser";
66
import path from "path";
77
import { fileURLToPath } from "url";
88
import helmet from "helmet";
9+
import morgan from "morgan";
910
import rateLimit from "express-rate-limit";
1011
import { authRouter } from "./module/auth/auth.routes.js";
1112
import { jobRouter } from "./module/job/job.routes.js";
@@ -35,6 +36,7 @@ import { internshipRouter } from "./module/internship/internship.routes.js";
3536
import { campusDriveRouter } from "./module/campus-drive/campus-drive.routes.js";
3637
import { badgeRouter } from "./module/badge/badge.routes.js";
3738
import { leetcodeRouter } from "./module/leetcode/leetcode.routes.js";
39+
import { universityRouter } from "./module/university/university.routes.js";
3840
import { errorMiddleware } from "./middleware/error.middleware.js";
3941
import { prisma } from "./database/db.js";
4042
import { initServiceProviders } from "./lib/ai-provider-registry.js";
@@ -110,6 +112,11 @@ app.use((req, _res, next) => {
110112
next();
111113
});
112114

115+
// ── HTTP request logging (dev only) ──
116+
if (process.env["NODE_ENV"] !== "production") {
117+
app.use(morgan("dev"));
118+
}
119+
113120
// ── Rate limiters ──
114121
const globalLimiter = rateLimit({
115122
windowMs: 15 * 60 * 1000,
@@ -163,6 +170,7 @@ app.use("/api/internships", internshipRouter);
163170
app.use("/api/campus-drives", campusDriveRouter);
164171
app.use("/api/badges", badgeRouter);
165172
app.use("/api/leetcode", leetcodeRouter);
173+
app.use("/api/universities", universityRouter);
166174

167175
// Public external jobs endpoint (no auth)
168176
const publicAdminController = new AdminController(new AdminService());

server/src/module/auth/auth.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ export class AuthService {
261261
linkedinUrl: true,
262262
githubUrl: true,
263263
portfolioUrl: true,
264+
leetcodeUrl: true,
264265
jobStatus: true,
265266
isProfilePublic: true,
266267
projects: true,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Router } from "express";
2+
import { searchUniversities } from "./university.service.js";
3+
4+
export const universityRouter = Router();
5+
6+
// GET /api/universities/search?name=...
7+
universityRouter.get("/search", async (req, res) => {
8+
const name = String(req.query["name"] || "").trim();
9+
if (name.length < 2) {
10+
res.json([]);
11+
return;
12+
}
13+
14+
try {
15+
const results = await searchUniversities(name);
16+
res.json(results);
17+
} catch {
18+
res.status(502).json({ message: "University search unavailable" });
19+
}
20+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
interface University {
2+
name: string;
3+
country: string;
4+
"state-province": string | null;
5+
}
6+
7+
const DATA_URL =
8+
"https://raw.githubusercontent.com/Hipo/university-domains-list/master/world_universities_and_domains.json";
9+
10+
let cache: University[] | null = null;
11+
let loadingPromise: Promise<University[]> | null = null;
12+
13+
async function loadData(): Promise<University[]> {
14+
if (cache) return cache;
15+
if (loadingPromise) return loadingPromise;
16+
17+
loadingPromise = fetch(DATA_URL)
18+
.then((res) => {
19+
if (!res.ok) throw new Error(`Failed to fetch university data: ${res.status}`);
20+
return res.json() as Promise<University[]>;
21+
})
22+
.then((data) => {
23+
cache = data;
24+
loadingPromise = null;
25+
return data;
26+
})
27+
.catch((err) => {
28+
loadingPromise = null;
29+
throw err;
30+
});
31+
32+
return loadingPromise;
33+
}
34+
35+
export async function searchUniversities(query: string, limit = 8): Promise<University[]> {
36+
const data = await loadData();
37+
const q = query.toLowerCase();
38+
39+
const seen = new Set<string>();
40+
const results: University[] = [];
41+
42+
for (const u of data) {
43+
const key = u.name.toLowerCase();
44+
if (!key.includes(q)) continue;
45+
if (seen.has(key)) continue;
46+
seen.add(key);
47+
results.push({ name: u.name, country: u.country, "state-province": u["state-province"] });
48+
if (results.length >= limit) break;
49+
}
50+
51+
return results;
52+
}

0 commit comments

Comments
 (0)