122 lines
4.4 KiB
TypeScript
122 lines
4.4 KiB
TypeScript
"use client";
|
|
import { useState, useEffect } from "react";
|
|
import { motion, AnimatePresence } from "framer-motion";
|
|
import Loader from '@/app/(landing)/components/Loader';
|
|
import { useTestimonials } from '@/app/(landing)/home/services/testimonials';
|
|
import type { ThemeProps } from '@/app/(landing)/types/ui';
|
|
|
|
export const Testimonial = ({ isNavyBg }: ThemeProps) => {
|
|
const { data, isLoading } = useTestimonials();
|
|
const [current, setCurrent] = useState(0);
|
|
|
|
const testimonials = data ?? [];
|
|
|
|
useEffect(() => {
|
|
if (testimonials.length < 2) return;
|
|
const t = setInterval(
|
|
() => setCurrent((i) => (i + 1) % testimonials.length),
|
|
6000,
|
|
);
|
|
return () => clearInterval(t);
|
|
}, [testimonials.length]);
|
|
|
|
const item = testimonials[current];
|
|
const bg = isNavyBg ? "var(--background)" : "#f9f9f7";
|
|
const border = isNavyBg ? "rgba(255,255,255,0.08)" : "rgba(0,0,0,0.08)";
|
|
const sub = isNavyBg ? "rgba(255,255,255,0.45)" : "#6a6866";
|
|
const text = isNavyBg ? "var(--foreground)" : "#0a0a0a";
|
|
|
|
if (!isLoading && testimonials.length === 0) return null; // sección se oculta si no hay testimonios
|
|
|
|
return (
|
|
<section
|
|
aria-label="Testimonios de clientes"
|
|
style={{ background: bg }}
|
|
className="px-8 py-24 md:px-14 transition-colors duration-500"
|
|
>
|
|
<div className="mx-auto max-w-4xl">
|
|
<div
|
|
aria-hidden="true"
|
|
className="mx-auto mb-8 flex h-12 w-12 items-center justify-center rounded-full"
|
|
style={{ background: "rgba(59,130,246,0.1)" }}
|
|
>
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="#3b82f6">
|
|
<path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z" />
|
|
<path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z" />
|
|
</svg>
|
|
</div>
|
|
|
|
{isLoading ? (
|
|
<Loader className="py-12" />
|
|
) : (
|
|
<>
|
|
<AnimatePresence mode="wait">
|
|
<motion.div
|
|
key={current}
|
|
initial={{ opacity: 0, y: 16 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
exit={{ opacity: 0, y: -16 }}
|
|
transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
|
|
className="text-center"
|
|
>
|
|
<blockquote>
|
|
<p
|
|
className="font-display font-bold leading-snug"
|
|
style={{
|
|
fontSize: "clamp(1.25rem, 2.5vw, 1.75rem)",
|
|
color: text,
|
|
letterSpacing: "-0.02em",
|
|
maxWidth: "36ch",
|
|
margin: "0 auto 2.5rem",
|
|
}}
|
|
>
|
|
"{item.quote}"
|
|
</p>
|
|
</blockquote>
|
|
<div className="flex flex-col items-center gap-1">
|
|
{item.author && (
|
|
<p
|
|
className="font-display font-bold"
|
|
style={{ color: text, fontSize: "0.95rem" }}
|
|
>
|
|
{item.author}
|
|
</p>
|
|
)}
|
|
<p
|
|
className="text-sm font-semibold"
|
|
style={{ color: "#3b82f6" }}
|
|
>
|
|
{item.company}
|
|
</p>
|
|
</div>
|
|
</motion.div>
|
|
</AnimatePresence>
|
|
|
|
{testimonials.length > 1 && (
|
|
<div className="flex justify-center gap-2 mt-10">
|
|
{testimonials.map((_, i) => (
|
|
<button
|
|
key={i}
|
|
onClick={() => setCurrent(i)}
|
|
style={{
|
|
width: i === current ? 24 : 8,
|
|
height: 8,
|
|
borderRadius: 20,
|
|
background: i === current ? "#3b82f6" : sub,
|
|
border: "none",
|
|
cursor: "pointer",
|
|
padding: 0,
|
|
transition: "width 300ms, background 300ms",
|
|
}}
|
|
aria-label={`Testimonio ${i + 1}`}
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|