summaryrefslogtreecommitdiff
path: root/src-migrate/modules/product-promo/components
diff options
context:
space:
mode:
Diffstat (limited to 'src-migrate/modules/product-promo/components')
-rw-r--r--src-migrate/modules/product-promo/components/AddToCart.tsx111
-rw-r--r--src-migrate/modules/product-promo/components/Card.tsx204
-rw-r--r--src-migrate/modules/product-promo/components/Modal.tsx8
-rw-r--r--src-migrate/modules/product-promo/components/ModalContent.tsx8
-rw-r--r--src-migrate/modules/product-promo/components/Section.tsx9
5 files changed, 255 insertions, 85 deletions
diff --git a/src-migrate/modules/product-promo/components/AddToCart.tsx b/src-migrate/modules/product-promo/components/AddToCart.tsx
index 192dd231..10904f90 100644
--- a/src-migrate/modules/product-promo/components/AddToCart.tsx
+++ b/src-migrate/modules/product-promo/components/AddToCart.tsx
@@ -7,18 +7,36 @@ import { getAuth } from '~/libs/auth'
import { upsertUserCart } from '~/services/cart'
import { IPromotion } from '~/types/promotion'
+import DesktopView from '../../../../src/core/components/views/DesktopView';
+import MobileView from '../../../../src/core/components/views/MobileView';
+import BottomPopup from '@/core/components/elements/Popup/BottomPopup'
+import ImageNext from 'next/image';
+import Link from 'next/link'
+import LazyLoad from 'react-lazy-load'
+import ProductSimilar from '../../../../src/lib/product/components/ProductSimilar';
+import { IProductDetail } from '~/types/product';
+import { useProductCartContext } from '@/contexts/ProductCartContext'
type Props = {
promotion: IPromotion
+ product: IProductDetail
}
type Status = 'idle' | 'loading' | 'success'
-const ProductPromoAddToCart = ({ promotion }: Props) => {
+const ProductPromoAddToCart = ({product, promotion }: Props) => {
const auth = getAuth()
const toast = useToast()
const router = useRouter()
const [status, setStatus] = useState<Status>('idle')
+ const { productCart, setRefreshCart, setProductCart, refreshCart, isLoading, setIsloading } =
+ useProductCartContext()
+
+ const productSimilarQuery = [
+ promotion?.name,
+ `fq=-product_id_i:${promotion.products[0].product_id}`,
+ ].join('&');
+ const [addCartAlert, setAddCartAlert] = useState(false);
const handleButton = async () => {
if (typeof auth !== 'object') {
@@ -39,7 +57,8 @@ const ProductPromoAddToCart = ({ promotion }: Props) => {
qtyAppend: true
})
setStatus('idle')
-
+ setRefreshCart(true);
+ setAddCartAlert(true);
toast({
title: 'Tambah ke keranjang',
description: 'Berhasil menambahkan barang ke keranjang belanja',
@@ -55,21 +74,81 @@ const ProductPromoAddToCart = ({ promotion }: Props) => {
}, [status])
return (
- <Button
- colorScheme='yellow'
- px={2}
- w='110px'
- gap={1}
- isDisabled={status === 'loading'}
- onClick={handleButton}
- >
- {status === 'success' && <CheckIcon size={16} />}
- {status === 'loading' && <Spinner size='xs' mr={1.5} />}
- {status === 'idle' && <PlusIcon size={16} />}
+ <div>
+ <MobileView>
+ <Button
+ colorScheme='yellow'
+ px={2}
+ w='36px'
+ gap={1}
+ isDisabled={status === 'loading'}
+ onClick={handleButton}
+ >
+ {status === 'success' && <CheckIcon size={16} />}
+ {status === 'loading' && <Spinner size='xs' mr={1.5} />}
+ {status === 'idle' && <PlusIcon size={16} />}
+
+ {status === 'success' && <span>Berhasil</span>}
+ {/* {status !== 'success' && <span>Keranjang</span>} */}
+ </Button>
+ </MobileView>
+ <DesktopView>
+ <Button
+ colorScheme='yellow'
+ px={2}
+ w='110px'
+ gap={1}
+ isDisabled={status === 'loading'}
+ onClick={handleButton}
+ >
+ {status === 'success' && <CheckIcon size={16} />}
+ {status === 'loading' && <Spinner size='xs' mr={1.5} />}
+ {status === 'idle' && <PlusIcon size={16} />}
- {status === 'success' && <span>Berhasil</span>}
- {status !== 'success' && <span>Keranjang</span>}
- </Button>
+ {status === 'success' && <span>Berhasil</span>}
+ {status !== 'success' && <span>Keranjang</span>}
+ </Button>
+ <BottomPopup
+ className='!container'
+ title='Berhasil Ditambahkan'
+ active={addCartAlert}
+ close={() => {
+ setAddCartAlert(false);
+ }}
+ >
+ <div className='flex mt-4'>
+ <div className='w-[10%]'>
+ <ImageNext
+ src={product.image}
+ alt={product.name}
+ className='h-32 object-contain object-center w-full border border-gray_r-4'
+ width={80}
+ height={80}
+ />
+ </div>
+ <div className='ml-3 flex flex-1 items-center font-normal'>
+ {product.name}
+ </div>
+ <div className='ml-3 flex items-center font-normal'>
+ <Link
+ href='/shop/cart'
+ className='flex-1 py-2 text-gray_r-12 btn-yellow'
+ >
+ Lihat Keranjang
+ </Link>
+ </div>
+ </div>
+ <div className='mt-8 mb-4'>
+ <div className='text-h-sm font-semibold mb-6'>
+ Kamu Mungkin Juga Suka
+ </div>
+ <LazyLoad>
+ <ProductSimilar query={productSimilarQuery} />
+ </LazyLoad>
+ </div>
+ </BottomPopup>
+ </DesktopView>
+ </div>
)
}
diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx
index 59110098..5c323276 100644
--- a/src-migrate/modules/product-promo/components/Card.tsx
+++ b/src-migrate/modules/product-promo/components/Card.tsx
@@ -15,39 +15,53 @@ import clsxm from '~/libs/clsxm'
import ProductPromoItem from './Item'
import ProductPromoAddToCart from "./AddToCart"
import ProductPromoCardCountdown from "./CardCountdown"
+import { IProductDetail } from '~/types/product';
+import MobileView from '../../../../src/core/components/views/MobileView';
+import DesktopView from '../../../../src/core/components/views/DesktopView';
type Props = {
promotion: IPromotion
+ product: IProductDetail
}
-const ProductPromoCard = ({ promotion }: Props) => {
+const ProductPromoCard = ({product, promotion}: Props) => {
const [products, setProducts] = useState<IProductVariantPromo[]>([])
+ const [freeProducts, setFreeProducts] = useState<IProductVariantPromo[]>([])
+ const [error, setError] = useState<string | null>(null)
useEffect(() => {
const getProducts = async () => {
- const datas = []
- for (const product of promotion.products) {
- const res = await getVariantById(product.product_id)
- res.data.qty = product.qty
- datas.push(res.data)
+ try {
+ const datas = []
+ for (const product of promotion.products) {
+ const res = await getVariantById(product.product_id)
+ res.data.qty = product.qty
+ datas.push(res.data)
+ }
+ setProducts(datas)
+ } catch (err) {
+ setError('Failed to fetch product variants.')
+ console.error(err)
}
- setProducts(datas)
}
getProducts()
}, [promotion.products])
- const [freeProducts, setFreeProducts] = useState<IProductVariantPromo[]>([])
-
useEffect(() => {
const getFreeProducts = async () => {
- const datas = []
- for (const product of promotion.free_products) {
- const res = await getVariantById(product.product_id)
- res.data.qty = product.qty
- datas.push(res.data)
+ try {
+ const datas = []
+ for (const product of promotion.free_products) {
+ const res = await getVariantById(product.product_id)
+ res.data.qty = product.qty
+ datas.push(res.data)
+ }
+ setFreeProducts(datas)
+ } catch (err) {
+ setError('Failed to fetch free product variants.')
+ console.error(err)
}
- setFreeProducts(datas)
}
getFreeProducts()
@@ -63,62 +77,130 @@ const ProductPromoCard = ({ promotion }: Props) => {
const allProducts = [...products, ...freeProducts]
- return (
- <div className={style.card}>
- <ProductPromoCardCountdown promotion={promotion} />
+
- <div className='px-4 mt-4 text-caption-1'>
- <div className="flex justify-between items-center">
- <div className={style.title}>{promotion.name}</div>
+ return (
+ <div>
+ <MobileView>
+ <div className={style.card}>
+ <ProductPromoCardCountdown promotion={promotion} />
+
+ <div className='px-4 mt-4 text-caption-1'>
+ <div className="flex justify-between items-center">
+ <div className={style.title}>{promotion.name}</div>
+
+ <Tooltip label={PROMO_CATEGORY[promotion.type.value].description} placement="top" bgColor='red.600' p={1} rounded={6}>
+ <div className={style.badgeType} >
+ Paket {PROMO_CATEGORY[promotion.type.value].alias}
+ <InfoIcon className={style.badgeType} size={25} />
+ </div>
+ </Tooltip>
+ </div>
- <Tooltip label={PROMO_CATEGORY[promotion.type.value].description} placement="top" bgColor='red.600' p={2} rounded={6}>
- <div className={style.badgeType}>
- Paket {PROMO_CATEGORY[promotion.type.value].alias}
- <InfoIcon size={16} />
+ <Skeleton className={clsxm(style.productSection, { 'justify-center': allProducts.length === 2 })} isLoaded={allProducts.length > 0}>
+ {allProducts.map((product, index) => (
+ <React.Fragment key={product.id}>
+ <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.6 }}>
+ <ProductPromoItem
+ variant={product}
+ isFree={index + 1 > products.length && promotion.type.value === 'merchandise'}
+ // isFree={index + 1 > products.length }
+ />
+ </motion.div>
+ <motion.div initial={{ y: 30, opacity: 0 }} animate={{ y: 0, opacity: 1 }} transition={{ duration: 0.5, delay: 0.1 }}>
+ {index + 1 < allProducts.length && (
+ <div className="h-fit p-1 rounded-full border border-danger-500 text-danger-500 mt-[38px]">
+ <PlusIcon size={14} strokeWidth='2px' />
+ </div>
+ )}
+ </motion.div>
+ </React.Fragment>
+ ))}
+ </Skeleton>
+
+ <div className={style.priceSection}>
+ <div className={style.priceCol}>
+ <Skeleton className={style.priceRow} isLoaded={priceTotal > 0}>
+ <span className={style.basePrice}>Rp{formatCurrency(priceTotal)}</span>
+ <span className="text-[11px]">Hemat <span className={style.savingAmt}>Rp {formatCurrency(priceTotal - promotion.price)}</span></span>
+ </Skeleton>
+
+ <div className={style.priceRow}>
+ <span className={style.price}>Rp{formatCurrency(promotion.price)}</span>
+ <span className={style.totalItems}>(Total {promotion.total_qty} barang)</span>
+ </div>
+
</div>
- </Tooltip>
- </div>
-
- <Skeleton className={clsxm(style.productSection, { 'justify-center': allProducts.length === 2 })} isLoaded={allProducts.length > 0}>
- {allProducts.map((product, index) => (
- <>
- <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.6 }}>
- <ProductPromoItem
- variant={product}
- isFree={index + 1 > products.length && promotion.type.value === 'merchandise'}
- />
- </motion.div>
- <motion.div initial={{ y: 30, opacity: 0 }} animate={{ y: 0, opacity: 1 }} transition={{ duration: 0.5, delay: 0.1 }}>
- {index + 1 < allProducts.length && (
- <div className="h-fit p-1 rounded-full border border-danger-500 text-danger-500 mt-[38px]">
- <PlusIcon size={14} strokeWidth='2px' />
- </div>
- )}
- </motion.div>
- </>
- ))}
- </Skeleton>
-
- <div className={style.priceSection}>
- <div className={style.priceCol}>
- <Skeleton className={style.priceRow} isLoaded={priceTotal > 0}>
- <span className={style.basePrice}>Rp{formatCurrency(priceTotal)}</span>
- <span>Hemat <span className={style.savingAmt}>Rp {formatCurrency(priceTotal - promotion.price)}</span></span>
- </Skeleton>
-
- <div className={style.priceRow}>
- <span className={style.price}>Rp{formatCurrency(promotion.price)}</span>
- <span className={style.totalItems}>(Total {promotion.total_qty} barang)</span>
+ <div>
+ <ProductPromoAddToCart product={product} promotion={promotion} />
</div>
+
</div>
- <div>
- <ProductPromoAddToCart promotion={promotion} />
+ </div>
+ </div>
+ </MobileView>
+ <DesktopView>
+ <div className={style.card}>
+ <ProductPromoCardCountdown promotion={promotion} />
+
+ <div className='px-4 mt-4 text-caption-1'>
+ <div className="flex justify-between items-center">
+ <div className={style.title}>{promotion.name}</div>
+
+ <Tooltip label={PROMO_CATEGORY[promotion.type.value].description} placement="top" bgColor='red.600' p={2} rounded={6}>
+ <div className={style.badgeType}>
+ Paket {PROMO_CATEGORY[promotion.type.value].alias}
+ <InfoIcon size={16} />
+ </div>
+ </Tooltip>
</div>
+ <Skeleton className={clsxm(style.productSection, { 'justify-center': allProducts.length === 2 })} isLoaded={allProducts.length > 0}>
+ {allProducts.map((product, index) => (
+ <React.Fragment key={product.id}>
+ <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.6 }}>
+ <ProductPromoItem
+ variant={product}
+ isFree={index + 1 > products.length && promotion.type.value === 'merchandise'}
+ // isFree={index + 1 > products.length }
+ />
+ </motion.div>
+ <motion.div initial={{ y: 30, opacity: 0 }} animate={{ y: 0, opacity: 1 }} transition={{ duration: 0.5, delay: 0.1 }}>
+ {index + 1 < allProducts.length && (
+ <div className="h-fit p-1 rounded-full border border-danger-500 text-danger-500 mt-[38px]">
+ <PlusIcon size={14} strokeWidth='2px' />
+ </div>
+ )}
+ </motion.div>
+ </React.Fragment>
+ ))}
+ </Skeleton>
+
+ <div className={style.priceSection}>
+ <div className={style.priceCol}>
+ <Skeleton className={style.priceRow} isLoaded={priceTotal > 0}>
+ <span className={style.basePrice}>Rp{formatCurrency(priceTotal)}</span>
+ <span>Hemat <span className={style.savingAmt}>Rp {formatCurrency(priceTotal - promotion.price)}</span></span>
+ </Skeleton>
+
+ <div className={style.priceRow}>
+ <span className={style.price}>Rp{formatCurrency(promotion.price)}</span>
+ <span className={style.totalItems}>(Total {promotion.total_qty} barang)</span>
+ </div>
+ </div>
+ <div>
+ <ProductPromoAddToCart product={product} promotion={promotion} />
+ </div>
+
+ </div>
</div>
</div>
+ </DesktopView>
</div>
+ // shouldRender && (
+
+ // )
)
}
-export default ProductPromoCard \ No newline at end of file
+export default ProductPromoCard
diff --git a/src-migrate/modules/product-promo/components/Modal.tsx b/src-migrate/modules/product-promo/components/Modal.tsx
index 0de672c2..1722b9df 100644
--- a/src-migrate/modules/product-promo/components/Modal.tsx
+++ b/src-migrate/modules/product-promo/components/Modal.tsx
@@ -3,8 +3,12 @@ import { Modal } from "~/components/ui/modal"
import { useModalStore } from '../stores/useModalStore'
import ProductPromoCategoryTab from './CategoryTab'
import ProductPromoModalContent from './ModalContent'
+import { IProductDetail } from '~/types/product';
-const ProductPromoModal = () => {
+type Props = {
+ product: IProductDetail
+}
+const ProductPromoModal = ({product}:Props) => {
const { active, closeModal } = useModalStore()
return (
@@ -17,7 +21,7 @@ const ProductPromoModal = () => {
<div className='h-4' />
- <ProductPromoModalContent />
+ <ProductPromoModalContent product={product} />
</Modal>
)
}
diff --git a/src-migrate/modules/product-promo/components/ModalContent.tsx b/src-migrate/modules/product-promo/components/ModalContent.tsx
index ab5129f8..256ef61a 100644
--- a/src-migrate/modules/product-promo/components/ModalContent.tsx
+++ b/src-migrate/modules/product-promo/components/ModalContent.tsx
@@ -6,7 +6,11 @@ import { getVariantPromoByCategory } from "~/services/productVariant"
import { useModalStore } from "../stores/useModalStore"
import ProductPromoCard from "./Card"
-const ProductPromoModalContent = () => {
+import { IProductDetail } from '~/types/product';
+type Props = {
+ product: IProductDetail
+}
+const ProductPromoModalContent = ({product}:Props) => {
const { activeTab, variantId } = useModalStore()
const promotionsQuery = useQuery(
@@ -24,7 +28,7 @@ const ProductPromoModalContent = () => {
<Skeleton isLoaded={!promotionsQuery.isLoading} className='min-h-[70vh] max-h-[70vh]'>
<div className="grid grid-cols-1 gap-y-6 pb-6">
{promotions?.data.map((promo) => (
- <ProductPromoCard key={promo.id} promotion={promo} />
+ <ProductPromoCard key={promo.id} promotion={promo} product={product} />
))}
{promotions?.data.length === 0 && (
<div className="py-10 rounded-lg h-fit text-center text-body-1 font-semibold text-gray-800 bg-gray-200">Belum ada promo pada kategori ini</div>
diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx
index 5fc0da4c..e1719998 100644
--- a/src-migrate/modules/product-promo/components/Section.tsx
+++ b/src-migrate/modules/product-promo/components/Section.tsx
@@ -9,12 +9,14 @@ import { IPromotion } from '~/types/promotion'
import { useModalStore } from "../stores/useModalStore"
import ProductPromoCard from './Card'
import ProductPromoModal from "./Modal"
+import { IProductDetail } from '~/types/product';
type Props = {
productId: number;
+ product: IProductDetail;
}
-const ProductPromoSection = ({ productId }: Props) => {
+const ProductPromoSection = ({ product, productId }: Props) => {
const promotionsQuery = useQuery({
queryKey: [`promotions.highlight`, productId],
queryFn: async () => await fetch(`/api/product-variant/${productId}/promotion/highlight`).then((res) => res.json()) as { data: IPromotion[] }
@@ -23,14 +25,13 @@ const ProductPromoSection = ({ productId }: Props) => {
const promotions = promotionsQuery.data
const { openModal } = useModalStore()
-
return (
<SmoothRender
isLoaded={(promotions?.data && promotions?.data.length > 0) || false}
height='450px'
duration='700ms'
>
- <ProductPromoModal />
+ <ProductPromoModal product={product}/>
{promotions?.data && promotions?.data.length > 0 && (
<div className={style.titleWrapper}>
@@ -50,7 +51,7 @@ const ProductPromoSection = ({ productId }: Props) => {
>
{promotions?.data.map((promotion) => (
<div key={promotion.id} className="min-w-[400px] max-w-[400px]">
- <ProductPromoCard promotion={promotion} />
+ <ProductPromoCard product={product} promotion={promotion} />
</div>
))}
</Skeleton>