1+ "use client" ;
12import { useEffect , useState } from "react" ;
23import { type IPaper } from "@/interface" ;
34import Image from "next/image" ;
4- import { Eye , Download } from "lucide-react" ;
5+ import { Eye , Download , Check } from "lucide-react" ;
56import {
6- capsuleGreen ,
7+ capsule ,
78 extractBracketContent ,
89 extractWithoutBracketContent ,
910} from "@/util/utils" ;
10- import { capsule } from "@/util/utils" ;
1111import axios from "axios" ;
12- import { useRouter } from "next/navigation" ;
1312import 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 ) ;
0 commit comments