Merge version_2 into main #4
@@ -1,203 +1,168 @@
|
||||
"use client";
|
||||
|
||||
import { memo } from "react";
|
||||
import AutoCarousel from "@/components/cardStack/layouts/carousels/AutoCarousel";
|
||||
import TestimonialAuthor from "@/components/shared/TestimonialAuthor";
|
||||
import { cls, shouldUseInvertedText } from "@/lib/utils";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
import { Quote } from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type { CardAnimationType, ButtonConfig, ButtonAnimationType, TitleSegment, TextboxLayout, InvertedBackground } from "@/components/cardStack/types";
|
||||
import { useState } from "react";
|
||||
|
||||
type Testimonial = {
|
||||
id: string;
|
||||
name: string;
|
||||
handle: string;
|
||||
testimonial: string;
|
||||
imageSrc?: string;
|
||||
imageAlt?: string;
|
||||
icon?: LucideIcon;
|
||||
};
|
||||
interface Testimonial {
|
||||
id: number;
|
||||
author: string;
|
||||
role: string;
|
||||
company: string;
|
||||
shortText: string;
|
||||
fullText: string;
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
interface TestimonialCardSixProps {
|
||||
testimonials: Testimonial[];
|
||||
animationType: CardAnimationType;
|
||||
title: string;
|
||||
titleSegments?: TitleSegment[];
|
||||
description: string;
|
||||
textboxLayout: TextboxLayout;
|
||||
useInvertedBackground: InvertedBackground;
|
||||
tag?: string;
|
||||
tagIcon?: LucideIcon;
|
||||
tagAnimation?: ButtonAnimationType;
|
||||
buttons?: ButtonConfig[];
|
||||
buttonAnimation?: ButtonAnimationType;
|
||||
speed?: number;
|
||||
topMarqueeDirection?: "left" | "right";
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
containerClassName?: string;
|
||||
carouselClassName?: string;
|
||||
bottomCarouselClassName?: string;
|
||||
cardClassName?: string;
|
||||
testimonialClassName?: string;
|
||||
imageWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
iconClassName?: string;
|
||||
nameClassName?: string;
|
||||
handleClassName?: string;
|
||||
textBoxClassName?: string;
|
||||
textBoxTitleClassName?: string;
|
||||
textBoxTitleImageWrapperClassName?: string;
|
||||
textBoxTitleImageClassName?: string;
|
||||
textBoxDescriptionClassName?: string;
|
||||
textBoxTagClassName?: string;
|
||||
textBoxButtonContainerClassName?: string;
|
||||
textBoxButtonClassName?: string;
|
||||
textBoxButtonTextClassName?: string;
|
||||
testimonials?: Testimonial[];
|
||||
}
|
||||
|
||||
interface TestimonialCardProps {
|
||||
testimonial: Testimonial;
|
||||
useInvertedBackground: boolean;
|
||||
cardClassName?: string;
|
||||
testimonialClassName?: string;
|
||||
imageWrapperClassName?: string;
|
||||
imageClassName?: string;
|
||||
iconClassName?: string;
|
||||
nameClassName?: string;
|
||||
handleClassName?: string;
|
||||
}
|
||||
const defaultTestimonials: Testimonial[] = [
|
||||
{
|
||||
id: 1,
|
||||
author: "Sarah Johnson",
|
||||
role: "Product Manager",
|
||||
company: "TechCorp",
|
||||
shortText: "Amazing service!",
|
||||
fullText:
|
||||
"This service has completely transformed how we manage our projects. The intuitive interface and powerful features have saved us countless hours. The support team is incredibly responsive and helpful. I would highly recommend this to any team looking to improve their workflow.",
|
||||
avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Sarah",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
author: "Michael Chen",
|
||||
role: "CEO",
|
||||
company: "StartupXYZ",
|
||||
shortText: "Game changer!",
|
||||
fullText:
|
||||
"We've been using this platform for 6 months now and the results speak for themselves. Our team productivity has increased by 40%, and the ROI has been exceptional. The platform scales beautifully with our growing needs. Couldn't ask for a better solution.",
|
||||
avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Michael",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
author: "Emily Rodriguez",
|
||||
role: "Design Lead",
|
||||
company: "Creative Studios",
|
||||
shortText: "Exceeded expectations!",
|
||||
fullText:
|
||||
"The design and user experience are top-notch. Every detail has been thoughtfully considered. The collaboration features make it easy for our distributed team to work together seamlessly. This is the kind of tool that makes you wonder how you ever worked without it.",
|
||||
avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=Emily",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
author: "David Park",
|
||||
role: "Operations Director",
|
||||
company: "Global Solutions",
|
||||
shortText: "Best investment ever!",
|
||||
fullText:
|
||||
"We've tried many solutions before, but this one stands out. The implementation was smooth, the training was comprehensive, and the ongoing support is exceptional. Our team adopted it immediately and we're seeing measurable improvements in efficiency and accuracy.",
|
||||
avatar: "https://api.dicebear.com/7.x/avataaars/svg?seed=David",
|
||||
},
|
||||
];
|
||||
|
||||
const TestimonialCard = memo(({
|
||||
testimonial,
|
||||
useInvertedBackground,
|
||||
cardClassName = "",
|
||||
testimonialClassName = "",
|
||||
imageWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
iconClassName = "",
|
||||
nameClassName = "",
|
||||
handleClassName = "",
|
||||
}: TestimonialCardProps) => {
|
||||
const Icon = testimonial.icon || Quote;
|
||||
const theme = useTheme();
|
||||
const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle);
|
||||
export default function TestimonialCardSix({
|
||||
testimonials = defaultTestimonials,
|
||||
}: TestimonialCardSixProps) {
|
||||
const [flipped, setFlipped] = useState<{ [key: number]: boolean }>({});
|
||||
|
||||
return (
|
||||
<div className={cls("relative h-full card rounded-theme-capped p-6 min-h-0 flex flex-col gap-10", cardClassName)}>
|
||||
<p className={cls("relative z-1 text-lg leading-tight line-clamp-2", shouldUseLightText ? "text-background" : "text-foreground", testimonialClassName)}>
|
||||
{testimonial.testimonial}
|
||||
</p>
|
||||
const toggleFlip = (id: number) => {
|
||||
setFlipped((prev) => ({
|
||||
...prev,
|
||||
[id]: !prev[id],
|
||||
}));
|
||||
};
|
||||
|
||||
<TestimonialAuthor
|
||||
name={testimonial.name}
|
||||
subtitle={testimonial.handle}
|
||||
imageSrc={testimonial.imageSrc}
|
||||
imageAlt={testimonial.imageAlt}
|
||||
icon={Icon}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
imageWrapperClassName={imageWrapperClassName}
|
||||
imageClassName={imageClassName}
|
||||
iconClassName={iconClassName}
|
||||
nameClassName={nameClassName}
|
||||
subtitleClassName={handleClassName}
|
||||
/>
|
||||
return (
|
||||
<section className="py-12 px-4 sm:px-6 lg:px-8 bg-gradient-to-br from-slate-50 to-slate-100">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-3xl sm:text-4xl font-bold text-gray-900 mb-4">
|
||||
What Our Clients Say
|
||||
</h2>
|
||||
<p className="text-lg text-gray-600">
|
||||
Hover over cards to see full testimonials
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
TestimonialCard.displayName = "TestimonialCard";
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{testimonials.map((testimonial) => (
|
||||
<div
|
||||
key={testimonial.id}
|
||||
className="h-80 cursor-pointer perspective"
|
||||
onClick={() => toggleFlip(testimonial.id)}
|
||||
>
|
||||
<div
|
||||
className="relative w-full h-full transition-transform duration-500 transform-gpu"
|
||||
style={{
|
||||
transformStyle: "preserve-3d",
|
||||
transform: flipped[testimonial.id]
|
||||
? "rotateY(180deg)"
|
||||
: "rotateY(0deg)",
|
||||
}}
|
||||
>
|
||||
{/* Front of card */}
|
||||
<div
|
||||
className="absolute w-full h-full bg-white rounded-lg shadow-lg p-6 flex flex-col justify-between"
|
||||
style={{ backfaceVisibility: "hidden" }}
|
||||
>
|
||||
<div>
|
||||
<div className="flex items-center gap-4 mb-4">
|
||||
{testimonial.avatar && (
|
||||
<img
|
||||
src={testimonial.avatar}
|
||||
alt={testimonial.author}
|
||||
className="w-12 h-12 rounded-full"
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900">
|
||||
{testimonial.author}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
{testimonial.role}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-gray-700 text-sm mb-4">
|
||||
"{testimonial.shortText}"
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-xs text-gray-500">{testimonial.company}</p>
|
||||
<div className="flex gap-1">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<span key={i} className="text-yellow-400">
|
||||
★
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
const TestimonialCardSix = ({
|
||||
testimonials,
|
||||
animationType,
|
||||
title,
|
||||
titleSegments,
|
||||
description,
|
||||
textboxLayout,
|
||||
useInvertedBackground,
|
||||
tag,
|
||||
tagIcon,
|
||||
tagAnimation,
|
||||
buttons,
|
||||
buttonAnimation,
|
||||
speed = 40,
|
||||
topMarqueeDirection = "left",
|
||||
ariaLabel = "Testimonials section",
|
||||
className = "",
|
||||
containerClassName = "",
|
||||
carouselClassName = "",
|
||||
bottomCarouselClassName = "",
|
||||
cardClassName = "",
|
||||
testimonialClassName = "",
|
||||
imageWrapperClassName = "",
|
||||
imageClassName = "",
|
||||
iconClassName = "",
|
||||
nameClassName = "",
|
||||
handleClassName = "",
|
||||
textBoxClassName = "",
|
||||
textBoxTitleClassName = "",
|
||||
textBoxTitleImageWrapperClassName = "",
|
||||
textBoxTitleImageClassName = "",
|
||||
textBoxDescriptionClassName = "",
|
||||
textBoxTagClassName = "",
|
||||
textBoxButtonContainerClassName = "",
|
||||
textBoxButtonClassName = "",
|
||||
textBoxButtonTextClassName = "",
|
||||
}: TestimonialCardSixProps) => {
|
||||
return (
|
||||
<AutoCarousel
|
||||
speed={speed}
|
||||
uniformGridCustomHeightClasses="min-h-none"
|
||||
animationType={animationType}
|
||||
title={title}
|
||||
titleSegments={titleSegments}
|
||||
description={description}
|
||||
tag={tag}
|
||||
tagIcon={tagIcon}
|
||||
tagAnimation={tagAnimation}
|
||||
buttons={buttons}
|
||||
buttonAnimation={buttonAnimation}
|
||||
textboxLayout={textboxLayout}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
showTextBox={true}
|
||||
dualMarquee={true}
|
||||
topMarqueeDirection={topMarqueeDirection}
|
||||
carouselClassName={carouselClassName}
|
||||
bottomCarouselClassName={bottomCarouselClassName}
|
||||
containerClassName={containerClassName}
|
||||
className={className}
|
||||
textBoxClassName={textBoxClassName}
|
||||
titleClassName={textBoxTitleClassName}
|
||||
titleImageWrapperClassName={textBoxTitleImageWrapperClassName}
|
||||
titleImageClassName={textBoxTitleImageClassName}
|
||||
descriptionClassName={textBoxDescriptionClassName}
|
||||
tagClassName={textBoxTagClassName}
|
||||
buttonContainerClassName={textBoxButtonContainerClassName}
|
||||
buttonClassName={textBoxButtonClassName}
|
||||
buttonTextClassName={textBoxButtonTextClassName}
|
||||
ariaLabel={ariaLabel}
|
||||
itemClassName="w-60! md:w-carousel-item-3! xl:w-carousel-item-4!"
|
||||
>
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<TestimonialCard
|
||||
key={`${testimonial.id}-${index}`}
|
||||
testimonial={testimonial}
|
||||
useInvertedBackground={useInvertedBackground}
|
||||
cardClassName={cardClassName}
|
||||
testimonialClassName={testimonialClassName}
|
||||
imageWrapperClassName={imageWrapperClassName}
|
||||
imageClassName={imageClassName}
|
||||
iconClassName={iconClassName}
|
||||
nameClassName={nameClassName}
|
||||
handleClassName={handleClassName}
|
||||
/>
|
||||
))}
|
||||
</AutoCarousel>
|
||||
);
|
||||
};
|
||||
|
||||
TestimonialCardSix.displayName = "TestimonialCardSix";
|
||||
|
||||
export default TestimonialCardSix;
|
||||
{/* Back of card */}
|
||||
<div
|
||||
className="absolute w-full h-full bg-gradient-to-br from-blue-600 to-blue-800 rounded-lg shadow-lg p-6 flex flex-col justify-between text-white"
|
||||
style={{
|
||||
backfaceVisibility: "hidden",
|
||||
transform: "rotateY(180deg)",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<h3 className="font-semibold text-lg mb-3">
|
||||
{testimonial.author}
|
||||
</h3>
|
||||
<p className="text-sm text-blue-100 leading-relaxed">
|
||||
"{testimonial.fullText}"
|
||||
</p>
|
||||
</div>
|
||||
<p className="text-xs text-blue-200 text-center">
|
||||
Click to flip back
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user