summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json3
-rw-r--r--src-migrate/modules/product-detail/components/Information.tsx126
-rw-r--r--src-migrate/modules/product-detail/components/PriceAction.tsx45
-rw-r--r--src-migrate/modules/product-detail/components/ProductDetail.tsx194
-rw-r--r--src-migrate/modules/product-detail/stores/useProductDetail.ts12
-rw-r--r--src/pages/_app.jsx2
6 files changed, 242 insertions, 140 deletions
diff --git a/package.json b/package.json
index 28fbc5d8..98d4077c 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"dependencies": {
"@chakra-ui/next-js": "^2.1.5",
"@chakra-ui/react": "^2.8.1",
+ "@choc-ui/chakra-autocomplete": "^5.6.2",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@heroicons/react": "^2.0.13",
@@ -25,7 +26,7 @@
"clsx": "^2.0.0",
"cookies-next": "^2.1.1",
"flowbite": "^1.6.4",
- "framer-motion": "^7.10.3",
+ "framer-motion": "^11.3.28",
"http-proxy-middleware": "^3.0.0",
"lodash-contrib": "^4.1200.1",
"lucide-react": "^0.279.0",
diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx
index 75ae3c41..5d70e534 100644
--- a/src-migrate/modules/product-detail/components/Information.tsx
+++ b/src-migrate/modules/product-detail/components/Information.tsx
@@ -1,56 +1,130 @@
-import style from '../styles/information.module.css'
+import style from '../styles/information.module.css';
+import {
+ AutoComplete,
+ AutoCompleteInput,
+ AutoCompleteItem,
+ AutoCompleteList,
+} from '@choc-ui/chakra-autocomplete';
-import React from 'react'
-import dynamic from 'next/dynamic'
-import Link from 'next/link'
-import { useQuery } from 'react-query'
+import React, { useEffect, useState } from 'react';
+import dynamic from 'next/dynamic';
+import Link from 'next/link';
+import { useQuery } from 'react-query';
-import { IProductDetail } from '~/types/product'
-import { IProductVariantSLA } from '~/types/productVariant'
-import { createSlug } from '~/libs/slug'
-import { getVariantSLA } from '~/services/productVariant'
-import { formatToShortText } from '~/libs/formatNumber'
+import { IProductDetail } from '~/types/product';
+import { IProductVariantSLA } from '~/types/productVariant';
+import { createSlug } from '~/libs/slug';
+import { getVariantSLA } from '~/services/productVariant';
+import { formatToShortText } from '~/libs/formatNumber';
+import currencyFormat from '@/core/utils/currencyFormat';
+import { Icon, InputGroup, InputRightElement } from '@chakra-ui/react';
+import { CheckIcon, ChevronDownIcon, FingerPrintIcon } from '@heroicons/react/24/outline';
+import { useProductDetail } from '../stores/useProductDetail';
-const Skeleton = dynamic(() => import('@chakra-ui/react').then((mod) => mod.Skeleton))
+const Skeleton = dynamic(() =>
+ import('@chakra-ui/react').then((mod) => mod.Skeleton)
+);
type Props = {
- product: IProductDetail
-}
+ product: IProductDetail;
+};
const Information = ({ product }: Props) => {
+ const { selectedVariant, setSelectedVariant, setSla, setActive, } = useProductDetail()
+ const variantOptions = product?.variants;
+
const querySLA = useQuery<IProductVariantSLA>({
- queryKey: ['variant-sla', product.variants[0]?.id],
- queryFn: () => getVariantSLA(product.variants[0].id),
- enabled: product.variant_total === 1
- })
+ queryKey: ['variant-sla', selectedVariant?.id],
+ queryFn: () => getVariantSLA(selectedVariant?.id),
+ enabled: selectedVariant?.id === 1,
+ });
+ const sla = querySLA?.data;
+
+ useEffect(() => {
+ setSla(querySLA?.data);
+ }, [selectedVariant]);
- const sla = querySLA?.data
+ const handleOnChange = (vals: any) => {
+ let code = vals.split(" ")[0];
+ let variant = variantOptions.find((item) => item.code === code);
+ setSelectedVariant(variant);
+ }
return (
<div className={style['wrapper']}>
+ <div className='realtive mb-5'>
+ <label className='form-label mb-2 text-lg'>Pilih Variant * : <span className='text-gray_r-9 text-sm'>{product?.variant_total} Variants</span> </label>
+ <AutoComplete openOnFocus className='form-input' onChange={vals => handleOnChange(vals)}>
+ <InputGroup>
+ <AutoCompleteInput value={selectedVariant?.code + ' - ' + selectedVariant?.attributes[0]} />
+ <InputRightElement>
+ <ChevronDownIcon className='h-6 w-6 text-gray-500' />
+ </InputRightElement>
+ </InputGroup>
+
+ <AutoCompleteList>
+ {variantOptions.map((option, cid) => (
+ <AutoCompleteItem
+ key={`option-${cid}`}
+ value={option.code + ' - ' + option.attributes[0]}
+ textTransform='capitalize'
+ >
+ <div className='flex gap-x-2 justify-between w-full'>
+ <div className='text-small'>
+ {option.code + ' - ' + option.attributes[0]}
+ </div>
+ <div className='grid grid-cols-3 items-start'>
+ <div className='badge-solid-red text-xs'>
+ {Math.floor(option?.price?.discount_percentage)}%
+ </div>
+ <div className='text-gray_r-11 line-through text-[11px] sm:text-caption-2'>
+ {currencyFormat(option?.price?.price)}
+ </div>
+ <div className='text-danger-500 font-semibold mb-2'>
+ {currencyFormat(option?.price?.price_discount)}
+ </div>
+ </div>
+ </div>
+ </AutoCompleteItem>
+ ))}
+ </AutoCompleteList>
+ </AutoComplete>
+ </div>
<div className={style['row']}>
- <div className={style['label']}>SKU Number</div>
- <div className={style['value']}>SKU-{product.id}</div>
+ <div className={style['label']}>Item Code</div>
+ <div className={style['value']}>{selectedVariant?.code}</div>
</div>
<div className={style['row']}>
<div className={style['label']}>Manufacture</div>
<div className={style['value']}>
{!!product.manufacture.name ? (
<Link
- href={createSlug('/shop/brands/', product.manufacture.name, product.manufacture.id.toString())}
+ href={createSlug(
+ '/shop/brands/',
+ product.manufacture.name,
+ product.manufacture.id.toString()
+ )}
className='text-danger-500 hover:underline'
>
{product.manufacture.name}
</Link>
- ) : '-'}
+ ) : (
+ '-'
+ )}
</div>
</div>
<div className={style['row']}>
<div className={style['label']}>Terjual</div>
- <div className={style['value']}>{product.qty_sold > 0 ? formatToShortText(product.qty_sold) : '-'}</div>
+ <div className={style['value']}>
+ {product.qty_sold > 0 ? formatToShortText(product.qty_sold) : '-'}
+ </div>
+ </div>
+ <div className={style['row']}>
+ <div className={style['label']}>Persiapan Barang</div>
+ <div className={style['value']}>{sla?.sla_date}</div>
</div>
</div>
- )
-}
+ );
+};
-export default Information \ No newline at end of file
+export default Information;
diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx
index 81271f6e..405eb12b 100644
--- a/src-migrate/modules/product-detail/components/PriceAction.tsx
+++ b/src-migrate/modules/product-detail/components/PriceAction.tsx
@@ -22,23 +22,22 @@ const PriceAction = ({ product }: Props) => {
askAdminUrl,
isApproval,
setIsApproval,
+ selectedVariant,
+ sla,
} = useProductDetail();
useEffect(() => {
- setActive(product.variants[0])
- if(product.variants.length > 2 && product.variants[0].price.price === 0){
- const variants = product.variants
+ setActive(selectedVariant);
+ if (product.variants.length > 2 && product.variants[0].price.price === 0) {
+ const variants = product.variants;
for (let i = 0; i < variants.length; i++) {
- if(variants[i].price.price > 0){
- setActive(variants[i])
+ if (variants[i].price.price > 0) {
+ setActive(variants[i]);
break;
}
}
}
-
- }, [product, setActive]);
-
-
+ }, [product, setActive, selectedVariant]);
return (
<div
@@ -84,18 +83,26 @@ const PriceAction = ({ product }: Props) => {
)}
<div className='h-4' />
+ <div className='flex gap-x-5 items-center'>
+ <div>
+ <label htmlFor='quantity' className='hidden'>
+ Quantity
+ </label>
+ <input
+ type='number'
+ id='quantity'
+ value={quantityInput}
+ onChange={(e) => setQuantityInput(e.target.value)}
+ className={style['quantity-input']}
+ />
+ </div>
+ <div>
+ <span className={sla?.qty < 10 ? 'text-red-600 font-medium' : ''} > Stock : {sla?.qty} </span>
+ </div>
+ </div>
+ <div className='h-4' />
<div className={style['action-wrapper']}>
- <label htmlFor='quantity' className='hidden'>
- Quantity
- </label>
- <input
- type='number'
- id='quantity'
- value={quantityInput}
- onChange={(e) => setQuantityInput(e.target.value)}
- className={style['quantity-input']}
- />
<AddToCart
variantId={activeVariantId}
quantity={Number(quantityInput)}
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx
index fad35a7d..0997466c 100644
--- a/src-migrate/modules/product-detail/components/ProductDetail.tsx
+++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx
@@ -1,46 +1,53 @@
-import style from '../styles/product-detail.module.css'
-
-import Link from 'next/link'
-import { useRouter } from 'next/router'
-import { useEffect } from 'react'
-
-import { Button } from '@chakra-ui/react'
-import { MessageCircleIcon, Share2Icon } from 'lucide-react'
-import { LazyLoadComponent } from 'react-lazy-load-image-component'
-import { RWebShare } from 'react-web-share'
-
-import useDevice from '@/core/hooks/useDevice'
-import { whatsappUrl } from '~/libs/whatsappUrl'
-import ProductPromoSection from '~/modules/product-promo/components/Section'
-import { IProductDetail } from '~/types/product'
-import { useProductDetail } from '../stores/useProductDetail'
-import AddToWishlist from './AddToWishlist'
-import Breadcrumb from './Breadcrumb'
-import ProductImage from './Image'
-import Information from './Information'
-import PriceAction from './PriceAction'
-import SimilarBottom from './SimilarBottom'
-import SimilarSide from './SimilarSide'
-import VariantList from './VariantList'
-import { getAuth } from '~/libs/auth'
-
-import { gtagProductDetail } from '@/core/utils/googleTag'
+import style from '../styles/product-detail.module.css';
+
+import Link from 'next/link';
+import { useRouter } from 'next/router';
+import { useEffect } from 'react';
+
+import { Button } from '@chakra-ui/react';
+import { MessageCircleIcon, Share2Icon } from 'lucide-react';
+import { LazyLoadComponent } from 'react-lazy-load-image-component';
+import { RWebShare } from 'react-web-share';
+
+import useDevice from '@/core/hooks/useDevice';
+import { whatsappUrl } from '~/libs/whatsappUrl';
+import ProductPromoSection from '~/modules/product-promo/components/Section';
+import { IProductDetail } from '~/types/product';
+import { useProductDetail } from '../stores/useProductDetail';
+import AddToWishlist from './AddToWishlist';
+import Breadcrumb from './Breadcrumb';
+import ProductImage from './Image';
+import Information from './Information';
+import PriceAction from './PriceAction';
+import SimilarBottom from './SimilarBottom';
+import SimilarSide from './SimilarSide';
+import VariantList from './VariantList';
+import { getAuth } from '~/libs/auth';
+
+import { gtagProductDetail } from '@/core/utils/googleTag';
type Props = {
- product: IProductDetail
-}
+ product: IProductDetail;
+};
-const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST
+const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST;
const ProductDetail = ({ product }: Props) => {
- const { isDesktop, isMobile } = useDevice()
- const router = useRouter()
- const auth = getAuth()
- const { setAskAdminUrl, askAdminUrl, activeVariantId, setIsApproval, isApproval } = useProductDetail()
+ const { isDesktop, isMobile } = useDevice();
+ const router = useRouter();
+ const auth = getAuth();
+ const {
+ setAskAdminUrl,
+ askAdminUrl,
+ activeVariantId,
+ setIsApproval,
+ isApproval,
+ setSelectedVariant,
+ } = useProductDetail();
useEffect(() => {
gtagProductDetail(product);
- },[product])
+ }, [product]);
useEffect(() => {
const createdAskUrl = whatsappUrl({
@@ -48,76 +55,43 @@ const ProductDetail = ({ product }: Props) => {
payload: {
manufacture: product.manufacture.name,
productName: product.name,
- url: process.env.NEXT_PUBLIC_SELF_HOST + router.asPath
+ url: process.env.NEXT_PUBLIC_SELF_HOST + router.asPath,
},
- fallbackUrl: router.asPath
- })
+ fallbackUrl: router.asPath,
+ });
- setAskAdminUrl(createdAskUrl)
- }, [router.asPath, product.manufacture.name, product.name, setAskAdminUrl])
+ setAskAdminUrl(createdAskUrl);
+ }, [router.asPath, product.manufacture.name, product.name, setAskAdminUrl]);
useEffect(() => {
if (typeof auth === 'object') {
setIsApproval(auth?.feature?.soApproval);
}
+ setSelectedVariant(product?.variants[0])
}, []);
return (
<>
<div className='md:flex md:flex-wrap'>
- <div className="w-full mb-4 md:mb-0 px-4 md:px-0">
+ <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'>
- <div className="md:w-4/12">
+ <div className='md:w-4/12'>
<ProductImage product={product} />
</div>
<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>
+ <h1 className={style['title']}>{product.name}</h1>
<div className='h-6 md:h-8' />
<Information product={product} />
<div className='h-6' />
-
- <div className="flex gap-x-5">
- <Button
- as={Link}
- href={askAdminUrl}
- variant='link'
- target='_blank'
- colorScheme='gray'
- leftIcon={<MessageCircleIcon size={18} />}
- >
- Ask Admin
- </Button>
-
- <AddToWishlist productId={product.id} />
-
- <RWebShare
- data={{
- text: 'Check out this product',
- title: `${product.name} - Indoteknik.com`,
- url: SELF_HOST + router.asPath
- }}
- >
- <Button
- variant='link'
- colorScheme='gray'
- leftIcon={<Share2Icon size={18} />}
- >
- Share
- </Button>
- </RWebShare>
- </div>
-
</div>
</div>
@@ -129,7 +103,9 @@ const ProductDetail = ({ product }: Props) => {
)}
<div className='h-4 md:h-10' />
- {!!activeVariantId && !isApproval && <ProductPromoSection productId={activeVariantId} />}
+ {!!activeVariantId && !isApproval && (
+ <ProductPromoSection productId={activeVariantId} />
+ )}
<div className={style['section-card']}>
<h2 className={style['heading']}>
@@ -142,27 +118,61 @@ const ProductDetail = ({ product }: Props) => {
<div className='h-0 md:h-6' />
<div className={style['section-card']}>
- <h2 className={style['heading']}>
- Informasi Produk
- </h2>
+ <h2 className={style['heading']}>Informasi Produk</h2>
<div className='h-4' />
<div
className={style['description']}
- dangerouslySetInnerHTML={{ __html: !product.description || product.description == '<p><br></p>' ? 'Belum ada deskripsi' : product.description }}
+ dangerouslySetInnerHTML={{
+ __html:
+ !product.description || product.description == '<p><br></p>'
+ ? 'Belum ada deskripsi'
+ : product.description,
+ }}
/>
</div>
</div>
</div>
{isDesktop && (
- <div className="md:w-3/12">
+ <div className='md:w-3/12'>
<PriceAction product={product} />
+ <div className='flex gap-x-5 items-center justify-center'>
+ <Button
+ as={Link}
+ href={askAdminUrl}
+ variant='link'
+ target='_blank'
+ colorScheme='gray'
+ leftIcon={<MessageCircleIcon size={18} />}
+ >
+ Ask Admin
+ </Button>
+
+ <span>|</span>
+
+ <AddToWishlist productId={product.id} />
+
+ <span>|</span>
+
+ <RWebShare
+ data={{
+ text: 'Check out this product',
+ title: `${product.name} - Indoteknik.com`,
+ url: SELF_HOST + router.asPath,
+ }}
+ >
+ <Button
+ variant='link'
+ colorScheme='gray'
+ leftIcon={<Share2Icon size={18} />}
+ >
+ Share
+ </Button>
+ </RWebShare>
+ </div>
<div className='h-6' />
-
- <div className={style['heading']}>
- Produk Serupa
- </div>
+ <div className={style['heading']}>Produk Serupa</div>
<div className='h-4' />
@@ -171,9 +181,7 @@ const ProductDetail = ({ product }: Props) => {
)}
<div className='md:w-full pt-4 md:py-10 px-4 md:px-0'>
- <div className={style['heading']}>
- Kamu Mungkin Juga Suka
- </div>
+ <div className={style['heading']}>Kamu Mungkin Juga Suka</div>
<div className='h-6' />
@@ -185,7 +193,7 @@ const ProductDetail = ({ product }: Props) => {
<div className='h-6 md:h-0' />
</div>
</>
- )
-}
+ );
+};
-export default ProductDetail \ No newline at end of file
+export default ProductDetail;
diff --git a/src-migrate/modules/product-detail/stores/useProductDetail.ts b/src-migrate/modules/product-detail/stores/useProductDetail.ts
index eb409930..dee6b342 100644
--- a/src-migrate/modules/product-detail/stores/useProductDetail.ts
+++ b/src-migrate/modules/product-detail/stores/useProductDetail.ts
@@ -7,6 +7,8 @@ type State = {
quantityInput: string;
askAdminUrl: string;
isApproval : boolean;
+ selectedVariant : any;
+ sla : any;
};
type Action = {
@@ -14,6 +16,8 @@ type Action = {
setQuantityInput: (value: string) => void;
setAskAdminUrl: (url: string) => void;
setIsApproval : (value : boolean) => void;
+ setSelectedVariant : (value : any) => void;
+ setSla : (value : any) => void;
};
export const useProductDetail = create<State & Action>((set, get) => ({
@@ -22,6 +26,8 @@ export const useProductDetail = create<State & Action>((set, get) => ({
quantityInput: '1',
askAdminUrl: '',
isApproval : false,
+ selectedVariant: null,
+ sla : null,
setActive: (variant) => {
set({ activeVariantId: variant?.id, activePrice: variant?.price });
},
@@ -33,5 +39,11 @@ export const useProductDetail = create<State & Action>((set, get) => ({
},
setIsApproval : (value : boolean) => {
set({ isApproval : value })
+ },
+ setSelectedVariant : (value : any) => {
+ set({ selectedVariant : value })
+ },
+ setSla : (value : any ) => {
+ set({ sla : value })
}
}));
diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx
index bcb41dd6..f52aa5f7 100644
--- a/src/pages/_app.jsx
+++ b/src/pages/_app.jsx
@@ -85,7 +85,7 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }) {
return (
<SessionProvider session={session}>
<ScrollToTop />
-
+
<AnimatePresence>
{animateLoader && (
<motion.div