vkashti / app / components / ClientContent.tsx
ClientContent.tsx
Raw
'use client';

import Link from 'next/link';
import Image from 'next/image';
import { motion } from 'framer-motion';
import { DynamicImport } from '@/components/DynamicImport';
import { REVIEWS } from '@/data/reviews';
import dynamic from 'next/dynamic';

// Dynamically import heavy components with proper code splitting
const LazyMap = dynamic(() => import('@/components/ui/Map'), {
  loading: () => <div className="h-[400px] w-full bg-slate-100 animate-pulse rounded-lg"></div>,
  ssr: false,
});

const LazyFAQ = dynamic(() => import('@/components/ui/FAQ').then(mod => mod.default), {
  loading: () => <div className="py-4 space-y-2" aria-label="Loading FAQs">
    {[1, 2, 3].map(i => (
      <div key={i} className="h-20 bg-slate-100 animate-pulse rounded-lg mb-2"></div>
    ))}
  </div>,
});

const LazyReviews = dynamic(() => import('@/components/ui/Reviews').then(mod => mod.default), {
  loading: () => <div className="py-4 space-y-2" aria-label="Loading reviews">
    {[1, 2, 3].map(i => (
      <div key={i} className="h-32 bg-slate-100 animate-pulse rounded-lg mb-2"></div>
    ))}
  </div>,
  ssr: false,
});

const LazyEvents = dynamic(() => import('@/app/events/page'), {
  loading: () => <div className="py-4" aria-label="Loading events">
    <div className="h-64 bg-slate-100 animate-pulse rounded-lg"></div>
  </div>,
  ssr: false,
});

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

const staggerContainer = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.1
    }
  }
};

const itemVariant = {
  hidden: { opacity: 0, y: 20 },
  visible: {
    opacity: 1,
    y: 0,
    transition: {
      duration: 0.5,
      ease: 'easeOut'
    }
  }
};

type ClientContentProps = {
  mediaItems: Array<{
    src: string;
    alt: string;
    width?: number;
    height?: number;
    quality?: number;
  }>;
  services: Array<{
    image: string;
    title: string;
    description: string;
    url: string;
    cta: string;
  }>;
  faq: Array<{
    question: string;
    answer: string;
  }>;
};

// Menu categories data
const MENU_CATEGORIES = [
  {
    icon: (
      <svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10 text-orange-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
        <path d="M18 8h1a4 4 0 0 1 0 8h-1"></path>
        <path d="M2 8h16v9a4 4 0 0 1-4 4H6a4 4 0 0 1-4-4V8z"></path>
        <line x1="6" y1="1" x2="6" y2="4"></line>
        <line x1="10" y1="1" x2="10" y2="4"></line>
        <line x1="14" y1="1" x2="14" y2="4"></line>
      </svg>
    ),
    title: "Майсторски Коктейли",
    description: "Класически, Сауър и наши уникални Авторски рецепти.",
    bgColor: "bg-orange-100",
    iconColor: "text-orange-600"
  },
  {
    icon: (
      <svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10 text-amber-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
        <path d="M8 22h8"></path>
        <path d="M7 10h10"></path>
        <path d="M12 15v-5"></path>
        <path d="M12 15a3 3 0 1 0 0 6 3 3 0 0 0 0-6z"></path>
        <path d="M17 10a5 5 0 0 0-10 0"></path>
      </svg>
    ),
    title: "Богат Бар",
    description: "Впечатляваща селекция от уиски, джин, водка, ром, ракия и други качествени алкохолни напитки.",
    bgColor: "bg-amber-100",
    iconColor: "text-amber-600"
  },
  {
    icon: (
      <svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10 text-amber-800" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
        <path d="M17 8h1a4 4 0 1 1 0 8h-1"></path>
        <path d="M3 8h14v9a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V8z"></path>
        <line x1="6" y1="1" x2="6" y2="4"></line>
        <line x1="10" y1="1" x2="10" y2="4"></line>
        <line x1="14" y1="1" x2="14" y2="4"></line>
      </svg>
    ),
    title: "Кафе Специалитети",
    description: "От перфектно еспресо до креативни латета и топъл шоколад.",
    bgColor: "bg-brown-100",
    iconColor: "text-amber-800"
  },
  {
    icon: (
      <svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10 text-blue-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
        <path d="M8 2h8"></path>
        <path d="M12 2v10"></path>
        <path d="M17 12a5 5 0 0 1-10 0"></path>
        <path d="M12 12v8"></path>
        <path d="M8 20h8"></path>
      </svg>
    ),
    title: "Свежест във Всяка Глътка",
    description: "Домашни лимонади, фрешове, смутита и разнообразие от безалкохолни.",
    bgColor: "bg-blue-100",
    iconColor: "text-blue-600"
  },
  {
    icon: (
      <svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10 text-red-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
        <path d="M10 6.5c1 -1 2 -1 3 -1s2 0 3 1c1 1 1 2 1 3c0 1 0 2 -1 3c-1 1 -2 1 -3 1s-2 0 -3 -1c-1 -1 -1 -2 -1 -3c0 -1 0 -2 1 -3z"></path>
        <path d="M9 6.5c0 3.5 -4 3.5 -4 6c0 1.5 1.5 2.5 3 2.5c1.5 0 3 -1 3 -2.5"></path>
        <path d="M9 18l3 3l3 -3"></path>
        <path d="M15 16l3.5 -5.5"></path>
      </svg>
    ),
    title: "Вино и Бира",
    description: "Подбрани вина на чаша/бутилка и микс от крафт и класически бири.",
    bgColor: "bg-red-100",
    iconColor: "text-red-600"
  },
  {
    icon: (
      <svg xmlns="http://www.w3.org/2000/svg" className="h-10 w-10 text-green-600" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
        <path d="M6 19c0 1 1 2 2 2h8c1 0 2 -1 2 -2"></path>
        <path d="M4 5v1a1 1 0 0 0 1 1h1"></path>
        <path d="M14 5v1a1 1 0 0 1 -1 1h-1"></path>
        <path d="M6 8v12"></path>
        <path d="M18 8v12"></path>
        <path d="M6 8h12"></path>
        <path d="M3 5a2 2 0 0 1 2 -2h2m4 0h2m4 0h2a2 2 0 0 1 2 2"></path>
      </svg>
    ),
    title: "Апетитна Храна",
    description: "Идеални за споделяне плата, хрупкави картофки, начос, сандвичи, салати и сладки изкушения.",
    bgColor: "bg-green-100",
    iconColor: "text-green-600"
  }
];

export function ClientContent({ mediaItems, services, faq }: ClientContentProps) {
  return (
    <>
      {/* Optimized Image Carousel */}
      <motion.div
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        transition={{ duration: 0.5, delay: 0.4 }}
        className="relative overflow-hidden"
      >
        <div 
          className="carousel carousel-center rounded-xl space-x-4 p-4 w-full"
          style={{ 
            scrollBehavior: 'smooth',
            overscrollBehavior: 'none',
            WebkitOverflowScrolling: 'touch'
          }}
          onScroll={(e) => {
            const target = e.target as HTMLDivElement;
            const scrollLeft = target.scrollLeft;
            const scrollWidth = target.scrollWidth;
            const clientWidth = target.clientWidth;

            // Only handle infinite scroll if we're actively scrolling
            if (target.dataset.isScrolling === 'true') {
              // If we're at the end, jump back to the middle without animation
              if (scrollLeft + clientWidth >= scrollWidth - 50) {
                target.scrollLeft = scrollWidth / 2;
              }
              // If we're at the start, jump to the middle without animation
              if (scrollLeft <= 50) {
                target.scrollLeft = scrollWidth / 2;
              }
            }
          }}
          onTouchStart={(e) => {
            const target = e.target as HTMLDivElement;
            target.dataset.isScrolling = 'true';
          }}
          onTouchEnd={(e) => {
            const target = e.target as HTMLDivElement;
            target.dataset.isScrolling = 'false';
          }}
          onMouseDown={(e) => {
            const target = e.target as HTMLDivElement;
            target.dataset.isScrolling = 'true';
          }}
          onMouseUp={(e) => {
            const target = e.target as HTMLDivElement;
            target.dataset.isScrolling = 'false';
          }}
        >
          {/* Duplicate last few images at the start for infinite scroll */}
          {mediaItems.slice(-3).map((item, index) => (
            <motion.div
              className="carousel-item"
              key={`start-${index}`}
              whileHover={{ scale: 1.02 }}
              transition={{ duration: 0.3 }}
            >
              <Image
                src={item.src}
                alt={item.alt}
                className="h-[300px] w-auto object-cover rounded-lg shadow-md"
                width={item.width || 400}
                height={item.height || 250}
                loading="lazy"
                sizes="(max-width: 640px) 100vw, (max-width: 768px) 50vw, 400px"
                quality={item.quality || 75}
              />
            </motion.div>
          ))}

          {/* Main images */}
          {mediaItems.map((item, index) => (
            <motion.div
              className="carousel-item"
              key={index}
              whileHover={{ scale: 1.02 }}
              transition={{ duration: 0.3 }}
            >
              <Image
                src={item.src}
                alt={item.alt}
                className="h-[300px] w-auto object-cover rounded-lg shadow-md"
                width={item.width || 400}
                height={item.height || 250}
                loading={index < 2 ? "eager" : "lazy"}
                fetchPriority={index === 0 ? "high" : "auto"}
                sizes="(max-width: 640px) 100vw, (max-width: 768px) 50vw, 400px"
                priority={index === 0}
                quality={item.quality || 75}
              />
            </motion.div>
          ))}

          {/* Duplicate first few images at the end for infinite scroll */}
          {mediaItems.slice(0, 3).map((item, index) => (
            <motion.div
              className="carousel-item"
              key={`end-${index}`}
              whileHover={{ scale: 1.02 }}
              transition={{ duration: 0.3 }}
            >
              <Image
                src={item.src}
                alt={item.alt}
                className="h-[300px] w-auto object-cover rounded-lg shadow-md"
                width={item.width || 400}
                height={item.height || 250}
                loading="lazy"
                sizes="(max-width: 640px) 100vw, (max-width: 768px) 50vw, 400px"
                quality={item.quality || 75}
              />
            </motion.div>
          ))}
        </div>
      </motion.div>

      <div className="h-20" />
      
      {/* Menu Section */}
      <motion.div
        initial="hidden"
        whileInView="visible"
        viewport={{ once: true, amount: 0.2 }}
        variants={fadeIn}
        transition={{ duration: 0.8 }}
      >
        <motion.h3
          className="text-2xl font-bold text-center md:text-3xl"
          variants={fadeIn}
        >
          Открийте Нашите Вкусове: Кафе, Коктейли, Храна и Още
        </motion.h3>
        <div className="h-5" />
        
        <motion.div
          className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 gap-y-6"
          variants={staggerContainer}
        >
          {MENU_CATEGORIES.map((category, index) => (
            <motion.div 
              key={index}
              className="flex flex-col items-center" 
              variants={itemVariant}
            >
              <div className={`p-4 rounded-full mb-3`}>
                {category.icon}
              </div>
              <h4 className="font-bold text-lg mb-1">{category.title}</h4>
              <p className="text-center text-gray-600">{category.description}</p>
            </motion.div>
          ))}
        </motion.div>
        
        <div className="h-8" />
        
        <motion.div 
          className="flex justify-center"
          variants={fadeIn}
          transition={{ delay: 0.4, duration: 0.8 }}
        >
          <Link href="/menu">
            <motion.button
              className="btn btn-outline"
              whileHover={{ scale: 1.05 }}
              whileTap={{ scale: 0.95 }}
            >
              Разгледай пълното меню
            </motion.button>
          </Link>
        </motion.div>
      </motion.div>

      <div className="h-20" />

      {/* Events Section */}
      <motion.div
        initial="hidden"
        whileInView="visible"
        viewport={{ once: true, amount: 0.2 }}
        variants={fadeIn}
        transition={{ duration: 0.8 }}
      >
        <DynamicImport loadingStrategy="on-visible">
          <LazyEvents />
        </DynamicImport>
      </motion.div>

      <div className="h-20" />

      {/* Services Section */}
      <motion.div
        initial="hidden"
        whileInView="visible"
        viewport={{ once: true, amount: 0.2 }}
        variants={fadeIn}
        transition={{ duration: 0.8 }}
      >
        <motion.h3
          className="text-2xl font-bold text-center md:text-3xl"
          variants={fadeIn}
        >
          Какво предлагаме
        </motion.h3>
        <div className="h-5" />
        <motion.div
          className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 gap-6"
          variants={staggerContainer}
        >
          {services.map((service, index) => (
            <motion.div
              className="card bg-white shadow-xl col-span-1 rounded-lg overflow-hidden"
              key={index}
              variants={itemVariant}
              whileHover={{
                y: -10,
                boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)'
              }}
              transition={{ duration: 0.3 }}
            >
              <figure className="relative overflow-hidden">
                <Image
                  className="rounded-t-lg w-full h-48 object-cover transition-transform duration-500"
                  src={service.image}
                  alt={service.title}
                  width={400}
                  height={300}
                  loading={index < 1 ? "eager" : "lazy"}
                  sizes="(max-width: 640px) 100vw, (max-width: 1200px) 50vw, 33vw"
                  quality={75}
                />
              </figure>
              <div className="card-body px-4 py-4">
                <h2 className="card-title font-bold">{service.title}</h2>
                <p className="text-gray-600">{service.description}</p>
                <div className="card-actions justify-end mt-2">
                  <Link href={service.url}>
                    <motion.button
                      className="btn btn-outline btn-sm"
                      whileHover={{ scale: 1.05 }}
                      whileTap={{ scale: 0.95 }}
                    >
                      {service.cta}
                    </motion.button>
                  </Link>
                </div>
              </div>
            </motion.div>
          ))}
        </motion.div>
      </motion.div>

      <div className="h-20" />

      {/* FAQ Section */}
      <motion.div
        initial="hidden"
        whileInView="visible"
        viewport={{ once: true, amount: 0.2 }}
        variants={fadeIn}
        transition={{ duration: 0.8 }}
      >
        <motion.h3
          className="text-2xl font-bold text-center md:text-3xl"
          variants={fadeIn}
        >
          Често задавани въпроси
        </motion.h3>
        <div className="h-5" />
        <DynamicImport loadingStrategy="on-visible">
          <LazyFAQ questions={faq} />
        </DynamicImport>
      </motion.div>

      <div className="h-20" />

      {/* Reviews Section */}
      <motion.div
        initial="hidden"
        whileInView="visible"
        viewport={{ once: true, amount: 0.2 }}
        variants={fadeIn}
        transition={{ duration: 0.8 }}
      >
        <motion.h3
          className="text-2xl font-bold text-center md:text-3xl"
          variants={fadeIn}
        >
          Какво казват нашите клиенти
        </motion.h3>
        <div className="h-5" />
        <motion.p
          className="text-lg text-center md:text-xl text-gray-600 mb-8"
          variants={fadeIn}
          transition={{ delay: 0.2, duration: 0.8 }}
        >
          Отзиви от Google Maps
        </motion.p>
        <DynamicImport loadingStrategy="on-visible">
          <LazyReviews reviews={REVIEWS} />
        </DynamicImport>
      </motion.div>

      <div className="h-20" />

      {/* Map Section - Load this last and only when visible */}
      <motion.div
        initial="hidden"
        whileInView="visible"
        viewport={{ once: true, amount: 0.2 }}
        variants={fadeIn}
        transition={{ duration: 0.8 }}
      >
        <motion.h3
          className="text-2xl font-bold text-center md:text-3xl"
          variants={fadeIn}
        >
          Къде се намираме
        </motion.h3>
        <div className="h-10" />
        <DynamicImport loadingStrategy="on-visible">
          <LazyMap />
        </DynamicImport>
      </motion.div>
    </>
  );
}