1- import React from 'react' ;
1+
2+ import { motion } from "framer-motion" ;
23import { useAuth } from '../store/auth' ;
34import { useTheme } from '../context/ThemeContext' ;
45import { toast } from 'react-toastify' ;
56import { MdBookmarkBorder , MdBookmarkAdded , MdEdit , MdPlayCircleOutline } from "react-icons/md" ;
67import { Link , useNavigate } from 'react-router-dom' ;
78import { useLoading } from './loadingContext' ;
8- import { getYouTubeUrlType } from '../utils/youtubeUtils' ;
9+ import PropTypes from 'prop-types' ;
10+
911
1012const CardBody = ( { course, watchlist = [ ] , updateWatchlist, onClick } ) => {
1113 const { setIsLoading } = useLoading ( ) ;
@@ -16,8 +18,7 @@ const CardBody = ({ course, watchlist = [], updateWatchlist, onClick }) => {
1618 const token = localStorage . getItem ( 'token' ) ;
1719 const navigate = useNavigate ( ) ;
1820
19- // Analyze YouTube link type
20- const youtubeUrlInfo = getYouTubeUrlType ( creator_youtube_link ) ;
21+
2122
2223 // Handle card click
2324 const handleCardClick = ( e ) => {
@@ -63,8 +64,7 @@ const CardBody = ({ course, watchlist = [], updateWatchlist, onClick }) => {
6364 throw new Error ( `HTTP error! status: ${ response . status } , message: ${ errorMessage } ` ) ;
6465 }
6566
66- const result = await response . json ( ) ;
67- //console.log("Watchlist update success:", result);
67+ await response . json ( ) ;
6868
6969 // Show appropriate toast message
7070 if ( isInWatchlist ) {
@@ -88,28 +88,50 @@ const CardBody = ({ course, watchlist = [], updateWatchlist, onClick }) => {
8888 return (
8989 < div
9090 className = { `
91- w-[300px] border rounded-md overflow-hidden shadow-md transition-all duration-200
91+ group relative w-[330px] rounded-xl shadow-lg flex flex-col
92+ hover:border-b-2 hover:border-r-2 transition-all duration-300 overflow-hidden
9293 ${ isDark
93- ? 'bg-dark-bg- secondary border-dark-border '
94- : 'bg-light-bg-secondary border-light-border ' }
95- hover:scale-[1.02] hover: shadow-lg cursor-pointer
94+ ? 'bg-gradient-to-br from-gray-800 to- secondary-1000 backdrop-blur-xl '
95+ : 'bg-gradient-to-br from-blue-50 to-indigo-50 backdrop-blur-xl ' }
96+ hover:shadow-xl cursor-pointer
9697 ` }
9798 onClick = { handleCardClick }
9899 >
100+ { /* Animated borders */ }
101+ < motion . div
102+ className = "absolute top-0 right-0 w-0 h-full bg-primary rounded-r-2xl"
103+ whileHover = { {
104+ width : "3px" ,
105+ transition : { duration : 0.3 , ease : "easeOut" }
106+ } }
107+ />
108+ < motion . div
109+ className = "absolute bottom-0 left-0 w-full h-0 bg-primary rounded-b-2xl"
110+ whileHover = { {
111+ height : "3px" ,
112+ transition : { duration : 0.3 , ease : "easeOut" , delay : 0.05 }
113+ } }
114+ />
115+
99116 < div className = "relative" >
100117 < img
101118 src = { course_image }
102119 alt = { course_title }
103- className = "w-full h-[180px] object-cover"
120+ className = "w-full h-[180px] object-cover rounded-lg "
104121 />
105- < div className = "absolute inset-0 bg-black/30 opacity-0 hover:opacity-100 transition-opacity flex items-center justify-center" >
106- < MdPlayCircleOutline className = "text-white text-5xl" />
122+ < div className = "absolute inset-0 bg-gradient-to-t from-black/60 via-black/30 to-transparent opacity-0 group-hover:opacity-100 transition-all duration-300 flex items-center justify-center rounded-xl" >
123+ < motion . div
124+ whileHover = { { scale : 1.1 } }
125+ className = "transform translate-y-4 group-hover:translate-y-0 transition-transform duration-300"
126+ >
127+ < MdPlayCircleOutline className = "text-white text-6xl" />
128+ </ motion . div >
107129 </ div >
108130 </ div >
109131
110- < div className = "p -4 flex flex-col gap-5 relative" >
111- < h3 className = { `text-lg font-medium ${ isDark ? 'text-dark-text-primary' : 'text-light-text-primary' } ` } >
112- { course_title . length > 30 ? ` ${ course_title . slice ( 0 , 30 ) } ...` : course_title }
132+ < div className = "px-4 sm:px-6 pb-3 pt -4 flex flex-col gap-5 relative" >
133+ < h3 className = { `text-lg font-medium leading-normal ${ isDark ? 'text-dark-text-primary' : 'text-light-text-primary' } group-hover:text-primary transition-colors duration-300 line-clamp-3 min-h-[4.5rem] ` } >
134+ { course_title }
113135 </ h3 >
114136
115137 < div className = "flex items-center gap-2" >
@@ -134,10 +156,9 @@ const CardBody = ({ course, watchlist = [], updateWatchlist, onClick }) => {
134156 e . stopPropagation ( ) ;
135157 navigate ( `/courses/${ course . _id } ` ) ;
136158 } }
137- className = { `
138- py-2 px-5 rounded-full flex items-center justify-center text-white transition-colors
139- ${ isDark ? 'bg-primary hover:bg-primary-dark' : 'bg-primary hover:bg-primary-dark' }
140- ` }
159+ className = "py-2.5 px-6 rounded-full flex items-center justify-center text-white font-medium
160+ bg-primary hover:bg-primary-dark transition-all duration-300 transform hover:scale-[1.02]
161+ shadow-md hover:shadow-lg shadow-primary/20"
141162 >
142163 Watch Now
143164 </ button >
@@ -146,10 +167,15 @@ const CardBody = ({ course, watchlist = [], updateWatchlist, onClick }) => {
146167 < button
147168 onClick = { handleWatchlist }
148169 className = { `
149- h-10 w-10 rounded-full flex items-center justify-center transition-colors
170+ h-11 w-11 rounded-full flex items-center justify-center transition-all duration-300
150171 ${ isInWatchlist
151- ? 'bg-primary text-white'
152- : `${ isDark ? 'bg-dark-bg-tertiary text-dark-text-primary border border-dark-border' : 'bg-light-bg-tertiary text-light-text-primary border border-light-border' } ` }
172+ ? 'bg-primary text-white shadow-md shadow-primary/20'
173+ : `${ isDark
174+ ? 'bg-dark-bg-tertiary/80 text-dark-text-primary hover:bg-dark-bg-tertiary'
175+ : 'bg-light-bg-tertiary/80 text-light-text-primary hover:bg-light-bg-tertiary' }
176+ backdrop-blur-sm border border-current/10`
177+ }
178+ hover:scale-[1.05]
153179 ` }
154180 aria-label = { isInWatchlist ? "Remove from watchlist" : "Add to watchlist" }
155181 >
@@ -163,8 +189,11 @@ const CardBody = ({ course, watchlist = [], updateWatchlist, onClick }) => {
163189 { userdata . isAdmin && (
164190 < Link
165191 className = { `
166- h-10 w-10 rounded-full flex items-center justify-center transition-colors
167- ${ isDark ? 'bg-dark-bg-tertiary text-primary hover:bg-dark-bg-primary' : 'bg-light-bg-tertiary text-primary hover:bg-light-bg-primary' }
192+ h-11 w-11 rounded-full flex items-center justify-center transition-all duration-300
193+ ${ isDark
194+ ? 'bg-dark-bg-tertiary/80 text-primary hover:bg-dark-bg-tertiary'
195+ : 'bg-light-bg-tertiary/80 text-primary hover:bg-light-bg-tertiary' }
196+ backdrop-blur-sm border border-current/10 hover:scale-[1.05]
168197 ` }
169198 to = { `/admin/courses/update/${ course . _id } ` }
170199 aria-label = "Edit course"
@@ -179,4 +208,20 @@ const CardBody = ({ course, watchlist = [], updateWatchlist, onClick }) => {
179208 ) ;
180209} ;
181210
211+ CardBody . propTypes = {
212+ course : PropTypes . shape ( {
213+ _id : PropTypes . string . isRequired ,
214+ course_title : PropTypes . string . isRequired ,
215+ creator_youtube_link : PropTypes . string . isRequired ,
216+ creator_name : PropTypes . string . isRequired ,
217+ creator_image : PropTypes . string . isRequired ,
218+ course_image : PropTypes . string . isRequired
219+ } ) . isRequired ,
220+ watchlist : PropTypes . arrayOf ( PropTypes . shape ( {
221+ _id : PropTypes . string . isRequired
222+ } ) ) ,
223+ updateWatchlist : PropTypes . func ,
224+ onClick : PropTypes . func
225+ } ;
226+
182227export default CardBody ;
0 commit comments