summaryrefslogtreecommitdiff
path: root/src2/components/products
diff options
context:
space:
mode:
Diffstat (limited to 'src2/components/products')
-rw-r--r--src2/components/products/ProductCard.js69
-rw-r--r--src2/components/products/ProductCategories.js62
-rw-r--r--src2/components/products/ProductSimilar.js25
-rw-r--r--src2/components/products/ProductSlider.js39
4 files changed, 195 insertions, 0 deletions
diff --git a/src2/components/products/ProductCard.js b/src2/components/products/ProductCard.js
new file mode 100644
index 00000000..c79a4900
--- /dev/null
+++ b/src2/components/products/ProductCard.js
@@ -0,0 +1,69 @@
+import Link from "../elements/Link";
+import currencyFormat from "@/core/utils/currencyFormat";
+import { createSlug } from "@/core/utils/slug";
+import { ChevronRightIcon } from "@heroicons/react/20/solid";
+import Image from "../elements/Image";
+
+
+export default function ProductCard({
+ data,
+ simpleProductTitleLine = false
+}) {
+ let product = data;
+ return (
+ <div className="product-card">
+ <Link href={'/shop/product/' + createSlug(product.name, product.id)} className="block relative bg-white">
+ <Image
+ src={product.image}
+ alt={product.name}
+ className="product-card__image"
+ />
+ {product.variant_total > 1 ? (
+ <div className="absolute bottom-2 left-2 badge-gray">{product.variant_total} Varian</div>
+ ) : ''}
+ </Link>
+ <div className="product-card__content">
+ <div>
+ {typeof product.manufacture.name !== "undefined" ? (
+ <Link href={'/shop/brands/' + createSlug(product.manufacture.name, product.manufacture.id)} className="product-card__brand">{product.manufacture.name}</Link>
+ ) : (
+ <span className="product-card__brand">-</span>
+ )}
+ <Link href={'/shop/product/' + createSlug(product.name, product.id)} className={`product-card__title ${simpleProductTitleLine ? 'wrap-line-ellipsis-2' : 'wrap-line-ellipsis-3'}`}>
+ {product.name}
+ </Link>
+ </div>
+ <div className="mt-2">
+ {product.lowest_price.discount_percentage > 0 ? (
+ <div className="flex gap-x-1 items-center mb-1">
+ <p className="text-caption-2 text-gray_r-11 line-through">{currencyFormat(product.lowest_price.price)}</p>
+ <span className="badge-solid-red">{product.lowest_price.discount_percentage}%</span>
+ </div>
+ ) : ''}
+
+ {product.lowest_price.price_discount > 0 ? (
+ <p className="text-caption-1 text-gray_r-12 font-bold">
+ {currencyFormat(product.lowest_price.price_discount)}
+ </p>
+ ) : (
+ <a
+ href="https://wa.me"
+ target="_blank"
+ rel="noreferrer"
+ className="flex items-center gap-x-1 text-caption-1"
+ >
+ Tanya Harga <ChevronRightIcon className="text-yellow_r-11 w-5 h-5" />
+ </a>
+ )}
+
+ {product.stock_total > 0 ? (
+ <div className="flex gap-x-1 mt-2">
+ <div className="badge-solid-red">Ready Stock</div>
+ <div className="badge-gray">{product.stock_total > 5 ? '> 5' : '< 5'}</div>
+ </div>
+ ) : ''}
+ </div>
+ </div>
+ </div>
+ )
+} \ No newline at end of file
diff --git a/src2/components/products/ProductCategories.js b/src2/components/products/ProductCategories.js
new file mode 100644
index 00000000..3b671f29
--- /dev/null
+++ b/src2/components/products/ProductCategories.js
@@ -0,0 +1,62 @@
+import { useEffect, useState } from "react";
+import ProductSlider from "./ProductSlider";
+import apiOdoo from "@/core/utils/apiOdoo";
+import { LazyLoadComponent } from "react-lazy-load-image-component";
+import { SkeletonProduct } from "../elements/Skeleton";
+
+const ProductCategory = ({ id }) => {
+ const [ content, setContent ] = useState(null);
+
+ useEffect(() => {
+ const loadContent = async () => {
+ if (!content) {
+ const dataContent = await apiOdoo('GET', `/api/v1/categories_homepage?id=${id}`);
+ setContent(dataContent[0]);
+ }
+ }
+ loadContent();
+ }, [id, content]);
+
+ return (
+ <div className="p-4 relative bg-yellow_r-2">
+ { content ? (
+ <ProductSlider
+ products={{
+ products: content.products,
+ banner: {
+ image: content.image,
+ name: content.name,
+ url: `/shop/search?category=${content.name}`
+ }
+ }}
+ simpleProductTitleLine
+ bannerMode
+ />
+ ) : <SkeletonProduct /> }
+ </div>
+ );
+}
+
+export default function ProductCategories() {
+ const [ contentIds, setContentIds ] = useState([]);
+
+ useEffect(() => {
+ const getContentIds = async () => {
+ if (contentIds.length == 0) {
+ const dataContentIds = await apiOdoo('GET', '/api/v1/categories_homepage/ids');
+ setContentIds(dataContentIds);
+ }
+ }
+ getContentIds();
+ }, [ contentIds ]);
+
+ return (
+ <div className="flex flex-col gap-y-6">
+ { contentIds.map((contentId) => (
+ <LazyLoadComponent placeholder={<SkeletonProduct/>} key={contentId}>
+ <ProductCategory id={contentId} />
+ </LazyLoadComponent>
+ )) }
+ </div>
+ )
+} \ No newline at end of file
diff --git a/src2/components/products/ProductSimilar.js b/src2/components/products/ProductSimilar.js
new file mode 100644
index 00000000..9e2292cb
--- /dev/null
+++ b/src2/components/products/ProductSimilar.js
@@ -0,0 +1,25 @@
+import apiOdoo from '@/core/utils/apiOdoo';
+import { useEffect, useState } from 'react';
+import ProductSlider from './ProductSlider';
+
+export default function ProductSimilar({ productId }) {
+ const [similarProducts, setSimilarProducts] = useState(null);
+
+ useEffect(() => {
+ const getSimilarProducts = async () => {
+ if (productId && !similarProducts) {
+ const dataSimilarProducts = await apiOdoo('GET', `/api/v1/product/${productId}/similar?limit=20`);
+ setSimilarProducts(dataSimilarProducts);
+ }
+ }
+ getSimilarProducts();
+ }, [productId, similarProducts]);
+
+
+ return (
+ <div className="p-4">
+ <h2 className="font-bold mb-4">Kamu Mungkin Juga Suka</h2>
+ <ProductSlider products={similarProducts}/>
+ </div>
+ )
+} \ No newline at end of file
diff --git a/src2/components/products/ProductSlider.js b/src2/components/products/ProductSlider.js
new file mode 100644
index 00000000..662a6511
--- /dev/null
+++ b/src2/components/products/ProductSlider.js
@@ -0,0 +1,39 @@
+import { Swiper, SwiperSlide } from "swiper/react";
+import ProductCard from "./ProductCard";
+import "swiper/css";
+import Image from "../elements/Image";
+import Link from "../elements/Link";
+import { SkeletonProduct } from "../elements/Skeleton";
+import { useState } from "react";
+
+export default function ProductSlider({
+ products,
+ simpleProductTitleLine = false,
+ bannerMode = false
+}) {
+ const [ activeIndex, setActiveIndex ] = useState(0);
+ const swiperSliderFirstMove = (swiper) => {
+ setActiveIndex(swiper.activeIndex);
+ };
+
+ return (
+ <>
+ { bannerMode && (
+ <Image src={products.banner.image} alt={products.banner.name} className={`absolute rounded-r top-0 left-0 h-full max-w-[52%] idt-transition border border-gray_r-6 ` + (activeIndex > 0 ? 'opacity-0' : 'opacity-100')} />
+ ) }
+ <Swiper freeMode={true} slidesPerView={2.2} spaceBetween={8} onSlideChange={swiperSliderFirstMove} prefix="product">
+ { bannerMode && (
+ <SwiperSlide>
+ <Link href={products.banner.url} className="w-full h-full block"></Link>
+ </SwiperSlide>
+ ) }
+ {products?.products?.map((product, index) => (
+ <SwiperSlide key={index}>
+ <ProductCard data={product} simpleProductTitleLine={simpleProductTitleLine} />
+ </SwiperSlide>
+ ))}
+ </Swiper>
+ { !products ? <SkeletonProduct /> : ''}
+ </>
+ )
+} \ No newline at end of file