Skip to content

Commit a6baf37

Browse files
Merge pull request #217 from Advik-Gupta/starredPapers
Created new component to display users starred papers
2 parents 178ec0a + 6fb6e87 commit a6baf37

File tree

6 files changed

+125
-17
lines changed

6 files changed

+125
-17
lines changed

src/app/api/papers/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export async function GET(req: NextRequest) {
2929
if (papers.length === 0) {
3030
return NextResponse.json(
3131
{ message: "No papers found for the specified subject" },
32-
{ status: 404 },
32+
{ status: 200 },
3333
);
3434
}
3535

src/app/api/upcoming-papers/route.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@ export async function GET() {
1818
{ status: 404 },
1919
);
2020
}
21-
const nextSlot = String.fromCharCode(slot.charCodeAt(0) + 1)
22-
const correspondingSlots = [slot + "1", slot + "2", nextSlot + "1", nextSlot + "2"];
21+
const nextSlot = String.fromCharCode(slot.charCodeAt(0) + 1);
22+
const correspondingSlots = [
23+
slot + "1",
24+
slot + "2",
25+
nextSlot + "1",
26+
nextSlot + "2",
27+
];
2328
const selectedSubjects = await UpcomingSubject.find({
2429
slots: { $in: correspondingSlots },
2530
});

src/app/api/user-papers/route.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { NextResponse } from "next/server";
2+
import { connectToDatabase } from "@/lib/mongoose";
3+
import Paper from "@/db/papers";
4+
5+
export const dynamic = "force-dynamic";
6+
7+
export async function POST(req: Request) {
8+
try {
9+
await connectToDatabase();
10+
const body = await req.json();
11+
12+
const subjects: string[] = body;
13+
14+
const usersPapers = await Paper.find({
15+
subject: { $in: subjects },
16+
});
17+
18+
const transformedPapers = usersPapers.reduce((acc, paper) => {
19+
const existing = acc.find((item) => item.subject === paper.subject);
20+
21+
if (existing) {
22+
existing.slots.push(paper.slot);
23+
} else {
24+
acc.push({ subject: paper.subject, slots: [paper.slot] });
25+
}
26+
27+
return acc;
28+
}, []);
29+
30+
// check duplicates
31+
const seenSubjects = new Set();
32+
const uniquePapers = transformedPapers.filter((paper) => {
33+
if (seenSubjects.has(paper.subject)) return false;
34+
seenSubjects.add(paper.subject);
35+
return true;
36+
});
37+
38+
return NextResponse.json(uniquePapers, {
39+
status: 200,
40+
});
41+
} catch (error) {
42+
console.error("Error fetching papers:", error);
43+
return NextResponse.json(
44+
{
45+
error: "Failed to fetch papers.",
46+
},
47+
{ status: 500 },
48+
);
49+
}
50+
}

src/components/StoredPapers.tsx renamed to src/components/PapersCarousel.tsx

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import {
1515
import Autoplay from "embla-carousel-autoplay";
1616
import { chunkArray } from "@/util/utils";
1717

18-
function StoredPapers() {
18+
function PapersCarousel({
19+
carouselType,
20+
}: {
21+
carouselType: "users" | "default";
22+
}) {
1923
const [displayPapers, setDisplayPapers] = useState<IUpcomingPaper[]>([]);
2024
const [isLoading, setIsLoading] = useState(true);
2125
const [chunkSize, setChunkSize] = useState<number>(4);
@@ -29,6 +33,17 @@ function StoredPapers() {
2933
}
3034
};
3135

36+
localStorage.setItem(
37+
"userSubjects",
38+
JSON.stringify([
39+
"Information Security [CBS3002]",
40+
"Foundations of Data Analytics [BCSE351E]",
41+
"Design and Analysis of Algorithms [MCSE502L]",
42+
"Complex Variables and Linear Algebra [BMAT201L]",
43+
"Differential Equations and Transforms [BMAT102L]",
44+
]),
45+
);
46+
3247
handleResize();
3348
window.addEventListener("resize", handleResize);
3449

@@ -43,10 +58,18 @@ function StoredPapers() {
4358
async function fetchPapers() {
4459
try {
4560
setIsLoading(true);
46-
const response = await axios.get<IUpcomingPaper[]>(
47-
"/api/upcoming-papers",
48-
);
49-
setDisplayPapers(response.data);
61+
if (carouselType === "users") {
62+
const storedSubjects = JSON.parse(
63+
localStorage.getItem("userSubjects"),
64+
);
65+
const response = await axios.post("/api/user-papers", storedSubjects);
66+
setDisplayPapers(response.data);
67+
} else {
68+
const response = await axios.get<IUpcomingPaper[]>(
69+
"/api/upcoming-papers",
70+
);
71+
setDisplayPapers(response.data);
72+
}
5073
} catch (error) {
5174
console.error("Failed to fetch papers:", error);
5275
} finally {
@@ -66,7 +89,7 @@ function StoredPapers() {
6689
return (
6790
<div className="px-4">
6891
<p className="my-8 text-center font-play text-lg font-semibold">
69-
Upcoming Papers
92+
{carouselType === "users" ? "Your Papers" : "Upcoming Papers"}
7093
</p>
7194

7295
<div className="">
@@ -107,4 +130,4 @@ function StoredPapers() {
107130
);
108131
}
109132

110-
export default StoredPapers;
133+
export default PapersCarousel;

src/components/Searchbar/searchbar-child.tsx

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Search } from "lucide-react";
55
import { useRouter } from "next/navigation";
66
import { Input } from "@/components/ui/input";
77
import Fuse from "fuse.js";
8+
import axios from "axios";
89

910
function SearchBarChild({
1011
initialSubjects,
@@ -19,7 +20,26 @@ function SearchBarChild({
1920
const suggestionsRef = useRef<HTMLUListElement | null>(null);
2021
const fuzzy = new Fuse(initialSubjects);
2122

22-
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
23+
const fetchPaperQuantityByName = async (subjectName: string) => {
24+
try {
25+
const response = await axios.get("/api/papers", {
26+
params: { subject: subjectName },
27+
});
28+
29+
if (
30+
response.data.message === "No papers found for the specified subject"
31+
) {
32+
return 0;
33+
}
34+
35+
return response.data.papers.length;
36+
} catch (error) {
37+
console.error("Error fetching paper quantity:", error);
38+
return "request-error";
39+
}
40+
};
41+
42+
const handleSearchChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
2343
const text = e.target.value;
2444
setSearchText(text);
2545

@@ -32,7 +52,16 @@ function SearchBarChild({
3252
.map((item) => item.item)
3353
.slice(0, 10);
3454

35-
setSuggestions(filteredSuggestions);
55+
const suggestionsWithCount = await Promise.all(
56+
filteredSuggestions.map(async (suggestion) => {
57+
const count = await fetchPaperQuantityByName(suggestion);
58+
return count !== "request-error"
59+
? `${suggestion} (${count})`
60+
: suggestion;
61+
}),
62+
);
63+
64+
setSuggestions(suggestionsWithCount);
3665
} else {
3766
setSuggestions([]);
3867
}
@@ -62,7 +91,7 @@ function SearchBarChild({
6291
}, []);
6392

6493
return (
65-
<div className="font-play mx-auto w-full max-w-xl">
94+
<div className="mx-auto w-full max-w-xl font-play">
6695
<form
6796
onSubmit={(e) => {
6897
e.preventDefault();
@@ -78,7 +107,7 @@ function SearchBarChild({
78107
value={searchText}
79108
onChange={handleSearchChange}
80109
placeholder="Search by subject..."
81-
className={`text-md font-play rounded-lg bg-[#B2B8FF] px-4 py-6 pr-10 tracking-wider text-black shadow-sm ring-0 placeholder:text-black focus:outline-none focus:ring-0 dark:bg-[#7480FF66] dark:text-white placeholder:dark:text-white ${suggestions.length > 0 ? "rounded-b-none" : ""}`}
110+
className={`text-md rounded-lg bg-[#B2B8FF] px-4 py-6 pr-10 font-play tracking-wider text-black shadow-sm ring-0 placeholder:text-black focus:outline-none focus:ring-0 dark:bg-[#7480FF66] dark:text-white placeholder:dark:text-white ${suggestions.length > 0 ? "rounded-b-none" : ""}`}
82111
/>
83112
<button
84113
type="submit"

src/components/screens/Hero.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import React from "react";
22
import SearchBar from "../Searchbar/searchbar";
3-
import StoredPapers from "../StoredPapers";
3+
import PapersCarousel from "../PapersCarousel";
44

55
const Hero = () => {
66
return (
77
<div id="hero" className="flex flex-col justify-between">
8-
<h1 className="font-vipnabd mx-auto my-8 text-center text-3xl font-extrabold">
8+
<h1 className="mx-auto my-8 text-center font-vipnabd text-3xl font-extrabold">
99
Built by Students for Students
1010
</h1>
1111
<div className="px-6">
1212
<SearchBar />
1313
</div>
14-
<StoredPapers />
14+
<PapersCarousel carouselType="users" />
15+
<PapersCarousel carouselType="default" />
1516
{/* <div className="hidden lg:flex flex-col items-center whitespace-nowrap text-center">
1617
<h1 className="font-play text-md">Learn More</h1>
1718
<Link

0 commit comments

Comments
 (0)