Skip to content

Commit 846b0ca

Browse files
authored
fix: add avatar fallback in testimonial and developer testimonial com… (#423)
* fix: add avatar fallback in testimonial and developer testimonial components * chore: format code with Prettier
1 parent 430ad17 commit 846b0ca

File tree

2 files changed

+54
-18
lines changed

2 files changed

+54
-18
lines changed

src/components/DeveloperTestimonials.tsx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React from 'react';
12
import { motion } from 'framer-motion';
23
import { Marquee } from '@/components/magicui/Marquee';
34
import { developertestimonials } from '@/constants/VolunteerAndDev/DeveloperTestimonials';
@@ -24,6 +25,11 @@ const ReviewCard = ({
2425
body: string;
2526
delay?: number;
2627
}) => {
28+
const [imgError, setImgError] = React.useState(false);
29+
30+
// First initial of name
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}
@@ -93,7 +110,7 @@ export function DeveloperTestimonials() {
93110
/>
94111

95112
<div className="relative flex items-center justify-center gap-4 md:gap-6 lg:gap-8">
96-
{/* Left Apostrophe (Hidden Below 400px) */}
113+
{/* Left Apostrophe */}
97114
<motion.img
98115
src={stats.apostrophie}
99116
alt="Apostrophe Left"
@@ -113,7 +130,7 @@ export function DeveloperTestimonials() {
113130
</span>
114131
</motion.h2>
115132

116-
{/* Right Apostrophe (Flipped, Hidden Below 400px) */}
133+
{/* Right Apostrophe */}
117134
<motion.img
118135
src={stats.apostrophie}
119136
alt="Apostrophe Right"
@@ -152,6 +169,7 @@ export function DeveloperTestimonials() {
152169
))}
153170
</Marquee>
154171

172+
{/* Gradient edges */}
155173
<div className="pointer-events-none absolute inset-y-0 left-0 w-1/4 bg-gradient-to-r from-background"></div>
156174
<div className="pointer-events-none absolute inset-y-0 right-0 w-1/4 bg-gradient-to-l from-background"></div>
157175
</motion.div>

src/components/Testimonials.tsx

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import React from 'react';
12
import { motion } from 'framer-motion';
23
import { Marquee } from '@/components/magicui/Marquee'; // Marquee for scrolling effect
34
import { 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

Comments
 (0)