Skip to content

Commit 6a09a98

Browse files
restructure
1 parent abca4c8 commit 6a09a98

File tree

11 files changed

+466
-314
lines changed

11 files changed

+466
-314
lines changed

src/app/adminupload/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Image from "next/image";
1111
import { Trash } from "lucide-react";
1212
import Link from "next/link";
1313
import toast from "react-hot-toast";
14-
import { handleAPIError } from "../util/error";
14+
import { handleAPIError } from "../../util/error";
1515
import { ApiError } from "next/dist/server/api-utils";
1616
import { courses, slots } from "../upload/select_options";
1717

src/app/catalogue/page.tsx

Lines changed: 2 additions & 295 deletions
Original file line numberDiff line numberDiff line change
@@ -1,151 +1,7 @@
11
"use client";
22

3-
import { useSearchParams, useRouter } from "next/navigation";
4-
import { useEffect, useState } from "react";
5-
import axios, { type AxiosError } from "axios";
6-
import Cryptr from "cryptr";
3+
import CatalogueContent from "@/components/CatalogueContent";
74
import { Suspense } from "react";
8-
import Image from "next/image";
9-
import { Search, Download, Eye, Filter} from "lucide-react";
10-
import { boolean } from "zod";
11-
import {
12-
Dialog,
13-
DialogContent,
14-
DialogDescription,
15-
DialogHeader,
16-
DialogTitle,
17-
DialogTrigger,
18-
} from "@/components/ui/dialog";
19-
20-
import { MultiSelect } from "@/components/multi-select";
21-
import { Button } from "@/components/ui/button";
22-
import { slots } from "../upload/select_options";
23-
24-
interface Paper {
25-
_id: string;
26-
exam: string;
27-
finalUrl: string;
28-
thumbnailUrl: string;
29-
slot: string;
30-
subject: string;
31-
year: string;
32-
}
33-
34-
interface Filters {
35-
paper: Paper;
36-
uniqueExams: string[];
37-
uniqueSlots: string[];
38-
uniqueYears: string[];
39-
}
40-
const cryptr = new Cryptr(
41-
process.env.NEXT_PUBLIC_CRYPTO_SECRET ?? "default_crypto_secret",
42-
);
43-
44-
const CatalogueContent = () => {
45-
const router = useRouter();
46-
const searchParams = useSearchParams();
47-
const subject = searchParams.get("subject");
48-
const exams = searchParams.get("exams")?.split(",");
49-
const slots = searchParams.get("slots")?.split(",");
50-
const years = searchParams.get("years")?.split(",");
51-
52-
const [papers, setPapers] = useState<Paper[]>([]);
53-
const [error, setError] = useState<string | null>(null);
54-
const [loading, setLoading] = useState<boolean>(false);
55-
const [filterOptions, setFilterOptions] = useState<Filters>();
56-
57-
useEffect(() => {
58-
if (subject) {
59-
const fetchPapers = async () => {
60-
setLoading(true);
61-
62-
try {
63-
console.log(subject)
64-
const papersResponse = await axios.get("/api/papers", {
65-
// Digital Logic and Microprocessors[BITE202L]
66-
params: { subject },
67-
});
68-
const { res: encryptedPapersResponse } = papersResponse.data;
69-
const decryptedPapersResponse = cryptr.decrypt(
70-
encryptedPapersResponse,
71-
);
72-
const papersData: Paper[] = JSON.parse(
73-
decryptedPapersResponse,
74-
).papers;
75-
const filters: Filters = JSON.parse(decryptedPapersResponse);
76-
setFilterOptions(filters);
77-
const papersDataWithFilters = papersData
78-
.filter((paper)=>{
79-
const examCondition = exams && exams.length ? exams.includes(paper.exam) : true;
80-
const slotCondition = slots && slots.length ? slots.includes(paper.slot) : true;
81-
const yearCondition = years && years.length ? years.includes(paper.year) : true;
82-
83-
return examCondition && slotCondition && yearCondition;
84-
})
85-
86-
if(papersDataWithFilters.length > 0)
87-
// setPapers(papersData);
88-
89-
setPapers(papersDataWithFilters);
90-
else
91-
setPapers(papersData);
92-
} catch (error) {
93-
if (axios.isAxiosError(error)) {
94-
const axiosError = error as AxiosError<{ message?: string }>;
95-
const errorMessage =
96-
axiosError.response?.data?.message ?? "Error fetching papers";
97-
setError(errorMessage);
98-
} else {
99-
setError("Error fetching papers");
100-
}
101-
} finally {
102-
setLoading(false);
103-
}
104-
};
105-
106-
void fetchPapers();
107-
}
108-
}, [subject, searchParams]);
109-
// console.log(papers);
110-
111-
return (
112-
<div className="min-h-screen bg-gray-50 p-8">
113-
<div className="flex mb-4 mx-40 justify-center gap-10 items-center">
114-
{/* <button
115-
onClick={() => router.push("/")}
116-
className=" rounded-md bg-blue-500 px-4 py-2 text-white"
117-
>
118-
Back to Search
119-
</button> */}
120-
<div className="relative w-full">
121-
<div className="absolute flex-grow flex top-3 left-2">
122-
<Search className="w-5 text-gray-400" />
123-
</div>
124-
<input
125-
type="search"
126-
className="border w-full px-10 py-3 bg-[#7480FF33] bg-opacity-20 rounded-2xl "
127-
placeholder="Search..."
128-
></input>
129-
</div>
130-
{ subject && filterOptions && <FilterDialog subject={subject} filterOptions={filterOptions}/>}
131-
</div>
132-
{/* <h1 className="mb-4 text-2xl font-bold">Papers for {subject}</h1> */}
133-
{error && <p className="text-red-500">{error}</p>}
134-
135-
{loading ? (
136-
<p>Loading papers...</p>
137-
) : papers.length > 0 ? (
138-
<div className="flex flex-wrap gap-10">
139-
{papers.map((paper) => (
140-
<Card key={paper._id} paper={paper} />
141-
))}
142-
</div>
143-
) : (
144-
<p>No papers available for this subject.</p>
145-
)}
146-
</div>
147-
);
148-
};
1495

1506
const Catalogue = () => {
1517
return (
@@ -154,154 +10,5 @@ const Catalogue = () => {
15410
</Suspense>
15511
);
15612
};
157-
export default Catalogue;
158-
159-
function Card({ paper }: { paper: Paper }) {
160-
const [checked, setChecked] = useState<boolean>(false);
161-
function handleCheckboxChange(): void {
162-
setChecked(!checked);
163-
}
164-
165-
return (
166-
<div
167-
key={paper._id}
168-
className={`space-y-1 w-56 rounded-md border border-black border-opacity-50 ${checked ? "bg-[#EEF2FF]" : "bg-white"} p-4 `}
169-
>
170-
<Image
171-
src={paper.thumbnailUrl}
172-
alt={paper.subject}
173-
// layout="responsive"
174-
width={320} // Adjust width to maintain aspect ratio if needed
175-
height={180} // Fixed height
176-
className="mb-2 h-[180px] w-full object-cover" // Ensure it takes the full width of the container
177-
></Image>
178-
<div className="text-sm font-medium">
179-
{extractBracketContent(paper.subject)}
180-
</div>
181-
<div className="text-md font-medium">
182-
{extractWithoutBracketContent(paper.subject)}
183-
</div>
184-
{/* <p className="text-lg">{extractWithoutBracketContent(paper.subject)}</p> */}
185-
<div className="mb-2 flex gap-2 ">
186-
{capsule(paper.exam)}
187-
{capsule(paper.slot)}
188-
{capsule(paper.year)}
189-
</div>
190-
<div className="mt-5 flex items-center justify-between gap-2 ">
191-
<div className="flex items-center gap-1">
192-
<input
193-
onChange={handleCheckboxChange}
194-
className="h-3 w-3 rounded-lg"
195-
type="checkbox"
196-
/>
197-
<p className="text-sm">Select</p>
198-
</div>
199-
<div className="flex gap-2">
200-
<a
201-
href={paper.finalUrl}
202-
target="_blank"
203-
rel="noopener noreferrer"
204-
// className="text-blue-500 hover:underline"
205-
>
206-
<Eye />
207-
</a>
208-
<a href={paper.finalUrl} download>
209-
<Download />
210-
</a>
211-
</div>
212-
</div>
213-
</div>
214-
);
215-
}
216-
217-
function capsule(data: string) {
218-
return (
219-
<div className=" rounded-md bg-[#7480FF] p-1 px-3 text-sm">{data}</div>
220-
);
221-
}
222-
const FilterDialog = ({subject, filterOptions}: {subject:string, filterOptions: Filters}) => {
223-
const router = useRouter()
224-
const [filterExams, setuniqueExams] = useState<string[]>();
225-
const [FilterSlots, setFilterSlots] = useState<string[]>();
226-
const [filterYears, setFilterYears] = useState<string[]>();
227-
const handleFilterClick = () => {
228-
if (subject) {
229-
let pushContent = "/catalogue"
230-
if(subject)
231-
{
232-
pushContent = pushContent.concat(`?subject=${encodeURIComponent(subject)}`)
233-
}
234-
if(filterExams)
235-
{
236-
pushContent = pushContent.concat(`&exams=${encodeURIComponent(filterExams.join(','))}`)
237-
}
238-
if(FilterSlots)
239-
{
240-
pushContent= pushContent.concat(`&slots=${encodeURIComponent(FilterSlots.join(','))}`)
241-
}
242-
if(filterYears)
243-
{
244-
pushContent = pushContent.concat(`&years=${encodeURIComponent(filterYears.join(','))}`)
245-
246-
}
247-
router.push(pushContent);
248-
}
249-
};
250-
251-
return (
252-
<Dialog>
253-
<DialogTrigger className="rounded-lg bg-[#7480FF] px-8 py-3 text-white">
254-
<div className="flex gap-3">Filter <Filter className=""/></div>
255-
</DialogTrigger>
256-
<DialogContent>
257-
<DialogHeader>
258-
<DialogTitle className="mb-5">Choose your filters</DialogTitle>
259-
<DialogDescription className="space-y-5">
260-
{filterOptions && (
261-
<div className="space-y-5">
262-
<MultiSelect
263-
options={filterOptions.uniqueExams.map((exam: string) => ({
264-
label: exam,
265-
value: exam,
266-
}))}
267-
onValueChange={setuniqueExams}
268-
placeholder="Exam"
269-
/>
270-
<MultiSelect
271-
options={filterOptions.uniqueSlots.map((slots: string) => ({
272-
label: slots,
273-
value: slots,
274-
}))}
275-
onValueChange={setFilterSlots}
276-
placeholder="Slots"
277-
/>
278-
<MultiSelect
279-
options={filterOptions.uniqueYears.map((years: string) => ({
280-
label: years,
281-
value: years,
282-
}))}
283-
onValueChange={setFilterYears}
284-
placeholder="Years"
285-
/>
286-
</div>
287-
)}
288-
<Button variant="outline" onClick={handleFilterClick}>
289-
Filter
290-
</Button>
291-
</DialogDescription>
292-
</DialogHeader>
293-
</DialogContent>
294-
</Dialog>
295-
);
296-
};
297-
298-
299-
function extractBracketContent(subject: string): string | null {
300-
const match = subject.match(/\[(.*?)\]/);
301-
return match && match[1] ? match[1] : "BMAT102L"; //MAKE SURE IT WORKS WHEN URL IS DONE FROM BACKEND
302-
}
303-
304-
function extractWithoutBracketContent(subject: string): string {
305-
return subject.replace(/\s*\[.*?\]\s*/g, "").trim();
306-
}
30713

14+
export default Catalogue;

src/app/papersadminlogin/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
type DecryptedLoginResponse,
99
} from "@/interface";
1010
import Cryptr from "cryptr";
11-
import { handleAPIError } from "@/app/util/error";
11+
import { handleAPIError } from "@/util/error";
1212
import { totalmem } from "os";
1313
import toast from "react-hot-toast";
1414
import { ApiError } from "next/dist/server/api-utils";

src/app/upload/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import JSZip from "jszip";
55
import axios from "axios";
66
import { slots, courses } from "./select_options";
77
import toast, { Toaster } from "react-hot-toast";
8-
import { handleAPIError } from "../util/error";
8+
import { handleAPIError } from "../../util/error";
99
import { useRouter } from "next/navigation";
1010
import { type ApiError } from "next/dist/server/api-utils";
1111

src/app/util/utils.tsx

Lines changed: 0 additions & 16 deletions
This file was deleted.

0 commit comments

Comments
 (0)