Skip to content

Commit ad3f462

Browse files
authored
Merge pull request #131 from Abh1noob/papers-card
New UI for Card Component
2 parents b9dbcc2 + dd8854d commit ad3f462

File tree

3 files changed

+76
-67
lines changed

3 files changed

+76
-67
lines changed

src/components/Card.tsx

+72-61
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,39 @@
1+
"use client";
12
import { useEffect, useState } from "react";
23
import { type IPaper } from "@/interface";
34
import Image from "next/image";
4-
import { Eye, Download } from "lucide-react";
5+
import { Eye, Download, Check } from "lucide-react";
56
import {
6-
capsuleGreen,
7+
capsule,
78
extractBracketContent,
89
extractWithoutBracketContent,
910
} from "@/util/utils";
10-
import { capsule } from "@/util/utils";
1111
import axios from "axios";
12-
import { useRouter } from "next/navigation";
1312
import Link from "next/link";
13+
import { cn } from "@/lib/utils";
1414

15-
const Card = ({
16-
paper,
17-
onSelect,
18-
isSelected,
19-
}: {
15+
interface CardProps {
2016
paper: IPaper;
2117
onSelect: (paper: IPaper, isSelected: boolean) => void;
2218
isSelected: boolean;
23-
}) => {
24-
const [checked, setChecked] = useState<boolean>(false);
19+
}
20+
21+
const Card = ({ paper, onSelect, isSelected }: CardProps) => {
22+
const [checked, setChecked] = useState<boolean>(isSelected);
2523

2624
useEffect(() => {
2725
setChecked(isSelected);
2826
}, [isSelected]);
2927

30-
const handleDownload = async (paper: IPaper) => {
28+
const getSecureUrl = (url: string): string =>
29+
url.startsWith("http://") ? url.replace("http://", "https://") : url;
30+
31+
const generateFileName = (paper: IPaper): string => {
3132
const extension = paper.finalUrl.split(".").pop();
32-
const fileName = `${extractBracketContent(paper.subject)}-${paper.exam}-${paper.slot}-${paper.year}.${extension}`;
33-
await downloadFile(paper.finalUrl, fileName);
33+
return `${extractBracketContent(paper.subject)}-${paper.exam}-${paper.slot}-${paper.year}.${extension}`;
3434
};
3535

36-
function handleCheckboxChange() {
37-
setChecked(!checked);
38-
onSelect(paper, !checked);
39-
}
40-
41-
async function downloadFile(url: string, filename: string) {
36+
const downloadFile = async (url: string, filename: string): Promise<void> => {
4237
try {
4338
const response = await axios.get(url, { responseType: "blob" });
4439
const blob = new Blob([response.data]);
@@ -47,70 +42,86 @@ const Card = ({
4742
link.download = filename;
4843
link.click();
4944
window.URL.revokeObjectURL(link.href);
50-
} catch (error) {}
51-
}
45+
} catch (error) {
46+
console.error("Download failed:", error);
47+
}
48+
};
49+
50+
const handleDownload = async (paper: IPaper) => {
51+
await downloadFile(getSecureUrl(paper.finalUrl), generateFileName(paper));
52+
};
53+
54+
const handleCheckboxChange = () => {
55+
setChecked((prev) => {
56+
const newChecked = !prev;
57+
onSelect(paper, newChecked);
58+
return newChecked;
59+
});
60+
};
61+
62+
const paperLink = `/paper/${paper._id}`;
5263

53-
if (paper.finalUrl.startsWith("http://")) {
54-
paper.finalUrl = paper.finalUrl.replace("http://", "https://");
55-
}
5664
return (
5765
<div
58-
key={paper._id}
59-
className={`mb-2 flex w-[70%] flex-col justify-between space-y-1 rounded-xl border-2 border-black bg-white hover:border-[#434dba] dark:border-[#434dba] dark:bg-black dark:hover:border-white md:w-64 ${checked ? "bg-[#EEF2FF] dark:bg-[#050b1f]" : ""} p-4 `}
66+
className={cn(
67+
"overflow-hidden rounded-md border-2 border-[#36266D] bg-[#171720] hover:bg-[#262635]",
68+
checked && "bg-[#262635]",
69+
)}
6070
>
61-
<Link
62-
href={`/paper/${paper._id}`}
63-
target="_blank"
64-
rel="noopener noreferrer"
65-
>
71+
<Link href={paperLink} target="_blank" rel="noopener noreferrer">
6672
<Image
6773
src={paper.thumbnailUrl}
6874
alt={paper.subject}
6975
width={320}
7076
height={180}
71-
className="mb-2 h-[160px] w-full object-cover md:h-[180px]"
77+
className="h-[160px] w-full object-cover p-4 pb-3 md:h-[250px]"
7278
/>
7379

74-
<div className="h-30 justify-center space-y-2">
75-
<div className="font-sans text-sm font-medium">
76-
{extractBracketContent(paper.subject)}
77-
</div>
78-
<div className="font-sans text-base font-semibold">
79-
{extractWithoutBracketContent(paper.subject)}
80+
<div className="justify-center">
81+
<div className="flex flex-row items-center justify-between px-4 pb-2">
82+
<div className="font-sans text-sm font-medium">
83+
{extractBracketContent(paper.subject)}
84+
</div>
85+
<div className="flex gap-2">
86+
<Link href={paperLink} target="_blank" rel="noopener noreferrer">
87+
<Eye size={22} />
88+
</Link>
89+
<Download size={20} onClick={() => handleDownload(paper)} />
90+
</div>
8091
</div>
81-
<div className="flex flex-wrap gap-2 py-2">
82-
{capsule(paper.exam)}
83-
{capsule(paper.slot)}
84-
{capsule(paper.year)}
85-
{/* {capsule(paper.campus)} */}
86-
{capsule(paper.semester)}
87-
{paper.answerKeyIncluded && capsuleGreen("Answer key included")}
92+
93+
<div className="h-[1px] w-full bg-[#36266D]" />
94+
95+
<div className="space-y-2 p-4">
96+
<div className="font-sans text-base font-semibold">
97+
{extractWithoutBracketContent(paper.subject)}
98+
</div>
99+
<div className="flex flex-wrap gap-2">
100+
{capsule(paper.exam)}
101+
{capsule(paper.slot)}
102+
{capsule(paper.year)}
103+
{capsule(paper.semester)}
104+
</div>
88105
</div>
89106
</div>
90107
</Link>
91108

92-
<div className="hidden items-center justify-between gap-2 pt-4 md:flex">
109+
<div className="hidden items-center justify-between gap-2 px-4 pb-4 md:flex">
93110
<div className="flex items-center gap-2">
94111
<input
95112
checked={checked}
96113
onChange={handleCheckboxChange}
97-
className="h-4 w-4 rounded-lg"
114+
className="h-5 w-5 accent-[#7480FF]"
98115
type="checkbox"
99116
/>
100-
<p className="font-sans text-sm">Select</p>
101-
</div>
102-
<div className="flex gap-2">
103-
<Link
104-
href={`/paper/${paper._id}`}
105-
target="_blank"
106-
rel="noopener noreferrer"
107-
>
108-
<Eye size={20} />
109-
</Link>
110-
<button onClick={() => handleDownload(paper)}>
111-
<Download size={20} />
112-
</button>
117+
<p>Select</p>
113118
</div>
119+
{paper.answerKeyIncluded && (
120+
<div className="flex items-center gap-2 font-normal text-[#7480FF]">
121+
<Check color="#7480FF" />
122+
Answer Key
123+
</div>
124+
)}
114125
</div>
115126
</div>
116127
);

src/components/CatalogueContent.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ const CatalogueContent = () => {
216216
<Loader />
217217
) : papers.length > 0 ? (
218218
<>
219-
<div className="mx-auto flex flex-col flex-wrap items-center justify-center gap-10 md:flex-row md:justify-normal">
219+
<div className="grid grid-cols-4 gap-8">
220220
{papers.map((paper) => (
221221
<Card
222222
key={paper._id}

src/util/utils.tsx

+3-5
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export function toSentenceCase(input: string): string {
1616
export function extractBracketContent(subject: string): string | null {
1717
const regex = /\[(.*?)\]/;
1818
const match = regex.exec(subject);
19-
return match?.[1] ?? "BMAT102L"; //MAKE SURE IT WORKS WHEN URL IS DONE FROM BACKEND
19+
return match?.[1] ?? "BMAT102L"; //MAKE SURE IT WORKS WHEN URL IS DONE FROM BACKEND
2020
}
2121

2222
export function extractWithoutBracketContent(subject: string): string {
@@ -25,11 +25,9 @@ export function extractWithoutBracketContent(subject: string): string {
2525

2626
export function capsule(data: string) {
2727
return (
28-
<div className=" rounded-md bg-[#7480FF] p-1 px-3 text-sm">{data}</div>
28+
<div className="rounded-md bg-[#7480FF]/80 p-1 px-3 text-sm">{data}</div>
2929
);
3030
}
3131
export function capsuleGreen(data: string) {
32-
return (
33-
<div className=" rounded-md bg-[#3cc923] p-1 px-3 text-sm">{data}</div>
34-
);
32+
return <div className="rounded-md bg-[#3cc923] p-1 px-3 text-sm">{data}</div>;
3533
}

0 commit comments

Comments
 (0)