vkashti / components / ui / Reviews.tsx
Reviews.tsx
Raw
import React, { useRef } from 'react';
import Image from 'next/image';
import { motion } from 'framer-motion';
import { FaStar, FaStarHalfAlt, FaRegStar } from 'react-icons/fa';
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';

// Define the interface for a single review
export interface Review {
  personName: string;
  stars: number;
  content: string;
  date: string;
  googleMapsUrl: string;
  avatarUrl?: string; // Optional avatar image
}

// Interface for the component props
interface ReviewsProps {
  reviews: Review[];
}

// Helper function to render stars
const renderStars = (rating: number) => {
  const stars = [];
  const fullStars = Math.floor(rating);
  const hasHalfStar = rating % 1 !== 0;
  
  // Add full stars
  for (let i = 0; i < fullStars; i++) {
    stars.push(<FaStar key={`full-${i}`} className="text-yellow-400" />);
  }
  
  // Add half star if needed
  if (hasHalfStar) {
    stars.push(<FaStarHalfAlt key="half" className="text-yellow-400" />);
  }
  
  // Add empty stars
  const emptyStars = 5 - fullStars - (hasHalfStar ? 1 : 0);
  for (let i = 0; i < emptyStars; i++) {
    stars.push(<FaRegStar key={`empty-${i}`} className="text-yellow-400" />);
  }
  
  return stars;
};

// Animation variants
const fadeIn = {
  hidden: { opacity: 0, y: 20 },
  visible: { opacity: 1, y: 0 }
};

const Reviews: React.FC<ReviewsProps> = ({ reviews }) => {
  const carouselRef = useRef<HTMLDivElement>(null);

  // Function to scroll left
  const scrollLeft = () => {
    if (carouselRef.current) {
      carouselRef.current.scrollBy({ left: -300, behavior: 'smooth' });
    }
  };

  // Function to scroll right
  const scrollRight = () => {
    if (carouselRef.current) {
      carouselRef.current.scrollBy({ left: 300, behavior: 'smooth' });
    }
  };

  return (
    <div className="w-full relative">
      {/* Left Navigation Button */}
      <button
        onClick={scrollLeft}
        className="absolute left-0 top-1/2 transform -translate-y-1/2 z-10 bg-white/80 hover:bg-white text-gray-700 rounded-full p-2 shadow-md focus:outline-none"
        aria-label="Scroll left"
      >
        <FaChevronLeft className="h-5 w-5" />
      </button>

      <div className="relative overflow-hidden">
        <div 
          ref={carouselRef}
          className="carousel carousel-center space-x-4 p-4 w-full"
          style={{ scrollBehavior: 'smooth' }}
        >
          {reviews.map((review, index) => (
            <motion.div
              key={index}
              className="carousel-item"
              whileHover={{ scale: 1.03 }}
              transition={{ duration: 0.3 }}
            >
              <a
                href={review.googleMapsUrl}
                target="_blank"
                rel="noopener noreferrer"
                className="block w-72 bg-white rounded-xl shadow-lg p-5 cursor-pointer hover:shadow-xl transition-shadow duration-300"
              >
                <div className="flex items-center mb-3">
                  {review.avatarUrl ? (
                    <div className="mr-3">
                      <Image 
                        src={review.avatarUrl} 
                        alt={review.personName} 
                        width={40} 
                        height={40} 
                        className="rounded-full" 
                        unoptimized
                        onError={(e) => {
                          // Replace with a fallback avatar
                          const target = e.target as HTMLImageElement;
                          target.style.display = 'none';
                          target.parentElement!.innerHTML = `
                            <div class="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center">
                              <span class="text-gray-600 font-bold text-lg">
                                ${review.personName.charAt(0)}
                              </span>
                            </div>
                          `;
                        }}
                      />
                    </div>
                  ) : (
                    <div className="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center mr-3">
                      <span className="text-gray-600 font-bold text-lg">
                        {review.personName.charAt(0)}
                      </span>
                    </div>
                  )}
                  <div>
                    <h4 className="font-bold">{review.personName}</h4>
                    <div className="flex">
                      {renderStars(review.stars)}
                    </div>
                  </div>
                </div>
                <p className="text-gray-600 mb-3 line-clamp-5">{review.content}</p>
                <p className="text-gray-400 text-sm">{review.date}</p>
              </a>
            </motion.div>
          ))}
        </div>
        
        {/* Show navigation indicators for mobile */}
        <div className="flex justify-center mt-4 md:hidden">
          <div className="flex space-x-2">
            {reviews.map((_, index) => (
              <div 
                key={index} 
                className="w-2 h-2 rounded-full bg-gray-300"
              />
            ))}
          </div>
        </div>
      </div>

      {/* Right Navigation Button */}
      <button
        onClick={scrollRight}
        className="absolute right-0 top-1/2 transform -translate-y-1/2 z-10 bg-white/80 hover:bg-white text-gray-700 rounded-full p-2 shadow-md focus:outline-none"
        aria-label="Scroll right"
      >
        <FaChevronRight className="h-5 w-5" />
      </button>
    </div>
  );
};

export default Reviews;