Files
aramesh-backend/frontend/src/components/home/Testimonials.tsx
T

86 lines
4.2 KiB
TypeScript

import React, { useState } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import { ChevronLeftIcon, ChevronRightIcon, QuoteIcon } from 'lucide-react';
import { SectionHeading } from '../SectionHeading';
import { useSite } from '../../api';
import { useLang } from '../../i18n/lang';
import { EditableItem } from '../../admin/Editable';
export function Testimonials() {
const { testimonials } = useSite();
const { t } = useLang();
const [index, setIndex] = useState(0);
const current = testimonials[Math.min(index, Math.max(testimonials.length - 1, 0))];
if (!current) return null;
const go = (dir: number) => {
setIndex((prev) => (prev + dir + testimonials.length) % testimonials.length);
};
return (
<section className="border-y border-ivory/10 bg-char py-24 lg:py-32" aria-labelledby="testimonials-title">
<div className="mx-auto max-w-site px-6 lg:px-10">
<SectionHeading headingKey="testimonials" align="center" />
<div className="relative mx-auto mt-14 max-w-3xl text-center">
<QuoteIcon className="mx-auto h-8 w-8 text-copper/60" aria-hidden="true" />
<AnimatePresence mode="wait">
<motion.blockquote
key={current.id}
initial={{ opacity: 0, y: 16 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -12 }}
transition={{ duration: 0.28, ease: [0.23, 1, 0.32, 1] }}>
<EditableItem resource="testimonials" item={current}>
<p className="mt-8 text-lg font-light leading-[2.4rem] text-ivory/85 md:text-xl">«{current.quote}»</p>
<footer className="mt-8">
<p className="text-base font-bold text-copper">{current.name}</p>
<p className="mt-1 text-sm font-light text-ivory/45">{current.role}</p>
</footer>
</EditableItem>
</motion.blockquote>
</AnimatePresence>
<div className="mt-12 flex items-center justify-center gap-5">
<button
type="button"
onClick={() => go(1)}
aria-label={t('prevTestimonial')}
className="group flex h-12 w-12 items-center justify-center rounded-full border border-copper/35 bg-char/80 text-ivory/80 backdrop-blur-md shadow-[0_4px_20px_rgba(0,0,0,0.35)] transition-all duration-300 ease-lux hover:scale-110 hover:border-copper hover:bg-copper hover:text-char hover:shadow-[0_0_25px_rgba(197,143,92,0.45)] active:scale-95"
>
<ChevronRightIcon className="h-5 w-5 ltr:rotate-180 transition-transform duration-300 ease-lux rtl:group-hover:translate-x-0.5 ltr:group-hover:-translate-x-0.5" />
</button>
<div className="flex items-center gap-2" role="tablist" aria-label={t('selectTestimonialAria')}>
{testimonials.map((item, i) => (
<button
key={item.id}
type="button"
role="tab"
aria-selected={i === index}
aria-label={`${t("testimonialByPrefix")} ${item.name}`}
onClick={() => setIndex(i)}
className={`h-2 rounded-full transition-all duration-300 ease-lux ${
i === index
? 'w-8 bg-copper shadow-[0_0_12px_rgba(197,143,92,0.6)]'
: 'w-2.5 bg-ivory/20 hover:bg-ivory/40'
}`}
/>
))}
</div>
<button
type="button"
onClick={() => go(-1)}
aria-label={t('nextTestimonial')}
className="group flex h-12 w-12 items-center justify-center rounded-full border border-copper/35 bg-char/80 text-ivory/80 backdrop-blur-md shadow-[0_4px_20px_rgba(0,0,0,0.35)] transition-all duration-300 ease-lux hover:scale-110 hover:border-copper hover:bg-copper hover:text-char hover:shadow-[0_0_25px_rgba(197,143,92,0.45)] active:scale-95"
>
<ChevronLeftIcon className="h-5 w-5 ltr:rotate-180 transition-transform duration-300 ease-lux rtl:group-hover:-translate-x-0.5 ltr:group-hover:translate-x-0.5" />
</button>
</div>
</div>
</div>
</section>
);
}