1
+ "use client" ;
1
2
import { useEffect , useState } from "react" ;
2
3
import { type IPaper } from "@/interface" ;
3
4
import Image from "next/image" ;
4
- import { Eye , Download } from "lucide-react" ;
5
+ import { Eye , Download , Check } from "lucide-react" ;
5
6
import {
6
- capsuleGreen ,
7
+ capsule ,
7
8
extractBracketContent ,
8
9
extractWithoutBracketContent ,
9
10
} from "@/util/utils" ;
10
- import { capsule } from "@/util/utils" ;
11
11
import axios from "axios" ;
12
- import { useRouter } from "next/navigation" ;
13
12
import Link from "next/link" ;
13
+ import { cn } from "@/lib/utils" ;
14
14
15
- const Card = ( {
16
- paper,
17
- onSelect,
18
- isSelected,
19
- } : {
15
+ interface CardProps {
20
16
paper : IPaper ;
21
17
onSelect : ( paper : IPaper , isSelected : boolean ) => void ;
22
18
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 ) ;
25
23
26
24
useEffect ( ( ) => {
27
25
setChecked ( isSelected ) ;
28
26
} , [ isSelected ] ) ;
29
27
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 => {
31
32
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 } ` ;
34
34
} ;
35
35
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 > => {
42
37
try {
43
38
const response = await axios . get ( url , { responseType : "blob" } ) ;
44
39
const blob = new Blob ( [ response . data ] ) ;
@@ -47,70 +42,86 @@ const Card = ({
47
42
link . download = filename ;
48
43
link . click ( ) ;
49
44
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 } ` ;
52
63
53
- if ( paper . finalUrl . startsWith ( "http://" ) ) {
54
- paper . finalUrl = paper . finalUrl . replace ( "http://" , "https://" ) ;
55
- }
56
64
return (
57
65
< 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
+ ) }
60
70
>
61
- < Link
62
- href = { `/paper/${ paper . _id } ` }
63
- target = "_blank"
64
- rel = "noopener noreferrer"
65
- >
71
+ < Link href = { paperLink } target = "_blank" rel = "noopener noreferrer" >
66
72
< Image
67
73
src = { paper . thumbnailUrl }
68
74
alt = { paper . subject }
69
75
width = { 320 }
70
76
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 ]"
72
78
/>
73
79
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 >
80
91
</ 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 >
88
105
</ div >
89
106
</ div >
90
107
</ Link >
91
108
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" >
93
110
< div className = "flex items-center gap-2" >
94
111
< input
95
112
checked = { checked }
96
113
onChange = { handleCheckboxChange }
97
- className = "h-4 w-4 rounded-lg "
114
+ className = "h-5 w-5 accent-[#7480FF] "
98
115
type = "checkbox"
99
116
/>
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 >
113
118
</ 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
+ ) }
114
125
</ div >
115
126
</ div >
116
127
) ;
0 commit comments