@@ -2,6 +2,7 @@ import React from "react";
22import { motion } from "framer-motion" ;
33import { Avatar , AvatarFallback , AvatarImage } from "../ui/avatar" ;
44import { useSafeColorMode } from "../../utils/useSafeColorMode" ;
5+ import { ExternalLink , Quote } from "lucide-react" ;
56
67interface TestimonialCardProps {
78 name : string ;
@@ -22,92 +23,120 @@ const TestimonialCard: React.FC<TestimonialCardProps> = ({
2223} ) => {
2324 const { colorMode, isDark } = useSafeColorMode ( ) ;
2425
25- // Function to format the link display
2626 const formatLinkDisplay = ( url : string ) => {
2727 try {
2828 const urlObj = new URL ( url ) ;
2929 return urlObj . hostname + urlObj . pathname ;
3030 } catch {
31- // If URL parsing fails, return the original link
3231 return url ;
3332 }
3433 } ;
3534
3635 return (
3736 < motion . div
38- initial = { { opacity : 0 } }
39- animate = { { opacity : 1 } }
40- exit = { { opacity : 0 } }
41- className = { `flex h-[250px] flex-col justify-between rounded-2xl p-6 shadow-lg transition-shadow duration-300 hover:shadow-xl ${
42- isDark ? "bg-[#1a1a1a] text-white" : "bg-white text-gray-900"
37+ initial = { { opacity : 0 , y : 20 } }
38+ animate = { { opacity : 1 , y : 0 } }
39+ exit = { { opacity : 0 , y : - 20 } }
40+ whileHover = { { y : - 5 } }
41+ transition = { { duration : 0.3 } }
42+ className = { `group relative h-full overflow-hidden rounded-2xl border backdrop-blur-sm transition-all duration-300 hover:shadow-2xl ${
43+ isDark
44+ ? "border-gray-700/50 bg-gray-900/80 shadow-xl"
45+ : "border-gray-200/50 bg-white/90 shadow-lg"
4346 } `}
4447 >
45- { /* Header with Avatar and Name */ }
46- < div className = "flex items-center gap-4" >
47- < Avatar className = "h-24 w-24 rounded-full" >
48- < AvatarImage src = { avatar } className = "object-contain" />
49- < AvatarFallback > CN</ AvatarFallback >
50- </ Avatar >
51- < div >
52- < h3
53- className = { `text-lg font-semibold ${ isDark ? "text-white" : "text-gray-900" } ` }
54- >
55- { name }
56- </ h3 >
57- < p
58- className = { `text-sm ${ isDark ? "text-gray-400" : "text-gray-500" } ` }
59- >
60- @{ username }
48+ { /* Gradient Background */ }
49+ < div className = "absolute inset-0 bg-gradient-to-br from-purple-500/5 via-blue-500/5 to-pink-500/5" />
50+
51+ { /* Quote Icon */ }
52+ < div className = "absolute top-4 right-4 opacity-20" >
53+ < Quote size = { 32 } className = "text-purple-500" />
54+ </ div >
55+
56+ < div className = "relative flex h-full flex-col p-6" >
57+ { /* Header */ }
58+ < div className = "mb-6 flex items-center gap-4" >
59+ < div className = "relative" >
60+ < Avatar className = "h-16 w-16 border-2 border-gradient-to-r from-purple-500 to-pink-500" >
61+ < AvatarImage src = { avatar } className = "object-contain" />
62+ < AvatarFallback className = "bg-gradient-to-br from-purple-500 to-pink-500 text-white font-semibold" >
63+ { name . charAt ( 0 ) }
64+ </ AvatarFallback >
65+ </ Avatar >
66+ < div className = "absolute -bottom-1 -right-1 h-5 w-5 rounded-full bg-green-500 border-2 border-white" />
67+ </ div >
68+ < div className = "flex-1" >
69+ < h3 className = { `text-lg font-bold ${
70+ isDark ? "text-white" : "text-gray-900"
71+ } `} >
72+ { name }
73+ </ h3 >
74+ < p className = { `text-sm ${
75+ isDark ? "text-gray-400" : "text-gray-500"
76+ } `} >
77+ @{ username }
78+ </ p >
79+ </ div >
80+ </ div >
81+
82+ { /* Content */ }
83+ < div className = "flex-1" >
84+ < p className = { `text-base leading-relaxed ${
85+ isDark ? "text-gray-300" : "text-gray-700"
86+ } `} >
87+ { content . replace ( / # \w + / g, '' ) . trim ( ) }
6188 </ p >
6289 </ div >
63- </ div >
6490
65- { /* Content */ }
66- < p
67- className = { `my-4 line-clamp-3 flex-grow ${ isDark ? "text-gray-300" : "text-gray-700" } ` }
68- >
69- { content }
70- </ p >
91+ { /* Footer */ }
92+ < div className = { `mt-6 space-y-4 border-t pt-4 ${
93+ isDark ? "border-gray-700/50" : "border-gray-200/50"
94+ } `} >
95+ { /* Hashtags */ }
96+ < div className = "flex flex-wrap gap-2" >
97+ { content . match ( / # \w + / g) ?. map ( ( hashtag , index ) => (
98+ < span
99+ key = { index }
100+ className = { `rounded-full px-3 py-1 text-xs font-medium transition-colors hover:scale-105 ${
101+ isDark
102+ ? "bg-blue-500/20 text-blue-400 hover:bg-blue-500/30"
103+ : "bg-blue-100 text-blue-600 hover:bg-blue-200"
104+ } `}
105+ >
106+ { hashtag }
107+ </ span >
108+ ) ) }
109+ </ div >
71110
72- { /* Footer with Hashtags and Date */ }
73- < div
74- className = { `flex flex-col gap-2 border-t pt-2 text-sm ${
75- isDark ? "border-gray-700" : "border-gray-100"
76- } `}
77- >
78- { /* Hashtags */ }
79- < div className = "flex flex-wrap gap-2" >
80- { content . match ( / # \w + / g) ?. map ( ( hashtag , index ) => (
81- < span
82- key = { index }
83- className = "cursor-pointer text-blue-500 hover:text-blue-600"
111+ { /* Link and Date */ }
112+ < div className = "flex items-center justify-between" >
113+ < a
114+ href = { link }
115+ target = "_blank"
116+ rel = "noopener noreferrer"
117+ className = { `group/link flex items-center gap-2 text-sm font-medium transition-colors ${
118+ isDark
119+ ? "text-purple-400 hover:text-purple-300"
120+ : "text-purple-600 hover:text-purple-700"
121+ } `}
84122 >
85- { hashtag }
123+ < span className = "truncate" > { formatLinkDisplay ( link ) } </ span >
124+ < ExternalLink size = { 14 } className = "transition-transform group-hover/link:translate-x-0.5 group-hover/link:-translate-y-0.5" />
125+ </ a >
126+ < span className = { `text-xs ${
127+ isDark ? "text-gray-500" : "text-gray-400"
128+ } `} >
129+ { date }
86130 </ span >
87- ) ) }
88- </ div >
89-
90- { /* Link and Date Row */ }
91- < div className = "flex items-center justify-between" >
92- < a
93- href = { link }
94- target = "_blank"
95- rel = "noopener noreferrer"
96- className = { `cursor-pointer hover:underline ${
97- isDark
98- ? "text-blue-400 hover:text-blue-300"
99- : "text-blue-600 hover:text-blue-700"
100- } `}
101- >
102- { formatLinkDisplay ( link ) }
103- </ a >
104- < span className = { isDark ? "text-gray-500" : "text-gray-400" } >
105- { date }
106- </ span >
131+ </ div >
107132 </ div >
108133 </ div >
134+
135+ { /* Hover Effect Border */ }
136+ < div className = "absolute inset-0 rounded-2xl border-2 border-transparent bg-gradient-to-r from-purple-500/20 via-blue-500/20 to-pink-500/20 opacity-0 transition-opacity duration-300 group-hover:opacity-100"
137+ style = { { mask : 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)' , maskComposite : 'xor' } } />
109138 </ motion . div >
110139 ) ;
111140} ;
112141
113- export default TestimonialCard ;
142+ export default TestimonialCard ;
0 commit comments