Skip to content

Commit 0d85ce8

Browse files
Merge pull request #316 from Advik-Gupta/prod
Course List Context
2 parents 438f17e + 68dc7ab commit 0d85ce8

17 files changed

Lines changed: 463 additions & 458 deletions

File tree

src/app/api/course-list/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { NextResponse } from "next/server";
22
import { connectToDatabase } from "@/lib/mongoose";
3-
import { Course } from "@/db/papers";
3+
import { Course } from "@/db/course";
44

55
export const dynamic = "force-dynamic";
66

@@ -14,7 +14,7 @@ export async function GET() {
1414
console.error(error);
1515
return NextResponse.json(
1616
{ message: "Failed to fetch courses", error },
17-
{ status: 500 }
17+
{ status: 500 },
1818
);
1919
}
2020
}

src/app/api/papers/count/route.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
import { NextResponse } from "next/server";
22
import { connectToDatabase } from "@/lib/mongoose";
3-
import Paper from "@/db/papers";
3+
import CourseCount from "@/db/course";
44

55
export const dynamic = "force-dynamic";
6+
67
export async function GET(req: Request) {
78
try {
89
await connectToDatabase();
910

10-
const { searchParams } = new URL(req.url);
11-
const subject = searchParams.get("subject");
11+
const count = await CourseCount.find().lean();
1212

13-
const filter = subject ? { subject } : {};
14-
const count = await Paper.countDocuments(filter);
13+
const formatted = count.map((item) => ({
14+
name: item.name,
15+
count: item.count,
16+
}));
1517

16-
return NextResponse.json({ count }, { status: 200 });
18+
return NextResponse.json(formatted, { status: 200 });
1719
} catch (error) {
1820
return NextResponse.json(
19-
{ message: "Failed to fetch papers", error },
21+
{ message: "Failed to fetch course counts", error },
2022
{ status: 500 },
2123
);
2224
}

src/app/layout.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import type { Metadata } from "next";
77
import Navbar from "@/components/Navbar";
88
import Footer from "@/components/Footer";
99
import ChildrenWrapper from "@/components/ChildrenWrapper";
10+
import { CoursesProvider } from "@/context/courseContext";
11+
1012
export const metadata: Metadata = {
1113
metadataBase: new URL("https://papers.codechefvit.com/"),
1214
title: "Papers by CodeChef-VIT | Explore VIT Previous Year Question Papers",
@@ -119,9 +121,11 @@ export default function RootLayout({
119121
>
120122
<Toaster position="top-right" reverseOrder={false} />
121123
<div className="bg-[#F3F5FF] dark:bg-[#070114]">
122-
<Navbar />
123-
<ChildrenWrapper>{children}</ChildrenWrapper>
124-
<Footer />
124+
<CoursesProvider>
125+
<Navbar />
126+
<ChildrenWrapper>{children}</ChildrenWrapper>
127+
<Footer />
128+
</CoursesProvider>
125129
</div>
126130
</ThemeProvider>
127131
</body>

src/app/pinned/page.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,23 @@ import { useState } from "react";
88

99
const Pinned = () => {
1010
const [displayPapers, setDisplayPapers] = useState<IUpcomingPaper[]>([]);
11+
1112
return (
1213
<div id="pinned" className="mt-5 flex flex-col justify-between">
1314
<h1 className="mx-auto my-8 hidden text-center font-vipnabd text-3xl font-extrabold md:block">
1415
Pinned Subjects
1516
</h1>
1617
<div className="mb-3 flex w-full flex-col items-center gap-2 px-6">
1718
<div className="w-full">
18-
<SearchBar type="pinned" displayPapers = {displayPapers.length > 0} />
19+
<SearchBar type="pinned" displayPapers={displayPapers.length > 0} />
1920
</div>
2021
</div>
2122
<div className="min-h-[40vh]">
22-
<PinnedPapersCarousel carouselType="users" displayPapers = {displayPapers} setDisplayPapers = {setDisplayPapers} />
23+
<PinnedPapersCarousel
24+
carouselType="users"
25+
displayPapers={displayPapers}
26+
setDisplayPapers={setDisplayPapers}
27+
/>
2328
</div>
2429
{/* <div className="mt-6 flex w-full items-center justify-center">
2530
<p>You can pin upto 8 Subjects here</p>

src/app/request/page.tsx

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import UpcomingPaper from "../../components/UpcomingPaper";
1919
import toast from "react-hot-toast";
2020
import { Search } from "lucide-react";
2121
import SkeletonPaperCard from "@/components/SkeletonPaperCard";
22+
import { useCourses } from "@/context/courseContext";
2223

2324
type Course = {
2425
name?: string | null;
@@ -37,23 +38,11 @@ export default function PaperRequest() {
3738
const suggestionsRef = useRef<HTMLUListElement | null>(null);
3839
const [displayPapers, setDisplayPapers] = useState<IUpcomingPaper[]>([]);
3940
const [isLoading, setIsLoading] = useState(true);
41+
const { courses, loading, error, refetch } = useCourses();
4042

4143
useEffect(() => {
42-
async function fetchSubjects() {
43-
try {
44-
const response = await axios.get<Course[]>(`/api/course-list`);
45-
const courses: Course[] = response.data;
46-
const names = courses
47-
.map((course) => course.name ?? course.courseName ?? course.title)
48-
.filter(Boolean) as string[];
49-
50-
setSubjects(names);
51-
} catch (err) {
52-
console.error("Error fetching subjects:", err);
53-
}
54-
}
55-
void fetchSubjects();
56-
}, []);
44+
setSubjects(courses.map((course) => course.name));
45+
}, [courses]);
5746

5847
useEffect(() => {
5948
async function fetchPapers() {
@@ -138,7 +127,7 @@ export default function PaperRequest() {
138127
loading: "Submitting your request...",
139128
success: "Your paper request was submitted successfully",
140129
error: "Failed to submit your request. Please try again later.",
141-
}
130+
},
142131
);
143132

144133
setSearchText("");

src/components/CatalogueContent.tsx

Lines changed: 75 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import { useSearchParams } from "next/navigation";
44
import { useCallback, useEffect, useState } from "react";
55
import axios, { type AxiosError } from "axios";
66
import { Button } from "@/components/ui/button";
7-
import {
8-
type IPaper,
9-
type Filters,
10-
type StoredSubjects
11-
} from "@/interface";
7+
import { type IPaper, type Filters, type StoredSubjects } from "@/interface";
128
import Card from "./Card";
139
import { useRouter } from "next/navigation";
1410
import Loader from "./ui/loader";
@@ -17,7 +13,7 @@ import Error from "./Error";
1713
import { Filter } from "lucide-react";
1814
import { Sheet, SheetContent, SheetTrigger } from "./ui/sheet";
1915
import { Pin } from "lucide-react";
20-
import SearchBarChild from "./Searchbar/searchbar-child"
16+
import SearchBarChild from "./Searchbar/searchbar-child";
2117
import Link from "next/link";
2218
import { usePathname } from "next/navigation";
2319
import {
@@ -28,6 +24,7 @@ import {
2824
import type { ICourses } from "@/interface";
2925
import JSZip from "jszip";
3026
import { toast } from "react-hot-toast";
27+
import { useCourses } from "@/context/courseContext";
3128

3229
const CatalogueContent = () => {
3330
const router = useRouter();
@@ -54,6 +51,8 @@ const CatalogueContent = () => {
5451
const [appliedFilters, setAppliedFilters] = useState<boolean>(false);
5552
const [pinned, setPinned] = useState<boolean>(false);
5653
const [relatedSubjects, setRelatedSubjects] = useState<string[]>([]);
54+
const { courses } = useCourses();
55+
5756
// Fetch related subjects when subject changes
5857
useEffect(() => {
5958
if (!subject) return;
@@ -80,31 +79,11 @@ const CatalogueContent = () => {
8079
void fetchRelatedSubjects();
8180
}, [subject]);
8281

83-
useEffect(() => {
84-
if (pathname !== "/catalogue") return;
85-
86-
const getSubjects = async () => {
87-
try {
88-
const res = await fetch("/api/course-list");
89-
if (!res.ok) return;
90-
91-
const json: unknown = await res.json();
92-
93-
if (Array.isArray(json)) {
94-
const filtered = json
95-
.filter((item): item is ICourses => typeof item === "object" && item !== null && "name" in item)
96-
.map(item => item.name);
97-
setSubjects(filtered);
98-
} else {
99-
console.error("Invalid data returned from API:", json);
100-
}
101-
} catch (err) {
102-
console.error("Failed to fetch courses", err);
103-
}
104-
};
105-
106-
void getSubjects();
107-
}, [pathname]);
82+
useEffect(() => {
83+
if (pathname !== "/catalogue") return;
84+
const filteredSubjects = courses.map((course) => course.name);
85+
setSubjects(filteredSubjects);
86+
}, [pathname, courses]);
10887
// Set initial state from searchParams on client-side mount
10988
useEffect(() => {
11089
setIsMounted(true);
@@ -233,7 +212,7 @@ const CatalogueContent = () => {
233212
const uniquePapers = Array.from(
234213
new Set(selectedPapers.map((paper) => paper._id)),
235214
).map((id) => selectedPapers.find((paper) => paper._id === id)) as IPaper[];
236-
if(!uniquePapers){
215+
if (!uniquePapers) {
237216
toast.error("No papers selected for download.");
238217
}
239218
for (const paper of uniquePapers) {
@@ -247,7 +226,7 @@ const CatalogueContent = () => {
247226
console.error(`Failed to fetch ${paper.final_url}`, err);
248227
}
249228
}
250-
229+
251230
const zipBlob = await zip.generateAsync({ type: "blob" });
252231
const url = URL.createObjectURL(zipBlob);
253232
const a = document.createElement("a");
@@ -257,7 +236,7 @@ const CatalogueContent = () => {
257236
a.click();
258237
a.remove();
259238
URL.revokeObjectURL(url);
260-
toast.success("Download Initiated")
239+
toast.success("Download Initiated");
261240
}, [selectedPapers]);
262241

263242
const handleApplyFilters = useCallback(
@@ -342,68 +321,72 @@ const CatalogueContent = () => {
342321

343322
return (
344323
<div className="relative flex min-h-screen justify-center p-0 md:justify-normal">
345-
{papers.length > 0 && <div className="hidden !w-[22%] min-w-[22%] max-w-[22%] flex-shrink-0 md:block">
346-
<SideBar
347-
filtersNotPulled={filtersNotPulled}
348-
loading={loading}
349-
selectedExams={selectedExams}
350-
selectedSlots={selectedSlots}
351-
selectedYears={selectedYears}
352-
selectedSemesters={selectedSemesters}
353-
selectedCampuses={selectedCampuses}
354-
selectedAnswerKeyIncluded={selectedAnswerKeyIncluded}
355-
noAppliedFilters={noAppliedFilters}
356-
handleApplyFilters={handleApplyFilters}
357-
handleSelectAll={handleSelectAll}
358-
handleDeselectAll={handleDeselectAll}
359-
selectedPapers={selectedPapers}
360-
subject={subject}
361-
filterOptions={filterOptions}
362-
handleDownloadSelected={handleDownloadSelected}
363-
closeFilters={closeFilters}
364-
/>
365-
</div>}
324+
{papers.length > 0 && (
325+
<div className="hidden !w-[22%] min-w-[22%] max-w-[22%] flex-shrink-0 md:block">
326+
<SideBar
327+
filtersNotPulled={filtersNotPulled}
328+
loading={loading}
329+
selectedExams={selectedExams}
330+
selectedSlots={selectedSlots}
331+
selectedYears={selectedYears}
332+
selectedSemesters={selectedSemesters}
333+
selectedCampuses={selectedCampuses}
334+
selectedAnswerKeyIncluded={selectedAnswerKeyIncluded}
335+
noAppliedFilters={noAppliedFilters}
336+
handleApplyFilters={handleApplyFilters}
337+
handleSelectAll={handleSelectAll}
338+
handleDeselectAll={handleDeselectAll}
339+
selectedPapers={selectedPapers}
340+
subject={subject}
341+
filterOptions={filterOptions}
342+
handleDownloadSelected={handleDownloadSelected}
343+
closeFilters={closeFilters}
344+
/>
345+
</div>
346+
)}
366347

367348
<div className="w-full">
368-
{papers.length > 0 && <Sheet>
369-
<SheetTrigger className="mx-8 mt-8 block md:hidden">
370-
<Button
371-
variant="outline"
372-
className="flex gap-2 border-2 border-black font-sans font-semibold hover:bg-slate-800 hover:text-white dark:border-[#434dba] dark:hover:border-white dark:hover:bg-slate-900"
349+
{papers.length > 0 && (
350+
<Sheet>
351+
<SheetTrigger className="mx-8 mt-8 block md:hidden">
352+
<Button
353+
variant="outline"
354+
className="flex gap-2 border-2 border-black font-sans font-semibold hover:bg-slate-800 hover:text-white dark:border-[#434dba] dark:hover:border-white dark:hover:bg-slate-900"
355+
>
356+
<Filter size={18} />
357+
Add Filters
358+
</Button>
359+
</SheetTrigger>
360+
<SheetContent
361+
side={"left"}
362+
className="m-0 bg-[#f3f5ff] p-0 pt-4 dark:bg-[#070114]"
373363
>
374-
<Filter size={18} />
375-
Add Filters
376-
</Button>
377-
</SheetTrigger>
378-
<SheetContent
379-
side={"left"}
380-
className="m-0 bg-[#f3f5ff] p-0 pt-4 dark:bg-[#070114]"
381-
>
382-
<SideBar
383-
filtersNotPulled={filtersNotPulled}
384-
loading={loading}
385-
selectedExams={selectedExams}
386-
selectedSlots={selectedSlots}
387-
selectedYears={selectedYears}
388-
selectedSemesters={selectedSemesters}
389-
selectedCampuses={selectedCampuses}
390-
selectedAnswerKeyIncluded={selectedAnswerKeyIncluded}
391-
noAppliedFilters={noAppliedFilters}
392-
handleApplyFilters={handleApplyFilters}
393-
handleSelectAll={handleSelectAll}
394-
handleDeselectAll={handleDeselectAll}
395-
selectedPapers={selectedPapers}
396-
subject={subject}
397-
filterOptions={filterOptions}
398-
handleDownloadSelected={handleDownloadSelected}
399-
closeFilters={closeFilters}
400-
/>
401-
</SheetContent>
402-
</Sheet>}
364+
<SideBar
365+
filtersNotPulled={filtersNotPulled}
366+
loading={loading}
367+
selectedExams={selectedExams}
368+
selectedSlots={selectedSlots}
369+
selectedYears={selectedYears}
370+
selectedSemesters={selectedSemesters}
371+
selectedCampuses={selectedCampuses}
372+
selectedAnswerKeyIncluded={selectedAnswerKeyIncluded}
373+
noAppliedFilters={noAppliedFilters}
374+
handleApplyFilters={handleApplyFilters}
375+
handleSelectAll={handleSelectAll}
376+
handleDeselectAll={handleDeselectAll}
377+
selectedPapers={selectedPapers}
378+
subject={subject}
379+
filterOptions={filterOptions}
380+
handleDownloadSelected={handleDownloadSelected}
381+
closeFilters={closeFilters}
382+
/>
383+
</SheetContent>
384+
</Sheet>
385+
)}
403386

404-
<div className="p-7 flex flex-col items-start">
405-
<div className="md:hidden flex flex-col items-start w-full mb-8">
406-
<SearchBarChild initialSubjects={subjects} />
387+
<div className="flex flex-col items-start p-7">
388+
<div className="mb-8 flex w-full flex-col items-start md:hidden">
389+
<SearchBarChild initialSubjects={courses} />
407390
</div>
408391
<div className="flex items-center gap-2">
409392
<div>

0 commit comments

Comments
 (0)