Skip to content

Commit 7fe47cf

Browse files
committed
Merge branch 'staging' into starredPapers
2 parents 8ed2ecd + 4033edf commit 7fe47cf

File tree

13 files changed

+467
-224
lines changed

13 files changed

+467
-224
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { connectToDatabase } from "@/lib/mongoose";
33
import Paper from "@/db/papers";
44

55
export const dynamic = "force-dynamic";
6-
76
export async function GET(req: Request) {
87
try {
98
await connectToDatabase();

src/app/api/papers/route.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export async function GET(req: NextRequest) {
1313
const escapeRegExp = (text: string) => {
1414
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1515
};
16-
const escapedSubject = escapeRegExp(subject!);
16+
const escapedSubject = escapeRegExp(subject ?? "");
1717

1818
if (!subject) {
1919
return NextResponse.json(
@@ -28,8 +28,15 @@ export async function GET(req: NextRequest) {
2828

2929
if (papers.length === 0) {
3030
return NextResponse.json(
31-
{ message: "No papers found for the specified subject" },
32-
{ status: 404 },
31+
{
32+
papers,
33+
uniqueYears: [],
34+
uniqueSlots: [],
35+
uniqueExams: [],
36+
uniqueCampuses: [],
37+
uniqueSemesters: [],
38+
},
39+
{ status: 200 },
3340
);
3441
}
3542

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import { StoredSubjects } from "@/interface";
55

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

8+
interface TransformedPaper {
9+
subject: string;
10+
slots: string[];
11+
}
12+
813
export async function POST(req: Request) {
914
try {
1015
await connectToDatabase();

src/app/request/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import PapersPage from "@/components/screens/PapersPage";
2+
3+
export default function RequestPage() {
4+
return <PapersPage />;
5+
}

src/components/CatalogueContent.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export async function downloadFile(url: string, filename: string) {
2525
link.download = filename;
2626
link.click();
2727
window.URL.revokeObjectURL(link.href);
28-
} catch (error) {}
28+
} catch (error) { }
2929
}
3030

3131
const CatalogueContent = () => {
@@ -104,12 +104,12 @@ const CatalogueContent = () => {
104104
try {
105105
const papersResponse = await axios.get<Filters>("/api/papers", {
106106
params: { subject },
107-
});
107+
});g
108108
const data: Filters = papersResponse.data;
109109
const papersData = data.papers;
110110
setFilterOptions(data);
111111
setPapers(papersData);
112-
// Apply filters from URL params
112+
// Apply filters from URL paramsfilterOptions?
113113
const filtered = papersData.filter((paper) => {
114114
const examCondition = selectedExams.length
115115
? selectedExams.includes(paper.exam)
@@ -127,7 +127,7 @@ const CatalogueContent = () => {
127127
? selectedCampuses.includes(paper.campus)
128128
: true;
129129
const answerkeyCondition = selectedAnswerKeyIncluded
130-
? paper.answerKeyIncluded
130+
? paper.answerKeyIncluded === true
131131
: true;
132132
return (
133133
examCondition &&
@@ -146,7 +146,7 @@ const CatalogueContent = () => {
146146
setError(
147147
axios.isAxiosError(axiosError)
148148
? ((axiosError.response?.data as { message?: string })?.message ??
149-
"Error fetching papers")
149+
"Error fetching papers")
150150
: "Error fetching papers",
151151
);
152152
} finally {
@@ -177,13 +177,21 @@ const CatalogueContent = () => {
177177
);
178178

179179
const handleDownloadAll = useCallback(async () => {
180+
/* if (typeof window !== "undefined" && window.gtag) {
181+
window.gtag("event", "download_all_clicked", {
182+
event_category: "Paper Downloads",
183+
event_label: "Download All Clicked",
184+
});
185+
} */
186+
180187
for (const paper of selectedPapers) {
181188
const extension = paper.finalUrl.split(".").pop();
182189
const fileName = `${extractBracketContent(paper.subject)}-${paper.exam}-${paper.slot}-${paper.year}.${extension}`;
183190
await downloadFile(paper.finalUrl, fileName);
184191
}
185192
}, [selectedPapers]);
186193

194+
187195
const handleApplyFilters = useCallback(
188196
(
189197
exams: string[],
@@ -226,7 +234,7 @@ const CatalogueContent = () => {
226234
const campusCondition = campus.length
227235
? campus.includes(paper.campus)
228236
: true;
229-
const answerkeyCondition = anskey ? paper.answerKeyIncluded : true;
237+
const answerkeyCondition = anskey ? paper.answerKeyIncluded === true : true;
230238
return (
231239
examCondition &&
232240
slotCondition &&

src/components/PapersCarousel.tsx

Lines changed: 64 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { useEffect, useState } from "react";
44
import axios from "axios";
55
import { type IUpcomingPaper } from "@/interface";
6-
import Loader from "./ui/loader";
76
import UpcomingPaper from "./UpcomingPaper";
87
import {
98
Carousel,
@@ -14,11 +13,13 @@ import {
1413
} from "@/components/ui/carousel";
1514
import Autoplay from "embla-carousel-autoplay";
1615
import { chunkArray } from "@/util/utils";
16+
import { Skeleton } from "@/components/ui/skeleton";
1717

18+
function PapersCarousel() {
1819
function PapersCarousel() {
1920
const [displayPapers, setDisplayPapers] = useState<IUpcomingPaper[]>([]);
2021
const [isLoading, setIsLoading] = useState(true);
21-
const [chunkSize, setChunkSize] = useState<number>(4);
22+
const [chunkSize, setChunkSize] = useState<number>(4); // dynamic chunk size
2223

2324
useEffect(() => {
2425
const handleResize = () => {
@@ -29,16 +30,11 @@ function PapersCarousel() {
2930
}
3031
};
3132

32-
handleResize();
33+
handleResize(); // initialize
3334
window.addEventListener("resize", handleResize);
34-
35-
return () => {
36-
window.removeEventListener("resize", handleResize);
37-
};
35+
return () => window.removeEventListener("resize", handleResize);
3836
}, []);
3937

40-
const chunkedPapers = chunkArray(displayPapers, chunkSize);
41-
4238
useEffect(() => {
4339
async function fetchPapers() {
4440
try {
@@ -57,10 +53,7 @@ function PapersCarousel() {
5753
void fetchPapers();
5854
}, []);
5955

60-
if (isLoading) {
61-
return <Loader prop="m-10" />;
62-
}
63-
56+
const chunkedPapers = chunkArray(displayPapers, chunkSize);
6457
const plugins = [Autoplay({ delay: 8000, stopOnInteraction: true })];
6558

6659
return (
@@ -69,40 +62,66 @@ function PapersCarousel() {
6962
Upcoming Exams
7063
</p>
7164

72-
<div className="">
73-
<Carousel
74-
opts={{
75-
align: "start",
76-
loop: true,
77-
}}
78-
plugins={plugins}
79-
className="w-full"
80-
>
81-
<div className="relative mt-4 flex justify-end gap-4">
82-
<CarouselPrevious className="relative" />
83-
<CarouselNext className="relative" />
84-
</div>
85-
<CarouselContent>
86-
{chunkedPapers.map((paperGroup, index) => {
87-
return (
88-
<CarouselItem
89-
key={`carousel-item-${index}`}
90-
className="grid grid-cols-2 grid-rows-2 gap-4 md:grid-cols-4 lg:auto-rows-fr"
65+
<Carousel
66+
opts={{ align: "start", loop: true }}
67+
plugins={plugins}
68+
className="w-full"
69+
>
70+
<div className="relative mt-4 flex justify-end gap-4">
71+
<CarouselPrevious className="relative" />
72+
<CarouselNext className="relative" />
73+
</div>
74+
75+
<CarouselContent>
76+
{isLoading ? (
77+
<CarouselItem
78+
className={`grid ${
79+
chunkSize === 4
80+
? "grid-cols-2 grid-rows-2"
81+
: "grid-cols-4"
82+
} gap-4 lg:auto-rows-fr`}
83+
>
84+
{Array.from({ length: chunkSize }).map((_, idx) => (
85+
<div
86+
key={idx}
87+
className="cursor-pointer rounded-sm border-2 border-[#734DFF] bg-[#FFFFFF] text-black shadow-lg transition duration-150 ease-in-out hover:bg-[#EFEAFF] dark:border-[#36266D] dark:bg-[#171720] dark:text-white hover:dark:bg-[#262635]"
9188
>
92-
{paperGroup.map((paper, subIndex) => (
93-
<div key={subIndex} className="h-full">
94-
<UpcomingPaper
95-
subject={paper.subject}
96-
slots={paper.slots}
97-
/>
89+
<div className="border-b-2 border-[#453D60] p-2">
90+
<Skeleton className="h-6 w-24 rounded-md" />
91+
</div>
92+
<div className="flex flex-col justify-between p-4">
93+
<Skeleton className="mb-4 h-6 w-32 rounded-md" />
94+
<div className="flex gap-2">
95+
<Skeleton className="h-7 w-16 rounded-full" />
96+
<Skeleton className="h-7 w-16 rounded-full" />
9897
</div>
99-
))}
100-
</CarouselItem>
101-
);
102-
})}
103-
</CarouselContent>
104-
</Carousel>
105-
</div>
98+
</div>
99+
</div>
100+
))}
101+
</CarouselItem>
102+
) : (
103+
chunkedPapers.map((paperGroup, index) => (
104+
<CarouselItem
105+
key={`carousel-item-${index}`}
106+
className={`grid ${
107+
chunkSize === 4
108+
? "grid-cols-2 grid-rows-2"
109+
: "grid-cols-4"
110+
} gap-4 lg:auto-rows-fr`}
111+
>
112+
{paperGroup.map((paper, subIndex) => (
113+
<div key={subIndex} className="h-full">
114+
<UpcomingPaper
115+
subject={paper.subject}
116+
slots={paper.slots}
117+
/>
118+
</div>
119+
))}
120+
</CarouselItem>
121+
))
122+
)}
123+
</CarouselContent>
124+
</Carousel>
106125
</div>
107126
);
108127
}

src/components/Searchbar/searchbar-child.tsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,24 @@ function SearchBarChild({
1717
const router = useRouter();
1818
const [searchText, setSearchText] = useState("");
1919
const [suggestions, setSuggestions] = useState<string[]>([]);
20+
const [subjectCounts, setSubjectCounts] = useState<Record<string, number>>({});
2021
const suggestionsRef = useRef<HTMLUListElement | null>(null);
2122
const fuzzy = new Fuse(initialSubjects);
2223

24+
const fetchPaperCount = async (subjectName: string) => {
25+
try {
26+
const cleanSubject = subjectName.replace(/^"|"$/g, "");
27+
const encodedSubject = encodeURIComponent(cleanSubject);
28+
29+
const response = await axios.get(`/api/papers/count?subject=${encodedSubject}`);
30+
31+
return response.data.count ?? 0;
32+
} catch (error) {
33+
console.error("Error fetching count for", subjectName, error);
34+
return 0;
35+
}
36+
};
37+
2338
const handleSearchChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
2439
const text = e.target.value;
2540
setSearchText(text);
@@ -34,8 +49,24 @@ function SearchBarChild({
3449
.slice(0, 10);
3550

3651
setSuggestions(filteredSuggestions);
52+
53+
// Fetch counts in parallel for each suggestion
54+
const counts = await Promise.all(
55+
filteredSuggestions.map(async (subject) => {
56+
const count = await fetchPaperCount(subject);
57+
return { subject, count };
58+
})
59+
);
60+
61+
const countsMap = counts.reduce((acc, { subject, count }) => {
62+
acc[subject] = count;
63+
return acc;
64+
}, {} as Record<string, number>);
65+
66+
setSubjectCounts(countsMap);
3767
} else {
3868
setSuggestions([]);
69+
setSubjectCounts({});
3970
}
4071
};
4172

@@ -97,9 +128,10 @@ function SearchBarChild({
97128
<li
98129
key={index}
99130
onClick={() => handleSelectSuggestion(suggestion)}
100-
className="cursor-pointer truncate p-2 hover:bg-gray-100 dark:hover:bg-gray-800"
131+
className="flex items-center rounded cursor-pointer truncate p-2 hover:bg-gray-100 dark:hover:bg-gray-800"
101132
>
102-
{suggestion}
133+
<div id="paper_count" className="bg-[#171720] w-10 h-10 flex items-center justify-center rounded-md text-white text-sm font-semibold mr-4">{subjectCounts[suggestion] ?? "0"}</div>
134+
<span id="subject" className="text-white items-center text-sm sm:text-base tracking-wide">{suggestion}</span>
103135
</li>
104136
))}
105137
</ul>

src/components/Searchbar/searchbar.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export async function fetchSubjects() {
1010
const response = await axios.get<ICourses[]>(
1111
`${process.env.SERVER_URL}/api/course-list`,
1212
);
13+
console.log("Fetched subjects:", response.data);
1314
return response.data.map((course) => course.name);
1415
} catch (err) {
1516
console.error("Error fetching subjects:", err);

0 commit comments

Comments
 (0)