From 753be9690f95c288aec2ab92269529131626254d Mon Sep 17 00:00:00 2001
From: Miqdad <ahmadmiqdad27@gmail.com>
Date: Fri, 22 Aug 2025 15:30:08 +0700
Subject: <Miqdad> Image slider when mobile

---
 .../product-detail/components/ProductDetail.tsx    | 177 ++++++++++++++++-----
 1 file changed, 134 insertions(+), 43 deletions(-)

(limited to 'src-migrate/modules/product-detail/components/ProductDetail.tsx')

diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx
index 192e1dc3..a6b4e6de 100644
--- a/src-migrate/modules/product-detail/components/ProductDetail.tsx
+++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx
@@ -2,7 +2,7 @@ import style from '../styles/product-detail.module.css';
 
 import Link from 'next/link';
 import { useRouter } from 'next/router';
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState, UIEvent } from 'react';
 
 import { Button } from '@chakra-ui/react';
 import { MessageCircleIcon, Share2Icon } from 'lucide-react';
@@ -35,6 +35,7 @@ const ProductDetail = ({ product }: Props) => {
   const { isDesktop, isMobile } = useDevice();
   const router = useRouter();
   const auth = getAuth();
+
   const {
     setAskAdminUrl,
     askAdminUrl,
@@ -71,70 +72,168 @@ const ProductDetail = ({ product }: Props) => {
       product?.variants?.find((variant) => variant.is_in_bu) ||
       product?.variants?.[0];
     setSelectedVariant(selectedVariant);
-    // setSelectedVariant(product?.variants[0])
-  }, []);
+  }, []); // eslint-disable-line react-hooks/exhaustive-deps
 
   // Gabungkan semua gambar produk (utama + tambahan)
-  const allImages = product.image_carousel ? [...product.image_carousel] : [];
-
-  if (product.image) {
-    allImages.unshift(product.image); // Tambahkan gambar utama di awal array
-  }
-  console.log(product);
+  // Gabungkan semua gambar produk (utama + tambahan)
+  const allImages = (() => {
+    const arr: string[] = [];
+    if (product?.image) arr.push(product.image); // selalu masukkan utama, baik mobile maupun desktop
+    if (
+      Array.isArray(product?.image_carousel) &&
+      product.image_carousel.length
+    ) {
+      // hindari duplikat jika image utama juga ada di carousel
+      const set = new Set(arr);
+      for (const img of product.image_carousel) {
+        if (!set.has(img)) {
+          arr.push(img);
+          set.add(img);
+        }
+      }
+    }
+    return arr;
+  })();
 
   const [mainImage, setMainImage] = useState(allImages[0] || '');
 
+  useEffect(() => {
+    // update mainImage jika sumber gambar berubah dan mainImage tidak ada di daftar
+    if (!allImages.includes(mainImage)) {
+      setMainImage(allImages[0] || '');
+    }
+  }, [allImages]); // eslint-disable-line react-hooks/exhaustive-deps
+
+  // ===== Slider mobile (tanpa dependency) =====
+  const sliderRef = useRef<HTMLDivElement | null>(null);
+  const [currentIdx, setCurrentIdx] = useState(0);
+
+  const handleMobileScroll = (e: UIEvent<HTMLDivElement>) => {
+    const el = e.currentTarget;
+    if (!el) return;
+    const idx = Math.round(el.scrollLeft / el.clientWidth);
+    if (idx !== currentIdx) {
+      setCurrentIdx(idx);
+      setMainImage(allImages[idx] || '');
+    }
+  };
+
+  const scrollToIndex = (i: number) => {
+    const el = sliderRef.current;
+    if (!el) return;
+    el.scrollTo({ left: i * el.clientWidth, behavior: 'smooth' });
+    setCurrentIdx(i);
+    setMainImage(allImages[i] || '');
+  };
+  // ============================================
+
   return (
     <>
       <div className='md:flex md:flex-wrap'>
         <div className='w-full mb-4 md:mb-0 px-4 md:px-0'>
           <Breadcrumb id={product.id} name={product.name} />
         </div>
+
         <div className='md:w-9/12 md:flex md:flex-col md:pr-4 md:pt-6'>
           <div className='md:flex md:flex-wrap'>
+            {/* ===== Kolom kiri: gambar ===== */}
             <div className='md:w-4/12'>
-              <ProductImage product={{ ...product, image: mainImage }} />
-
-              {/* Carousel horizontal */}
-              {allImages.length > 0 && (
-                <div className='mt-4 overflow-x-auto'>
-                  <div className='flex space-x-3 pb-3'>
-                    {allImages.map((img, index) => (
-                      <div
-                        key={index}
-                        className={`flex-shrink-0 w-16 h-16 cursor-pointer border-2 rounded-md transition-colors ${
-                          mainImage === img
-                            ? 'border-red-500 ring-2 ring-red-200'
-                            : 'border-gray-200 hover:border-gray-300'
-                        }`}
-                        onClick={() => setMainImage(img)}
-                      >
+              {/* === MOBILE: Slider swipeable, tanpa thumbnail carousel === */}
+              {isMobile ? (
+                <div className='relative'>
+                  <div
+                    ref={sliderRef}
+                    onScroll={handleMobileScroll}
+                    className='flex overflow-x-auto snap-x snap-mandatory scroll-smooth no-scrollbar'
+                    style={{
+                      scrollBehavior: 'smooth',
+                      msOverflowStyle: 'none', // IE/Edge lama
+                      scrollbarWidth: 'none', // Firefox
+                    }}
+                  >
+                    {allImages.length > 0 ? (
+                      allImages.map((img, i) => (
                         <img
+                          key={i}
                           src={img}
-                          alt={`Thumbnail ${index + 1}`}
-                          className='w-full h-full object-cover rounded-sm'
-                          loading='lazy'
+                          alt={`Gambar ${i + 1}`}
+                          className='w-full aspect-square object-cover flex-shrink-0 snap-center'
                           onError={(e) => {
                             (e.target as HTMLImageElement).src =
                               '/path/to/fallback-image.jpg';
                           }}
                         />
-                      </div>
-                    ))}
+                      ))
+                    ) : (
+                      <img
+                        src={mainImage || '/path/to/fallback-image.jpg'}
+                        alt='Gambar produk'
+                        className='w-full aspect-square object-cover flex-shrink-0 snap-center'
+                      />
+                    )}
                   </div>
+
+                  {/* Dots indicator */}
+                  {allImages.length > 1 && (
+                    <div className='absolute bottom-2 left-0 right-0 flex justify-center gap-2'>
+                      {allImages.map((_, i) => (
+                        <button
+                          key={i}
+                          aria-label={`Ke slide ${i + 1}`}
+                          className={`w-2 h-2 rounded-full ${
+                            currentIdx === i ? 'bg-gray-800' : 'bg-gray-300'
+                          }`}
+                          onClick={() => scrollToIndex(i)}
+                        />
+                      ))}
+                    </div>
+                  )}
                 </div>
+              ) : (
+                <>
+                  {/* === DESKTOP: Tetap seperti sebelumnya === */}
+                  <ProductImage product={{ ...product, image: mainImage }} />
+
+                  {/* Carousel horizontal (thumbnail) – hanya desktop */}
+                  {allImages.length > 0 && (
+                    <div className='mt-4 overflow-x-auto'>
+                      <div className='flex space-x-3 pb-3'>
+                        {allImages.map((img, index) => (
+                          <div
+                            key={index}
+                            className={`flex-shrink-0 w-16 h-16 cursor-pointer border-2 rounded-md transition-colors ${
+                              mainImage === img
+                                ? 'border-red-500 ring-2 ring-red-200'
+                                : 'border-gray-200 hover:border-gray-300'
+                            }`}
+                            onClick={() => setMainImage(img)}
+                          >
+                            <img
+                              src={img}
+                              alt={`Thumbnail ${index + 1}`}
+                              className='w-full h-full object-cover rounded-sm'
+                              loading='lazy'
+                              onError={(e) => {
+                                (e.target as HTMLImageElement).src =
+                                  '/path/to/fallback-image.jpg';
+                              }}
+                            />
+                          </div>
+                        ))}
+                      </div>
+                    </div>
+                  )}
+                </>
               )}
             </div>
+            {/* <<=== TUTUP kolom kiri */}
 
+            {/* ===== Kolom kanan: info ===== */}
             <div className='md:w-8/12 px-4 md:pl-6'>
               <div className='h-6 md:h-0' />
-
               <h1 className={style['title']}>{product.name}</h1>
-
               <div className='h-3 md:h-0' />
-
               <Information product={product} />
-
               <div className='h-6' />
             </div>
           </div>
@@ -154,14 +253,6 @@ const ProductDetail = ({ product }: Props) => {
               />
             )}
 
-            {/* <div className={style['section-card']}>
-              <h2 className={style['heading']}>
-                Variant ({product.variant_total})
-              </h2>
-              <div className='h-4' />
-              <VariantList variants={product.variants} />
-            </div> */}
-
             <div className='h-0 md:h-6' />
 
             <div className={style['section-card']}>
@@ -246,4 +337,4 @@ const ProductDetail = ({ product }: Props) => {
   );
 };
 
-export default ProductDetail;
+export default ProductDetail;
\ No newline at end of file
-- 
cgit v1.2.3

