1+ import React from 'react' ;
12import { motion } from 'framer-motion' ;
23import { Marquee } from '@/components/magicui/Marquee' ; // Marquee for scrolling effect
34import { testimonials } from '@/constants/Testimonials' ;
@@ -24,6 +25,11 @@ const ReviewCard = ({
2425 body : string ;
2526 delay ?: number ;
2627} ) => {
28+ const [ imgError , setImgError ] = React . useState ( false ) ;
29+
30+ // Extract first initial
31+ const initial = name ?. charAt ( 0 ) . toUpperCase ( ) || '?' ;
32+
2733 return (
2834 < motion . div
2935 className = "bg-white dark:bg-gray-900 rounded-xl p-6 flex flex-col items-center text-center min-h-[250px] h-auto w-[350px] shadow-lg border border-gray-200 dark:border-gray-700 mx-2 justify-between"
@@ -52,12 +58,23 @@ const ReviewCard = ({
5258
5359 { /* User Info */ }
5460 < div className = "flex items-center mt-4 space-x-3 text-left" >
55- < motion . img
56- src = { img }
57- alt = { name }
58- className = "w-12 h-12 rounded-full border border-gray-300"
59- variants = { avatarReveal }
60- />
61+ { ! imgError ? (
62+ < motion . img
63+ src = { img }
64+ alt = { name }
65+ className = "w-12 h-12 rounded-full border border-gray-300 object-cover"
66+ variants = { avatarReveal }
67+ onError = { ( ) => setImgError ( true ) }
68+ />
69+ ) : (
70+ < motion . div
71+ className = "w-12 h-12 flex items-center justify-center rounded-full border border-gray-300 bg-gray-400 text-white text-lg font-bold"
72+ variants = { avatarReveal }
73+ >
74+ { initial }
75+ </ motion . div >
76+ ) }
77+
6178 < motion . div variants = { testimonialText } >
6279 < h3 className = "text-lg font-semibold text-gray-900 dark:text-white" >
6380 { name }
@@ -96,7 +113,7 @@ export function Testimonials() {
96113 />
97114
98115 < div className = "relative flex items-center justify-center gap-4 md:gap-6 lg:gap-8" >
99- { /* Left Apostrophe (Hidden Below 400px) */ }
116+ { /* Left Apostrophe */ }
100117 < motion . img
101118 src = { stats . apostrophie }
102119 alt = "Apostrophe Left"
@@ -116,7 +133,7 @@ export function Testimonials() {
116133 </ span >
117134 </ motion . h2 >
118135
119- { /* Right Apostrophe (Flipped, Hidden Below 400px) */ }
136+ { /* Right Apostrophe */ }
120137 < motion . img
121138 src = { stats . apostrophie }
122139 alt = "Apostrophe Right"
@@ -145,7 +162,7 @@ export function Testimonials() {
145162 viewport = { { once : true , amount : 0.1 } }
146163 variants = { marqueeContainer }
147164 >
148- { /* First Row (left to right) */ }
165+ { /* First Row */ }
149166 < Marquee pauseOnHover className = "w-full" >
150167 { firstRow . map ( ( review , index ) => (
151168 < ReviewCard
@@ -156,7 +173,7 @@ export function Testimonials() {
156173 ) ) }
157174 </ Marquee >
158175
159- { /* Second Row (right to left) */ }
176+ { /* Second Row */ }
160177 < Marquee reverse pauseOnHover className = "w-full mt-4" >
161178 { secondRow . map ( ( review , index ) => (
162179 < ReviewCard
@@ -167,6 +184,7 @@ export function Testimonials() {
167184 ) ) }
168185 </ Marquee >
169186
187+ { /* Gradient Fades */ }
170188 < div className = "pointer-events-none absolute inset-y-0 left-0 w-6 md:w-10 bg-gradient-to-r from-background" > </ div >
171189 < div className = "pointer-events-none absolute inset-y-0 right-0 w-6 md:w-10 bg-gradient-to-l from-background" > </ div >
172190 </ motion . div >
0 commit comments