Skip to content

Commit 1ae7e52

Browse files
committed
feat: add paper preview modal with PDF viewer (handles CORS using Google viewer)
1 parent e915c2b commit 1ae7e52

1 file changed

Lines changed: 97 additions & 62 deletions

File tree

src/components/Card.tsx

Lines changed: 97 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import React from "react";
34
import { type IPaper } from "@/interface";
45
import Image from "next/image";
56
import { Eye, Download, Check } from "lucide-react";
@@ -23,6 +24,8 @@ interface CardProps {
2324
}
2425

2526
const Card = ({ paper, onSelect, isSelected }: CardProps) => {
27+
const [previewOpen, setPreviewOpen] = React.useState(false);
28+
2629
const handleDownload = async (paper: IPaper) => {
2730
await downloadFile(getSecureUrl(paper.file_url), generateFileName(paper));
2831
};
@@ -34,77 +37,109 @@ const Card = ({ paper, onSelect, isSelected }: CardProps) => {
3437
const paperLink = `/paper/${paper._id}`;
3538

3639
return (
37-
<div
38-
className={cn(
39-
"overflow-hidden rounded-sm border-2 border-[#734DFF] bg-[#FFFFFF] font-play transition-all duration-150 hover:bg-[#EFEAFF] dark:border-[#36266D] dark:bg-[#171720] hover:dark:bg-[#262635]",
40-
isSelected && "bg-white",
41-
)}
42-
>
43-
<Link href={paperLink} target="_blank" rel="noopener noreferrer">
44-
<Image
45-
src={paper.thumbnail_url}
46-
alt={paper.subject}
47-
width={320}
48-
height={180}
49-
className="w-full object-cover p-4 pb-3 md:h-[250px]"
50-
/>
51-
52-
<div className="justify-center">
53-
<div className="flex flex-row items-center justify-between px-4 pb-2">
54-
<div className="text-md font-play font-medium">
55-
{extractBracketContent(paper.subject)}
56-
</div>
57-
<div className="flex gap-2">
58-
<Link href={paperLink} target="_blank" rel="noopener noreferrer">
59-
<Eye size={22} />
60-
</Link>
61-
<Download
62-
size={20}
63-
onClick={(e) => {
64-
e.preventDefault();
65-
e.stopPropagation();
66-
void handleDownload(paper);
67-
}}
68-
className="cursor-pointer"
69-
/>
40+
<>
41+
<div
42+
className={cn(
43+
"overflow-hidden rounded-sm border-2 border-[#734DFF] bg-[#FFFFFF] font-play transition-all duration-150 hover:bg-[#EFEAFF] dark:border-[#36266D] dark:bg-[#171720] hover:dark:bg-[#262635]",
44+
isSelected && "bg-white",
45+
)}
46+
>
47+
<Link href={paperLink} target="_blank" rel="noopener noreferrer">
48+
<Image
49+
src={paper.thumbnail_url}
50+
alt={paper.subject}
51+
width={320}
52+
height={180}
53+
className="w-full object-cover p-4 pb-3 md:h-[250px]"
54+
/>
55+
56+
<div className="justify-center">
57+
<div className="flex flex-row items-center justify-between px-4 pb-2">
58+
<div className="text-md font-play font-medium">
59+
{extractBracketContent(paper.subject)}
60+
</div>
7061
</div>
71-
</div>
7262

73-
<div className="h-[1px] w-full bg-[#734DFF] dark:bg-[#36266D]" />
63+
<div className="h-[1px] w-full bg-[#734DFF] dark:bg-[#36266D]" />
7464

75-
<div className="space-y-2 p-4">
76-
<div className="font-play text-lg font-semibold">
77-
{extractWithoutBracketContent(paper.subject)}
78-
</div>
79-
<div className="flex flex-wrap gap-2">
80-
<Capsule>{paper.exam}</Capsule>
81-
<Capsule>{paper.slot}</Capsule>
82-
<Capsule>{paper.year}</Capsule>
83-
<Capsule>{paper.semester}</Capsule>
65+
<div className="space-y-2 p-4">
66+
<div className="font-play text-lg font-semibold">
67+
{extractWithoutBracketContent(paper.subject)}
68+
</div>
69+
<div className="flex flex-wrap gap-2">
70+
<Capsule>{paper.exam}</Capsule>
71+
<Capsule>{paper.slot}</Capsule>
72+
<Capsule>{paper.year}</Capsule>
73+
<Capsule>{paper.semester}</Capsule>
74+
</div>
8475
</div>
8576
</div>
86-
</div>
87-
</Link>
88-
89-
<div className="flex items-center justify-between gap-2 px-4 pb-4 font-play">
90-
<div className="flex items-center gap-2">
91-
<input
92-
checked={isSelected}
93-
onChange={handleCheckboxChange}
94-
className="h-5 w-5 accent-[#7480FF]"
95-
type="checkbox"
77+
</Link>
78+
79+
<div className="flex justify-end gap-2 px-4 pb-2">
80+
<Eye
81+
size={22}
82+
className="cursor-pointer"
83+
onClick={(e) => {
84+
e.stopPropagation();
85+
setPreviewOpen(true);
86+
}}
87+
/>
88+
89+
<Download
90+
size={20}
91+
onClick={(e) => {
92+
e.stopPropagation();
93+
void handleDownload(paper);
94+
}}
95+
className="cursor-pointer"
9696
/>
97-
<p>Select</p>
9897
</div>
99-
{paper.answer_key_included && (
100-
<div className="flex items-center gap-2 font-normal text-[#7480FF]">
101-
<Check color="#7480FF" />
102-
Answer Key
98+
99+
<div className="flex items-center justify-between gap-2 px-4 pb-4 font-play">
100+
<div className="flex items-center gap-2">
101+
<input
102+
checked={isSelected}
103+
onChange={handleCheckboxChange}
104+
className="h-5 w-5 accent-[#7480FF]"
105+
type="checkbox"
106+
/>
107+
<p>Select</p>
103108
</div>
104-
)}
109+
110+
{paper.answer_key_included && (
111+
<div className="flex items-center gap-2 font-normal text-[#7480FF]">
112+
<Check color="#7480FF" />
113+
Answer Key
114+
</div>
115+
)}
116+
</div>
105117
</div>
106-
</div>
118+
119+
{previewOpen && (
120+
<div
121+
className="fixed inset-0 z-50 flex items-center justify-center bg-black/80"
122+
onClick={() => setPreviewOpen(false)}
123+
>
124+
<div
125+
className="relative w-[95%] max-w-5xl h-[90vh] rounded-lg bg-white p-2 dark:bg-[#171720]"
126+
onClick={(e) => e.stopPropagation()}
127+
>
128+
<button
129+
className="absolute right-3 top-3 z-10 text-xl"
130+
onClick={() => setPreviewOpen(false)}
131+
>
132+
133+
</button>
134+
<iframe
135+
src={paper.file_url}
136+
className="w-full h-full rounded-md"
137+
/>
138+
</div>
139+
</div>
140+
)}
141+
</>
107142
);
108143
};
109144

110-
export default Card;
145+
export default Card;

0 commit comments

Comments
 (0)