@@ -6,6 +6,7 @@ import { useMemo, useState } from "react";
66import Image from "next/image" ;
77import Link from "next/link" ;
88import CodevBadge from "@/components/CodevBadge" ;
9+ import { getTotalCodevPoints } from "@/utils/codev-qualification" ;
910import {
1011 AnimatePresence ,
1112 motion ,
@@ -31,41 +32,32 @@ interface Props {
3132 color : string ;
3233}
3334
34- const STATUS_CONFIG : Record <
35- InternalStatus ,
36- { label : string ; className : string }
37- > = {
35+ const STATUS_CONFIG = {
3836 TRAINING : {
3937 label : "Training" ,
40- className :
41- "bg-yellow-500/20 backdrop-blur-sm text-yellow-200 border border-yellow-500/30 dark:bg-yellow-500/10 dark:text-yellow-300" ,
38+ className : "bg-yellow-500/20 backdrop-blur-sm text-yellow-200 border border-yellow-500/30 dark:bg-yellow-500/10 dark:text-yellow-300" ,
4239 } ,
4340 GRADUATED : {
4441 label : "Graduated" ,
45- className :
46- "bg-green-500/20 backdrop-blur-sm text-green-200 border border-green-500/30 dark:bg-green-500/10 dark:text-green-300" ,
42+ className : "bg-green-500/20 backdrop-blur-sm text-green-200 border border-green-500/30 dark:bg-green-500/10 dark:text-green-300" ,
4743 } ,
4844 INACTIVE : {
4945 label : "Inactive" ,
50- className :
51- "bg-gray-500/20 backdrop-blur-sm text-gray-200 border border-gray-500/30 dark:bg-gray-500/10 dark:text-gray-300" ,
46+ className : "bg-gray-500/20 backdrop-blur-sm text-gray-200 border border-gray-500/30 dark:bg-gray-500/10 dark:text-gray-300" ,
5247 } ,
5348 MENTOR : {
5449 label : "Mentor" ,
55- className :
56- "bg-purple-500/20 backdrop-blur-sm text-purple-200 border border-purple-500/30 dark:bg-purple-500/10 dark:text-purple-300" ,
50+ className : "bg-purple-500/20 backdrop-blur-sm text-purple-200 border border-purple-500/30 dark:bg-purple-500/10 dark:text-purple-300" ,
5751 } ,
5852 ADMIN : {
5953 label : "Admin" ,
60- className :
61- "bg-blue-500/20 backdrop-blur-sm text-blue-200 border border-blue-500/30 dark:bg-blue-500/10 dark:text-blue-300" ,
54+ className : "bg-blue-500/20 backdrop-blur-sm text-blue-200 border border-blue-500/30 dark:bg-blue-500/10 dark:text-blue-300" ,
6255 } ,
6356 DEPLOYED : {
6457 label : "Deployed" ,
65- className :
66- "bg-indigo-500/20 backdrop-blur-sm text-indigo-200 border border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300" ,
58+ className : "bg-indigo-500/20 backdrop-blur-sm text-indigo-200 border border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-300" ,
6759 } ,
68- } ;
60+ } as const ;
6961
7062const CodevCard = ( { codev, color } : Props ) => {
7163 const [ hovered , setHovered ] = useState ( false ) ;
@@ -86,13 +78,10 @@ const CodevCard = ({ codev, color }: Props) => {
8678 } ;
8779
8880 const internalStatus = codev . internal_status || "MENTOR" ;
89- const statusConfig =
90- STATUS_CONFIG [ internalStatus as InternalStatus ] || STATUS_CONFIG . MENTOR ;
81+ const statusConfig = STATUS_CONFIG [ internalStatus as keyof typeof STATUS_CONFIG ] || STATUS_CONFIG . MENTOR ;
9182
9283 const filteredLevel = useMemo ( ( ) => {
93- return codev . level &&
94- codev . codev_points &&
95- Array . isArray ( codev . codev_points )
84+ return codev . level && codev . codev_points && Array . isArray ( codev . codev_points )
9685 ? Object . fromEntries (
9786 Object . entries ( codev . level )
9887 . filter ( ( [ skillCategoryId , levelValue ] ) => {
@@ -104,160 +93,82 @@ const CodevCard = ({ codev, color }: Props) => {
10493 ) ;
10594 } )
10695 . sort ( ( [ skillCategoryIdA ] , [ skillCategoryIdB ] ) => {
107- const pointsA =
108- codev . codev_points ! . find (
109- ( point ) => point ?. skill_category_id === skillCategoryIdA ,
110- ) ?. points || 0 ;
111- const pointsB =
112- codev . codev_points ! . find (
113- ( point ) => point ?. skill_category_id === skillCategoryIdB ,
114- ) ?. points || 0 ;
96+ const pointsA = codev . codev_points ! . find ( ( point ) => point ?. skill_category_id === skillCategoryIdA ) ?. points || 0 ;
97+ const pointsB = codev . codev_points ! . find ( ( point ) => point ?. skill_category_id === skillCategoryIdB ) ?. points || 0 ;
11598 return pointsB - pointsA ;
11699 } ) ,
117100 )
118101 : { } ;
119102 } , [ codev . level , codev . codev_points ] ) ;
120103
121- // Line 119: Enhanced card wrapper with click redirect
104+ const totalPoints = getTotalCodevPoints ( codev . codev_points ) ;
105+
122106 return (
123- < div
124- className = "h-64"
125- onMouseEnter = { ( ) => setHovered ( true ) }
126- onMouseLeave = { ( ) => setHovered ( false ) }
127- >
128- < div
129- onClick = { ( ) =>
130- ( window . location . href = `https://www.codebility.tech/profiles/${ codev . id } ` )
131- }
132- className = "group relative flex h-full w-full cursor-pointer flex-col items-center justify-between rounded-lg border border-white/20 bg-white/10 p-4 shadow-2xl backdrop-blur-md transition-all duration-300 hover:scale-[1.02] hover:bg-white/20 dark:border-white/10 dark:bg-white/5 dark:hover:bg-white/10"
133- >
134- { /* Background decoration */ }
107+ < div className = "h-64" onMouseEnter = { ( ) => setHovered ( true ) } onMouseLeave = { ( ) => setHovered ( false ) } >
108+ < div onClick = { ( ) => window . location . href = `https://www.codebility.tech/profiles/${ codev . id } ` } className = "group relative flex h-full w-full flex-col items-center justify-between rounded-lg border border-white/20 bg-white/10 p-4 shadow-2xl backdrop-blur-md transition-all duration-300 cursor-pointer hover:bg-white/20 hover:scale-[1.02] dark:border-white/10 dark:bg-white/5 dark:hover:bg-white/10" >
135109 < div className = "from-customBlue-50/30 dark:from-customBlue-950/10 absolute inset-0 bg-gradient-to-br to-purple-50/30 opacity-0 transition-opacity duration-300 group-hover:opacity-100 dark:to-purple-950/10" />
136110 < div className = "absolute -right-4 -top-4 h-32 w-32 rounded-full bg-gradient-to-br from-yellow-400/10 to-orange-400/10 opacity-0 blur-2xl transition-opacity duration-500 group-hover:opacity-100" />
137111 < div className = "grid w-full grid-cols-4 gap-2" >
138112 < div className = "col-span-1" />
139113 < div className = "relative col-span-2 flex justify-center" >
140- < Image
141- alt = { `${ codev . first_name } Avatar` }
142- src = {
143- codev . image_url &&
144- ( codev . image_url . startsWith ( "http" ) ||
145- codev . image_url . startsWith ( "/" ) )
146- ? codev . image_url
147- : "/assets/svgs/icon-codebility-black.svg"
148- }
149- width = { 70 }
150- height = { 70 }
151- className = { `${ color } h-[70px] w-[70px] rounded-full bg-cover object-cover p-0.5` }
152- onMouseMove = { handleMouseMove }
153- onError = { ( e ) => {
154- console . error (
155- `Failed to load image for ${ codev . first_name } : ${ codev . image_url } ` ,
156- ) ;
157- e . currentTarget . src = "/assets/svgs/icon-codebility-black.svg" ;
158- } }
159- />
114+ < div className = "absolute -top-2 -right-2 z-20" >
115+ < TooltipProvider >
116+ < Tooltip delayDuration = { 100 } >
117+ < TooltipTrigger asChild >
118+ < div className = "flex items-center gap-1 rounded-full border border-yellow-500/30 bg-gradient-to-br from-yellow-500/20 to-orange-500/20 px-2.5 py-1 shadow-lg backdrop-blur-sm transition-all duration-300 hover:scale-110 hover:from-yellow-500/30 hover:to-orange-500/30" >
119+ < span className = "text-xs font-bold text-yellow-200" > ⭐</ span >
120+ < span className = "text-xs font-bold text-yellow-200" > { totalPoints } </ span >
121+ </ div >
122+ </ TooltipTrigger >
123+ < TooltipContent className = "border border-white/20 bg-white/10 px-3 py-2 text-xs text-white backdrop-blur-md dark:border-white/10 dark:bg-white/5" >
124+ < div className = "flex flex-col gap-1" >
125+ < p className = "font-semibold" > Total Skill Points</ p >
126+ < p className = "text-gray-200" > Qualification: ≥100 points</ p >
127+ </ div >
128+ </ TooltipContent >
129+ </ Tooltip >
130+ </ TooltipProvider >
131+ </ div >
132+ < Image alt = { `${ codev . first_name } Avatar` } src = { codev . image_url && ( codev . image_url . startsWith ( "http" ) || codev . image_url . startsWith ( "/" ) ) ? codev . image_url : "/assets/svgs/icon-codebility-black.svg" } width = { 70 } height = { 70 } className = { `${ color } h-[70px] w-[70px] rounded-full bg-cover object-cover p-0.5` } onMouseMove = { handleMouseMove } onError = { ( e ) => { console . error ( `Failed to load image for ${ codev . first_name } : ${ codev . image_url } ` ) ; e . currentTarget . src = "/assets/svgs/icon-codebility-black.svg" ; } } />
160133 < AnimatePresence >
161134 { hovered && internalStatus && (
162- < motion . div
163- initial = { { opacity : 0 , y : 20 , scale : 0.6 } }
164- animate = { {
165- opacity : 1 ,
166- y : 0 ,
167- scale : 1 ,
168- transition : {
169- type : "spring" ,
170- stiffness : 260 ,
171- damping : 10 ,
172- } ,
173- } }
174- exit = { { opacity : 0 , y : 20 , scale : 0.6 } }
175- style = { {
176- translateX : translateX ,
177- rotate : rotate ,
178- whiteSpace : "nowrap" ,
179- } }
180- className = { cn (
181- "absolute -top-8 right-1/2 z-50 flex -translate-x-1/2 transform flex-col items-center justify-center rounded-lg px-4 py-2 shadow-xl" ,
182- statusConfig . className ,
183- ) }
184- >
185- < div className = "relative z-30 text-base" >
186- { statusConfig . label }
187- </ div >
135+ < motion . div initial = { { opacity : 0 , y : 20 , scale : 0.6 } } animate = { { opacity : 1 , y : 0 , scale : 1 , transition : { type : "spring" , stiffness : 260 , damping : 10 } } } exit = { { opacity : 0 , y : 20 , scale : 0.6 } } style = { { translateX : translateX , rotate : rotate , whiteSpace : "nowrap" } } className = { cn ( "absolute -top-8 right-1/2 z-50 flex -translate-x-1/2 transform flex-col items-center justify-center rounded-lg px-4 py-2 shadow-xl" , statusConfig . className ) } >
136+ < div className = "relative z-30 text-base" > { statusConfig . label } </ div >
188137 </ motion . div >
189138 ) }
190139 </ AnimatePresence >
191140 < div className = "absolute bottom-[1px] right-[1px]" >
192- < p
193- className = { cn (
194- "rounded-full border-2 p-2 text-[9px]" ,
195- codev . availability_status
196- ? "border-green-600 bg-green-500"
197- : "border-red-600 bg-red-500" ,
198- ) }
199- > </ p >
141+ < p className = { cn ( "rounded-full border-2 p-2 text-[9px]" , codev . availability_status ? "border-green-600 bg-green-500" : "border-red-600 bg-red-500" ) } > </ p >
200142 </ div >
201143 </ div >
202144 < div className = "col-span-1 flex justify-end" >
203145 < TooltipProvider >
204146 < Tooltip delayDuration = { 100 } >
205147 < TooltipTrigger asChild >
206- { /* Line 217: Added stopPropagation to prevent card click when button is clicked */ }
207- < Link
208- href = { `/profiles/${ codev . id } ` }
209- target = "_blank"
210- onClick = { ( e ) => e . stopPropagation ( ) }
211- >
212- < Button
213- variant = "outline"
214- size = "icon"
215- className = "h-8 w-8 rounded-full border-white/30 bg-white/20 backdrop-blur-sm transition-all duration-300 hover:bg-white/30 dark:border-white/20 dark:bg-white/10 dark:hover:bg-white/20"
216- >
148+ < Link href = { `/profiles/${ codev . id } ` } target = "_blank" onClick = { ( e ) => e . stopPropagation ( ) } >
149+ < Button variant = "outline" size = "icon" className = "h-8 w-8 rounded-full border-white/30 bg-white/20 backdrop-blur-sm transition-all duration-300 hover:bg-white/30 dark:border-white/20 dark:bg-white/10 dark:hover:bg-white/20" >
217150 < BookOpenIcon className = "h-3.5 w-3.5" />
218151 </ Button >
219152 </ Link >
220153 </ TooltipTrigger >
221- < TooltipContent className = "border border-white/20 bg-white/10 px-2 py-1 text-xs text-white backdrop-blur-md dark:border-white/10 dark:bg-white/5" >
222- Read Bio
223- </ TooltipContent >
154+ < TooltipContent className = "border border-white/20 bg-white/10 px-2 py-1 text-xs text-white backdrop-blur-md dark:border-white/10 dark:bg-white/5" > Read Bio</ TooltipContent >
224155 </ Tooltip >
225156 </ TooltipProvider >
226157 </ div >
227158 </ div >
228159 < div className = "flex flex-col items-center gap-0.5" >
229160 < div className = "flex flex-col gap-1 text-center" >
230- < p className = "md:text-md relative z-10 text-sm capitalize text-white dark:text-white lg:text-base" >
231- { codev . first_name } { codev . last_name }
232- </ p >
233- { codev . display_position ? (
234- < p className = "relative z-10 text-sm text-gray-200 dark:text-gray-300" >
235- { codev . display_position }
236- </ p >
237- ) : (
238- < div className = "relative z-10 text-sm lg:text-base" > </ div >
239- ) }
161+ < p className = "md:text-md relative z-10 text-sm capitalize text-white dark:text-white lg:text-base" > { codev . first_name } { codev . last_name } </ p >
162+ { codev . display_position ? ( < p className = "relative z-10 text-sm text-gray-200 dark:text-gray-300" > { codev . display_position } </ p > ) : ( < div className = "relative z-10 text-sm lg:text-base" > </ div > ) }
240163 </ div >
241164 </ div >
242165 < div className = "flex min-h-[20px] items-center justify-center" >
243- { filteredLevel && Object . keys ( filteredLevel ) . length > 0 ? (
244- < CodevBadge
245- level = { filteredLevel }
246- className = "transition-transform group-hover:scale-100"
247- />
248- ) : null }
249- </ div >
250- < div
251- className = "relative z-20"
252- onMouseEnter = { ( e ) => e . stopPropagation ( ) }
253- onMouseLeave = { ( e ) => e . stopPropagation ( ) }
254- onClick = { ( e ) => e . stopPropagation ( ) }
255- >
256- < CodevHireCodevButton codevId = { codev . id } />
166+ { filteredLevel && Object . keys ( filteredLevel ) . length > 0 ? ( < CodevBadge level = { filteredLevel } className = "transition-transform group-hover:scale-100" /> ) : null }
257167 </ div >
168+ < CodevHireCodevButton codevId = { codev . id } />
258169 </ div >
259170 </ div >
260171 ) ;
261172} ;
262173
263- export default CodevCard ;
174+ export default CodevCard ;
0 commit comments