Skip to content

Commit aa36444

Browse files
Merge pull request #414 from Advik-Gupta/staging
Fixed pinned section order issue and added sorting button
2 parents b3c407d + 1d7fd71 commit aa36444

4 files changed

Lines changed: 170 additions & 13 deletions

File tree

src/components/CatalogueContent.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { useCourses } from "@/context/courseContext";
1818
import { FilterProvider, useFilters } from "@/context/filterContext";
1919
import EmptyState from "./ui/EmptyState";
2020
import SidebarButton from "./SidebarButton";
21+
import SortComponent from "./ui/sorting";
2122

2223
const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
2324
const [isMounted, setIsMounted] = useState(false);
@@ -27,6 +28,7 @@ const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
2728
const [pinned, setPinned] = useState<boolean>(false);
2829
const [relatedSubjects, setRelatedSubjects] = useState<string[]>([]);
2930
const { courses } = useCourses();
31+
const [sortOption, setSortOption] = useState<"asc" | "desc" | "none">("none");
3032

3133
// Use filter context
3234
const {
@@ -175,6 +177,31 @@ const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
175177
void fetchPapers();
176178
}, [subject, isMounted, setPapers, setFilterOptions]);
177179

180+
useEffect(() => {
181+
if (!papers.length) return;
182+
183+
const filtered = [...papers];
184+
185+
if (sortOption === "asc") {
186+
filtered.sort((a, b) => a.year.localeCompare(b.year));
187+
} else if (sortOption === "desc") {
188+
filtered.sort((a, b) => b.year.localeCompare(a.year));
189+
}
190+
191+
setFilteredPapers(filtered);
192+
}, [
193+
papers,
194+
selectedExams,
195+
selectedSlots,
196+
selectedYears,
197+
selectedSemesters,
198+
selectedCampuses,
199+
selectedAnswerKeyIncluded,
200+
sortOption,
201+
setFilteredPapers,
202+
setAppliedFilters,
203+
]);
204+
178205
useEffect(() => {
179206
if (!papers.length) return;
180207

@@ -206,6 +233,11 @@ const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
206233
answerkeyCondition
207234
);
208235
});
236+
if (sortOption === "asc") {
237+
filtered.sort((a, b) => a.year.localeCompare(b.year));
238+
} else if (sortOption === "desc") {
239+
filtered.sort((a, b) => b.year.localeCompare(a.year));
240+
}
209241
setFilteredPapers(filtered);
210242
setAppliedFilters(
211243
selectedExams.length > 0 ||
@@ -285,6 +317,10 @@ const CatalogueContentInner = ({ subject }: { subject: string | null }) => {
285317

286318
{/* Select/Deselect/Download All Buttons */}
287319
<div className="mb-8 flex w-full items-center justify-end gap-4">
320+
<SortComponent
321+
onSortChange={setSortOption}
322+
currentSort={sortOption}
323+
/>
288324
<SidebarButton onClick={handleSelectAll}>Select All</SidebarButton>
289325
<SidebarButton onClick={handleDeselectAll}>
290326
Deselect All

src/components/PinnedPapersCarousel.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,16 @@ function PinnedPapersCarousel() {
8484

8585
const allDisplayPapers = [...fetchedPapers, ...missingSubjects];
8686

87+
allDisplayPapers.sort((a, b) => {
88+
const aIndex = storedSubjects.indexOf(a.subject);
89+
const bIndex = storedSubjects.indexOf(b.subject);
90+
91+
return (
92+
(aIndex === -1 ? Number.MAX_SAFE_INTEGER : aIndex) -
93+
(bIndex === -1 ? Number.MAX_SAFE_INTEGER : bIndex)
94+
);
95+
});
96+
8797
setDisplayPapers(allDisplayPapers);
8898
} catch (error) {
8999
console.error("Failed to fetch papers:", error);
@@ -126,6 +136,16 @@ function PinnedPapersCarousel() {
126136

127137
const allDisplayPapers = [...fetchedPapers, ...missingSubjects];
128138

139+
allDisplayPapers.sort((a, b) => {
140+
const aIndex = storedSubjects.indexOf(a.subject);
141+
const bIndex = storedSubjects.indexOf(b.subject);
142+
143+
return (
144+
(aIndex === -1 ? Number.MAX_SAFE_INTEGER : aIndex) -
145+
(bIndex === -1 ? Number.MAX_SAFE_INTEGER : bIndex)
146+
);
147+
});
148+
129149
setDisplayPapers(allDisplayPapers);
130150
} catch (error) {
131151
console.error("Failed to fetch papers:", error);

src/components/ui/sorting.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React from "react";
2+
import { ArrowUpDown, ArrowUp, ArrowDown } from "lucide-react";
3+
import { Button } from "@/components/ui/button";
4+
import {
5+
DropdownMenu,
6+
DropdownMenuContent,
7+
DropdownMenuItem,
8+
DropdownMenuTrigger,
9+
} from "@/components/ui/dropdown-menu";
10+
11+
type SortOption = "asc" | "desc" | "none";
12+
13+
interface SortComponentProps {
14+
onSortChange: (sortOption: SortOption) => void;
15+
currentSort: SortOption;
16+
}
17+
18+
const SortComponent: React.FC<SortComponentProps> = ({
19+
onSortChange,
20+
currentSort,
21+
}) => {
22+
const handleSortChange = (option: SortOption) => {
23+
onSortChange(option);
24+
};
25+
26+
const getSortIcon = () => {
27+
if (currentSort === "asc") return <ArrowUp className="h-4 w-4" />;
28+
if (currentSort === "desc") return <ArrowDown className="h-4 w-4" />;
29+
return <ArrowUpDown className="h-4 w-4" />;
30+
};
31+
32+
const getSortLabel = () => {
33+
if (currentSort === "asc") return "Year (Old to New)";
34+
if (currentSort === "desc") return "Year (New to Old)";
35+
return "Sort by Year";
36+
};
37+
38+
return (
39+
<DropdownMenu>
40+
<DropdownMenuTrigger asChild>
41+
<Button
42+
variant="outline"
43+
className="flex gap-2 border-2 border-black bg-white font-sans font-semibold hover:bg-slate-800 hover:text-white dark:border-[#434dba] dark:bg-[#070114] dark:text-white dark:hover:border-white dark:hover:bg-slate-900"
44+
>
45+
{getSortIcon()}
46+
<span className="hidden sm:inline">{getSortLabel()}</span>
47+
<span className="sm:hidden">Sort</span>
48+
</Button>
49+
</DropdownMenuTrigger>
50+
<DropdownMenuContent
51+
align="end"
52+
className="w-48 border-2 border-black bg-white dark:border-[#434dba] dark:bg-[#0a0118]"
53+
>
54+
<DropdownMenuItem
55+
onClick={() => handleSortChange("desc")}
56+
className={`cursor-pointer font-semibold ${
57+
currentSort === "desc" ? "bg-violet-100 dark:bg-violet-900/30" : ""
58+
}`}
59+
>
60+
<ArrowDown className="mr-2 h-4 w-4" />
61+
Year (New to Old)
62+
</DropdownMenuItem>
63+
<DropdownMenuItem
64+
onClick={() => handleSortChange("asc")}
65+
className={`cursor-pointer font-semibold ${
66+
currentSort === "asc" ? "bg-violet-100 dark:bg-violet-900/30" : ""
67+
}`}
68+
>
69+
<ArrowUp className="mr-2 h-4 w-4" />
70+
Year (Old to New)
71+
</DropdownMenuItem>
72+
<DropdownMenuItem
73+
onClick={() => handleSortChange("none")}
74+
className={`cursor-pointer font-semibold ${
75+
currentSort === "none" ? "bg-violet-100 dark:bg-violet-900/30" : ""
76+
}`}
77+
>
78+
<ArrowUpDown className="mr-2 h-4 w-4" />
79+
Default Order
80+
</DropdownMenuItem>
81+
</DropdownMenuContent>
82+
</DropdownMenu>
83+
);
84+
};
85+
86+
export default SortComponent;

src/context/filterContext.tsx

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
"use client";
22

3-
import React, { createContext, useContext, useState, useCallback, type ReactNode } from "react";
3+
import React, {
4+
createContext,
5+
useContext,
6+
useState,
7+
useCallback,
8+
type ReactNode,
9+
} from "react";
410
import { useRouter, useSearchParams } from "next/navigation";
511
import { type IPaper, type Filters } from "@/interface";
612
import JSZip from "jszip";
@@ -27,25 +33,21 @@ interface FilterState {
2733
}
2834

2935
interface FilterActions {
30-
3136
setSelectedExams: (exams: string[]) => void;
3237
setSelectedSlots: (slots: string[]) => void;
3338
setSelectedYears: (years: string[]) => void;
3439
setSelectedSemesters: (semesters: string[]) => void;
3540
setSelectedCampuses: (campuses: string[]) => void;
3641
setSelectedAnswerKeyIncluded: (included: boolean) => void;
3742

38-
3943
setPapers: (papers: IPaper[]) => void;
4044
setFilteredPapers: (papers: IPaper[]) => void;
4145
setFilterOptions: (options: Filters | undefined) => void;
4246

43-
4447
setFiltersPulled: (pulled: boolean) => void;
4548
setAppliedFilters: (applied: boolean) => void;
4649
setCurrentPage: (page: number) => void;
4750

48-
4951
handleApplyFilters: (
5052
exams: string[],
5153
slots: string[],
@@ -62,7 +64,6 @@ interface FilterActions {
6264
noAppliedFilters: () => void;
6365
closeFilters: () => void;
6466

65-
6667
paginatedPapers: IPaper[];
6768
totalPages: number;
6869
}
@@ -76,15 +77,19 @@ interface FilterProviderProps {
7677
subject: string | null;
7778
}
7879

79-
export const FilterProvider: React.FC<FilterProviderProps> = ({ children, subject }) => {
80+
export const FilterProvider: React.FC<FilterProviderProps> = ({
81+
children,
82+
subject,
83+
}) => {
8084
const router = useRouter();
8185

8286
const [selectedExams, setSelectedExams] = useState<string[]>([]);
8387
const [selectedSlots, setSelectedSlots] = useState<string[]>([]);
8488
const [selectedYears, setSelectedYears] = useState<string[]>([]);
8589
const [selectedSemesters, setSelectedSemesters] = useState<string[]>([]);
8690
const [selectedCampuses, setSelectedCampuses] = useState<string[]>([]);
87-
const [selectedAnswerKeyIncluded, setSelectedAnswerKeyIncluded] = useState<boolean>(false);
91+
const [selectedAnswerKeyIncluded, setSelectedAnswerKeyIncluded] =
92+
useState<boolean>(false);
8893

8994
const [papers, setPapers] = useState<IPaper[]>([]);
9095
const [filteredPapers, setFilteredPapers] = useState<IPaper[]>([]);
@@ -128,7 +133,6 @@ export const FilterProvider: React.FC<FilterProviderProps> = ({ children, subjec
128133

129134
const searchParams = useSearchParams();
130135
const handleDownloadSelected = useCallback(async () => {
131-
132136
if (selectedPapers.length === 0) {
133137
toast.error("No papers selected for download.");
134138
return;
@@ -196,11 +200,20 @@ export const FilterProvider: React.FC<FilterProviderProps> = ({ children, subjec
196200
setSelectedAnswerKeyIncluded(anskey);
197201
setCurrentPage(1);
198202
},
199-
[router, subject, setSelectedExams, setSelectedSlots, setSelectedYears, setSelectedCampuses, setSelectedSemesters, setSelectedAnswerKeyIncluded, setCurrentPage],
203+
[
204+
router,
205+
subject,
206+
setSelectedExams,
207+
setSelectedSlots,
208+
setSelectedYears,
209+
setSelectedCampuses,
210+
setSelectedSemesters,
211+
setSelectedAnswerKeyIncluded,
212+
setCurrentPage,
213+
],
200214
);
201215

202-
203-
const paginatedPapers = (appliedFilters ? filteredPapers : papers).slice(
216+
const paginatedPapers = filteredPapers.slice(
204217
(currentPage - 1) * papersPerPage,
205218
currentPage * papersPerPage,
206219
);
@@ -251,7 +264,9 @@ export const FilterProvider: React.FC<FilterProviderProps> = ({ children, subjec
251264
totalPages,
252265
};
253266

254-
return <FilterContext.Provider value={value}>{children}</FilterContext.Provider>;
267+
return (
268+
<FilterContext.Provider value={value}>{children}</FilterContext.Provider>
269+
);
255270
};
256271

257272
export const useFilters = (): FilterContextType => {

0 commit comments

Comments
 (0)