summaryrefslogtreecommitdiff
path: root/src-migrate/modules/product
diff options
context:
space:
mode:
authorRafi Zadanly <zadanlyr@gmail.com>2023-12-15 17:15:32 +0700
committerRafi Zadanly <zadanlyr@gmail.com>2023-12-15 17:15:32 +0700
commitc9366090153e8aba3a673b2b77cbc8acc24e59a5 (patch)
tree9bad672e511d5585bb4be5b4e3190aca7c4a16af /src-migrate/modules/product
parenta5321d82f4b5e8404f575f1d62e92d0322d78db9 (diff)
Update promotion program feature
Diffstat (limited to 'src-migrate/modules/product')
-rw-r--r--src-migrate/modules/product/PromoCard.module.css62
-rw-r--r--src-migrate/modules/product/PromoCard.tsx117
-rw-r--r--src-migrate/modules/product/PromoProduct.module.css15
-rw-r--r--src-migrate/modules/product/PromoProduct.tsx22
-rw-r--r--src-migrate/modules/product/PromoSection.module.css11
-rw-r--r--src-migrate/modules/product/PromoSection.tsx42
6 files changed, 269 insertions, 0 deletions
diff --git a/src-migrate/modules/product/PromoCard.module.css b/src-migrate/modules/product/PromoCard.module.css
new file mode 100644
index 00000000..4d98671f
--- /dev/null
+++ b/src-migrate/modules/product/PromoCard.module.css
@@ -0,0 +1,62 @@
+.card {
+ @apply border border-gray_r-7
+ rounded-lg
+ min-w-[360px]
+ max-w-[360px]
+ py-3;
+}
+
+.countdownSection {
+ @apply w-fit p-2.5 pr-6
+ rounded-r-full
+ font-medium
+ flex items-center gap-x-2.5;
+}
+
+.countdown {
+ @apply flex gap-x-1;
+}
+
+.countdown span {
+ @apply py-0.5 w-8 bg-red-600 text-gray_r-4 rounded-md text-center;
+}
+
+.title {
+ @apply font-semibold text-h-md;
+}
+
+.productSection {
+ @apply flex gap-x-3 mt-4 min-h-[180px];
+}
+
+.priceSection {
+ @apply flex items-center justify-between mt-4;
+}
+
+.priceCol {
+ @apply flex flex-col gap-y-1;
+}
+
+.priceRow {
+ @apply flex gap-x-2 items-center;
+}
+
+.basePrice {
+ @apply line-through;
+}
+
+.savingAmt {
+ @apply text-success-600 font-medium;
+}
+
+.price {
+ @apply text-body-1 text-danger-600 font-medium;
+}
+
+.totalItems {
+ @apply text-gray_r-9;
+}
+
+.addToCartBtn {
+ @apply btn-yellow flex items-center gap-x-1 px-2 rounded-lg;
+}
diff --git a/src-migrate/modules/product/PromoCard.tsx b/src-migrate/modules/product/PromoCard.tsx
new file mode 100644
index 00000000..8bb48155
--- /dev/null
+++ b/src-migrate/modules/product/PromoCard.tsx
@@ -0,0 +1,117 @@
+import React, { useEffect, useMemo, useState } from 'react'
+import style from "./PromoCard.module.css"
+import { ClockIcon, PlusIcon } from "lucide-react"
+import { IProductVariantPromo, IPromotion } from '~/common/types/promotion'
+import formatCurrency from '~/common/libs/formatCurrency'
+import PromoProduct from './PromoProduct'
+import { Skeleton, Spinner } from '@chakra-ui/react'
+import clsxm from '~/common/libs/clsxm'
+import { useCountdown } from 'usehooks-ts'
+
+type Props = {
+ promotion: IPromotion
+}
+
+const PromoCard = ({ promotion }: Props) => {
+ // TODO: useCountdown()
+ const [products, setProducts] = useState<IProductVariantPromo[]>([])
+
+ useEffect(() => {
+ const getProducts = async () => {
+ const datas = []
+ for (const product of promotion.products) {
+ const response = await fetch(`/api/product-variant/${product.product_id}`)
+ const res = await response.json()
+ res.data.qty = product.qty
+ datas.push(res.data)
+ }
+ setProducts(datas)
+ }
+
+ getProducts()
+ }, [promotion.products])
+
+ const [freeProducts, setFreeProducts] = useState<IProductVariantPromo[]>([])
+
+ useEffect(() => {
+ const getFreeProducts = async () => {
+ const datas = []
+ for (const product of promotion.free_products) {
+ const response = await fetch(`/api/product-variant/${product.product_id}`)
+ const res = await response.json()
+ res.data.qty = product.qty
+ datas.push(res.data)
+ }
+ setFreeProducts(datas)
+ }
+
+ getFreeProducts()
+ }, [promotion.free_products])
+
+ const priceTotal = useMemo(() => {
+ let total = 0;
+ [...products, ...freeProducts].forEach((product) => {
+ total += product.price.price_discount * product.qty
+ console.log({ product });
+
+ })
+ return total
+ }, [products, freeProducts])
+
+ const countdownClass = {
+ 'text-white': true,
+ 'bg-[#312782]': promotion.type.value === 'bundling',
+ 'bg-[#329E44]': promotion.type.value === 'discount_loading',
+ 'bg-[#FAD147]': promotion.type.value === 'merchandise',
+ 'text-gray-700': promotion.type.value === 'merchandise',
+ }
+
+ return (
+ <div className={style.card}>
+ <div className={clsxm(style.countdownSection, countdownClass)}>
+ <span>
+ <ClockIcon size={20} />
+ </span>
+ <span>Berakhir dalam</span>
+ <div className={style.countdown}>
+ <span>00</span>
+ <span>01</span>
+ <span>35</span>
+ </div>
+ </div>
+
+ <div className='px-4 mt-4 text-caption-1'>
+ <div className={style.title}>{promotion.name}</div>
+
+ <Skeleton className={style.productSection} isLoaded={[...products, ...freeProducts].length > 0}>
+ {products.map((product) => <PromoProduct key={product.id} variant={product} />)}
+ {freeProducts.map((product) => <PromoProduct key={product.id} variant={product} />)}
+ </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>
+ <button className={style.addToCartBtn}>
+ {/* <PlusIcon size={16} /> */}
+ <Spinner size='xs' mr={1.5} />
+ Keranjang
+ </button>
+ </div>
+
+ </div>
+ </div>
+ </div>
+ )
+}
+
+export default PromoCard \ No newline at end of file
diff --git a/src-migrate/modules/product/PromoProduct.module.css b/src-migrate/modules/product/PromoProduct.module.css
new file mode 100644
index 00000000..c13bccb8
--- /dev/null
+++ b/src-migrate/modules/product/PromoProduct.module.css
@@ -0,0 +1,15 @@
+.product {
+ @apply w-4/12;
+}
+
+.image {
+ @apply border border-gray_r-6 p-2.5 rounded-lg;
+}
+
+.fillDesc {
+ @apply mt-2 text-danger-600;
+}
+
+.name {
+ @apply mt-1 line-clamp-3 font-medium;
+}
diff --git a/src-migrate/modules/product/PromoProduct.tsx b/src-migrate/modules/product/PromoProduct.tsx
new file mode 100644
index 00000000..83b05e88
--- /dev/null
+++ b/src-migrate/modules/product/PromoProduct.tsx
@@ -0,0 +1,22 @@
+import React from 'react'
+import style from './PromoProduct.module.css'
+import { IProductVariantPromo } from '~/common/types/promotion'
+import Image from 'next/image'
+
+type Props = {
+ variant: IProductVariantPromo
+}
+
+const PromoProduct = ({ variant }: Props) => {
+ return (
+ <div className={style.product}>
+ <div className={style.image}>
+ <Image src={variant.image} alt={variant.display_name} width={320} height={320} />
+ </div>
+ <div className={style.fillDesc}>Isi {variant.qty} barang</div>
+ <div className={style.name}>{variant.name}</div>
+ </div>
+ )
+}
+
+export default PromoProduct \ No newline at end of file
diff --git a/src-migrate/modules/product/PromoSection.module.css b/src-migrate/modules/product/PromoSection.module.css
new file mode 100644
index 00000000..a9c9b704
--- /dev/null
+++ b/src-migrate/modules/product/PromoSection.module.css
@@ -0,0 +1,11 @@
+.titleWrapper {
+ @apply w-full mb-4 h-20 bg-[#C70817] rounded-lg flex items-center justify-between px-4 py-1;
+}
+
+.seeMore {
+ @apply py-2 px-3 btn-yellow rounded-lg text-body-2;
+}
+
+.title {
+ @apply font-semibold text-xl text-white;
+}
diff --git a/src-migrate/modules/product/PromoSection.tsx b/src-migrate/modules/product/PromoSection.tsx
new file mode 100644
index 00000000..299cbb78
--- /dev/null
+++ b/src-migrate/modules/product/PromoSection.tsx
@@ -0,0 +1,42 @@
+import React from 'react'
+import style from "./PromoSection.module.css"
+import PromoCard from './PromoCard'
+import { useQuery } from 'react-query'
+import { Skeleton } from '@chakra-ui/react'
+import { IPromotion } from '~/common/types/promotion'
+
+type Props = {
+ productId: number
+}
+
+const PromoSection = ({ productId }: Props) => {
+ const promotionsQuery = useQuery(
+ `promotions-highlight:${productId}`,
+ async () => await fetch(`/api/product-variant/${productId}/promotion/highlight`).then((res) => res.json()) as { data: IPromotion[] },
+ )
+
+ const promotions = promotionsQuery.data
+
+ const handleSeeMore = () => { }
+
+ return (
+ <div className='w-full'>
+ {promotions?.data && promotions?.data.length > 0 && (
+ <div className={style.titleWrapper}>
+ <span className={style.title}>Promo Tersedia</span>
+ <button type='button' onClick={handleSeeMore} className={style.seeMore}>
+ Lihat Semua
+ </button>
+ </div>
+ )}
+
+ <Skeleton isLoaded={promotionsQuery.isSuccess} className="flex gap-x-4 overflow-x-auto min-h-[340px]">
+ {promotions?.data.map((promotion) => (
+ <PromoCard key={promotion.id} promotion={promotion} />
+ ))}
+ </Skeleton>
+ </div>
+ )
+}
+
+export default PromoSection \ No newline at end of file