testimonial added

This commit is contained in:
JahidHasanPintu 2025-09-29 12:36:31 +06:00
parent d68dc98244
commit 3ed0667d99
8 changed files with 218 additions and 14 deletions

View file

@ -2,6 +2,22 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
images: {
remotePatterns: [
{
protocol: "https",
hostname: "api.dicebear.com",
port: "",
pathname: "/**",
},
{
protocol: "https",
hostname: "static.wikia.nocookie.net",
port: "",
pathname: "/**",
},
],
},
};
export default nextConfig;

26
package-lock.json generated
View file

@ -14,7 +14,7 @@
"@radix-ui/react-switch": "^1.1.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.4.7",
"framer-motion": "^12.23.22",
"lucide-react": "^0.476.0",
"next": "15.1.7",
"next-sitemap": "^4.2.3",
@ -3217,13 +3217,13 @@
}
},
"node_modules/framer-motion": {
"version": "12.4.7",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.4.7.tgz",
"integrity": "sha512-VhrcbtcAMXfxlrjeHPpWVu2+mkcoR31e02aNSR7OUS/hZAciKa8q6o3YN2mA1h+jjscRsSyKvX6E1CiY/7OLMw==",
"version": "12.23.22",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.22.tgz",
"integrity": "sha512-ZgGvdxXCw55ZYvhoZChTlG6pUuehecgvEAJz0BHoC5pQKW1EC5xf1Mul1ej5+ai+pVY0pylyFfdl45qnM1/GsA==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.4.5",
"motion-utils": "^12.0.0",
"motion-dom": "^12.23.21",
"motion-utils": "^12.23.6",
"tslib": "^2.4.0"
},
"peerDependencies": {
@ -4348,18 +4348,18 @@
}
},
"node_modules/motion-dom": {
"version": "12.4.5",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.4.5.tgz",
"integrity": "sha512-Q2xmhuyYug1CGTo0jdsL05EQ4RhIYXlggFS/yPhQQRNzbrhjKQ1tbjThx5Plv68aX31LsUQRq4uIkuDxdO5vRQ==",
"version": "12.23.21",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.21.tgz",
"integrity": "sha512-5xDXx/AbhrfgsQmSE7YESMn4Dpo6x5/DTZ4Iyy4xqDvVHWvFVoV+V2Ri2S/ksx+D40wrZ7gPYiMWshkdoqNgNQ==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.0.0"
"motion-utils": "^12.23.6"
}
},
"node_modules/motion-utils": {
"version": "12.0.0",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.0.0.tgz",
"integrity": "sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==",
"version": "12.23.6",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
"license": "MIT"
},
"node_modules/ms": {

View file

@ -15,7 +15,7 @@
"@radix-ui/react-switch": "^1.1.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"framer-motion": "^12.4.7",
"framer-motion": "^12.23.22",
"lucide-react": "^0.476.0",
"next": "15.1.7",
"next-sitemap": "^4.2.3",

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View file

@ -4,6 +4,7 @@ import FeatureSection from "@/components/FeatureSection";
import HeroSection from "@/components/HeroSection";
import PricingSection from "@/components/PricingSection";
import SocialSection from "@/components/SocialSection";
import TestimonialSection from "@/components/TestimonialSection";
import React from "react";
const Home = () => {
@ -238,6 +239,7 @@ const Home = () => {
<FeatureCardSection {...allInOneToolsData} />
<PricingSection />
<TestimonialSection />
<FAQSection />
</div>
);

View file

@ -0,0 +1,186 @@
"use client";
import { ChevronLeft, ChevronRight, Star } from "lucide-react";
import * as React from "react";
import { motion, AnimatePresence } from "framer-motion";
import Image from "next/image";
interface Testimonial {
id: string;
name: string;
role: string;
company: string;
image: string;
rating: number;
content: string;
}
const testimonials: Testimonial[] = [
{
id: "1",
name: "Mahmud Hasan ",
role: "Marketer",
company: "Freelancer",
image: "/assets/images/testimonial/mahmudulhasan.webp",
rating: 5,
content:
"I used to spend 50-60 hours every week managing my client's social media content and scheduling. With PlanPost AI, I now finish everything in just 2 hours! This has saved me huge amounts of time and allowed me to take on more clients—boosting both my productivity and income.",
},
{
id: "2",
name: "Abdus Salam Jomadder",
role: "E-commerce Entrepreneu",
company: "ফ্লাভিয়া",
image: "/assets/images/testimonial/Abdus-Salam-Jomadder.webp",
rating: 5,
content:
"My product images improved significantly with PlanPost AI. They look more professional and eye-catching, which directly helped me increase sales. The best part? I dont need any design skills—its simple, fast, and effective!",
},
{
id: "3",
name: "Farhana Akter",
role: "Owner",
company: "Small Business",
image: "/assets/images/testimonial/business-women.jpg",
rating: 5,
content:
"Staying consistent on social media was always tough. With PlanPost AIs bulk generation, I now create a months content in minutes and schedule it instantly. My brand engagement has grown by 3x, and I can focus more on scaling my business.",
},
];
export default function TestimonialSection() {
const [currentIndex, setCurrentIndex] = React.useState(0);
const [direction, setDirection] = React.useState(0);
const slidesPerView = 3;
const maxIndex = Math.max(0, testimonials.length - slidesPerView);
const handlePrevious = () => {
setDirection(-1);
setCurrentIndex((prev) => Math.max(0, prev - 1));
};
const handleNext = () => {
setDirection(1);
setCurrentIndex((prev) => Math.min(maxIndex, prev + 1));
};
const visibleTestimonials = testimonials.slice(
currentIndex,
currentIndex + slidesPerView
);
return (
<div className="py-16 px-4 md:px-6 lg:px-8 bg-white">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold mb-4">
What Our <span className="text-[#7c3aed]">Customers</span> Say
</h2>
<p className="text-gray-600 text-lg max-w-2xl mx-auto">
Join thousands of satisfied customers who have transformed their
social media presence with Planpost AI
</p>
</div>
<div className="relative">
<div className="overflow-hidden">
<AnimatePresence initial={false} custom={direction} mode="wait">
<motion.div
key={currentIndex}
custom={direction}
initial={{ opacity: 0, x: direction > 0 ? 100 : -100 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: direction > 0 ? -100 : 100 }}
transition={{
duration: 0.4,
ease: [0.4, 0, 0.2, 1],
}}
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"
>
{visibleTestimonials.map((testimonial) => (
<motion.div
key={testimonial.id}
whileHover={{ y: -8 }}
transition={{ duration: 0.3 }}
className="bg-[#f5f1ff] rounded-lg p-6 flex flex-col"
>
<div className="flex items-center gap-1 mb-4">
{[...Array(testimonial.rating)].map((_, i) => (
<Star
key={i}
className="w-5 h-5 fill-[#7c3aed] text-[#7c3aed]"
/>
))}
</div>
<p className="text-gray-700 mb-6 flex-grow">
{testimonial.content}
</p>
<div className="flex items-center gap-3">
<Image
src={testimonial.image}
alt={testimonial.name}
height={50}
width={50}
className="w-16 h-16 rounded-full bg-white"
/>
<div>
<h4 className="font-semibold text-gray-900">
{testimonial.name}
</h4>
<p className="text-sm text-gray-600">
{testimonial.role} at {testimonial.company}
</p>
</div>
</div>
</motion.div>
))}
</motion.div>
</AnimatePresence>
</div>
<div className="flex items-center justify-center gap-4 mt-8">
<motion.button
onClick={handlePrevious}
disabled={currentIndex === 0}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
className="w-12 h-12 rounded-full bg-[#7c3aed] text-white flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed hover:bg-[#6d28d9] transition-colors"
>
<ChevronLeft className="w-6 h-6" />
</motion.button>
<div className="flex gap-2">
{[...Array(maxIndex + 1)].map((_, index) => (
<button
key={index}
onClick={() => {
setDirection(index > currentIndex ? 1 : -1);
setCurrentIndex(index);
}}
className={`h-2 rounded-full transition-all duration-300 ${
index === currentIndex
? "w-8 bg-[#7c3aed]"
: "w-2 bg-gray-300 hover:bg-gray-400"
}`}
/>
))}
</div>
<motion.button
onClick={handleNext}
disabled={currentIndex === maxIndex}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.95 }}
className="w-12 h-12 rounded-full bg-[#7c3aed] text-white flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed hover:bg-[#6d28d9] transition-colors"
>
<ChevronRight className="w-6 h-6" />
</motion.button>
</div>
</div>
</div>
</div>
);
}