From 54b093ec81cd9733356e1f7a0bc06f192ed39380 Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Tue, 12 Dec 2023 17:05:00 +0700 Subject: delete button daftar di popup information --- src-migrate/modules/popup-information/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx index 78e9dcf2..f54e9f8a 100644 --- a/src-migrate/modules/popup-information/index.tsx +++ b/src-migrate/modules/popup-information/index.tsx @@ -28,9 +28,9 @@ const PagePopupInformation = () => { - + {/* Daftar Sekarang - + */} -- cgit v1.2.3 From f22762cb1f2e7cb93bc839de03315c892d29d8ba Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Thu, 14 Dec 2023 10:31:47 +0700 Subject: feeback go live, update width and height --- src-migrate/modules/popup-information/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx index f54e9f8a..96e82e1d 100644 --- a/src-migrate/modules/popup-information/index.tsx +++ b/src-migrate/modules/popup-information/index.tsx @@ -20,11 +20,11 @@ const PagePopupInformation = () => {
setActive(false)} mode='desktop' > -
+
-- cgit v1.2.3 From 700c0d0537e5eb4c8acb6cc372dac160a2026a33 Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Thu, 14 Dec 2023 15:44:15 +0700 Subject: feback popup information di mobile --- src-migrate/modules/popup-information/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx index 96e82e1d..cd1fd5f2 100644 --- a/src-migrate/modules/popup-information/index.tsx +++ b/src-migrate/modules/popup-information/index.tsx @@ -24,7 +24,7 @@ const PagePopupInformation = () => { close={() => setActive(false)} mode='desktop' > -
+
-- cgit v1.2.3 From c9366090153e8aba3a673b2b77cbc8acc24e59a5 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Fri, 15 Dec 2023 17:15:32 +0700 Subject: Update promotion program feature --- src-migrate/modules/account-activation/index.tsx | 1 - src-migrate/modules/cart/components/CartDetail.tsx | 76 +++++++++++++ .../modules/cart/components/CartItemAction.tsx | 104 ++++++++++++++++++ .../modules/cart/components/CartItemSelect.tsx | 45 ++++++++ src-migrate/modules/cart/stores/useCartStore.ts | 63 +++++++++++ .../modules/cart/styles/CartDetail.module.css | 3 + .../modules/cart/styles/CartItem.module.css | 47 +++++++++ .../modules/cart/styles/CartItemAction.module.css | 32 ++++++ .../modules/cart/styles/CartSummary.module.css | 21 ++++ .../modules/cart/styles/ProductPromo.module.css | 24 +++++ src-migrate/modules/cart/ui/CartItem.tsx | 80 ++++++++++++++ src-migrate/modules/cart/ui/CartSummary.tsx | 74 +++++++++++++ src-migrate/modules/cart/ui/ProductPromo.tsx | 33 ++++++ src-migrate/modules/product/PromoCard.module.css | 62 +++++++++++ src-migrate/modules/product/PromoCard.tsx | 117 +++++++++++++++++++++ .../modules/product/PromoProduct.module.css | 15 +++ src-migrate/modules/product/PromoProduct.tsx | 22 ++++ .../modules/product/PromoSection.module.css | 11 ++ src-migrate/modules/product/PromoSection.tsx | 42 ++++++++ 19 files changed, 871 insertions(+), 1 deletion(-) create mode 100644 src-migrate/modules/cart/components/CartDetail.tsx create mode 100644 src-migrate/modules/cart/components/CartItemAction.tsx create mode 100644 src-migrate/modules/cart/components/CartItemSelect.tsx create mode 100644 src-migrate/modules/cart/stores/useCartStore.ts create mode 100644 src-migrate/modules/cart/styles/CartDetail.module.css create mode 100644 src-migrate/modules/cart/styles/CartItem.module.css create mode 100644 src-migrate/modules/cart/styles/CartItemAction.module.css create mode 100644 src-migrate/modules/cart/styles/CartSummary.module.css create mode 100644 src-migrate/modules/cart/styles/ProductPromo.module.css create mode 100644 src-migrate/modules/cart/ui/CartItem.tsx create mode 100644 src-migrate/modules/cart/ui/CartSummary.tsx create mode 100644 src-migrate/modules/cart/ui/ProductPromo.tsx create mode 100644 src-migrate/modules/product/PromoCard.module.css create mode 100644 src-migrate/modules/product/PromoCard.tsx create mode 100644 src-migrate/modules/product/PromoProduct.module.css create mode 100644 src-migrate/modules/product/PromoProduct.tsx create mode 100644 src-migrate/modules/product/PromoSection.module.css create mode 100644 src-migrate/modules/product/PromoSection.tsx (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/account-activation/index.tsx b/src-migrate/modules/account-activation/index.tsx index 97c96953..c6e2c683 100644 --- a/src-migrate/modules/account-activation/index.tsx +++ b/src-migrate/modules/account-activation/index.tsx @@ -1,4 +1,3 @@ -import { useRouter } from "next/router" import FormToken from "./components/FormToken" import FormEmail from "./components/FormEmail" import FormOTP from "./components/FormOTP" diff --git a/src-migrate/modules/cart/components/CartDetail.tsx b/src-migrate/modules/cart/components/CartDetail.tsx new file mode 100644 index 00000000..734c61d3 --- /dev/null +++ b/src-migrate/modules/cart/components/CartDetail.tsx @@ -0,0 +1,76 @@ +import React, { useEffect, useMemo } from 'react' +import { getAuth } from '~/common/libs/auth' +import { useCartStore } from '../stores/useCartStore' +import CartItem from '../ui/CartItem' +import style from '../styles/CartDetail.module.css' +import CartSummary from '../ui/CartSummary' +import { Button, Tooltip } from '@chakra-ui/react' + +const CartDetail = () => { + const auth = getAuth() + + const { loadCart, cart, summary } = useCartStore() + + useEffect(() => { + if (typeof auth === 'object' && !cart) loadCart(auth.id) + }, [auth, loadCart, cart]) + + const hasSelectedPromo = useMemo(() => { + if (!cart) return false + for (const item of cart.products) { + if (item.cart_type === 'promotion' && item.selected) return true + } + return false + }, [cart]) + + const hasSelected = useMemo(() => { + if (!cart) return false + for (const item of cart.products) { + if (item.selected) return true + } + return false + }, [cart]) + + return ( +
+
+ {/*
*/} +
+
Keranjang Belanja
+
+ {!cart && } +
+
+ {cart?.products.map((item) => )} +
+
+
+ +
+
+ +
+ + + + + + +
+
+
+
+ ) +} + +export default CartDetail \ No newline at end of file diff --git a/src-migrate/modules/cart/components/CartItemAction.tsx b/src-migrate/modules/cart/components/CartItemAction.tsx new file mode 100644 index 00000000..742d1a39 --- /dev/null +++ b/src-migrate/modules/cart/components/CartItemAction.tsx @@ -0,0 +1,104 @@ +import React, { useEffect, useState } from 'react' + +import { Spinner, Tooltip } from '@chakra-ui/react' +import { MinusIcon, PlusIcon, Trash2Icon } from 'lucide-react' + +import { CartItem } from '~/common/types/cart' +import { getAuth } from '~/common/libs/auth' +import { deleteUserCart, upsertUserCart } from '~/services/cart' + +import { useDebounce } from 'usehooks-ts' +import { useCartStore } from '../stores/useCartStore' + +import style from '../styles/CartItemAction.module.css' + +type Props = { + item: CartItem +} + +const CartItemAction = ({ item }: Props) => { + const auth = getAuth() + + const [isLoadDelete, setIsLoadDelete] = useState(false) + const [isLoadQuantity, setIsLoadQuantity] = useState(false) + + const [quantity, setQuantity] = useState(item.quantity) + + const { loadCart } = useCartStore() + + const limitQty = item.limit_qty?.transaction || 0 + + const handleDelete = async () => { + if (typeof auth !== 'object') return + + setIsLoadDelete(true) + await deleteUserCart(auth.id, [item.cart_id]) + await loadCart(auth.id) + setIsLoadDelete(false) + } + + const decreaseQty = () => { setQuantity((quantity) => quantity -= 1) } + const increaseQty = () => { setQuantity((quantity) => quantity += 1) } + const debounceQty = useDebounce(quantity, 1000) + useEffect(() => { + if (isNaN(debounceQty)) setQuantity(1) + if (limitQty > 0 && debounceQty > limitQty) setQuantity(limitQty) + }, [debounceQty, limitQty]) + + useEffect(() => { + const updateCart = async () => { + if (typeof auth !== 'object' || isNaN(debounceQty)) return + + setIsLoadQuantity(true) + await upsertUserCart(auth.id, item.cart_type, item.id, debounceQty, item.selected) + await loadCart(auth.id) + setIsLoadQuantity(false) + } + updateCart() + //eslint-disable-next-line react-hooks/exhaustive-deps + }, [debounceQty]) + + return ( +
+ + +
+ {isLoadQuantity && ( +
+ +
+ )} + + + + setQuantity(parseInt(e.target.value))} + value={quantity} + /> + + 0 ? `Max. ${limitQty}` : ''}> + + +
+
+ ) +} + +export default CartItemAction \ No newline at end of file diff --git a/src-migrate/modules/cart/components/CartItemSelect.tsx b/src-migrate/modules/cart/components/CartItemSelect.tsx new file mode 100644 index 00000000..f44b0d7e --- /dev/null +++ b/src-migrate/modules/cart/components/CartItemSelect.tsx @@ -0,0 +1,45 @@ +import { Checkbox, Spinner } from '@chakra-ui/react' +import React, { useState } from 'react' +import { getAuth } from '~/common/libs/auth' +import { CartItem } from '~/common/types/cart' +import { upsertUserCart } from '~/services/cart' +import { useCartStore } from '../stores/useCartStore' + +type Props = { + item: CartItem +} + +const CartItemSelect = ({ item }: Props) => { + const auth = getAuth() + const { loadCart } = useCartStore() + + const [isLoad, setIsLoad] = useState(false) + + const handleChange = async (e: React.ChangeEvent) => { + if (typeof auth !== 'object') return + + setIsLoad(true) + await upsertUserCart(auth.id, item.cart_type, item.id, item.quantity, e.target.checked) + await loadCart(auth.id) + setIsLoad(false) + } + + return ( +
+ {isLoad && ( + + )} + {!isLoad && ( + + )} +
+ ) +} + +export default CartItemSelect \ No newline at end of file diff --git a/src-migrate/modules/cart/stores/useCartStore.ts b/src-migrate/modules/cart/stores/useCartStore.ts new file mode 100644 index 00000000..1963df53 --- /dev/null +++ b/src-migrate/modules/cart/stores/useCartStore.ts @@ -0,0 +1,63 @@ +import { create } from 'zustand'; +import { CartProps } from '~/common/types/cart'; +import { deleteUserCart, getUserCart, upsertUserCart } from '~/services/cart'; + +type State = { + cart: CartProps | null; + isLoadCart: boolean; + summary: { + subtotal: number; + discount: number; + total: number; + tax: number; + grandTotal: number; + }; +}; + +type Action = { + loadCart: (userId: number) => Promise; +}; + +export const useCartStore = create((set, get) => ({ + cart: null, + isLoadCart: false, + summary: { + subtotal: 0, + discount: 0, + total: 0, + tax: 0, + grandTotal: 0, + }, + loadCart: async (userId) => { + if (get().isLoadCart === true) return; + + set({ isLoadCart: true }); + const cart: CartProps = (await getUserCart(userId)) as CartProps; + set({ cart }); + set({ isLoadCart: false }); + + const summary = computeSummary(cart); + set({ summary }); + }, +})); + +const computeSummary = (cart: CartProps) => { + let subtotal = 0; + let discount = 0; + for (const item of cart.products) { + if (!item.selected) continue; + + let price = 0; + if (item.cart_type === 'promotion') price = item?.package_price || 0; + else if (item.cart_type === 'product') + price = item.price.price * item.quantity; + + subtotal += price; + discount += price - item.price.price_discount * item.quantity; + } + let total = subtotal - discount; + let tax = total * 0.11; + let grandTotal = total + tax; + + return { subtotal, discount, total, tax, grandTotal }; +}; diff --git a/src-migrate/modules/cart/styles/CartDetail.module.css b/src-migrate/modules/cart/styles/CartDetail.module.css new file mode 100644 index 00000000..42d492bb --- /dev/null +++ b/src-migrate/modules/cart/styles/CartDetail.module.css @@ -0,0 +1,3 @@ +.wrapper { + @apply flex flex-wrap; +} diff --git a/src-migrate/modules/cart/styles/CartItem.module.css b/src-migrate/modules/cart/styles/CartItem.module.css new file mode 100644 index 00000000..8ee3d3e9 --- /dev/null +++ b/src-migrate/modules/cart/styles/CartItem.module.css @@ -0,0 +1,47 @@ +.wrapper { + @apply border-b border-gray-300 pb-8; +} + +.mainProdWrapper { + @apply flex; +} + +.image { + @apply h-32 w-32 rounded flex p-2 border border-gray-300; +} + +.noImage { + @apply m-auto font-semibold text-gray-400; +} + +.details { + @apply ml-4 flex flex-col gap-y-1; +} + +.name { + @apply font-medium; +} + +.spacing2 { + @apply h-2; +} + +.discPriceSection { + @apply flex gap-x-2.5; +} + +.priceBefore { + @apply line-through text-gray-500; +} + +.price { + @apply text-red-600 font-medium; +} + +.savingAmt { + @apply text-success-600; +} + +.weightLabel { + @apply text-gray-500; +} diff --git a/src-migrate/modules/cart/styles/CartItemAction.module.css b/src-migrate/modules/cart/styles/CartItemAction.module.css new file mode 100644 index 00000000..e4db7fa5 --- /dev/null +++ b/src-migrate/modules/cart/styles/CartItemAction.module.css @@ -0,0 +1,32 @@ +.actionSection { + @apply flex ml-auto h-fit my-auto; +} + +.deleteButton { + @apply bg-red-100 disabled:bg-gray-100 + text-red-700 disabled:text-gray-500 + hover:bg-red-200 + disabled:cursor-not-allowed + transition-all + p-2.5 rounded; +} + +.quantitySection { + @apply relative flex border border-gray-300 rounded ml-4 items-center text-red-700; +} + +.quantityLoading { + @apply absolute flex items-center justify-center text-white rounded w-full h-full bg-gray-900/50 z-10; +} + +.quantityControl { + @apply h-full w-8 flex items-center justify-center hover:bg-gray-100 + disabled:text-gray-500 + disabled:bg-transparent + disabled:cursor-not-allowed + transition; +} + +.quantity { + @apply text-gray-900 font-medium max-w-[28px] outline-none text-center; +} diff --git a/src-migrate/modules/cart/styles/CartSummary.module.css b/src-migrate/modules/cart/styles/CartSummary.module.css new file mode 100644 index 00000000..48ccec28 --- /dev/null +++ b/src-migrate/modules/cart/styles/CartSummary.module.css @@ -0,0 +1,21 @@ +.line { + @apply flex justify-between; +} + +.label, +.value { + @apply text-gray-700; +} + +.value, +.grandTotal { + @apply font-medium; +} + +.discount { + @apply text-red-700; +} + +.divider { + @apply my-0.5 h-0.5 bg-gray-200; +} diff --git a/src-migrate/modules/cart/styles/ProductPromo.module.css b/src-migrate/modules/cart/styles/ProductPromo.module.css new file mode 100644 index 00000000..3f6e7a05 --- /dev/null +++ b/src-migrate/modules/cart/styles/ProductPromo.module.css @@ -0,0 +1,24 @@ +.wrapper { + @apply ml-16 mt-4 flex; +} + +.imageWrapper { + @apply h-24 w-24 border border-gray-300 rounded p-2.5; +} + +.details { + @apply ml-4 flex flex-col gap-y-1; +} + +.name { + @apply font-medium; +} + +.code, +.weightLabel { + @apply text-gray-600; +} + +.quantity { + @apply py-2.5 bg-gray-100 border border-gray-300 h-fit my-auto rounded-md ml-auto font-medium w-12 text-center; +} diff --git a/src-migrate/modules/cart/ui/CartItem.tsx b/src-migrate/modules/cart/ui/CartItem.tsx new file mode 100644 index 00000000..70d50bff --- /dev/null +++ b/src-migrate/modules/cart/ui/CartItem.tsx @@ -0,0 +1,80 @@ +import Image from 'next/image' +import React from 'react' +import formatCurrency from '~/common/libs/formatCurrency' +import { CartItem as CartItemProps } from '~/common/types/cart' +import ProductPromo from './ProductPromo' +import { Skeleton, SkeletonProps } from '@chakra-ui/react' +import style from '../styles/CartItem.module.css' +import CartItemAction from '../components/CartItemAction' +import CartItemSelect from '../components/CartItemSelect' + +type Props = { + item: CartItemProps +} + +const CartItem = ({ item }: Props) => { + const image = item?.image || item?.parent?.image + + return ( +
+
+ +
+
+ {image && {item.name}} + {!image &&
No Image
} +
+ +
+
{item.name}
+
+ {item.cart_type === 'promotion' && ( +
+ + Rp {formatCurrency((item.package_price || 0))} + + + Hemat Rp {formatCurrency((item.package_price || 0) - item.subtotal)} + + + Rp {formatCurrency(item.subtotal)} + +
+ )} + {item.cart_type === 'product' && ( + <> +
+ Rp {formatCurrency(item.price.price)} +
+
{item.code}
+ + )} +
+ Berat barang: + {item.weight} Kg +
+
+ + +
+ +
+ {item.products?.map((product) => )} + {item.free_products?.map((product) => )} +
+
+ ) +} + +CartItem.Skeleton = function CartItemSkeleton(props: SkeletonProps & { count: number }) { + return Array.from({ length: props.count }).map((_, index) => ( + + )) +} + +export default CartItem \ No newline at end of file diff --git a/src-migrate/modules/cart/ui/CartSummary.tsx b/src-migrate/modules/cart/ui/CartSummary.tsx new file mode 100644 index 00000000..390c1c77 --- /dev/null +++ b/src-migrate/modules/cart/ui/CartSummary.tsx @@ -0,0 +1,74 @@ +import React from 'react' +import style from '../styles/CartSummary.module.css' +import formatCurrency from '~/common/libs/formatCurrency' +import clsxm from '~/common/libs/clsxm' +import { Skeleton } from '@chakra-ui/react' +import _ from 'lodash' + +type Props = { + total?: number + discount?: number + subtotal?: number + tax?: number + shipping?: number + grandTotal?: number + isLoaded: boolean +} + +const CartSummary = ({ + total, + discount, + subtotal, + tax, + shipping, + grandTotal, + isLoaded = false, +}: Props) => { + return ( + <> +
Ringkasan Pesanan
+ +
+ +
+ + Total Belanja + Rp {formatCurrency(subtotal || 0)} + + + + Total Diskon + - Rp {formatCurrency(discount || 0)} + + +
+ + + Subtotal + Rp {formatCurrency(total || 0)} + + + + Tax 11% + Rp {formatCurrency(tax || 0)} + + + + Biaya Kirim + Rp {formatCurrency(shipping || 0)} + + +
+ + + + Grand Total + + Rp {formatCurrency(grandTotal || 0)} + +
+ + ) +} + +export default CartSummary \ No newline at end of file diff --git a/src-migrate/modules/cart/ui/ProductPromo.tsx b/src-migrate/modules/cart/ui/ProductPromo.tsx new file mode 100644 index 00000000..a41afc97 --- /dev/null +++ b/src-migrate/modules/cart/ui/ProductPromo.tsx @@ -0,0 +1,33 @@ +import Image from 'next/image' +import React from 'react' +import { CartProduct } from '~/common/types/cart' +import style from '../styles/ProductPromo.module.css' + +type Props = { + product: CartProduct +} + +const ProductPromo = ({ product }: Props) => { + return ( +
+
+ {product?.image && {product.name}} +
+ +
+
{product.display_name}
+
{product.code}
+
+ Berat Barang: + {product.package_weight} Kg +
+
+ +
+ {product.qty} +
+
+ ) +} + +export default ProductPromo \ No newline at end of file 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([]) + + 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([]) + + 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 ( +
+
+ + + + Berakhir dalam +
+ 00 + 01 + 35 +
+
+ +
+
{promotion.name}
+ + 0}> + {products.map((product) => )} + {freeProducts.map((product) => )} + + +
+
+ 0}> + Rp{formatCurrency(priceTotal)} + Hemat Rp {formatCurrency(priceTotal - promotion.price)} + + +
+ Rp{formatCurrency(promotion.price)} + (Total {promotion.total_qty} barang) +
+
+
+ +
+ +
+
+
+ ) +} + +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 ( +
+
+ {variant.display_name} +
+
Isi {variant.qty} barang
+
{variant.name}
+
+ ) +} + +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 ( +
+ {promotions?.data && promotions?.data.length > 0 && ( +
+ Promo Tersedia + +
+ )} + + + {promotions?.data.map((promotion) => ( + + ))} + +
+ ) +} + +export default PromoSection \ No newline at end of file -- cgit v1.2.3 From 89f32128f37d99b490de7590e2116a9cfd853f89 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Fri, 22 Dec 2023 17:33:46 +0700 Subject: Update promotion program feature --- src-migrate/modules/cart/components/CartDetail.tsx | 76 ------------- .../modules/cart/components/CartItemAction.tsx | 104 ------------------ .../modules/cart/components/CartItemSelect.tsx | 45 -------- src-migrate/modules/cart/components/Detail.tsx | 84 +++++++++++++++ src-migrate/modules/cart/components/Item.tsx | 109 +++++++++++++++++++ src-migrate/modules/cart/components/ItemAction.tsx | 105 ++++++++++++++++++ src-migrate/modules/cart/components/ItemPromo.tsx | 41 +++++++ src-migrate/modules/cart/components/ItemSelect.tsx | 47 ++++++++ src-migrate/modules/cart/components/Summary.tsx | 75 +++++++++++++ src-migrate/modules/cart/stores/useCartStore.ts | 4 +- .../modules/cart/styles/CartDetail.module.css | 3 - .../modules/cart/styles/CartItem.module.css | 47 -------- .../modules/cart/styles/CartItemAction.module.css | 32 ------ .../modules/cart/styles/CartSummary.module.css | 21 ---- .../modules/cart/styles/ProductPromo.module.css | 24 ----- src-migrate/modules/cart/styles/detail.module.css | 3 + .../modules/cart/styles/item-action.module.css | 32 ++++++ .../modules/cart/styles/item-promo.module.css | 31 ++++++ src-migrate/modules/cart/styles/item.module.css | 60 +++++++++++ src-migrate/modules/cart/styles/summary.module.css | 21 ++++ src-migrate/modules/cart/ui/CartItem.tsx | 80 -------------- src-migrate/modules/cart/ui/CartSummary.tsx | 74 ------------- src-migrate/modules/cart/ui/ProductPromo.tsx | 33 ------ .../modules/product-promo/components/AddToCart.tsx | 61 +++++++++++ .../modules/product-promo/components/Card.tsx | 120 +++++++++++++++++++++ .../product-promo/components/CardCountdown.tsx | 67 ++++++++++++ .../product-promo/components/CategoryTab.tsx | 34 ++++++ .../modules/product-promo/components/Item.tsx | 24 +++++ .../modules/product-promo/components/Modal.tsx | 25 +++++ .../product-promo/components/ModalContent.tsx | 33 ++++++ .../modules/product-promo/components/Section.tsx | 50 +++++++++ .../modules/product-promo/stores/useModalStore.ts | 28 +++++ .../product-promo/styles/card-countdown.module.css | 14 +++ .../modules/product-promo/styles/card.module.css | 46 ++++++++ .../product-promo/styles/category-tab.module.css | 12 +++ .../modules/product-promo/styles/item.module.css | 19 ++++ .../product-promo/styles/section.module.css | 7 ++ src-migrate/modules/product/PromoCard.module.css | 62 ----------- src-migrate/modules/product/PromoCard.tsx | 117 -------------------- .../modules/product/PromoProduct.module.css | 15 --- src-migrate/modules/product/PromoProduct.tsx | 22 ---- .../modules/product/PromoSection.module.css | 11 -- src-migrate/modules/product/PromoSection.tsx | 42 -------- 43 files changed, 1150 insertions(+), 810 deletions(-) delete mode 100644 src-migrate/modules/cart/components/CartDetail.tsx delete mode 100644 src-migrate/modules/cart/components/CartItemAction.tsx delete mode 100644 src-migrate/modules/cart/components/CartItemSelect.tsx create mode 100644 src-migrate/modules/cart/components/Detail.tsx create mode 100644 src-migrate/modules/cart/components/Item.tsx create mode 100644 src-migrate/modules/cart/components/ItemAction.tsx create mode 100644 src-migrate/modules/cart/components/ItemPromo.tsx create mode 100644 src-migrate/modules/cart/components/ItemSelect.tsx create mode 100644 src-migrate/modules/cart/components/Summary.tsx delete mode 100644 src-migrate/modules/cart/styles/CartDetail.module.css delete mode 100644 src-migrate/modules/cart/styles/CartItem.module.css delete mode 100644 src-migrate/modules/cart/styles/CartItemAction.module.css delete mode 100644 src-migrate/modules/cart/styles/CartSummary.module.css delete mode 100644 src-migrate/modules/cart/styles/ProductPromo.module.css create mode 100644 src-migrate/modules/cart/styles/detail.module.css create mode 100644 src-migrate/modules/cart/styles/item-action.module.css create mode 100644 src-migrate/modules/cart/styles/item-promo.module.css create mode 100644 src-migrate/modules/cart/styles/item.module.css create mode 100644 src-migrate/modules/cart/styles/summary.module.css delete mode 100644 src-migrate/modules/cart/ui/CartItem.tsx delete mode 100644 src-migrate/modules/cart/ui/CartSummary.tsx delete mode 100644 src-migrate/modules/cart/ui/ProductPromo.tsx create mode 100644 src-migrate/modules/product-promo/components/AddToCart.tsx create mode 100644 src-migrate/modules/product-promo/components/Card.tsx create mode 100644 src-migrate/modules/product-promo/components/CardCountdown.tsx create mode 100644 src-migrate/modules/product-promo/components/CategoryTab.tsx create mode 100644 src-migrate/modules/product-promo/components/Item.tsx create mode 100644 src-migrate/modules/product-promo/components/Modal.tsx create mode 100644 src-migrate/modules/product-promo/components/ModalContent.tsx create mode 100644 src-migrate/modules/product-promo/components/Section.tsx create mode 100644 src-migrate/modules/product-promo/stores/useModalStore.ts create mode 100644 src-migrate/modules/product-promo/styles/card-countdown.module.css create mode 100644 src-migrate/modules/product-promo/styles/card.module.css create mode 100644 src-migrate/modules/product-promo/styles/category-tab.module.css create mode 100644 src-migrate/modules/product-promo/styles/item.module.css create mode 100644 src-migrate/modules/product-promo/styles/section.module.css delete mode 100644 src-migrate/modules/product/PromoCard.module.css delete mode 100644 src-migrate/modules/product/PromoCard.tsx delete mode 100644 src-migrate/modules/product/PromoProduct.module.css delete mode 100644 src-migrate/modules/product/PromoProduct.tsx delete mode 100644 src-migrate/modules/product/PromoSection.module.css delete mode 100644 src-migrate/modules/product/PromoSection.tsx (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/CartDetail.tsx b/src-migrate/modules/cart/components/CartDetail.tsx deleted file mode 100644 index 734c61d3..00000000 --- a/src-migrate/modules/cart/components/CartDetail.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import React, { useEffect, useMemo } from 'react' -import { getAuth } from '~/common/libs/auth' -import { useCartStore } from '../stores/useCartStore' -import CartItem from '../ui/CartItem' -import style from '../styles/CartDetail.module.css' -import CartSummary from '../ui/CartSummary' -import { Button, Tooltip } from '@chakra-ui/react' - -const CartDetail = () => { - const auth = getAuth() - - const { loadCart, cart, summary } = useCartStore() - - useEffect(() => { - if (typeof auth === 'object' && !cart) loadCart(auth.id) - }, [auth, loadCart, cart]) - - const hasSelectedPromo = useMemo(() => { - if (!cart) return false - for (const item of cart.products) { - if (item.cart_type === 'promotion' && item.selected) return true - } - return false - }, [cart]) - - const hasSelected = useMemo(() => { - if (!cart) return false - for (const item of cart.products) { - if (item.selected) return true - } - return false - }, [cart]) - - return ( -
-
- {/*
*/} -
-
Keranjang Belanja
-
- {!cart && } -
-
- {cart?.products.map((item) => )} -
-
-
- -
-
- -
- - - - - - -
-
-
-
- ) -} - -export default CartDetail \ No newline at end of file diff --git a/src-migrate/modules/cart/components/CartItemAction.tsx b/src-migrate/modules/cart/components/CartItemAction.tsx deleted file mode 100644 index 742d1a39..00000000 --- a/src-migrate/modules/cart/components/CartItemAction.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import React, { useEffect, useState } from 'react' - -import { Spinner, Tooltip } from '@chakra-ui/react' -import { MinusIcon, PlusIcon, Trash2Icon } from 'lucide-react' - -import { CartItem } from '~/common/types/cart' -import { getAuth } from '~/common/libs/auth' -import { deleteUserCart, upsertUserCart } from '~/services/cart' - -import { useDebounce } from 'usehooks-ts' -import { useCartStore } from '../stores/useCartStore' - -import style from '../styles/CartItemAction.module.css' - -type Props = { - item: CartItem -} - -const CartItemAction = ({ item }: Props) => { - const auth = getAuth() - - const [isLoadDelete, setIsLoadDelete] = useState(false) - const [isLoadQuantity, setIsLoadQuantity] = useState(false) - - const [quantity, setQuantity] = useState(item.quantity) - - const { loadCart } = useCartStore() - - const limitQty = item.limit_qty?.transaction || 0 - - const handleDelete = async () => { - if (typeof auth !== 'object') return - - setIsLoadDelete(true) - await deleteUserCart(auth.id, [item.cart_id]) - await loadCart(auth.id) - setIsLoadDelete(false) - } - - const decreaseQty = () => { setQuantity((quantity) => quantity -= 1) } - const increaseQty = () => { setQuantity((quantity) => quantity += 1) } - const debounceQty = useDebounce(quantity, 1000) - useEffect(() => { - if (isNaN(debounceQty)) setQuantity(1) - if (limitQty > 0 && debounceQty > limitQty) setQuantity(limitQty) - }, [debounceQty, limitQty]) - - useEffect(() => { - const updateCart = async () => { - if (typeof auth !== 'object' || isNaN(debounceQty)) return - - setIsLoadQuantity(true) - await upsertUserCart(auth.id, item.cart_type, item.id, debounceQty, item.selected) - await loadCart(auth.id) - setIsLoadQuantity(false) - } - updateCart() - //eslint-disable-next-line react-hooks/exhaustive-deps - }, [debounceQty]) - - return ( -
- - -
- {isLoadQuantity && ( -
- -
- )} - - - - setQuantity(parseInt(e.target.value))} - value={quantity} - /> - - 0 ? `Max. ${limitQty}` : ''}> - - -
-
- ) -} - -export default CartItemAction \ No newline at end of file diff --git a/src-migrate/modules/cart/components/CartItemSelect.tsx b/src-migrate/modules/cart/components/CartItemSelect.tsx deleted file mode 100644 index f44b0d7e..00000000 --- a/src-migrate/modules/cart/components/CartItemSelect.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { Checkbox, Spinner } from '@chakra-ui/react' -import React, { useState } from 'react' -import { getAuth } from '~/common/libs/auth' -import { CartItem } from '~/common/types/cart' -import { upsertUserCart } from '~/services/cart' -import { useCartStore } from '../stores/useCartStore' - -type Props = { - item: CartItem -} - -const CartItemSelect = ({ item }: Props) => { - const auth = getAuth() - const { loadCart } = useCartStore() - - const [isLoad, setIsLoad] = useState(false) - - const handleChange = async (e: React.ChangeEvent) => { - if (typeof auth !== 'object') return - - setIsLoad(true) - await upsertUserCart(auth.id, item.cart_type, item.id, item.quantity, e.target.checked) - await loadCart(auth.id) - setIsLoad(false) - } - - return ( -
- {isLoad && ( - - )} - {!isLoad && ( - - )} -
- ) -} - -export default CartItemSelect \ No newline at end of file diff --git a/src-migrate/modules/cart/components/Detail.tsx b/src-migrate/modules/cart/components/Detail.tsx new file mode 100644 index 00000000..c9de086b --- /dev/null +++ b/src-migrate/modules/cart/components/Detail.tsx @@ -0,0 +1,84 @@ +import style from '../styles/detail.module.css' + +import React, { useEffect, useMemo } from 'react' +import Link from 'next/link' +import { Button, Tooltip } from '@chakra-ui/react' + +import { getAuth } from '~/common/libs/auth' +import { useCartStore } from '../stores/useCartStore' + +import CartItem from './Item' +import CartSummary from './Summary' + +const CartDetail = () => { + const auth = getAuth() + + const { loadCart, cart, summary } = useCartStore() + + useEffect(() => { + if (typeof auth === 'object' && !cart) loadCart(auth.id) + }, [auth, loadCart, cart]) + + const hasSelectedPromo = useMemo(() => { + if (!cart) return false + for (const item of cart.products) { + if (item.cart_type === 'promotion' && item.selected) return true + } + return false + }, [cart]) + + const hasSelected = useMemo(() => { + if (!cart) return false + for (const item of cart.products) { + if (item.selected) return true + } + return false + }, [cart]) + + return ( +
+
+ {/*
*/} +
+
Keranjang Belanja
+
+ {!cart && } +
+
+ {cart?.products.map((item) => )} +
+
+
+ +
+
+ +
+ + + + + + +
+
+
+
+ ) +} + +export default CartDetail \ No newline at end of file diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx new file mode 100644 index 00000000..92beda86 --- /dev/null +++ b/src-migrate/modules/cart/components/Item.tsx @@ -0,0 +1,109 @@ +import style from '../styles/item.module.css' + +import Image from 'next/image' +import React from 'react' +import { Skeleton, SkeletonProps, Tooltip } from '@chakra-ui/react' + +import formatCurrency from '~/common/libs/formatCurrency' +import { CartItem as CartItemProps } from '~/common/types/cart' + +import CartItemPromo from './ItemPromo' +import CartItemAction from './ItemAction' +import CartItemSelect from './ItemSelect' +import { PROMO_CATEGORY } from '~/constants/promotion' +import { InfoIcon } from 'lucide-react' + +type Props = { + item: CartItemProps + editable?: boolean +} + +const CartItem = ({ item, editable = true }: Props) => { + const image = item?.image || item?.parent?.image + + return ( +
+ {item.cart_type === 'promotion' && ( +
+ {item.promotion_type?.value && ( + +
+ Paket {PROMO_CATEGORY[item.promotion_type?.value].alias} + +
+
+ )} +
+
+ Selamat! Pembelian anda lebih hemat {' '} + + Rp {formatCurrency((item.package_price || 0) - item.subtotal)} + +
+
+ )} + +
+ {editable && } +
+
+ {image && {item.name}} + {!image &&
No Image
} +
+ +
+
{item.name}
+
+
+ {item.cart_type === 'promotion' && ( +
+ + Rp {formatCurrency((item.package_price || 0))} + + + Rp {formatCurrency(item.subtotal)} + +
+ )} + + {item.cart_type === 'product' && ( + <> +
+ Rp {formatCurrency(item.price.price)} +
+
{item.code}
+ + )} + +
+ {item.weight} Kg +
+
+ + {editable && } + {!editable &&
{item.quantity}
} +
+
+ +
+ +
+ {item.products?.map((product) => )} + {item.free_products?.map((product) => )} +
+
+ ) +} + +CartItem.Skeleton = function CartItemSkeleton(props: SkeletonProps & { count: number }) { + return Array.from({ length: props.count }).map((_, index) => ( + + )) +} + +export default CartItem \ No newline at end of file diff --git a/src-migrate/modules/cart/components/ItemAction.tsx b/src-migrate/modules/cart/components/ItemAction.tsx new file mode 100644 index 00000000..3e264aef --- /dev/null +++ b/src-migrate/modules/cart/components/ItemAction.tsx @@ -0,0 +1,105 @@ +import style from '../styles/item-action.module.css' + +import React, { useEffect, useState } from 'react' + +import { Spinner, Tooltip } from '@chakra-ui/react' +import { MinusIcon, PlusIcon, Trash2Icon } from 'lucide-react' + +import { CartItem } from '~/common/types/cart' +import { getAuth } from '~/common/libs/auth' +import { deleteUserCart, upsertUserCart } from '~/services/cart' + +import { useDebounce } from 'usehooks-ts' +import { useCartStore } from '../stores/useCartStore' + + +type Props = { + item: CartItem +} + +const CartItemAction = ({ item }: Props) => { + const auth = getAuth() + + const [isLoadDelete, setIsLoadDelete] = useState(false) + const [isLoadQuantity, setIsLoadQuantity] = useState(false) + + const [quantity, setQuantity] = useState(item.quantity) + + const { loadCart } = useCartStore() + + const limitQty = item.limit_qty?.transaction || 0 + + const handleDelete = async () => { + if (typeof auth !== 'object') return + + setIsLoadDelete(true) + await deleteUserCart(auth.id, [item.cart_id]) + await loadCart(auth.id) + setIsLoadDelete(false) + } + + const decreaseQty = () => { setQuantity((quantity) => quantity -= 1) } + const increaseQty = () => { setQuantity((quantity) => quantity += 1) } + const debounceQty = useDebounce(quantity, 1000) + useEffect(() => { + if (isNaN(debounceQty)) setQuantity(1) + if (limitQty > 0 && debounceQty > limitQty) setQuantity(limitQty) + }, [debounceQty, limitQty]) + + useEffect(() => { + const updateCart = async () => { + if (typeof auth !== 'object' || isNaN(debounceQty)) return + + setIsLoadQuantity(true) + await upsertUserCart(auth.id, item.cart_type, item.id, debounceQty, item.selected) + await loadCart(auth.id) + setIsLoadQuantity(false) + } + updateCart() + //eslint-disable-next-line react-hooks/exhaustive-deps + }, [debounceQty]) + + return ( +
+ + +
+ {isLoadQuantity && ( +
+ +
+ )} + + + + setQuantity(parseInt(e.target.value))} + value={quantity} + /> + + 0 ? `Max. ${limitQty}` : ''}> + + +
+
+ ) +} + +export default CartItemAction \ No newline at end of file diff --git a/src-migrate/modules/cart/components/ItemPromo.tsx b/src-migrate/modules/cart/components/ItemPromo.tsx new file mode 100644 index 00000000..951d4d6a --- /dev/null +++ b/src-migrate/modules/cart/components/ItemPromo.tsx @@ -0,0 +1,41 @@ +import style from '../styles/item-promo.module.css' + +import Image from 'next/image' +import React from 'react' + +import { CartProduct } from '~/common/types/cart' + + +type Props = { + product: CartProduct +} + +const CartItemPromo = ({ product }: Props) => { + return ( +
+
+ {product?.image && {product.name}} +
+ +
+
{product.display_name}
+
+
+
{product.code}
+
+ Berat Barang: + {product.package_weight} Kg +
+
+ +
+ {product.qty} +
+
+
+ +
+ ) +} + +export default CartItemPromo \ No newline at end of file diff --git a/src-migrate/modules/cart/components/ItemSelect.tsx b/src-migrate/modules/cart/components/ItemSelect.tsx new file mode 100644 index 00000000..96e7c713 --- /dev/null +++ b/src-migrate/modules/cart/components/ItemSelect.tsx @@ -0,0 +1,47 @@ +import { Checkbox, Spinner } from '@chakra-ui/react' +import React, { useState } from 'react' + +import { getAuth } from '~/common/libs/auth' +import { CartItem } from '~/common/types/cart' +import { upsertUserCart } from '~/services/cart' + +import { useCartStore } from '../stores/useCartStore' + +type Props = { + item: CartItem +} + +const CartItemSelect = ({ item }: Props) => { + const auth = getAuth() + const { loadCart } = useCartStore() + + const [isLoad, setIsLoad] = useState(false) + + const handleChange = async (e: React.ChangeEvent) => { + if (typeof auth !== 'object') return + + setIsLoad(true) + await upsertUserCart(auth.id, item.cart_type, item.id, item.quantity, e.target.checked) + await loadCart(auth.id) + setIsLoad(false) + } + + return ( +
+ {isLoad && ( + + )} + {!isLoad && ( + + )} +
+ ) +} + +export default CartItemSelect \ No newline at end of file diff --git a/src-migrate/modules/cart/components/Summary.tsx b/src-migrate/modules/cart/components/Summary.tsx new file mode 100644 index 00000000..a835bca9 --- /dev/null +++ b/src-migrate/modules/cart/components/Summary.tsx @@ -0,0 +1,75 @@ +import style from '../styles/summary.module.css' + +import React from 'react' +import formatCurrency from '~/common/libs/formatCurrency' +import clsxm from '~/common/libs/clsxm' +import { Skeleton } from '@chakra-ui/react' +import _ from 'lodash' + +type Props = { + total?: number + discount?: number + subtotal?: number + tax?: number + shipping?: number + grandTotal?: number + isLoaded: boolean +} + +const CartSummary = ({ + total, + discount, + subtotal, + tax, + shipping, + grandTotal, + isLoaded = false, +}: Props) => { + return ( + <> +
Ringkasan Pesanan
+ +
+ +
+ + Total Belanja + Rp {formatCurrency(subtotal || 0)} + + + + Total Diskon + - Rp {formatCurrency(discount || 0)} + + +
+ + + Subtotal + Rp {formatCurrency(total || 0)} + + + + Tax 11% + Rp {formatCurrency(tax || 0)} + + + + Biaya Kirim + Rp {formatCurrency(shipping || 0)} + + +
+ + + + Grand Total + + Rp {formatCurrency(grandTotal || 0)} + +
+ + ) +} + +export default CartSummary \ No newline at end of file diff --git a/src-migrate/modules/cart/stores/useCartStore.ts b/src-migrate/modules/cart/stores/useCartStore.ts index 1963df53..d3eaadb7 100644 --- a/src-migrate/modules/cart/stores/useCartStore.ts +++ b/src-migrate/modules/cart/stores/useCartStore.ts @@ -1,6 +1,6 @@ import { create } from 'zustand'; import { CartProps } from '~/common/types/cart'; -import { deleteUserCart, getUserCart, upsertUserCart } from '~/services/cart'; +import { getUserCart } from '~/services/cart'; type State = { cart: CartProps | null; @@ -56,7 +56,7 @@ const computeSummary = (cart: CartProps) => { discount += price - item.price.price_discount * item.quantity; } let total = subtotal - discount; - let tax = total * 0.11; + let tax = Math.round(total * 0.11); let grandTotal = total + tax; return { subtotal, discount, total, tax, grandTotal }; diff --git a/src-migrate/modules/cart/styles/CartDetail.module.css b/src-migrate/modules/cart/styles/CartDetail.module.css deleted file mode 100644 index 42d492bb..00000000 --- a/src-migrate/modules/cart/styles/CartDetail.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.wrapper { - @apply flex flex-wrap; -} diff --git a/src-migrate/modules/cart/styles/CartItem.module.css b/src-migrate/modules/cart/styles/CartItem.module.css deleted file mode 100644 index 8ee3d3e9..00000000 --- a/src-migrate/modules/cart/styles/CartItem.module.css +++ /dev/null @@ -1,47 +0,0 @@ -.wrapper { - @apply border-b border-gray-300 pb-8; -} - -.mainProdWrapper { - @apply flex; -} - -.image { - @apply h-32 w-32 rounded flex p-2 border border-gray-300; -} - -.noImage { - @apply m-auto font-semibold text-gray-400; -} - -.details { - @apply ml-4 flex flex-col gap-y-1; -} - -.name { - @apply font-medium; -} - -.spacing2 { - @apply h-2; -} - -.discPriceSection { - @apply flex gap-x-2.5; -} - -.priceBefore { - @apply line-through text-gray-500; -} - -.price { - @apply text-red-600 font-medium; -} - -.savingAmt { - @apply text-success-600; -} - -.weightLabel { - @apply text-gray-500; -} diff --git a/src-migrate/modules/cart/styles/CartItemAction.module.css b/src-migrate/modules/cart/styles/CartItemAction.module.css deleted file mode 100644 index e4db7fa5..00000000 --- a/src-migrate/modules/cart/styles/CartItemAction.module.css +++ /dev/null @@ -1,32 +0,0 @@ -.actionSection { - @apply flex ml-auto h-fit my-auto; -} - -.deleteButton { - @apply bg-red-100 disabled:bg-gray-100 - text-red-700 disabled:text-gray-500 - hover:bg-red-200 - disabled:cursor-not-allowed - transition-all - p-2.5 rounded; -} - -.quantitySection { - @apply relative flex border border-gray-300 rounded ml-4 items-center text-red-700; -} - -.quantityLoading { - @apply absolute flex items-center justify-center text-white rounded w-full h-full bg-gray-900/50 z-10; -} - -.quantityControl { - @apply h-full w-8 flex items-center justify-center hover:bg-gray-100 - disabled:text-gray-500 - disabled:bg-transparent - disabled:cursor-not-allowed - transition; -} - -.quantity { - @apply text-gray-900 font-medium max-w-[28px] outline-none text-center; -} diff --git a/src-migrate/modules/cart/styles/CartSummary.module.css b/src-migrate/modules/cart/styles/CartSummary.module.css deleted file mode 100644 index 48ccec28..00000000 --- a/src-migrate/modules/cart/styles/CartSummary.module.css +++ /dev/null @@ -1,21 +0,0 @@ -.line { - @apply flex justify-between; -} - -.label, -.value { - @apply text-gray-700; -} - -.value, -.grandTotal { - @apply font-medium; -} - -.discount { - @apply text-red-700; -} - -.divider { - @apply my-0.5 h-0.5 bg-gray-200; -} diff --git a/src-migrate/modules/cart/styles/ProductPromo.module.css b/src-migrate/modules/cart/styles/ProductPromo.module.css deleted file mode 100644 index 3f6e7a05..00000000 --- a/src-migrate/modules/cart/styles/ProductPromo.module.css +++ /dev/null @@ -1,24 +0,0 @@ -.wrapper { - @apply ml-16 mt-4 flex; -} - -.imageWrapper { - @apply h-24 w-24 border border-gray-300 rounded p-2.5; -} - -.details { - @apply ml-4 flex flex-col gap-y-1; -} - -.name { - @apply font-medium; -} - -.code, -.weightLabel { - @apply text-gray-600; -} - -.quantity { - @apply py-2.5 bg-gray-100 border border-gray-300 h-fit my-auto rounded-md ml-auto font-medium w-12 text-center; -} diff --git a/src-migrate/modules/cart/styles/detail.module.css b/src-migrate/modules/cart/styles/detail.module.css new file mode 100644 index 00000000..42d492bb --- /dev/null +++ b/src-migrate/modules/cart/styles/detail.module.css @@ -0,0 +1,3 @@ +.wrapper { + @apply flex flex-wrap; +} diff --git a/src-migrate/modules/cart/styles/item-action.module.css b/src-migrate/modules/cart/styles/item-action.module.css new file mode 100644 index 00000000..e4db7fa5 --- /dev/null +++ b/src-migrate/modules/cart/styles/item-action.module.css @@ -0,0 +1,32 @@ +.actionSection { + @apply flex ml-auto h-fit my-auto; +} + +.deleteButton { + @apply bg-red-100 disabled:bg-gray-100 + text-red-700 disabled:text-gray-500 + hover:bg-red-200 + disabled:cursor-not-allowed + transition-all + p-2.5 rounded; +} + +.quantitySection { + @apply relative flex border border-gray-300 rounded ml-4 items-center text-red-700; +} + +.quantityLoading { + @apply absolute flex items-center justify-center text-white rounded w-full h-full bg-gray-900/50 z-10; +} + +.quantityControl { + @apply h-full w-8 flex items-center justify-center hover:bg-gray-100 + disabled:text-gray-500 + disabled:bg-transparent + disabled:cursor-not-allowed + transition; +} + +.quantity { + @apply text-gray-900 font-medium max-w-[28px] outline-none text-center; +} diff --git a/src-migrate/modules/cart/styles/item-promo.module.css b/src-migrate/modules/cart/styles/item-promo.module.css new file mode 100644 index 00000000..17dbf1c7 --- /dev/null +++ b/src-migrate/modules/cart/styles/item-promo.module.css @@ -0,0 +1,31 @@ +.wrapper { + @apply md:ml-16 ml-12 mt-4 flex; +} + +.imageWrapper { + @apply md:h-24 md:w-24 md:min-w-[96px] + h-20 w-20 min-w-[80px] + border border-gray-300 rounded + p-2.5; +} + +.image { + @apply w-full h-full object-contain; +} + +.details { + @apply ml-4 flex flex-col gap-y-1; +} + +.name { + @apply font-medium; +} + +.code, +.weightLabel { + @apply text-gray-600; +} + +.quantity { + @apply w-12 min-w-[48px] py-2.5 bg-gray-100 border border-gray-300 h-fit my-auto rounded-md ml-auto font-medium text-center; +} diff --git a/src-migrate/modules/cart/styles/item.module.css b/src-migrate/modules/cart/styles/item.module.css new file mode 100644 index 00000000..6380cdad --- /dev/null +++ b/src-migrate/modules/cart/styles/item.module.css @@ -0,0 +1,60 @@ +.wrapper { + @apply border-b border-gray-300 pb-8; +} + +.header { + @apply mb-4 flex items-center text-caption-1 leading-6; +} + +.badgeType { + @apply min-w-fit p-2 flex gap-x-1.5 rounded-md border border-danger-500 text-danger-500; +} + +.mainProdWrapper { + @apply flex; +} + +.image { + @apply md:h-32 md:w-32 md:min-w-[128px] + w-24 h-24 min-w-[96px] rounded flex p-2 border border-gray-300; +} + +.noImage { + @apply m-auto font-semibold text-gray-400; +} + +.details { + @apply ml-4 flex flex-col gap-y-1 w-full; +} + +.name { + @apply font-medium; +} + +.spacing2 { + @apply h-2; +} + +.discPriceSection { + @apply flex flex-col md:flex-row gap-x-2.5; +} + +.priceBefore { + @apply line-through text-gray-500; +} + +.price { + @apply text-red-600 font-medium; +} + +.savingAmt { + @apply text-success-600; +} + +.weightLabel { + @apply text-gray-500; +} + +.quantity { + @apply py-2.5 bg-red-100 border border-red-300 text-red-800 h-fit my-auto rounded-md ml-auto font-medium w-12 text-center; +} diff --git a/src-migrate/modules/cart/styles/summary.module.css b/src-migrate/modules/cart/styles/summary.module.css new file mode 100644 index 00000000..48ccec28 --- /dev/null +++ b/src-migrate/modules/cart/styles/summary.module.css @@ -0,0 +1,21 @@ +.line { + @apply flex justify-between; +} + +.label, +.value { + @apply text-gray-700; +} + +.value, +.grandTotal { + @apply font-medium; +} + +.discount { + @apply text-red-700; +} + +.divider { + @apply my-0.5 h-0.5 bg-gray-200; +} diff --git a/src-migrate/modules/cart/ui/CartItem.tsx b/src-migrate/modules/cart/ui/CartItem.tsx deleted file mode 100644 index 70d50bff..00000000 --- a/src-migrate/modules/cart/ui/CartItem.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import Image from 'next/image' -import React from 'react' -import formatCurrency from '~/common/libs/formatCurrency' -import { CartItem as CartItemProps } from '~/common/types/cart' -import ProductPromo from './ProductPromo' -import { Skeleton, SkeletonProps } from '@chakra-ui/react' -import style from '../styles/CartItem.module.css' -import CartItemAction from '../components/CartItemAction' -import CartItemSelect from '../components/CartItemSelect' - -type Props = { - item: CartItemProps -} - -const CartItem = ({ item }: Props) => { - const image = item?.image || item?.parent?.image - - return ( -
-
- -
-
- {image && {item.name}} - {!image &&
No Image
} -
- -
-
{item.name}
-
- {item.cart_type === 'promotion' && ( -
- - Rp {formatCurrency((item.package_price || 0))} - - - Hemat Rp {formatCurrency((item.package_price || 0) - item.subtotal)} - - - Rp {formatCurrency(item.subtotal)} - -
- )} - {item.cart_type === 'product' && ( - <> -
- Rp {formatCurrency(item.price.price)} -
-
{item.code}
- - )} -
- Berat barang: - {item.weight} Kg -
-
- - -
- -
- {item.products?.map((product) => )} - {item.free_products?.map((product) => )} -
-
- ) -} - -CartItem.Skeleton = function CartItemSkeleton(props: SkeletonProps & { count: number }) { - return Array.from({ length: props.count }).map((_, index) => ( - - )) -} - -export default CartItem \ No newline at end of file diff --git a/src-migrate/modules/cart/ui/CartSummary.tsx b/src-migrate/modules/cart/ui/CartSummary.tsx deleted file mode 100644 index 390c1c77..00000000 --- a/src-migrate/modules/cart/ui/CartSummary.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react' -import style from '../styles/CartSummary.module.css' -import formatCurrency from '~/common/libs/formatCurrency' -import clsxm from '~/common/libs/clsxm' -import { Skeleton } from '@chakra-ui/react' -import _ from 'lodash' - -type Props = { - total?: number - discount?: number - subtotal?: number - tax?: number - shipping?: number - grandTotal?: number - isLoaded: boolean -} - -const CartSummary = ({ - total, - discount, - subtotal, - tax, - shipping, - grandTotal, - isLoaded = false, -}: Props) => { - return ( - <> -
Ringkasan Pesanan
- -
- -
- - Total Belanja - Rp {formatCurrency(subtotal || 0)} - - - - Total Diskon - - Rp {formatCurrency(discount || 0)} - - -
- - - Subtotal - Rp {formatCurrency(total || 0)} - - - - Tax 11% - Rp {formatCurrency(tax || 0)} - - - - Biaya Kirim - Rp {formatCurrency(shipping || 0)} - - -
- - - - Grand Total - - Rp {formatCurrency(grandTotal || 0)} - -
- - ) -} - -export default CartSummary \ No newline at end of file diff --git a/src-migrate/modules/cart/ui/ProductPromo.tsx b/src-migrate/modules/cart/ui/ProductPromo.tsx deleted file mode 100644 index a41afc97..00000000 --- a/src-migrate/modules/cart/ui/ProductPromo.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import Image from 'next/image' -import React from 'react' -import { CartProduct } from '~/common/types/cart' -import style from '../styles/ProductPromo.module.css' - -type Props = { - product: CartProduct -} - -const ProductPromo = ({ product }: Props) => { - return ( -
-
- {product?.image && {product.name}} -
- -
-
{product.display_name}
-
{product.code}
-
- Berat Barang: - {product.package_weight} Kg -
-
- -
- {product.qty} -
-
- ) -} - -export default ProductPromo \ No newline at end of file diff --git a/src-migrate/modules/product-promo/components/AddToCart.tsx b/src-migrate/modules/product-promo/components/AddToCart.tsx new file mode 100644 index 00000000..9d856ccf --- /dev/null +++ b/src-migrate/modules/product-promo/components/AddToCart.tsx @@ -0,0 +1,61 @@ +import React, { useEffect, useState } from 'react' +import { CheckIcon, PlusIcon } from 'lucide-react' +import { IPromotion } from '~/common/types/promotion' +import { upsertUserCart } from '~/services/cart' +import { getAuth } from '~/common/libs/auth' +import { Button, Spinner, useToast } from '@chakra-ui/react' + +type Props = { + promotion: IPromotion +} + +type Status = 'idle' | 'loading' | 'success' + +const ProductPromoAddToCart = ({ promotion }: Props) => { + const auth = getAuth() + const toast = useToast() + + const [status, setStatus] = useState('idle') + + const handleButton = async () => { + if (typeof auth !== 'object') return + if (status === 'success') return + + setStatus('loading') + await upsertUserCart(auth.id, 'promotion', promotion.id, 1, true) + setStatus('idle') + + toast({ + title: 'Tambah ke keranjang', + description: 'Berhasil menambahkan barang ke keranjang belanja', + status: 'success', + duration: 3000, + isClosable: true, + position: 'top', + }) + } + + useEffect(() => { + if (status === 'success') setTimeout(() => { setStatus('idle') }, 3000) + }, [status]) + + return ( + + ) +} + +export default ProductPromoAddToCart \ No newline at end of file diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx new file mode 100644 index 00000000..2874c2cc --- /dev/null +++ b/src-migrate/modules/product-promo/components/Card.tsx @@ -0,0 +1,120 @@ +import style from "../styles/card.module.css" + +import React, { useEffect, useMemo, useState } from 'react' +import { InfoIcon, PlusIcon } from "lucide-react" +import { Skeleton, Tooltip } from '@chakra-ui/react' +import { motion } from "framer-motion" + +import { IProductVariantPromo, IPromotion } from '~/common/types/promotion' +import formatCurrency from '~/common/libs/formatCurrency' +import clsxm from '~/common/libs/clsxm' +import { PROMO_CATEGORY } from "~/constants/promotion" +import { getVariantById } from "~/services/variant" + +import ProductPromoItem from './Item' +import ProductPromoAddToCart from "./AddToCart" +import ProductPromoCardCountdown from "./CardCountdown" + +type Props = { + promotion: IPromotion +} + +const ProductPromoCard = ({ promotion }: Props) => { + const [products, setProducts] = useState([]) + + 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) + } + setProducts(datas) + } + + getProducts() + }, [promotion.products]) + + const [freeProducts, setFreeProducts] = useState([]) + + 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) + } + setFreeProducts(datas) + } + + getFreeProducts() + }, [promotion.free_products]) + + const priceTotal = useMemo(() => { + let total = 0; + [...products, ...freeProducts].forEach((product) => { + total += product.price.price_discount * product.qty + }) + return total + }, [products, freeProducts]) + + const allProducts = [...products, ...freeProducts] + + return ( +
+ + +
+
+
{promotion.name}
+ + +
+ Paket {PROMO_CATEGORY[promotion.type.value].alias} + +
+
+
+ + 0}> + {allProducts.map((product, index) => ( + <> + + + + + {index + 1 < allProducts.length && ( +
+ +
+ )} +
+ + ))} +
+ +
+
+ 0}> + Rp{formatCurrency(priceTotal)} + Hemat Rp {formatCurrency(priceTotal - promotion.price)} + + +
+ Rp{formatCurrency(promotion.price)} + (Total {promotion.total_qty} barang) +
+
+
+ +
+ +
+
+
+ ) +} + +export default ProductPromoCard \ No newline at end of file diff --git a/src-migrate/modules/product-promo/components/CardCountdown.tsx b/src-migrate/modules/product-promo/components/CardCountdown.tsx new file mode 100644 index 00000000..e398a390 --- /dev/null +++ b/src-migrate/modules/product-promo/components/CardCountdown.tsx @@ -0,0 +1,67 @@ +import style from '../styles/card-countdown.module.css' + +import React, { useEffect, useState } from 'react' +import { useQuery } from 'react-query' +import { ClockIcon } from 'lucide-react' +import { Skeleton } from '@chakra-ui/react' +import moment from 'moment' + +import clsxm from '~/common/libs/clsxm' +import { IPromotion } from '~/common/types/promotion' +import { getPromotionProgram } from '~/services/promotionProgram' + +type Props = { + promotion: IPromotion +} + +const ProductPromoCardCountdown = ({ promotion }: Props) => { + const query = useQuery(['promotion-program', promotion.program_id], async () => { + return await getPromotionProgram(promotion.program_id) + }) + + const program = query.data?.data || null + + const [count, setCount] = useState(program?.time_left || 0); + + useEffect(() => { + let interval: NodeJS.Timeout; + + if (program?.time_left && program?.time_left > 0) { + setCount(program?.time_left); + + interval = setInterval(() => { + setCount((prevCount) => prevCount - 1); + }, 1000); + } + + return () => { + clearInterval(interval); + }; + }, [program?.time_left]); + + const duration = moment.duration(count, 'seconds') + + 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 ( + + + + + Berakhir dalam +
+ {duration.hours().toString().padStart(2, '0')} + {duration.minutes().toString().padStart(2, '0')} + {duration.seconds().toString().padStart(2, '0')} +
+
+ ) +} + +export default ProductPromoCardCountdown \ No newline at end of file diff --git a/src-migrate/modules/product-promo/components/CategoryTab.tsx b/src-migrate/modules/product-promo/components/CategoryTab.tsx new file mode 100644 index 00000000..edc4aa92 --- /dev/null +++ b/src-migrate/modules/product-promo/components/CategoryTab.tsx @@ -0,0 +1,34 @@ +import React from 'react' +import style from '../styles/category-tab.module.css' +import { useModalStore } from '../stores/useModalStore' +import clsxm from '~/common/libs/clsxm' +import { ICategoryPromo } from '~/common/types/promotion' + +const TABS: ICategoryPromo[] = [ + { value: 'bundling', label: 'Bundling' }, + { value: 'discount_loading', label: 'Discount Loading' }, + { value: 'merchandise', label: 'Free Merchant' }, +] + +const ProductPromoCategoryTab = () => { + const { activeTab, changeTab } = useModalStore() + return ( +
+ {TABS.map((tab) => ( + + ))} +
+ ) +} + +export default ProductPromoCategoryTab \ No newline at end of file diff --git a/src-migrate/modules/product-promo/components/Item.tsx b/src-migrate/modules/product-promo/components/Item.tsx new file mode 100644 index 00000000..058b2f6c --- /dev/null +++ b/src-migrate/modules/product-promo/components/Item.tsx @@ -0,0 +1,24 @@ +import style from '../styles/item.module.css' + +import React from 'react' +import Image from 'next/image' + +import { IProductVariantPromo } from '~/common/types/promotion' + +type Props = { + variant: IProductVariantPromo +} + +const ProductPromoItem = ({ variant }: Props) => { + return ( +
+
+ {variant.display_name} +
{variant.qty} pcs
+
+
{variant.name}
+
+ ) +} + +export default ProductPromoItem \ No newline at end of file diff --git a/src-migrate/modules/product-promo/components/Modal.tsx b/src-migrate/modules/product-promo/components/Modal.tsx new file mode 100644 index 00000000..598b7bbe --- /dev/null +++ b/src-migrate/modules/product-promo/components/Modal.tsx @@ -0,0 +1,25 @@ +import React from 'react' +import Modal from '~/common/components/elements/Modal' +import { useModalStore } from '../stores/useModalStore' +import ProductPromoCategoryTab from './CategoryTab' +import ProductPromoModalContent from './ModalContent' + +const ProductPromoModal = () => { + const { active, closeModal } = useModalStore() + + return ( + + + +
+ + + + ) +} + +export default ProductPromoModal \ No newline at end of file diff --git a/src-migrate/modules/product-promo/components/ModalContent.tsx b/src-migrate/modules/product-promo/components/ModalContent.tsx new file mode 100644 index 00000000..45995af6 --- /dev/null +++ b/src-migrate/modules/product-promo/components/ModalContent.tsx @@ -0,0 +1,33 @@ +import { useQuery } from "react-query" +import { Skeleton } from "@chakra-ui/react" +import { motion } from "framer-motion" + +import { getVariantPromoByCategory } from "~/services/variant" + +import { useModalStore } from "../stores/useModalStore" +import ProductPromoCard from "./Card" + +const ProductPromoModalContent = () => { + const { activeTab, variantId } = useModalStore() + + const promotionsQuery = useQuery( + `variant-promo:${variantId}:${activeTab}`, + async () => { + if (!variantId) return + + return getVariantPromoByCategory(variantId, activeTab) + }, + ) + + const promotions = promotionsQuery.data + + return ( + + {promotions?.data.map((promo) => ( + + ))} + + ) +} + +export default ProductPromoModalContent \ No newline at end of file diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx new file mode 100644 index 00000000..47e1de29 --- /dev/null +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -0,0 +1,50 @@ +import style from "../styles/section.module.css" + +import React from 'react' +import { useQuery } from 'react-query' +import { Button, Skeleton } from '@chakra-ui/react' + +import ProductPromoCard from './Card' +import { IPromotion } from '~/common/types/promotion' +import ProductPromoModal from "./Modal" +import { useModalStore } from "../stores/useModalStore" + +type Props = { + productId: number +} + +const ProductPromoSection = ({ 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 { openModal } = useModalStore() + + return ( +
+ + + {promotions?.data && promotions?.data.length > 0 && ( +
+ Promo Tersedia + +
+ )} + + + {promotions?.data.map((promotion) => ( +
+ +
+ ))} +
+
+ ) +} + +export default ProductPromoSection \ No newline at end of file diff --git a/src-migrate/modules/product-promo/stores/useModalStore.ts b/src-migrate/modules/product-promo/stores/useModalStore.ts new file mode 100644 index 00000000..bbb2b1fb --- /dev/null +++ b/src-migrate/modules/product-promo/stores/useModalStore.ts @@ -0,0 +1,28 @@ +import { create } from 'zustand'; +import { CategoryPromo } from '~/common/types/promotion'; + +type State = { + active: boolean; + variantId?: number; + activeTab: CategoryPromo; +}; + +type Action = { + openModal: (variantId: number) => void; + closeModal: () => void; + changeTab: (tab: State['activeTab']) => void; +}; + +const defaultState: Omit = { + active: false, + variantId: undefined, +}; + +export const useModalStore = create((set) => ({ + ...defaultState, + activeTab: 'bundling', + openModal: (variantId: number) => set({ active: true, variantId }), + closeModal: () => set(defaultState), + // TABS + changeTab: (tab) => set({ activeTab: tab }), +})); diff --git a/src-migrate/modules/product-promo/styles/card-countdown.module.css b/src-migrate/modules/product-promo/styles/card-countdown.module.css new file mode 100644 index 00000000..dae8945f --- /dev/null +++ b/src-migrate/modules/product-promo/styles/card-countdown.module.css @@ -0,0 +1,14 @@ +.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; +} \ No newline at end of file diff --git a/src-migrate/modules/product-promo/styles/card.module.css b/src-migrate/modules/product-promo/styles/card.module.css new file mode 100644 index 00000000..a2ad9af6 --- /dev/null +++ b/src-migrate/modules/product-promo/styles/card.module.css @@ -0,0 +1,46 @@ +.card { + @apply border border-gray_r-7 + rounded-lg + h-fit + py-3; +} + +.title { + @apply font-semibold text-h-md; +} + +.badgeType { + @apply p-2 flex gap-x-1.5 rounded-md border border-danger-500 text-danger-500; +} + +.productSection { + @apply flex gap-x-2 overflow-x-auto overflow-y-hidden mt-4 min-h-[160px]; +} + +.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; +} diff --git a/src-migrate/modules/product-promo/styles/category-tab.module.css b/src-migrate/modules/product-promo/styles/category-tab.module.css new file mode 100644 index 00000000..cab2cb1b --- /dev/null +++ b/src-migrate/modules/product-promo/styles/category-tab.module.css @@ -0,0 +1,12 @@ +.tabs { + @apply flex gap-x-4; +} + +.tab { + @apply py-1.5 duration-300; + transition-property: background-color; +} + +.tabActive { + @apply cursor-default border-b-2 border-danger-500 font-medium; +} diff --git a/src-migrate/modules/product-promo/styles/item.module.css b/src-migrate/modules/product-promo/styles/item.module.css new file mode 100644 index 00000000..86127836 --- /dev/null +++ b/src-migrate/modules/product-promo/styles/item.module.css @@ -0,0 +1,19 @@ +.item { + @apply min-w-[110px] max-w-[110px]; +} + +.image { + @apply relative border border-gray_r-6 p-2.5 rounded-lg mb-3; +} + +.fillDesc { + @apply mt-2 text-danger-600; +} + +.quantity { + @apply backdrop-blur-lg border border-danger-300 text-danger-600 font-semibold px-2 py-1 text-caption-2 flex items-center justify-center rounded absolute bottom-2.5; +} + +.name { + @apply mt-1 line-clamp-2 leading-5 font-medium; +} diff --git a/src-migrate/modules/product-promo/styles/section.module.css b/src-migrate/modules/product-promo/styles/section.module.css new file mode 100644 index 00000000..d830f5d4 --- /dev/null +++ b/src-migrate/modules/product-promo/styles/section.module.css @@ -0,0 +1,7 @@ +.titleWrapper { + @apply w-full mb-4 h-20 bg-[#C70817] rounded-none md:rounded-lg flex items-center justify-between px-4 py-1; +} + +.title { + @apply font-semibold text-xl text-white; +} diff --git a/src-migrate/modules/product/PromoCard.module.css b/src-migrate/modules/product/PromoCard.module.css deleted file mode 100644 index 4d98671f..00000000 --- a/src-migrate/modules/product/PromoCard.module.css +++ /dev/null @@ -1,62 +0,0 @@ -.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 deleted file mode 100644 index 8bb48155..00000000 --- a/src-migrate/modules/product/PromoCard.tsx +++ /dev/null @@ -1,117 +0,0 @@ -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([]) - - 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([]) - - 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 ( -
-
- - - - Berakhir dalam -
- 00 - 01 - 35 -
-
- -
-
{promotion.name}
- - 0}> - {products.map((product) => )} - {freeProducts.map((product) => )} - - -
-
- 0}> - Rp{formatCurrency(priceTotal)} - Hemat Rp {formatCurrency(priceTotal - promotion.price)} - - -
- Rp{formatCurrency(promotion.price)} - (Total {promotion.total_qty} barang) -
-
-
- -
- -
-
-
- ) -} - -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 deleted file mode 100644 index c13bccb8..00000000 --- a/src-migrate/modules/product/PromoProduct.module.css +++ /dev/null @@ -1,15 +0,0 @@ -.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 deleted file mode 100644 index 83b05e88..00000000 --- a/src-migrate/modules/product/PromoProduct.tsx +++ /dev/null @@ -1,22 +0,0 @@ -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 ( -
-
- {variant.display_name} -
-
Isi {variant.qty} barang
-
{variant.name}
-
- ) -} - -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 deleted file mode 100644 index a9c9b704..00000000 --- a/src-migrate/modules/product/PromoSection.module.css +++ /dev/null @@ -1,11 +0,0 @@ -.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 deleted file mode 100644 index 299cbb78..00000000 --- a/src-migrate/modules/product/PromoSection.tsx +++ /dev/null @@ -1,42 +0,0 @@ -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 ( -
- {promotions?.data && promotions?.data.length > 0 && ( -
- Promo Tersedia - -
- )} - - - {promotions?.data.map((promotion) => ( - - ))} - -
- ) -} - -export default PromoSection \ No newline at end of file -- cgit v1.2.3 From 67398e6f10d6f7729d8f1ace7005ef13d32c5ddd Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Thu, 4 Jan 2024 10:05:25 +0700 Subject: Update promotion program feature --- src-migrate/modules/cart/components/Detail.tsx | 1 - src-migrate/modules/cart/components/Item.tsx | 9 +++++---- src-migrate/modules/cart/components/ItemPromo.tsx | 3 +-- src-migrate/modules/cart/stores/useCartStore.ts | 3 ++- src-migrate/modules/cart/styles/item-promo.module.css | 6 +++--- src-migrate/modules/cart/styles/item.module.css | 2 +- .../modules/product-promo/components/AddToCart.tsx | 19 ++++++++++++++++++- src-migrate/modules/product-promo/components/Card.tsx | 10 +++++++--- src-migrate/modules/product-promo/components/Item.tsx | 14 ++++++++++---- .../modules/product-promo/components/ModalContent.tsx | 14 +++++++++----- .../modules/product-promo/styles/item.module.css | 4 ++-- 11 files changed, 58 insertions(+), 27 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Detail.tsx b/src-migrate/modules/cart/components/Detail.tsx index c9de086b..ccb0bb4d 100644 --- a/src-migrate/modules/cart/components/Detail.tsx +++ b/src-migrate/modules/cart/components/Detail.tsx @@ -38,7 +38,6 @@ const CartDetail = () => { return (
- {/*
*/}
Keranjang Belanja
diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index 92beda86..baf48bb6 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -3,6 +3,9 @@ import style from '../styles/item.module.css' import Image from 'next/image' import React from 'react' import { Skeleton, SkeletonProps, Tooltip } from '@chakra-ui/react' +import { InfoIcon } from 'lucide-react' + +import { PROMO_CATEGORY } from '~/constants/promotion' import formatCurrency from '~/common/libs/formatCurrency' import { CartItem as CartItemProps } from '~/common/types/cart' @@ -10,8 +13,6 @@ import { CartItem as CartItemProps } from '~/common/types/cart' import CartItemPromo from './ItemPromo' import CartItemAction from './ItemAction' import CartItemSelect from './ItemSelect' -import { PROMO_CATEGORY } from '~/constants/promotion' -import { InfoIcon } from 'lucide-react' type Props = { item: CartItemProps @@ -37,7 +38,7 @@ const CartItem = ({ item, editable = true }: Props) => {
Selamat! Pembelian anda lebih hemat {' '} - Rp {formatCurrency((item.package_price || 0) - item.subtotal)} + Rp{formatCurrency((item.package_price || 0) * item.quantity - item.subtotal)}
@@ -61,7 +62,7 @@ const CartItem = ({ item, editable = true }: Props) => { Rp {formatCurrency((item.package_price || 0))} - Rp {formatCurrency(item.subtotal)} + Rp {formatCurrency(item.price.price)}
)} diff --git a/src-migrate/modules/cart/components/ItemPromo.tsx b/src-migrate/modules/cart/components/ItemPromo.tsx index 951d4d6a..bb286e8b 100644 --- a/src-migrate/modules/cart/components/ItemPromo.tsx +++ b/src-migrate/modules/cart/components/ItemPromo.tsx @@ -5,7 +5,6 @@ import React from 'react' import { CartProduct } from '~/common/types/cart' - type Props = { product: CartProduct } @@ -19,7 +18,7 @@ const CartItemPromo = ({ product }: Props) => {
{product.display_name}
-
+
{product.code}
diff --git a/src-migrate/modules/cart/stores/useCartStore.ts b/src-migrate/modules/cart/stores/useCartStore.ts index d3eaadb7..0643b8e6 100644 --- a/src-migrate/modules/cart/stores/useCartStore.ts +++ b/src-migrate/modules/cart/stores/useCartStore.ts @@ -48,7 +48,8 @@ const computeSummary = (cart: CartProps) => { if (!item.selected) continue; let price = 0; - if (item.cart_type === 'promotion') price = item?.package_price || 0; + if (item.cart_type === 'promotion') + price = (item?.package_price || 0) * item.quantity; else if (item.cart_type === 'product') price = item.price.price * item.quantity; diff --git a/src-migrate/modules/cart/styles/item-promo.module.css b/src-migrate/modules/cart/styles/item-promo.module.css index 17dbf1c7..5bc192c0 100644 --- a/src-migrate/modules/cart/styles/item-promo.module.css +++ b/src-migrate/modules/cart/styles/item-promo.module.css @@ -1,5 +1,5 @@ .wrapper { - @apply md:ml-16 ml-12 mt-4 flex; + @apply md:ml-12 ml-8 mt-4 flex; } .imageWrapper { @@ -14,7 +14,7 @@ } .details { - @apply ml-4 flex flex-col gap-y-1; + @apply ml-4 w-full flex flex-col gap-y-1; } .name { @@ -27,5 +27,5 @@ } .quantity { - @apply w-12 min-w-[48px] py-2.5 bg-gray-100 border border-gray-300 h-fit my-auto rounded-md ml-auto font-medium text-center; + @apply w-12 min-w-[42px] py-2.5 bg-gray-100 border border-gray-300 h-fit my-auto rounded-md ml-auto font-medium text-center; } diff --git a/src-migrate/modules/cart/styles/item.module.css b/src-migrate/modules/cart/styles/item.module.css index 6380cdad..dfbbf5e8 100644 --- a/src-migrate/modules/cart/styles/item.module.css +++ b/src-migrate/modules/cart/styles/item.module.css @@ -7,7 +7,7 @@ } .badgeType { - @apply min-w-fit p-2 flex gap-x-1.5 rounded-md border border-danger-500 text-danger-500; + @apply min-w-fit p-2 flex items-center gap-x-1.5 rounded-md border border-danger-500 text-danger-500; } .mainProdWrapper { diff --git a/src-migrate/modules/product-promo/components/AddToCart.tsx b/src-migrate/modules/product-promo/components/AddToCart.tsx index 9d856ccf..58bb2ad7 100644 --- a/src-migrate/modules/product-promo/components/AddToCart.tsx +++ b/src-migrate/modules/product-promo/components/AddToCart.tsx @@ -4,6 +4,8 @@ import { IPromotion } from '~/common/types/promotion' import { upsertUserCart } from '~/services/cart' import { getAuth } from '~/common/libs/auth' import { Button, Spinner, useToast } from '@chakra-ui/react' +import Link from 'next/link' +import { useRouter } from 'next/router' type Props = { promotion: IPromotion @@ -14,11 +16,26 @@ type Status = 'idle' | 'loading' | 'success' const ProductPromoAddToCart = ({ promotion }: Props) => { const auth = getAuth() const toast = useToast() + const router = useRouter() const [status, setStatus] = useState('idle') const handleButton = async () => { - if (typeof auth !== 'object') return + if (typeof auth !== 'object') { + const currentUrl = encodeURIComponent(router.asPath) + toast({ + title: 'Masuk Akun', + description: <> + Masuk akun untuk dapat menambahkan promo ke keranjang belanja. {' '} + Klik disini + , + status: 'error', + duration: 4000, + isClosable: true, + position: 'top', + }) + return + } if (status === 'success') return setStatus('loading') diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx index 2874c2cc..e894c143 100644 --- a/src-migrate/modules/product-promo/components/Card.tsx +++ b/src-migrate/modules/product-promo/components/Card.tsx @@ -5,11 +5,12 @@ import { InfoIcon, PlusIcon } from "lucide-react" import { Skeleton, Tooltip } from '@chakra-ui/react' import { motion } from "framer-motion" +import { PROMO_CATEGORY } from "~/constants/promotion" +import { getVariantById } from "~/services/variant" + import { IProductVariantPromo, IPromotion } from '~/common/types/promotion' import formatCurrency from '~/common/libs/formatCurrency' import clsxm from '~/common/libs/clsxm' -import { PROMO_CATEGORY } from "~/constants/promotion" -import { getVariantById } from "~/services/variant" import ProductPromoItem from './Item' import ProductPromoAddToCart from "./AddToCart" @@ -82,7 +83,10 @@ const ProductPromoCard = ({ promotion }: Props) => { {allProducts.map((product, index) => ( <> - + products.length && promotion.type.value === 'merchandise'} + /> {index + 1 < allProducts.length && ( diff --git a/src-migrate/modules/product-promo/components/Item.tsx b/src-migrate/modules/product-promo/components/Item.tsx index 058b2f6c..15ca4878 100644 --- a/src-migrate/modules/product-promo/components/Item.tsx +++ b/src-migrate/modules/product-promo/components/Item.tsx @@ -6,15 +6,21 @@ import Image from 'next/image' import { IProductVariantPromo } from '~/common/types/promotion' type Props = { - variant: IProductVariantPromo + variant: IProductVariantPromo, + isFree?: boolean } -const ProductPromoItem = ({ variant }: Props) => { +const ProductPromoItem = ({ + variant, + isFree = false +}: Props) => { return (
- {variant.display_name} -
{variant.qty} pcs
+ {variant.display_name} +
+ {variant.qty} pcs {isFree ? '(free)' : ''} +
{variant.name}
diff --git a/src-migrate/modules/product-promo/components/ModalContent.tsx b/src-migrate/modules/product-promo/components/ModalContent.tsx index 45995af6..90cf79e7 100644 --- a/src-migrate/modules/product-promo/components/ModalContent.tsx +++ b/src-migrate/modules/product-promo/components/ModalContent.tsx @@ -1,6 +1,5 @@ import { useQuery } from "react-query" import { Skeleton } from "@chakra-ui/react" -import { motion } from "framer-motion" import { getVariantPromoByCategory } from "~/services/variant" @@ -22,10 +21,15 @@ const ProductPromoModalContent = () => { const promotions = promotionsQuery.data return ( - - {promotions?.data.map((promo) => ( - - ))} + +
+ {promotions?.data.map((promo) => ( + + ))} + {promotions?.data.length === 0 && ( +
Belum ada promo pada kategori ini
+ )} +
) } diff --git a/src-migrate/modules/product-promo/styles/item.module.css b/src-migrate/modules/product-promo/styles/item.module.css index 86127836..b6a8b2ef 100644 --- a/src-migrate/modules/product-promo/styles/item.module.css +++ b/src-migrate/modules/product-promo/styles/item.module.css @@ -1,9 +1,9 @@ .item { - @apply min-w-[110px] max-w-[110px]; + @apply w-[100px] h-[100px]; } .image { - @apply relative border border-gray_r-6 p-2.5 rounded-lg mb-3; + @apply w-full h-[100px] relative border border-gray_r-6 p-2.5 rounded-lg mb-3; } .fillDesc { -- cgit v1.2.3 From ee0b5893ac039ab05fe8247647364a923d707da3 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Fri, 5 Jan 2024 09:30:08 +0700 Subject: Fixing UI cart page --- src-migrate/modules/cart/components/Detail.tsx | 83 ---------------------- src-migrate/modules/cart/components/ItemSelect.tsx | 3 +- src-migrate/modules/cart/styles/detail.module.css | 3 - .../modules/cart/styles/item-promo.module.css | 2 +- 4 files changed, 3 insertions(+), 88 deletions(-) delete mode 100644 src-migrate/modules/cart/components/Detail.tsx delete mode 100644 src-migrate/modules/cart/styles/detail.module.css (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Detail.tsx b/src-migrate/modules/cart/components/Detail.tsx deleted file mode 100644 index ccb0bb4d..00000000 --- a/src-migrate/modules/cart/components/Detail.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import style from '../styles/detail.module.css' - -import React, { useEffect, useMemo } from 'react' -import Link from 'next/link' -import { Button, Tooltip } from '@chakra-ui/react' - -import { getAuth } from '~/common/libs/auth' -import { useCartStore } from '../stores/useCartStore' - -import CartItem from './Item' -import CartSummary from './Summary' - -const CartDetail = () => { - const auth = getAuth() - - const { loadCart, cart, summary } = useCartStore() - - useEffect(() => { - if (typeof auth === 'object' && !cart) loadCart(auth.id) - }, [auth, loadCart, cart]) - - const hasSelectedPromo = useMemo(() => { - if (!cart) return false - for (const item of cart.products) { - if (item.cart_type === 'promotion' && item.selected) return true - } - return false - }, [cart]) - - const hasSelected = useMemo(() => { - if (!cart) return false - for (const item of cart.products) { - if (item.selected) return true - } - return false - }, [cart]) - - return ( -
-
-
-
Keranjang Belanja
-
- {!cart && } -
-
- {cart?.products.map((item) => )} -
-
-
- -
-
- -
- - - - - - -
-
-
-
- ) -} - -export default CartDetail \ No newline at end of file diff --git a/src-migrate/modules/cart/components/ItemSelect.tsx b/src-migrate/modules/cart/components/ItemSelect.tsx index 96e7c713..10d7493a 100644 --- a/src-migrate/modules/cart/components/ItemSelect.tsx +++ b/src-migrate/modules/cart/components/ItemSelect.tsx @@ -27,10 +27,11 @@ const CartItemSelect = ({ item }: Props) => { } return ( -
+
{isLoad && ( )} + {!isLoad && ( Date: Fri, 12 Jan 2024 15:18:00 +0700 Subject: Fix go to quotation button in cart page --- src-migrate/modules/cart/components/Detail.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Detail.tsx b/src-migrate/modules/cart/components/Detail.tsx index ccb0bb4d..99fe4c91 100644 --- a/src-migrate/modules/cart/components/Detail.tsx +++ b/src-migrate/modules/cart/components/Detail.tsx @@ -58,6 +58,8 @@ const CartDetail = () => { colorScheme='yellow' w='full' isDisabled={hasSelectedPromo || !hasSelected} + as={Link} + href='/shop/quotation' > Quotation -- cgit v1.2.3 From f62b2345f463695ef0f8f79830cd76b6e0332821 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Sat, 13 Jan 2024 10:35:22 +0700 Subject: Refactor src migrate folder --- .../account-activation/components/FormEmail.tsx | 6 +- .../account-activation/components/FormOTP.tsx | 6 +- .../account-activation/components/FormToken.tsx | 6 +- src-migrate/modules/cart/components/Item.tsx | 4 +- src-migrate/modules/cart/components/ItemAction.tsx | 4 +- src-migrate/modules/cart/components/ItemPromo.tsx | 2 +- src-migrate/modules/cart/components/ItemSelect.tsx | 4 +- src-migrate/modules/cart/components/Summary.tsx | 4 +- src-migrate/modules/cart/stores/useCartStore.ts | 2 +- .../modules/header/components/HeaderDesktop.tsx | 2 +- src-migrate/modules/page-content/index.tsx | 19 +++- src-migrate/modules/popup-information/index.tsx | 4 +- .../product-card/components/ProductCard.tsx | 104 +++++++++++++++++ src-migrate/modules/product-card/index.tsx | 3 + .../product-card/styles/product-card.module.css | 50 ++++++++ .../product-detail/components/AddToCart.tsx | 79 +++++++++++++ .../product-detail/components/AddToWishlist.tsx | 17 +++ .../modules/product-detail/components/Image.tsx | 37 ++++++ .../product-detail/components/Information.tsx | 84 ++++++++++++++ .../product-detail/components/PriceAction.tsx | 53 +++++++++ .../product-detail/components/ProductDetail.tsx | 126 +++++++++++++++++++++ .../product-detail/components/SimilarBottom.tsx | 21 ++++ .../product-detail/components/SimilarSide.tsx | 34 ++++++ .../product-detail/components/VariantList.tsx | 85 ++++++++++++++ src-migrate/modules/product-detail/index.ts | 3 + .../product-detail/stores/useProductDetail.ts | 25 ++++ .../product-detail/styles/information.module.css | 19 ++++ .../product-detail/styles/price-action.module.css | 12 ++ .../styles/product-detail.module.css | 15 +++ .../product-detail/styles/side-similar.module.css | 3 + .../product-detail/styles/variant-list.module.css | 19 ++++ .../modules/product-promo/components/AddToCart.tsx | 4 +- .../modules/product-promo/components/Card.tsx | 8 +- .../product-promo/components/CardCountdown.tsx | 4 +- .../product-promo/components/CategoryTab.tsx | 4 +- .../modules/product-promo/components/Item.tsx | 2 +- .../modules/product-promo/components/Modal.tsx | 2 +- .../product-promo/components/ModalContent.tsx | 2 +- .../modules/product-promo/components/Section.tsx | 2 +- .../modules/product-promo/stores/useModalStore.ts | 2 +- .../product-similar/hooks/useProductSimilar.tsx | 15 +++ .../product-slider/components/ProductSlider.tsx | 42 +++++++ src-migrate/modules/product-slider/index.ts | 3 + src-migrate/modules/register/components/Form.tsx | 4 +- .../modules/register/components/FormCaptcha.tsx | 4 +- .../modules/register/components/TermCondition.tsx | 4 +- .../modules/register/stores/useRegisterStore.ts | 60 ++++++++++ 47 files changed, 969 insertions(+), 45 deletions(-) create mode 100644 src-migrate/modules/product-card/components/ProductCard.tsx create mode 100644 src-migrate/modules/product-card/index.tsx create mode 100644 src-migrate/modules/product-card/styles/product-card.module.css create mode 100644 src-migrate/modules/product-detail/components/AddToCart.tsx create mode 100644 src-migrate/modules/product-detail/components/AddToWishlist.tsx create mode 100644 src-migrate/modules/product-detail/components/Image.tsx create mode 100644 src-migrate/modules/product-detail/components/Information.tsx create mode 100644 src-migrate/modules/product-detail/components/PriceAction.tsx create mode 100644 src-migrate/modules/product-detail/components/ProductDetail.tsx create mode 100644 src-migrate/modules/product-detail/components/SimilarBottom.tsx create mode 100644 src-migrate/modules/product-detail/components/SimilarSide.tsx create mode 100644 src-migrate/modules/product-detail/components/VariantList.tsx create mode 100644 src-migrate/modules/product-detail/index.ts create mode 100644 src-migrate/modules/product-detail/stores/useProductDetail.ts create mode 100644 src-migrate/modules/product-detail/styles/information.module.css create mode 100644 src-migrate/modules/product-detail/styles/price-action.module.css create mode 100644 src-migrate/modules/product-detail/styles/product-detail.module.css create mode 100644 src-migrate/modules/product-detail/styles/side-similar.module.css create mode 100644 src-migrate/modules/product-detail/styles/variant-list.module.css create mode 100644 src-migrate/modules/product-similar/hooks/useProductSimilar.tsx create mode 100644 src-migrate/modules/product-slider/components/ProductSlider.tsx create mode 100644 src-migrate/modules/product-slider/index.ts create mode 100644 src-migrate/modules/register/stores/useRegisterStore.ts (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/account-activation/components/FormEmail.tsx b/src-migrate/modules/account-activation/components/FormEmail.tsx index ec300ba4..f7925481 100644 --- a/src-migrate/modules/account-activation/components/FormEmail.tsx +++ b/src-migrate/modules/account-activation/components/FormEmail.tsx @@ -3,9 +3,9 @@ import Link from "next/link" import { useRouter } from "next/router" import { ChangeEvent, useEffect, useState } from "react" import { useMutation } from "react-query" -import Modal from "~/common/components/elements/Modal" -import { useRegisterStore } from "~/common/stores/useRegisterStore" -import { ActivationReqProps } from "~/common/types/auth" +import { Modal } from "~/components/ui/modal" +import { useRegisterStore } from "~/modules/register/stores/useRegisterStore" +import { ActivationReqProps } from "~/types/auth" import { activationReq } from "~/services/auth" const FormEmail = () => { diff --git a/src-migrate/modules/account-activation/components/FormOTP.tsx b/src-migrate/modules/account-activation/components/FormOTP.tsx index 6815a088..cf4da2db 100644 --- a/src-migrate/modules/account-activation/components/FormOTP.tsx +++ b/src-migrate/modules/account-activation/components/FormOTP.tsx @@ -3,9 +3,9 @@ import { useRouter } from "next/router" import { useEffect, useState } from "react" import { useMutation } from "react-query" import { useCountdown } from "usehooks-ts" -import Modal from '~/common/components/elements/Modal' -import { setAuth } from "~/common/libs/auth" -import { ActivationOtpProps, ActivationReqProps } from "~/common/types/auth" +import { Modal } from "~/components/ui/modal" +import { setAuth } from "~/libs/auth" +import { ActivationOtpProps, ActivationReqProps } from "~/types/auth" import { activationReq, activationUserOTP } from "~/services/auth" const FormOTP = () => { diff --git a/src-migrate/modules/account-activation/components/FormToken.tsx b/src-migrate/modules/account-activation/components/FormToken.tsx index b68b244f..2835ec0e 100644 --- a/src-migrate/modules/account-activation/components/FormToken.tsx +++ b/src-migrate/modules/account-activation/components/FormToken.tsx @@ -4,10 +4,10 @@ import { useEffect, useState } from "react" import Link from "next/link" import { useMutation } from "react-query" -import Modal from "~/common/components/elements/Modal" -import { ActivationTokenProps } from "~/common/types/auth" +import { Modal } from "~/components/ui/modal" +import { ActivationTokenProps } from "~/types/auth" import { activationUserToken } from "~/services/auth" -import { setAuth } from "~/common/libs/auth" +import { setAuth } from "~/libs/auth" const FormToken = () => { const router = useRouter() diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index baf48bb6..08823d19 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -7,8 +7,8 @@ import { InfoIcon } from 'lucide-react' import { PROMO_CATEGORY } from '~/constants/promotion' -import formatCurrency from '~/common/libs/formatCurrency' -import { CartItem as CartItemProps } from '~/common/types/cart' +import formatCurrency from '~/libs/formatCurrency' +import { CartItem as CartItemProps } from '~/types/cart' import CartItemPromo from './ItemPromo' import CartItemAction from './ItemAction' diff --git a/src-migrate/modules/cart/components/ItemAction.tsx b/src-migrate/modules/cart/components/ItemAction.tsx index 3e264aef..859c758c 100644 --- a/src-migrate/modules/cart/components/ItemAction.tsx +++ b/src-migrate/modules/cart/components/ItemAction.tsx @@ -5,8 +5,8 @@ import React, { useEffect, useState } from 'react' import { Spinner, Tooltip } from '@chakra-ui/react' import { MinusIcon, PlusIcon, Trash2Icon } from 'lucide-react' -import { CartItem } from '~/common/types/cart' -import { getAuth } from '~/common/libs/auth' +import { CartItem } from '~/types/cart' +import { getAuth } from '~/libs/auth' import { deleteUserCart, upsertUserCart } from '~/services/cart' import { useDebounce } from 'usehooks-ts' diff --git a/src-migrate/modules/cart/components/ItemPromo.tsx b/src-migrate/modules/cart/components/ItemPromo.tsx index bb286e8b..bc507578 100644 --- a/src-migrate/modules/cart/components/ItemPromo.tsx +++ b/src-migrate/modules/cart/components/ItemPromo.tsx @@ -3,7 +3,7 @@ import style from '../styles/item-promo.module.css' import Image from 'next/image' import React from 'react' -import { CartProduct } from '~/common/types/cart' +import { CartProduct } from '~/types/cart' type Props = { product: CartProduct diff --git a/src-migrate/modules/cart/components/ItemSelect.tsx b/src-migrate/modules/cart/components/ItemSelect.tsx index 10d7493a..1d8886a2 100644 --- a/src-migrate/modules/cart/components/ItemSelect.tsx +++ b/src-migrate/modules/cart/components/ItemSelect.tsx @@ -1,8 +1,8 @@ import { Checkbox, Spinner } from '@chakra-ui/react' import React, { useState } from 'react' -import { getAuth } from '~/common/libs/auth' -import { CartItem } from '~/common/types/cart' +import { getAuth } from '~/libs/auth' +import { CartItem } from '~/types/cart' import { upsertUserCart } from '~/services/cart' import { useCartStore } from '../stores/useCartStore' diff --git a/src-migrate/modules/cart/components/Summary.tsx b/src-migrate/modules/cart/components/Summary.tsx index a835bca9..2e55c8df 100644 --- a/src-migrate/modules/cart/components/Summary.tsx +++ b/src-migrate/modules/cart/components/Summary.tsx @@ -1,8 +1,8 @@ import style from '../styles/summary.module.css' import React from 'react' -import formatCurrency from '~/common/libs/formatCurrency' -import clsxm from '~/common/libs/clsxm' +import formatCurrency from '~/libs/formatCurrency' +import clsxm from '~/libs/clsxm' import { Skeleton } from '@chakra-ui/react' import _ from 'lodash' diff --git a/src-migrate/modules/cart/stores/useCartStore.ts b/src-migrate/modules/cart/stores/useCartStore.ts index 0643b8e6..3d9a0aed 100644 --- a/src-migrate/modules/cart/stores/useCartStore.ts +++ b/src-migrate/modules/cart/stores/useCartStore.ts @@ -1,5 +1,5 @@ import { create } from 'zustand'; -import { CartProps } from '~/common/types/cart'; +import { CartProps } from '~/types/cart'; import { getUserCart } from '~/services/cart'; type State = { diff --git a/src-migrate/modules/header/components/HeaderDesktop.tsx b/src-migrate/modules/header/components/HeaderDesktop.tsx index 3860bded..8f5a8efa 100644 --- a/src-migrate/modules/header/components/HeaderDesktop.tsx +++ b/src-migrate/modules/header/components/HeaderDesktop.tsx @@ -8,7 +8,7 @@ import Link from 'next/link' import SearchBar from "./SearchBar"; // Constants -import { SECONDARY_MENU_ITEMS } from "~/common/constants/menu"; +import { SECONDARY_MENU_ITEMS } from "~/constants/menu"; const LOGO_WIDTH = 210; const LOGO_HEIGHT = LOGO_WIDTH / 3; diff --git a/src-migrate/modules/page-content/index.tsx b/src-migrate/modules/page-content/index.tsx index 608079f8..547b1957 100644 --- a/src-migrate/modules/page-content/index.tsx +++ b/src-migrate/modules/page-content/index.tsx @@ -1,7 +1,6 @@ import { useMemo } from "react" import { useQuery } from "react-query" -import PageContentSkeleton from "~/common/components/skeleton/PageContentSkeleton" -import { PageContentProps } from "~/common/types/pageContent" +import { PageContentProps } from "~/types/pageContent" import { getPageContent } from "~/services/pageContent" type Props = { @@ -26,4 +25,20 @@ const PageContent = ({ path }: Props) => { ) } +const PageContentSkeleton = () => ( +
+
+
+
+
+
+
+
+
+
+
+
+
+) + export default PageContent \ No newline at end of file diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx index cd1fd5f2..3d537236 100644 --- a/src-migrate/modules/popup-information/index.tsx +++ b/src-migrate/modules/popup-information/index.tsx @@ -1,7 +1,7 @@ import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; -import Modal from '~/common/components/elements/Modal'; -import { getAuth } from '~/common/libs/auth'; +import { Modal } from "~/components/ui/modal" +import { getAuth } from '~/libs/auth'; import PageContent from '../page-content'; import Link from 'next/link'; diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx new file mode 100644 index 00000000..8cd96ce8 --- /dev/null +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -0,0 +1,104 @@ +import style from '../styles/product-card.module.css' + +import Link from 'next/link' +import React from 'react' +import Image from '~/components/ui/image' +import clsxm from '~/libs/clsxm' +import formatCurrency from '~/libs/formatCurrency' +import { formatToShortText } from '~/libs/formatNumber' +import { createSlug } from '~/libs/slug' +import { IProduct } from '~/types/product' + +type Props = { + product: IProduct + layout?: 'vertical' | 'horizontal' +} + +const ProductCard = ({ product, layout = 'vertical' }: Props) => { + const URL = { + product: createSlug('/shop/product/', product.name, product.id.toString()), + manufacture: createSlug('/shop/brands/', product.manufacture.name, product.manufacture.id.toString()), + } + + return ( +
+
+ + {product.name} + +
+ +
+ + {product.manufacture.name} + + +
+ + + {product.name} + +
+ +
+ Rp {formatCurrency(product.lowest_price.price)} +
+ +
+ +
+ Inc PPN: + Rp {formatCurrency(Math.round(product.lowest_price.price * 1.11))} +
+ +
+ +
+ {product.stock_total > 0 && ( +
+ Ready Stock +
+ )} + {product.qty_sold > 0 && ( +
+ {formatToShortText(product.qty_sold)} Terjual +
+ )} +
+ +
+
+ ) +} + +const classPrefix = ({ layout }: Props) => { + +} + +export default ProductCard \ No newline at end of file diff --git a/src-migrate/modules/product-card/index.tsx b/src-migrate/modules/product-card/index.tsx new file mode 100644 index 00000000..c87167bc --- /dev/null +++ b/src-migrate/modules/product-card/index.tsx @@ -0,0 +1,3 @@ +import ProductCard from "./components/ProductCard"; + +export default ProductCard \ No newline at end of file diff --git a/src-migrate/modules/product-card/styles/product-card.module.css b/src-migrate/modules/product-card/styles/product-card.module.css new file mode 100644 index 00000000..38b895f9 --- /dev/null +++ b/src-migrate/modules/product-card/styles/product-card.module.css @@ -0,0 +1,50 @@ +.wrapper { + @apply w-full flex; +} +.wrapper-v { + @apply flex-col border border-gray-300 rounded-md h-[350px]; +} +.wrapper-h { + @apply flex-row gap-x-2 pt-4; +} + +.image-v { + @apply w-full h-48 px-4 border-b border-gray-300; +} +.image-h { + @apply w-4/12 h-24 px-1; +} + +.content-v { + @apply w-full p-2; +} +.content-h { + @apply w-8/12; +} + +.brand { + @apply text-danger-500 font-medium block; +} + +.name { + @apply text-gray-700 font-medium line-clamp-3; +} +.name-v { + @apply min-h-[64px]; +} +.name-h { + @apply min-h-[32px]; +} + +.price { + @apply text-danger-500 font-medium; +} + +.ready-stock { + @apply bg-danger-500 text-white text-[11px] px-2 py-1 rounded-md; +} + +.price-inc, +.sold { + @apply text-gray-600 text-[11px]; +} diff --git a/src-migrate/modules/product-detail/components/AddToCart.tsx b/src-migrate/modules/product-detail/components/AddToCart.tsx new file mode 100644 index 00000000..4accab17 --- /dev/null +++ b/src-migrate/modules/product-detail/components/AddToCart.tsx @@ -0,0 +1,79 @@ +import React from 'react' +import { Button, useToast } from '@chakra-ui/react' +import { getAuth } from '~/libs/auth' +import { useRouter } from 'next/router' +import Link from 'next/link' +import { upsertUserCart } from '~/services/cart' + +type Props = { + variantId: number | null, + quantity?: number; + source?: 'buy' | 'add_to_cart'; +} + +const AddToCart = ({ + variantId, + quantity = 1, + source = 'add_to_cart' +}: Props) => { + const auth = getAuth() + const router = useRouter() + const toast = useToast({ + position: 'top', + isClosable: true + }) + + const handleClick = async () => { + if (typeof auth !== 'object') { + const currentUrl = encodeURIComponent(router.asPath) + toast({ + title: 'Masuk Akun', + description: <> + Masuk akun untuk dapat menambahkan barang ke keranjang belanja. {' '} + Klik disini + , + status: 'error', + duration: 4000, + }) + return; + } + + if ( + !variantId || + isNaN(quantity) || + typeof auth !== 'object' + ) return; + + toast.promise( + upsertUserCart(auth.id, 'product', variantId, quantity, true, source), + { + loading: { title: 'Menambahkan ke keranjang', description: 'Mohon tunggu...' }, + success: { title: 'Menambahkan ke keranjang', description: 'Berhasil menambahkan ke keranjang belanja' }, + error: { title: 'Menambahkan ke keranjang', description: 'Gagal menambahkan ke keranjang belanja' }, + } + ) + + if (source === 'buy') { + router.push('/shop/checkout?source=buy') + } + } + + const btnConfig = { + 'add_to_cart': { + colorScheme: 'yellow', + text: 'Keranjang' + }, + 'buy': { + colorScheme: 'red', + text: 'Beli' + } + } + + return ( + + ) +} + +export default AddToCart \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/AddToWishlist.tsx b/src-migrate/modules/product-detail/components/AddToWishlist.tsx new file mode 100644 index 00000000..eab3c7be --- /dev/null +++ b/src-migrate/modules/product-detail/components/AddToWishlist.tsx @@ -0,0 +1,17 @@ +import { Button } from '@chakra-ui/react' +import { HeartIcon } from 'lucide-react' +import React from 'react' + +const AddToWishlist = () => { + return ( + + ) +} + +export default AddToWishlist \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx new file mode 100644 index 00000000..361580ea --- /dev/null +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -0,0 +1,37 @@ +import React from 'react' +import { InfoIcon } from 'lucide-react' +import { Tooltip } from '@chakra-ui/react' + +import { IProductDetail } from '~/types/product' +import ImageUI from '~/components/ui/image' + +type Props = { + product: IProductDetail +} + +const Image = ({ product }: Props) => { + return ( +
+ +
+ +
+ +
+
+
+
+ ) +} + +export default Image \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx new file mode 100644 index 00000000..fd0e0b3c --- /dev/null +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -0,0 +1,84 @@ +import style from '../styles/information.module.css' + +import React 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' + +const Skeleton = dynamic(() => import('@chakra-ui/react').then((mod) => mod.Skeleton)) + +type Props = { + product: IProductDetail +} + +const Information = ({ product }: Props) => { + const querySLA = useQuery({ + queryKey: ['variant-sla', product.variants[0].id], + queryFn: () => getVariantSLA(product.variants[0].id), + enabled: product.variant_total === 1 + }) + + const sla = querySLA?.data + + return ( +
+
+
SKU Number
+
SKU-{product.id}
+
+ {/*
+
Part Number
+
{product.code || '-'}
+
*/} +
+
Manufacture
+
+ {!!product.manufacture.name ? ( + + {product.manufacture.name} + + ) : '-'} +
+
+ {/*
+
Preparation Time
+
+ {product.variant_total > 1 && 'Lihat Variant'} + {product.variant_total === 1 && ( + + {sla?.sla_date} + + )} +
+
+
+
Stock
+
+ {product.variant_total > 1 && 'Lihat Variant'} + {product.variant_total === 1 && ( + + {sla?.qty && sla.qty > 0 ? sla?.qty : '-'} + + )} +
+
+
+
Weight
+
+ {product.variant_total > 1 && 'Lihat Variant'} + {product.variant_total === 1 && (product.weight > 0 ? `${product.weight} kg` : '-')} +
+
*/} +
+ ) +} + +export default Information \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx new file mode 100644 index 00000000..8189e5bd --- /dev/null +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -0,0 +1,53 @@ +import style from '../styles/price-action.module.css' + +import React, { useEffect } from 'react' +import formatCurrency from '~/libs/formatCurrency' +import { formatToShortText } from '~/libs/formatNumber' +import { IProductDetail } from '~/types/product' +import { useProductDetail } from '../stores/useProductDetail' +import AddToCart from './AddToCart' + +type Props = { + product: IProductDetail +} + +const PriceAction = ({ product }: Props) => { + const { activePrice, setActive, activeVariantId, quantityInput, setQuantityInput } = useProductDetail() + + useEffect(() => { + setActive(product.variants[0]) + }, [product, setActive]); + + return ( +
+ {product.qty_sold > 0 && ( +
+ {formatToShortText(product.qty_sold)} Terjual +
+ )} +
+
+ Rp {formatCurrency(activePrice?.price || 0)} +
+
+
+ {!!activePrice && ( + <> + Termasuk PPN: {' '} + Rp {formatCurrency(Math.round(activePrice?.price * 1.11))} + + )} +
+ +
+ +
+ setQuantityInput(e.target.value)} className={style['quantity-input']} /> + + +
+
+ ) +} + +export default PriceAction \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx new file mode 100644 index 00000000..b752a138 --- /dev/null +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -0,0 +1,126 @@ +import style from '../styles/product-detail.module.css' + +import React from 'react' +import Link from 'next/link' +import { MessageCircleIcon } from 'lucide-react' +import { Button } from '@chakra-ui/react' + +import { IProductDetail } from '~/types/product' + +import ProductImage from './Image' +import Information from './Information' +import AddToWishlist from './AddToWishlist' +import VariantList from './VariantList' +import SimilarSide from './SimilarSide' +import SimilarBottom from './SimilarBottom' +import useDevice from '@/core/hooks/useDevice' +import PriceAction from './PriceAction' + +type Props = { + product: IProductDetail +} + +const ProductDetail = ({ product }: Props) => { + const { isDesktop, isMobile } = useDevice() + + return ( + <> +
+
+
+
+ +
+ +
+
+ +

+ {product.name} +

+ +
+ + + +
+ + +
+
+ +
+ {isMobile && ( +
+ +
+ )} + +
+ +
+

+ Variant ({product.variant_total}) +

+
+ +
+ +
+ +
+

+ Informasi Produk +

+
+

' ? 'Belum ada deskripsi' : product.description }} + /> +
+
+
+ + {isDesktop && ( +
+ + + + +
+ +
+ Produk Serupa +
+ +
+ + +
+ )} + +
+
+ Kamu Mungkin Juga Suka +
+ +
+ + +
+ +
+
+ + ) +} + +export default ProductDetail \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/SimilarBottom.tsx b/src-migrate/modules/product-detail/components/SimilarBottom.tsx new file mode 100644 index 00000000..9a12a6ef --- /dev/null +++ b/src-migrate/modules/product-detail/components/SimilarBottom.tsx @@ -0,0 +1,21 @@ +import React from 'react' +import useProductSimilar from '~/modules/product-similar/hooks/useProductSimilar' +import ProductSlider from '~/modules/product-slider' +import { IProductDetail } from '~/types/product' + +type Props = { + product: IProductDetail +} + +const SimilarBottom = ({ product }: Props) => { + const productSimilar = useProductSimilar({ + name: product.name, + except: { productId: product.id } + }) + + const products = productSimilar.data?.products || [] + + return ; +} + +export default SimilarBottom \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/SimilarSide.tsx b/src-migrate/modules/product-detail/components/SimilarSide.tsx new file mode 100644 index 00000000..646a1c51 --- /dev/null +++ b/src-migrate/modules/product-detail/components/SimilarSide.tsx @@ -0,0 +1,34 @@ +import style from '../styles/side-similar.module.css' + +import React from 'react' + +import ProductCard from '~/modules/product-card' +import useProductSimilar from '~/modules/product-similar/hooks/useProductSimilar' +import { IProductDetail } from '~/types/product' + +type Props = { + product: IProductDetail +} + +const SimilarSide = ({ product }: Props) => { + const productSimilar = useProductSimilar({ + name: product.name, + except: { productId: product.id, manufactureId: product.manufacture.id }, + }) + + const products = productSimilar.data?.products || [] + + return ( +
+ {products.map((product) => ( + + ))} +
+ ) +} + +export default SimilarSide \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/VariantList.tsx b/src-migrate/modules/product-detail/components/VariantList.tsx new file mode 100644 index 00000000..d07e6b23 --- /dev/null +++ b/src-migrate/modules/product-detail/components/VariantList.tsx @@ -0,0 +1,85 @@ +import style from '../styles/variant-list.module.css' + +import React from 'react' +import { Button, Skeleton } from '@chakra-ui/react' + +import formatCurrency from '~/libs/formatCurrency' +import clsxm from '~/libs/clsxm' +import { IProductVariantDetail, IProductVariantSLA } from '~/types/productVariant' +import { useProductDetail } from '../stores/useProductDetail' +import { LazyLoadComponent } from 'react-lazy-load-image-component'; +import { getVariantSLA } from '~/services/productVariant' +import { useQuery } from 'react-query' + +type Props = { + variants: IProductVariantDetail[] +} + +const VariantList = ({ variants }: Props) => { + return ( +
+
+
+
Part Number
+
Variant
+
Stock
+
Time
+
Weight
+
Price
+
+ {variants.map((variant) => ( + + + + ))} +
+
+ ) +} + +const Row = ({ variant }: { variant: IProductVariantDetail }) => { + const { activeVariantId, setActive } = useProductDetail() + const querySLA = useQuery({ + queryKey: ['variant-sla', variant.id], + queryFn: () => getVariantSLA(variant.id), + }) + + const sla = querySLA?.data + + return ( +
+
{variant.code}
+
{variant.attributes.join(', ')}
+
+ + {sla?.qty} + +
+
+ + {sla?.sla_date} + +
+
+ {variant.weight > 0 ? `${variant.weight} Kg` : '-'} +
+
+ Rp {formatCurrency(variant.price.price)} +
+
+ +
+
+ ) +} + +export default VariantList \ No newline at end of file diff --git a/src-migrate/modules/product-detail/index.ts b/src-migrate/modules/product-detail/index.ts new file mode 100644 index 00000000..246bc06a --- /dev/null +++ b/src-migrate/modules/product-detail/index.ts @@ -0,0 +1,3 @@ +import ProductDetail from './components/ProductDetail'; + +export default ProductDetail; diff --git a/src-migrate/modules/product-detail/stores/useProductDetail.ts b/src-migrate/modules/product-detail/stores/useProductDetail.ts new file mode 100644 index 00000000..984d7948 --- /dev/null +++ b/src-migrate/modules/product-detail/stores/useProductDetail.ts @@ -0,0 +1,25 @@ +import { create } from 'zustand'; +import { IProductVariantDetail } from '~/types/productVariant'; + +type State = { + activeVariantId: number | null; + activePrice: IProductVariantDetail['price'] | null; + quantityInput: string; +}; + +type Action = { + setActive: (variant: IProductVariantDetail) => void; + setQuantityInput: (value: string) => void; +}; + +export const useProductDetail = create((set, get) => ({ + activeVariantId: null, + activePrice: null, + quantityInput: '1', + setActive: (variant) => { + set({ activeVariantId: variant.id, activePrice: variant.price }); + }, + setQuantityInput: (value: string) => { + set({ quantityInput: value }); + }, +})); diff --git a/src-migrate/modules/product-detail/styles/information.module.css b/src-migrate/modules/product-detail/styles/information.module.css new file mode 100644 index 00000000..c9b29020 --- /dev/null +++ b/src-migrate/modules/product-detail/styles/information.module.css @@ -0,0 +1,19 @@ +.wrapper { + @apply grid grid-cols-1; +} + +.row { + @apply flex p-3 rounded; +} + +.row:nth-child(odd) { + @apply bg-gray-100; +} + +.label { + @apply w-1/2 md:w-1/3 font-medium text-gray-500; +} + +.value { + @apply w-1/2 md:w-3/4 text-gray-950; +} diff --git a/src-migrate/modules/product-detail/styles/price-action.module.css b/src-migrate/modules/product-detail/styles/price-action.module.css new file mode 100644 index 00000000..594167af --- /dev/null +++ b/src-migrate/modules/product-detail/styles/price-action.module.css @@ -0,0 +1,12 @@ +.secondary-text { + @apply font-medium text-gray-500; +} +.main-price { + @apply font-medium text-danger-500 text-title-md; +} +.action-wrapper { + @apply flex gap-x-2.5; +} +.quantity-input { + @apply px-2 rounded text-center border border-gray-300 w-14 h-10 focus:outline-none; +} \ No newline at end of file diff --git a/src-migrate/modules/product-detail/styles/product-detail.module.css b/src-migrate/modules/product-detail/styles/product-detail.module.css new file mode 100644 index 00000000..c668167c --- /dev/null +++ b/src-migrate/modules/product-detail/styles/product-detail.module.css @@ -0,0 +1,15 @@ +.title { + @apply font-medium text-h-lg leading-8 md:text-title-md md:leading-10; +} + +.section-card { + @apply p-4 md:p-6 md:bg-gray-50 rounded-xl; +} + +.heading { + @apply text-h-md md:text-h-lg font-medium; +} + +.description { + @apply leading-relaxed text-gray-700; +} diff --git a/src-migrate/modules/product-detail/styles/side-similar.module.css b/src-migrate/modules/product-detail/styles/side-similar.module.css new file mode 100644 index 00000000..08692efa --- /dev/null +++ b/src-migrate/modules/product-detail/styles/side-similar.module.css @@ -0,0 +1,3 @@ +.wrapper { + @apply max-h-[500px] overflow-auto grid grid-cols-1 gap-y-4 divide-y divide-gray-300 border border-gray-300 rounded-lg; +} diff --git a/src-migrate/modules/product-detail/styles/variant-list.module.css b/src-migrate/modules/product-detail/styles/variant-list.module.css new file mode 100644 index 00000000..40cbd1bb --- /dev/null +++ b/src-migrate/modules/product-detail/styles/variant-list.module.css @@ -0,0 +1,19 @@ +.wrapper { + @apply grid grid-cols-1 w-[200%] md:w-full; +} + +.header { + @apply flex py-2.5 px-4 font-medium bg-gray-200 rounded-md; +} + +.row { + @apply flex items-center py-2.5 px-4 text-gray-800; +} + +.select-btn { + @apply !bg-gray-200 hover:!bg-danger-500 hover:!text-white; +} + +.select-btn--active { + @apply !text-white !bg-danger-500 hover:!text-white; +} diff --git a/src-migrate/modules/product-promo/components/AddToCart.tsx b/src-migrate/modules/product-promo/components/AddToCart.tsx index 58bb2ad7..3bac3c66 100644 --- a/src-migrate/modules/product-promo/components/AddToCart.tsx +++ b/src-migrate/modules/product-promo/components/AddToCart.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from 'react' import { CheckIcon, PlusIcon } from 'lucide-react' -import { IPromotion } from '~/common/types/promotion' +import { IPromotion } from '~/types/promotion' import { upsertUserCart } from '~/services/cart' -import { getAuth } from '~/common/libs/auth' +import { getAuth } from '~/libs/auth' import { Button, Spinner, useToast } from '@chakra-ui/react' import Link from 'next/link' import { useRouter } from 'next/router' diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx index e894c143..59110098 100644 --- a/src-migrate/modules/product-promo/components/Card.tsx +++ b/src-migrate/modules/product-promo/components/Card.tsx @@ -6,11 +6,11 @@ import { Skeleton, Tooltip } from '@chakra-ui/react' import { motion } from "framer-motion" import { PROMO_CATEGORY } from "~/constants/promotion" -import { getVariantById } from "~/services/variant" +import { getVariantById } from "~/services/productVariant" -import { IProductVariantPromo, IPromotion } from '~/common/types/promotion' -import formatCurrency from '~/common/libs/formatCurrency' -import clsxm from '~/common/libs/clsxm' +import { IProductVariantPromo, IPromotion } from '~/types/promotion' +import formatCurrency from '~/libs/formatCurrency' +import clsxm from '~/libs/clsxm' import ProductPromoItem from './Item' import ProductPromoAddToCart from "./AddToCart" diff --git a/src-migrate/modules/product-promo/components/CardCountdown.tsx b/src-migrate/modules/product-promo/components/CardCountdown.tsx index e398a390..b61ad115 100644 --- a/src-migrate/modules/product-promo/components/CardCountdown.tsx +++ b/src-migrate/modules/product-promo/components/CardCountdown.tsx @@ -6,8 +6,8 @@ import { ClockIcon } from 'lucide-react' import { Skeleton } from '@chakra-ui/react' import moment from 'moment' -import clsxm from '~/common/libs/clsxm' -import { IPromotion } from '~/common/types/promotion' +import clsxm from '~/libs/clsxm' +import { IPromotion } from '~/types/promotion' import { getPromotionProgram } from '~/services/promotionProgram' type Props = { diff --git a/src-migrate/modules/product-promo/components/CategoryTab.tsx b/src-migrate/modules/product-promo/components/CategoryTab.tsx index edc4aa92..c8e698c2 100644 --- a/src-migrate/modules/product-promo/components/CategoryTab.tsx +++ b/src-migrate/modules/product-promo/components/CategoryTab.tsx @@ -1,8 +1,8 @@ import React from 'react' import style from '../styles/category-tab.module.css' import { useModalStore } from '../stores/useModalStore' -import clsxm from '~/common/libs/clsxm' -import { ICategoryPromo } from '~/common/types/promotion' +import clsxm from '~/libs/clsxm' +import { ICategoryPromo } from '~/types/promotion' const TABS: ICategoryPromo[] = [ { value: 'bundling', label: 'Bundling' }, diff --git a/src-migrate/modules/product-promo/components/Item.tsx b/src-migrate/modules/product-promo/components/Item.tsx index 15ca4878..6c5a14ce 100644 --- a/src-migrate/modules/product-promo/components/Item.tsx +++ b/src-migrate/modules/product-promo/components/Item.tsx @@ -3,7 +3,7 @@ import style from '../styles/item.module.css' import React from 'react' import Image from 'next/image' -import { IProductVariantPromo } from '~/common/types/promotion' +import { IProductVariantPromo } from '~/types/promotion' type Props = { variant: IProductVariantPromo, diff --git a/src-migrate/modules/product-promo/components/Modal.tsx b/src-migrate/modules/product-promo/components/Modal.tsx index 598b7bbe..0de672c2 100644 --- a/src-migrate/modules/product-promo/components/Modal.tsx +++ b/src-migrate/modules/product-promo/components/Modal.tsx @@ -1,5 +1,5 @@ import React from 'react' -import Modal from '~/common/components/elements/Modal' +import { Modal } from "~/components/ui/modal" import { useModalStore } from '../stores/useModalStore' import ProductPromoCategoryTab from './CategoryTab' import ProductPromoModalContent from './ModalContent' diff --git a/src-migrate/modules/product-promo/components/ModalContent.tsx b/src-migrate/modules/product-promo/components/ModalContent.tsx index 90cf79e7..ab5129f8 100644 --- a/src-migrate/modules/product-promo/components/ModalContent.tsx +++ b/src-migrate/modules/product-promo/components/ModalContent.tsx @@ -1,7 +1,7 @@ import { useQuery } from "react-query" import { Skeleton } from "@chakra-ui/react" -import { getVariantPromoByCategory } from "~/services/variant" +import { getVariantPromoByCategory } from "~/services/productVariant" import { useModalStore } from "../stores/useModalStore" import ProductPromoCard from "./Card" diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index 47e1de29..04cf1363 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -5,7 +5,7 @@ import { useQuery } from 'react-query' import { Button, Skeleton } from '@chakra-ui/react' import ProductPromoCard from './Card' -import { IPromotion } from '~/common/types/promotion' +import { IPromotion } from '~/types/promotion' import ProductPromoModal from "./Modal" import { useModalStore } from "../stores/useModalStore" diff --git a/src-migrate/modules/product-promo/stores/useModalStore.ts b/src-migrate/modules/product-promo/stores/useModalStore.ts index bbb2b1fb..464bb598 100644 --- a/src-migrate/modules/product-promo/stores/useModalStore.ts +++ b/src-migrate/modules/product-promo/stores/useModalStore.ts @@ -1,5 +1,5 @@ import { create } from 'zustand'; -import { CategoryPromo } from '~/common/types/promotion'; +import { CategoryPromo } from '~/types/promotion'; type State = { active: boolean; diff --git a/src-migrate/modules/product-similar/hooks/useProductSimilar.tsx b/src-migrate/modules/product-similar/hooks/useProductSimilar.tsx new file mode 100644 index 00000000..f2c49472 --- /dev/null +++ b/src-migrate/modules/product-similar/hooks/useProductSimilar.tsx @@ -0,0 +1,15 @@ +import { useQuery } from 'react-query' +import { GetProductSimilarProps, getProductSimilar } from '~/services/product' + +type Props = GetProductSimilarProps + +const useProductSimilar = (props: Props) => { + const similarQuery = useQuery({ + queryKey: ['product-similar', props], + queryFn: () => getProductSimilar(props), + }) + + return similarQuery +} + +export default useProductSimilar \ No newline at end of file diff --git a/src-migrate/modules/product-slider/components/ProductSlider.tsx b/src-migrate/modules/product-slider/components/ProductSlider.tsx new file mode 100644 index 00000000..6ef9f688 --- /dev/null +++ b/src-migrate/modules/product-slider/components/ProductSlider.tsx @@ -0,0 +1,42 @@ +import 'swiper/css' + +import React from 'react' +import { Swiper, SwiperSlide } from 'swiper/react' +import { FreeMode } from 'swiper' + +import ProductCard from '~/modules/product-card' +import { IProduct } from '~/types/product' +import useDevice from '@/core/hooks/useDevice' + +type Props = { + products: IProduct[], + productLayout?: 'vertical' | 'horizontal', +} + +const ProductSlider = ({ products, productLayout }: Props) => { + const { isDesktop, isMobile } = useDevice() + + return ( +
+ + {products.map((product) => ( + + + + ))} + +
+ ) +} + +export default ProductSlider \ No newline at end of file diff --git a/src-migrate/modules/product-slider/index.ts b/src-migrate/modules/product-slider/index.ts new file mode 100644 index 00000000..1593a543 --- /dev/null +++ b/src-migrate/modules/product-slider/index.ts @@ -0,0 +1,3 @@ +import ProductSlider from './components/ProductSlider'; + +export default ProductSlider; diff --git a/src-migrate/modules/register/components/Form.tsx b/src-migrate/modules/register/components/Form.tsx index dc9107b2..b834f97a 100644 --- a/src-migrate/modules/register/components/Form.tsx +++ b/src-migrate/modules/register/components/Form.tsx @@ -1,7 +1,7 @@ import { ChangeEvent, useMemo } from "react"; import { useMutation } from "react-query"; -import { useRegisterStore } from "~/common/stores/useRegisterStore"; -import { RegisterProps } from "~/common/types/auth"; +import { useRegisterStore } from "../stores/useRegisterStore"; +import { RegisterProps } from "~/types/auth"; import { registerUser } from "~/services/auth"; import TermCondition from "./TermCondition"; import FormCaptcha from "./FormCaptcha"; diff --git a/src-migrate/modules/register/components/FormCaptcha.tsx b/src-migrate/modules/register/components/FormCaptcha.tsx index 967be017..fbea2b10 100644 --- a/src-migrate/modules/register/components/FormCaptcha.tsx +++ b/src-migrate/modules/register/components/FormCaptcha.tsx @@ -1,5 +1,5 @@ -import ReCaptcha from '~/common/components/elements/ReCaptcha' -import { useRegisterStore } from '~/common/stores/useRegisterStore' +import { ReCaptcha } from '~/components/ui/re-captcha' +import { useRegisterStore } from "../stores/useRegisterStore"; const FormCaptcha = () => { const { updateValidCaptcha } = useRegisterStore() diff --git a/src-migrate/modules/register/components/TermCondition.tsx b/src-migrate/modules/register/components/TermCondition.tsx index 6b95ba19..b7729deb 100644 --- a/src-migrate/modules/register/components/TermCondition.tsx +++ b/src-migrate/modules/register/components/TermCondition.tsx @@ -1,7 +1,7 @@ import { Checkbox } from '@chakra-ui/react' import React from 'react' -import Modal from '~/common/components/elements/Modal' -import { useRegisterStore } from '~/common/stores/useRegisterStore' +import { Modal } from '~/components/ui/modal' +import { useRegisterStore } from "../stores/useRegisterStore"; import PageContent from '~/modules/page-content' const TermCondition = () => { diff --git a/src-migrate/modules/register/stores/useRegisterStore.ts b/src-migrate/modules/register/stores/useRegisterStore.ts new file mode 100644 index 00000000..d8abf52b --- /dev/null +++ b/src-migrate/modules/register/stores/useRegisterStore.ts @@ -0,0 +1,60 @@ +import { create } from 'zustand'; +import { RegisterProps } from '~/types/auth'; +import { registerSchema } from '~/validations/auth'; +import { ZodError } from 'zod'; + +type State = { + form: RegisterProps; + errors: { + [key in keyof RegisterProps]?: string; + }; + isCheckedTNC: boolean; + isOpenTNC: boolean; + isValidCaptcha: boolean; +}; + +type Action = { + updateForm: (name: string, value: string) => void; + updateValidCaptcha: (value: boolean) => void; + toggleCheckTNC: () => void; + openTNC: () => void; + closeTNC: () => void; + validate: () => void; +}; + +export const useRegisterStore = create((set, get) => ({ + form: { + company: '', + name: '', + email: '', + password: '', + phone: '', + }, + updateForm: (name, value) => + set((state) => ({ form: { ...state.form, [name]: value } })), + + errors: {}, + validate: () => { + try { + registerSchema.parse(get().form); + set({ errors: {} }); + } catch (error) { + if (error instanceof ZodError) { + const errors: State['errors'] = {}; + error.errors.forEach( + (e) => (errors[e.path[0] as keyof RegisterProps] = e.message) + ); + set({ errors }); + } + } + }, + isCheckedTNC: false, + toggleCheckTNC: () => set((state) => ({ isCheckedTNC: !state.isCheckedTNC })), + + isOpenTNC: false, + openTNC: () => set(() => ({ isOpenTNC: true })), + closeTNC: () => set(() => ({ isOpenTNC: false })), + + isValidCaptcha: false, + updateValidCaptcha: (value) => set(() => ({ isValidCaptcha: value })), +})); -- cgit v1.2.3 From c42f03768e4c009a247d5cacbecaf4ac952752c9 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 15 Jan 2024 13:54:30 +0700 Subject: Improve product detail performance --- .../product-detail/components/PriceAction.tsx | 42 +++++++++++++++------- .../product-detail/components/ProductDetail.tsx | 32 ++++++++++++++--- .../product-detail/components/VariantList.tsx | 12 +++++-- .../product-detail/stores/useProductDetail.ts | 6 ++++ .../product-detail/styles/price-action.module.css | 6 +++- .../modules/product-promo/components/Section.tsx | 9 ++++- 6 files changed, 86 insertions(+), 21 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index 8189e5bd..cfb596fa 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -6,13 +6,21 @@ import { formatToShortText } from '~/libs/formatNumber' import { IProductDetail } from '~/types/product' import { useProductDetail } from '../stores/useProductDetail' import AddToCart from './AddToCart' +import Link from 'next/link' type Props = { product: IProductDetail } const PriceAction = ({ product }: Props) => { - const { activePrice, setActive, activeVariantId, quantityInput, setQuantityInput } = useProductDetail() + const { + activePrice, + setActive, + activeVariantId, + quantityInput, + setQuantityInput, + askAdminUrl + } = useProductDetail() useEffect(() => { setActive(product.variants[0]) @@ -26,18 +34,28 @@ const PriceAction = ({ product }: Props) => {
)}
-
- Rp {formatCurrency(activePrice?.price || 0)} -
-
-
- {!!activePrice && ( - <> + + {!!activePrice && activePrice.price > 0 && ( + <> +
+ Rp {formatCurrency(activePrice.price || 0)} +
+
+
Termasuk PPN: {' '} - Rp {formatCurrency(Math.round(activePrice?.price * 1.11))} - - )} -
+ Rp {formatCurrency(Math.round(activePrice.price * 1.11))} +
+ + )} + + {!!activePrice && activePrice.price === 0 && ( + + Hubungi kami untuk dapatkan harga terbaik,{' '} + + klik disini + + + )}
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index b752a138..d38e0686 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -1,6 +1,6 @@ import style from '../styles/product-detail.module.css' -import React from 'react' +import React, { useEffect } from 'react' import Link from 'next/link' import { MessageCircleIcon } from 'lucide-react' import { Button } from '@chakra-ui/react' @@ -15,6 +15,10 @@ import SimilarSide from './SimilarSide' import SimilarBottom from './SimilarBottom' import useDevice from '@/core/hooks/useDevice' import PriceAction from './PriceAction' +import { whatsappUrl } from '~/libs/whatsappUrl' +import { useRouter } from 'next/router' +import { useProductDetail } from '../stores/useProductDetail' +import ProductPromoSection from '~/modules/product-promo/components/Section' type Props = { product: IProductDetail @@ -22,6 +26,22 @@ type Props = { const ProductDetail = ({ product }: Props) => { const { isDesktop, isMobile } = useDevice() + const router = useRouter() + const { setAskAdminUrl, askAdminUrl, activeVariantId } = useProductDetail() + + useEffect(() => { + const createdAskUrl = whatsappUrl({ + template: 'product', + payload: { + manufacture: product.manufacture.name, + productName: product.name, + url: process.env.NEXT_PUBLIC_SELF_HOST + router.asPath + }, + fallbackUrl: router.asPath + }) + + setAskAdminUrl(createdAskUrl) + }, [router.asPath, product.manufacture.name, product.name, setAskAdminUrl]) return ( <> @@ -47,8 +67,9 @@ const ProductDetail = ({ product }: Props) => {
)} -
+
+ {activeVariantId && ( + + )}

@@ -74,7 +98,7 @@ const ProductDetail = ({ product }: Props) => {

-
+

diff --git a/src-migrate/modules/product-detail/components/VariantList.tsx b/src-migrate/modules/product-detail/components/VariantList.tsx index d07e6b23..f8aa5565 100644 --- a/src-migrate/modules/product-detail/components/VariantList.tsx +++ b/src-migrate/modules/product-detail/components/VariantList.tsx @@ -48,11 +48,16 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => { return (
-
{variant.code}
+
{variant.code}
{variant.attributes.join(', ')}
- {sla?.qty} + {sla?.qty !== undefined && ( + <> + {sla.qty > 0 && sla.qty} + {sla.qty == 0 && '-'} + + )}
@@ -64,7 +69,8 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => { {variant.weight > 0 ? `${variant.weight} Kg` : '-'}
- Rp {formatCurrency(variant.price.price)} + {variant.price.price > 0 && `Rp ${formatCurrency(variant.price.price)}`} + {variant.price.price === 0 && '-'}
)} - + 0 + })} + > {promotions?.data.map((promotion) => (
-- cgit v1.2.3 From d9dafa74857959974e9d379dc1a3abfbaf2af83d Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 15 Jan 2024 15:13:20 +0700 Subject: Update improve product detail performance --- .../modules/product-detail/components/ProductDetail.tsx | 2 +- .../modules/product-detail/components/VariantList.tsx | 12 ++++++------ src-migrate/modules/product-promo/components/Item.tsx | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index d38e0686..08ad7d51 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -86,7 +86,7 @@ const ProductDetail = ({ product }: Props) => { )}
- {activeVariantId && ( + {!!activeVariantId && ( )} diff --git a/src-migrate/modules/product-detail/components/VariantList.tsx b/src-migrate/modules/product-detail/components/VariantList.tsx index f8aa5565..96b7486b 100644 --- a/src-migrate/modules/product-detail/components/VariantList.tsx +++ b/src-migrate/modules/product-detail/components/VariantList.tsx @@ -22,10 +22,10 @@ const VariantList = ({ variants }: Props) => {
Part Number
Variant
-
Stock
-
Time
-
Weight
-
Price
+
Stock
+
Masa Persiapan
+
Berat
+
Harga
{variants.map((variant) => ( @@ -50,7 +50,7 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => {
{variant.code}
{variant.attributes.join(', ')}
-
+
{sla?.qty !== undefined && ( <> @@ -72,7 +72,7 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => { {variant.price.price > 0 && `Rp ${formatCurrency(variant.price.price)}`} {variant.price.price === 0 && '-'}
-
+
+ + + + + + +
-
@@ -117,9 +147,7 @@ const ProductDetail = ({ product }: Props) => {
- - -
+
Produk Serupa @@ -131,7 +159,7 @@ const ProductDetail = ({ product }: Props) => {
)} -
+
Kamu Mungkin Juga Suka
diff --git a/src-migrate/modules/product-detail/components/VariantList.tsx b/src-migrate/modules/product-detail/components/VariantList.tsx index 96b7486b..e8c18921 100644 --- a/src-migrate/modules/product-detail/components/VariantList.tsx +++ b/src-migrate/modules/product-detail/components/VariantList.tsx @@ -22,10 +22,10 @@ const VariantList = ({ variants }: Props) => {
Part Number
Variant
-
Stock
+
Stock
Masa Persiapan
Berat
-
Harga
+
Harga
{variants.map((variant) => ( @@ -49,8 +49,8 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => { return (
{variant.code}
-
{variant.attributes.join(', ')}
-
+
{variant.attributes.join(', ') || '-'}
+
{sla?.qty !== undefined && ( <> @@ -68,7 +68,7 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => {
{variant.weight > 0 ? `${variant.weight} Kg` : '-'}
-
+
{variant.price.price > 0 && `Rp ${formatCurrency(variant.price.price)}`} {variant.price.price === 0 && '-'}
-- cgit v1.2.3 From 7072d220bc86b56e76716d114e28af98219e3f69 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 17 Jan 2024 09:54:59 +0700 Subject: Update image for performance --- src-migrate/modules/product-card/components/ProductCard.tsx | 1 - src-migrate/modules/product-detail/components/Image.tsx | 1 - 2 files changed, 2 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx index 8cd96ce8..c8a0b701 100644 --- a/src-migrate/modules/product-card/components/ProductCard.tsx +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -37,7 +37,6 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => { width={128} height={128} className='object-contain object-center h-full w-full' - classNames={{ wrapper: 'h-full' }} />
diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx index 2c5e989b..6ec715d8 100644 --- a/src-migrate/modules/product-detail/components/Image.tsx +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -18,7 +18,6 @@ const Image = ({ product }: Props) => { width={256} height={256} className='object-contain object-center h-full w-full' - classNames={{ wrapper: 'h-full w-full' }} loading='eager' priority /> -- cgit v1.2.3 From f7a0be1407da7edab60f6cb2ca3f1ef97acf811a Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 17 Jan 2024 16:03:48 +0700 Subject: Update product detail page ui --- .../product-card/styles/product-card.module.css | 2 +- .../product-detail/components/PriceAction.tsx | 3 +-- .../product-detail/components/VariantList.tsx | 31 +++++++++++++++++----- .../product-detail/styles/variant-list.module.css | 12 +++++++-- .../product-slider/components/ProductSlider.tsx | 2 +- 5 files changed, 38 insertions(+), 12 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-card/styles/product-card.module.css b/src-migrate/modules/product-card/styles/product-card.module.css index 38b895f9..aac27a84 100644 --- a/src-migrate/modules/product-card/styles/product-card.module.css +++ b/src-migrate/modules/product-card/styles/product-card.module.css @@ -41,7 +41,7 @@ } .ready-stock { - @apply bg-danger-500 text-white text-[11px] px-2 py-1 rounded-md; + @apply bg-danger-500 text-white text-[11px] px-2 py-1 rounded-md whitespace-nowrap; } .price-inc, diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index dd211f6f..cade21b8 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -2,7 +2,6 @@ import style from '../styles/price-action.module.css' import React, { useEffect } from 'react' import formatCurrency from '~/libs/formatCurrency' -import { formatToShortText } from '~/libs/formatNumber' import { IProductDetail } from '~/types/product' import { useProductDetail } from '../stores/useProductDetail' import AddToCart from './AddToCart' @@ -27,7 +26,7 @@ const PriceAction = ({ product }: Props) => { }, [product, setActive]); return ( -
+
{!!activePrice && activePrice.price > 0 && ( <>
diff --git a/src-migrate/modules/product-detail/components/VariantList.tsx b/src-migrate/modules/product-detail/components/VariantList.tsx index e8c18921..931563e0 100644 --- a/src-migrate/modules/product-detail/components/VariantList.tsx +++ b/src-migrate/modules/product-detail/components/VariantList.tsx @@ -10,6 +10,7 @@ import { useProductDetail } from '../stores/useProductDetail' import { LazyLoadComponent } from 'react-lazy-load-image-component'; import { getVariantSLA } from '~/services/productVariant' import { useQuery } from 'react-query' +import useDevice from '@/core/hooks/useDevice' type Props = { variants: IProductVariantDetail[] @@ -20,12 +21,13 @@ const VariantList = ({ variants }: Props) => {
-
Part Number
+
Part Number
Variant
Stock
Masa Persiapan
Berat
Harga
+
{variants.map((variant) => ( @@ -38,6 +40,8 @@ const VariantList = ({ variants }: Props) => { } const Row = ({ variant }: { variant: IProductVariantDetail }) => { + const { isMobile } = useDevice() + const { activeVariantId, setActive } = useProductDetail() const querySLA = useQuery({ queryKey: ['variant-sla', variant.id], @@ -46,17 +50,32 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => { const sla = querySLA?.data + const handleSelect = (variant: IProductVariantDetail) => { + const priceSectionEl = document.getElementById('price-section') + if (isMobile && priceSectionEl) { + window.scrollTo({ + top: priceSectionEl.offsetTop - 120, + behavior: 'smooth' + }) + } + setActive(variant) + } + return (
-
{variant.code}
+
{variant.code}
{variant.attributes.join(', ') || '-'}
{sla?.qty !== undefined && ( - <> +
0, + })} + > {sla.qty > 0 && sla.qty} {sla.qty == 0 && '-'} - +
)}
@@ -72,9 +91,9 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => { {variant.price.price > 0 && `Rp ${formatCurrency(variant.price.price)}`} {variant.price.price === 0 && '-'}
-
+
diff --git a/src-migrate/modules/product-detail/components/Breadcrumb.tsx b/src-migrate/modules/product-detail/components/Breadcrumb.tsx index 0ee5b115..ec445b60 100644 --- a/src-migrate/modules/product-detail/components/Breadcrumb.tsx +++ b/src-migrate/modules/product-detail/components/Breadcrumb.tsx @@ -18,7 +18,7 @@ const Breadcrumb = ({ id, name }: Props) => { const breadcrumbs = query.data || [] return ( -
+
Home / {breadcrumbs.map((category, index) => ( diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 93fa7118..80f43aea 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -52,10 +52,10 @@ const ProductDetail = ({ product }: Props) => { return ( <>
-
+
-
+
@@ -86,7 +86,7 @@ const ProductDetail = ({ product }: Props) => { Ask Admin - + Date: Thu, 18 Jan 2024 13:18:04 +0700 Subject: Update product detail performance --- src-migrate/modules/product-detail/components/AddToWishlist.tsx | 3 ++- src-migrate/modules/product-detail/components/Breadcrumb.tsx | 3 ++- src-migrate/modules/product-detail/components/VariantList.tsx | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/AddToWishlist.tsx b/src-migrate/modules/product-detail/components/AddToWishlist.tsx index cb11e837..697b2d5c 100644 --- a/src-migrate/modules/product-detail/components/AddToWishlist.tsx +++ b/src-migrate/modules/product-detail/components/AddToWishlist.tsx @@ -23,7 +23,8 @@ const AddToWishlist = ({ productId }: Props) => { queryFn: () => { if (typeof auth !== 'object') return null; return getUserWishlist(auth.id, searchParams) - } + }, + refetchOnWindowFocus: false }) const isAdded = query.data?.product_total ? query.data.product_total > 0 : false; diff --git a/src-migrate/modules/product-detail/components/Breadcrumb.tsx b/src-migrate/modules/product-detail/components/Breadcrumb.tsx index ec445b60..f41859a9 100644 --- a/src-migrate/modules/product-detail/components/Breadcrumb.tsx +++ b/src-migrate/modules/product-detail/components/Breadcrumb.tsx @@ -12,7 +12,8 @@ type Props = { const Breadcrumb = ({ id, name }: Props) => { const query = useQuery({ queryKey: ['product-category-breadcrumb'], - queryFn: () => getProductCategoryBreadcrumb(id) + queryFn: () => getProductCategoryBreadcrumb(id), + refetchOnWindowFocus: false }) const breadcrumbs = query.data || [] diff --git a/src-migrate/modules/product-detail/components/VariantList.tsx b/src-migrate/modules/product-detail/components/VariantList.tsx index 931563e0..1da478e7 100644 --- a/src-migrate/modules/product-detail/components/VariantList.tsx +++ b/src-migrate/modules/product-detail/components/VariantList.tsx @@ -46,6 +46,7 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => { const querySLA = useQuery({ queryKey: ['variant-sla', variant.id], queryFn: () => getVariantSLA(variant.id), + refetchOnWindowFocus: false, }) const sla = querySLA?.data -- cgit v1.2.3 From 5ac82c38ed3ec4db1fe4ae96e7493a55154716ef Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Thu, 18 Jan 2024 16:24:54 +0700 Subject: Update product detail page --- .../modules/product-detail/components/Image.tsx | 69 ++++++++++++++++++++-- .../product-detail/components/PriceAction.tsx | 18 +++++- .../product-detail/components/VariantList.tsx | 10 +++- .../modules/product-detail/styles/image.module.css | 35 +++++++++++ .../product-detail/styles/price-action.module.css | 8 +++ .../product-detail/styles/variant-list.module.css | 8 +++ 6 files changed, 139 insertions(+), 9 deletions(-) create mode 100644 src-migrate/modules/product-detail/styles/image.module.css (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx index 6ec715d8..2ab3ff59 100644 --- a/src-migrate/modules/product-detail/components/Image.tsx +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -1,27 +1,53 @@ -import React from 'react' +import style from '../styles/image.module.css'; + +import React, { useEffect, useState } from 'react' import { InfoIcon } from 'lucide-react' import { Tooltip } from '@chakra-ui/react' import { IProductDetail } from '~/types/product' import ImageUI from '~/components/ui/image' +import moment from 'moment'; type Props = { product: IProductDetail } const Image = ({ product }: Props) => { + const flashSale = product.flash_sale + + const [count, setCount] = useState(flashSale?.remaining_time || 0); + + useEffect(() => { + let interval: NodeJS.Timeout; + + if (flashSale?.remaining_time && flashSale.remaining_time > 0) { + setCount(flashSale.remaining_time); + + interval = setInterval(() => { + setCount((prevCount) => prevCount - 1); + }, 1000); + } + + return () => { + clearInterval(interval); + }; + }, [flashSale?.remaining_time]); + + const duration = moment.duration(count, 'seconds') + return ( -
+
-
+ +
{
+ + {flashSale.remaining_time > 0 && ( +
+
+ + +
+
+
{Math.floor(product.lowest_price.discount_percentage)}%
+
+ + {product.flash_sale.tag} +
+
+
+ {duration.hours().toString().padStart(2, '0')} + {duration.minutes().toString().padStart(2, '0')} + {duration.seconds().toString().padStart(2, '0')} +
+
+ +
+
+ )}
) } diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index cade21b8..f25847a5 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -29,13 +29,25 @@ const PriceAction = ({ product }: Props) => {
{!!activePrice && activePrice.price > 0 && ( <> -
- Rp {formatCurrency(activePrice.price || 0)} +
+ {activePrice.discount_percentage > 0 && ( + <> +
+ {Math.floor(activePrice.discount_percentage)}% +
+
+ Rp {formatCurrency(activePrice.price || 0)} +
+ + )} +
+ Rp {formatCurrency(activePrice.price_discount || 0)} +
Termasuk PPN: {' '} - Rp {formatCurrency(Math.round(activePrice.price * 1.11))} + Rp {formatCurrency(Math.round(activePrice.price_discount * 1.11))}
)} diff --git a/src-migrate/modules/product-detail/components/VariantList.tsx b/src-migrate/modules/product-detail/components/VariantList.tsx index 1da478e7..3d5b9b74 100644 --- a/src-migrate/modules/product-detail/components/VariantList.tsx +++ b/src-migrate/modules/product-detail/components/VariantList.tsx @@ -89,8 +89,14 @@ const Row = ({ variant }: { variant: IProductVariantDetail }) => { {variant.weight > 0 ? `${variant.weight} Kg` : '-'}
- {variant.price.price > 0 && `Rp ${formatCurrency(variant.price.price)}`} - {variant.price.price === 0 && '-'} + {variant.price.discount_percentage > 0 && ( +
+
{Math.floor(variant.price.discount_percentage)}%
+
Rp {formatCurrency(variant.price.price)}
+
+ )} + {variant.price.price_discount > 0 && `Rp ${formatCurrency(variant.price.price_discount)}`} + {variant.price.price_discount === 0 && '-'}
- - - - -
-
-
-
- ) -} - -export default CartDetail \ No newline at end of file diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index 08823d19..48e568e0 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -70,7 +70,8 @@ const CartItem = ({ item, editable = true }: Props) => { {item.cart_type === 'product' && ( <>
- Rp {formatCurrency(item.price.price)} + {item.price.price > 0 && `Rp ${formatCurrency(item.price.price)}`} + {item.price.price === 0 && '-'}
{item.code}
-- cgit v1.2.3 From 97d079e4b64aa02a51e5ab877a73f7f23c7c6296 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 22 Jan 2024 14:51:31 +0700 Subject: Add watermark on product image --- src-migrate/modules/product-card/components/ProductCard.tsx | 9 +++++++-- src-migrate/modules/product-detail/components/Image.tsx | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx index 0a97b344..34f6d6b1 100644 --- a/src-migrate/modules/product-card/components/ProductCard.tsx +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -1,7 +1,7 @@ import style from '../styles/product-card.module.css' import Link from 'next/link' -import React from 'react' +import React, { useMemo } from 'react' import Image from '~/components/ui/image' import clsxm from '~/libs/clsxm' import formatCurrency from '~/libs/formatCurrency' @@ -20,6 +20,11 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => { manufacture: createSlug('/shop/brands/', product.manufacture.name, product.manufacture.id.toString()), } + const image = useMemo(() => { + if (product.image) return product.image + '?watermark=true' + return '/images/noimage.jpeg' + }, [product.image]) + return (
{ })}> {product.name} { const duration = moment.duration(count, 'seconds') + const image = useMemo(() => { + if (product.image) return product.image + '?watermark=true' + return '/images/noimage.jpeg' + }, [product.image]) + return (
Date: Thu, 25 Jan 2024 14:51:22 +0700 Subject: Add square ratio on product image --- src-migrate/modules/product-card/components/ProductCard.tsx | 2 +- src-migrate/modules/product-detail/components/Image.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx index 34f6d6b1..8487cd94 100644 --- a/src-migrate/modules/product-card/components/ProductCard.tsx +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -21,7 +21,7 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => { } const image = useMemo(() => { - if (product.image) return product.image + '?watermark=true' + if (product.image) return product.image + '?ratio=square' return '/images/noimage.jpeg' }, [product.image]) diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx index fffe1480..b69cc87f 100644 --- a/src-migrate/modules/product-detail/components/Image.tsx +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -36,7 +36,7 @@ const Image = ({ product }: Props) => { const duration = moment.duration(count, 'seconds') const image = useMemo(() => { - if (product.image) return product.image + '?watermark=true' + if (product.image) return product.image + '?ratio=square' return '/images/noimage.jpeg' }, [product.image]) -- cgit v1.2.3 From 9c2b36fb15511b9f38492e133125a8b766c58a19 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 30 Jan 2024 13:51:19 +0700 Subject: Update product similar section --- .../modules/product-detail/components/ProductDetail.tsx | 5 ++++- .../modules/product-detail/components/SimilarBottom.tsx | 12 ++++++++++-- .../modules/product-detail/components/SimilarSide.tsx | 14 ++++++++------ .../modules/product-detail/styles/side-similar.module.css | 3 --- 4 files changed, 22 insertions(+), 12 deletions(-) delete mode 100644 src-migrate/modules/product-detail/styles/side-similar.module.css (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 80f43aea..2bd3c901 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -23,6 +23,7 @@ import SimilarBottom from './SimilarBottom' import PriceAction from './PriceAction' import ProductPromoSection from '~/modules/product-promo/components/Section' import Breadcrumb from './Breadcrumb' +import { LazyLoadComponent } from 'react-lazy-load-image-component' type Props = { product: IProductDetail @@ -166,7 +167,9 @@ const ProductDetail = ({ product }: Props) => {
- + + +
diff --git a/src-migrate/modules/product-detail/components/SimilarBottom.tsx b/src-migrate/modules/product-detail/components/SimilarBottom.tsx index 9a12a6ef..40d4dd82 100644 --- a/src-migrate/modules/product-detail/components/SimilarBottom.tsx +++ b/src-migrate/modules/product-detail/components/SimilarBottom.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import { Skeleton } from '@chakra-ui/react' import useProductSimilar from '~/modules/product-similar/hooks/useProductSimilar' import ProductSlider from '~/modules/product-slider' import { IProductDetail } from '~/types/product' @@ -15,7 +15,15 @@ const SimilarBottom = ({ product }: Props) => { const products = productSimilar.data?.products || [] - return ; + return ( + + + + ); } export default SimilarBottom \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/SimilarSide.tsx b/src-migrate/modules/product-detail/components/SimilarSide.tsx index 646a1c51..2ba8fa21 100644 --- a/src-migrate/modules/product-detail/components/SimilarSide.tsx +++ b/src-migrate/modules/product-detail/components/SimilarSide.tsx @@ -1,6 +1,4 @@ -import style from '../styles/side-similar.module.css' - -import React from 'react' +import { Skeleton } from '@chakra-ui/react' import ProductCard from '~/modules/product-card' import useProductSimilar from '~/modules/product-similar/hooks/useProductSimilar' @@ -13,13 +11,17 @@ type Props = { const SimilarSide = ({ product }: Props) => { const productSimilar = useProductSimilar({ name: product.name, - except: { productId: product.id, manufactureId: product.manufacture.id }, + except: { productId: product.id }, }) const products = productSimilar.data?.products || [] return ( -
+ {products.map((product) => ( { layout='horizontal' /> ))} -
+ ) } diff --git a/src-migrate/modules/product-detail/styles/side-similar.module.css b/src-migrate/modules/product-detail/styles/side-similar.module.css deleted file mode 100644 index 08692efa..00000000 --- a/src-migrate/modules/product-detail/styles/side-similar.module.css +++ /dev/null @@ -1,3 +0,0 @@ -.wrapper { - @apply max-h-[500px] overflow-auto grid grid-cols-1 gap-y-4 divide-y divide-gray-300 border border-gray-300 rounded-lg; -} -- cgit v1.2.3 From 0550b0dbe9b8e369cfe211b78ab0de49a6e1f49d Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 30 Jan 2024 15:54:28 +0700 Subject: Add except product similar --- src-migrate/modules/product-detail/components/SimilarSide.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/SimilarSide.tsx b/src-migrate/modules/product-detail/components/SimilarSide.tsx index 2ba8fa21..d70a314d 100644 --- a/src-migrate/modules/product-detail/components/SimilarSide.tsx +++ b/src-migrate/modules/product-detail/components/SimilarSide.tsx @@ -11,7 +11,7 @@ type Props = { const SimilarSide = ({ product }: Props) => { const productSimilar = useProductSimilar({ name: product.name, - except: { productId: product.id }, + except: { productId: product.id, manufactureId: product.manufacture.id }, }) const products = productSimilar.data?.products || [] -- cgit v1.2.3 From f1eba397ba49db8c1a3310c0b12d01d3ee7fc478 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 19 Feb 2024 10:29:42 +0700 Subject: Update promotion program spacing on product detail --- src-migrate/modules/product-detail/components/ProductDetail.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 2bd3c901..4b514944 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -117,9 +117,8 @@ const ProductDetail = ({ product }: Props) => { )}
- {!!activeVariantId && ( - - )} + {!!activeVariantId && } + {!!activeVariantId && !isMobile &&
}

-- cgit v1.2.3 From 0d66e85886e9057bda5aa6924be6564058d2e53f Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 19 Feb 2024 10:30:09 +0700 Subject: Update slides per view to 2.2 on product slider mobile --- src-migrate/modules/product-slider/components/ProductSlider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-slider/components/ProductSlider.tsx b/src-migrate/modules/product-slider/components/ProductSlider.tsx index 3d6e7593..05f8c322 100644 --- a/src-migrate/modules/product-slider/components/ProductSlider.tsx +++ b/src-migrate/modules/product-slider/components/ProductSlider.tsx @@ -19,7 +19,7 @@ const ProductSlider = ({ products, productLayout }: Props) => { return (
Date: Mon, 19 Feb 2024 13:16:08 +0700 Subject: Add link to product on cart item --- src-migrate/modules/cart/components/Item.tsx | 56 +++++++++++++++++++---- src-migrate/modules/cart/components/ItemPromo.tsx | 12 +++-- 2 files changed, 54 insertions(+), 14 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index 48e568e0..7a4c22cc 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -1,17 +1,18 @@ import style from '../styles/item.module.css' -import Image from 'next/image' -import React from 'react' import { Skeleton, SkeletonProps, Tooltip } from '@chakra-ui/react' import { InfoIcon } from 'lucide-react' +import Image from 'next/image' import { PROMO_CATEGORY } from '~/constants/promotion' import formatCurrency from '~/libs/formatCurrency' import { CartItem as CartItemProps } from '~/types/cart' -import CartItemPromo from './ItemPromo' +import Link from 'next/link' +import { createSlug } from '~/libs/slug' import CartItemAction from './ItemAction' +import CartItemPromo from './ItemPromo' import CartItemSelect from './ItemSelect' type Props = { @@ -20,8 +21,6 @@ type Props = { } const CartItem = ({ item, editable = true }: Props) => { - const image = item?.image || item?.parent?.image - return (
{item.cart_type === 'promotion' && ( @@ -47,13 +46,12 @@ const CartItem = ({ item, editable = true }: Props) => {
{editable && }
-
- {image && {item.name}} - {!image &&
No Image
} -
+ +
-
{item.name}
+ +
{item.cart_type === 'promotion' && ( @@ -97,6 +95,44 @@ const CartItem = ({ item, editable = true }: Props) => { ) } +CartItem.Image = function CartItemImage({ item }: { item: CartItemProps }) { + const image = item?.image || item?.parent?.image + + return ( + <> + {item.cart_type === 'promotion' && ( +
+ {image && {item.name}} + {!image &&
No Image
} +
+ )} + + {item.cart_type === 'product' && ( + + {image && {item.name}} + {!image &&
No Image
} + + )} + + ) +} + +CartItem.Name = function CartItemName({ item }: { item: CartItemProps }) { + return ( + <> + {item.cart_type === 'promotion' && ( +
{item.name}
+ )} + + {item.cart_type === 'product' && ( + + {item.name} + + )} + + ) +} + CartItem.Skeleton = function CartItemSkeleton(props: SkeletonProps & { count: number }) { return Array.from({ length: props.count }).map((_, index) => ( { return (
-
+ {product?.image && {product.name}} -
+
-
{product.display_name}
+ + {product.display_name} + +
{product.code}
-- cgit v1.2.3 From 410cc16690f97e59b6a93b4196a2c13caf498b4d Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 19 Feb 2024 13:28:11 +0700 Subject: Add discount price on cart item --- src-migrate/modules/cart/components/Item.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index 7a4c22cc..8a6f8822 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -66,15 +66,21 @@ const CartItem = ({ item, editable = true }: Props) => { )} {item.cart_type === 'product' && ( - <> +
+ {item.price.discount_percentage > 0 && ( + + Rp {formatCurrency((item.price.price || 0))} + + )} +
- {item.price.price > 0 && `Rp ${formatCurrency(item.price.price)}`} - {item.price.price === 0 && '-'} + {item.price.price_discount > 0 && `Rp ${formatCurrency(item.price.price_discount)}`} + {item.price.price_discount === 0 && '-'}
-
{item.code}
- +
)} +
{item.cart_type === 'product' && item.code}
{item.weight} Kg
-- cgit v1.2.3 From 95f49f1de12ae612957b60ed53cf5993b9b7ef1e Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 19 Feb 2024 16:07:25 +0700 Subject: Add promotion program smooth animation --- src-migrate/modules/cart/components/Item.tsx | 6 ++---- src-migrate/modules/product-promo/components/Section.tsx | 4 +++- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index 8a6f8822..ba7f7cdf 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -72,7 +72,7 @@ const CartItem = ({ item, editable = true }: Props) => { Rp {formatCurrency((item.price.price || 0))} )} - +
{item.price.price_discount > 0 && `Rp ${formatCurrency(item.price.price_discount)}`} {item.price.price_discount === 0 && '-'} @@ -81,9 +81,7 @@ const CartItem = ({ item, editable = true }: Props) => { )}
{item.cart_type === 'product' && item.code}
-
- {item.weight} Kg -
+
{item.weight} Kg
{editable && } diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index b6753be7..694a3705 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -25,7 +25,9 @@ const ProductPromoSection = ({ productId }: Props) => { const { openModal } = useModalStore() return ( -
+
0, + })}> {promotions?.data && promotions?.data.length > 0 && ( -- cgit v1.2.3 From ee8d9403a516b7271581d656e58156e43a1b3d20 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 09:24:34 +0700 Subject: Update promotion program line for smoother animation --- .../product-detail/components/ProductDetail.tsx | 3 +-- .../modules/product-promo/components/Section.tsx | 26 +++++++++++----------- 2 files changed, 14 insertions(+), 15 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 4b514944..f569ef46 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -117,8 +117,7 @@ const ProductDetail = ({ product }: Props) => { )}
- {!!activeVariantId && } - {!!activeVariantId && !isMobile &&
} + {!!activeVariantId && }

diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index 694a3705..5000e737 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -1,32 +1,32 @@ import style from "../styles/section.module.css" -import React from 'react' -import { useQuery } from 'react-query' import { Button, Skeleton } from '@chakra-ui/react' +import { useQuery } from 'react-query' -import ProductPromoCard from './Card' +import clsxm from "~/libs/clsxm" import { IPromotion } from '~/types/promotion' -import ProductPromoModal from "./Modal" import { useModalStore } from "../stores/useModalStore" -import clsxm from "~/libs/clsxm" +import ProductPromoCard from './Card' +import ProductPromoModal from "./Modal" type Props = { - productId: number + productId: number; + height: number; } -const ProductPromoSection = ({ 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 ProductPromoSection = ({ productId, height = 435 }: 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[] } + }) const promotions = promotionsQuery.data const { openModal } = useModalStore() return ( -
0, +
0, })}> -- cgit v1.2.3 From 8c8eea40f47bb0b669060a56ff0fa80648e3e3c4 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 09:26:56 +0700 Subject: Update promotion program line for smoother animation --- src-migrate/modules/product-promo/components/Section.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index 5000e737..ba305724 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -17,7 +17,8 @@ type Props = { const ProductPromoSection = ({ productId, height = 435 }: 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[] } + queryFn: async () => await fetch(`/api/product-variant/${productId}/promotion/highlight`).then((res) => res.json()) as { data: IPromotion[] }, + keepPreviousData: true }) const promotions = promotionsQuery.data -- cgit v1.2.3 From 49099ca1da44cc7097e50d88f8dc43913755aee4 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 09:36:36 +0700 Subject: Update promotion program line unused keep prev data --- src-migrate/modules/product-promo/components/Section.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index ba305724..5000e737 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -17,8 +17,7 @@ type Props = { const ProductPromoSection = ({ productId, height = 435 }: 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[] }, - keepPreviousData: true + queryFn: async () => await fetch(`/api/product-variant/${productId}/promotion/highlight`).then((res) => res.json()) as { data: IPromotion[] } }) const promotions = promotionsQuery.data -- cgit v1.2.3 From b41fcd98852b01bdaf1459fa0491b53f4fdf8b91 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 09:47:15 +0700 Subject: Update promotion program line --- .../product-detail/components/ProductDetail.tsx | 26 ++++++++++------------ .../modules/product-promo/components/Section.tsx | 5 ++--- 2 files changed, 14 insertions(+), 17 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index f569ef46..3b1bdbea 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -1,29 +1,27 @@ import style from '../styles/product-detail.module.css' -import React, { useEffect } from 'react' import Link from 'next/link' import { useRouter } from 'next/router' +import { useEffect } from 'react' -import { MessageCircleIcon, Share2Icon } from 'lucide-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 { IProductDetail } from '~/types/product' 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 { RWebShare } from 'react-web-share' +import AddToWishlist from './AddToWishlist' +import Breadcrumb from './Breadcrumb' import ProductImage from './Image' import Information from './Information' -import AddToWishlist from './AddToWishlist' -import VariantList from './VariantList' -import SimilarSide from './SimilarSide' -import SimilarBottom from './SimilarBottom' import PriceAction from './PriceAction' -import ProductPromoSection from '~/modules/product-promo/components/Section' -import Breadcrumb from './Breadcrumb' -import { LazyLoadComponent } from 'react-lazy-load-image-component' +import SimilarBottom from './SimilarBottom' +import SimilarSide from './SimilarSide' +import VariantList from './VariantList' type Props = { product: IProductDetail @@ -117,7 +115,7 @@ const ProductDetail = ({ product }: Props) => { )}
- {!!activeVariantId && } + {!!activeVariantId && }

diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index 5000e737..07a5df7b 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -11,10 +11,9 @@ import ProductPromoModal from "./Modal" type Props = { productId: number; - height: number; } -const ProductPromoSection = ({ productId, height = 435 }: Props) => { +const ProductPromoSection = ({ 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[] } @@ -26,7 +25,7 @@ const ProductPromoSection = ({ productId, height = 435 }: Props) => { return (
0, + 'h-[450px] opacity-100': promotions?.data && promotions?.data.length > 0, })}> -- cgit v1.2.3 From 6e396f4a0ebd3d8ed394ae55a6fb55f295fc9a11 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 10:31:36 +0700 Subject: Update add to cart button redirect to login page --- .../product-detail/components/AddToCart.tsx | 15 +++----------- .../modules/product-promo/components/AddToCart.tsx | 24 +++++++--------------- 2 files changed, 10 insertions(+), 29 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/AddToCart.tsx b/src-migrate/modules/product-detail/components/AddToCart.tsx index 4accab17..ebd6be7a 100644 --- a/src-migrate/modules/product-detail/components/AddToCart.tsx +++ b/src-migrate/modules/product-detail/components/AddToCart.tsx @@ -1,8 +1,7 @@ -import React from 'react' import { Button, useToast } from '@chakra-ui/react' -import { getAuth } from '~/libs/auth' import { useRouter } from 'next/router' -import Link from 'next/link' + +import { getAuth } from '~/libs/auth' import { upsertUserCart } from '~/services/cart' type Props = { @@ -26,15 +25,7 @@ const AddToCart = ({ const handleClick = async () => { if (typeof auth !== 'object') { const currentUrl = encodeURIComponent(router.asPath) - toast({ - title: 'Masuk Akun', - description: <> - Masuk akun untuk dapat menambahkan barang ke keranjang belanja. {' '} - Klik disini - , - status: 'error', - duration: 4000, - }) + router.push(`/login?next=${currentUrl}`) return; } diff --git a/src-migrate/modules/product-promo/components/AddToCart.tsx b/src-migrate/modules/product-promo/components/AddToCart.tsx index 3bac3c66..95d275fc 100644 --- a/src-migrate/modules/product-promo/components/AddToCart.tsx +++ b/src-migrate/modules/product-promo/components/AddToCart.tsx @@ -1,11 +1,11 @@ -import React, { useEffect, useState } from 'react' -import { CheckIcon, PlusIcon } from 'lucide-react' -import { IPromotion } from '~/types/promotion' -import { upsertUserCart } from '~/services/cart' -import { getAuth } from '~/libs/auth' import { Button, Spinner, useToast } from '@chakra-ui/react' -import Link from 'next/link' +import { CheckIcon, PlusIcon } from 'lucide-react' import { useRouter } from 'next/router' +import { useEffect, useState } from 'react' + +import { getAuth } from '~/libs/auth' +import { upsertUserCart } from '~/services/cart' +import { IPromotion } from '~/types/promotion' type Props = { promotion: IPromotion @@ -23,17 +23,7 @@ const ProductPromoAddToCart = ({ promotion }: Props) => { const handleButton = async () => { if (typeof auth !== 'object') { const currentUrl = encodeURIComponent(router.asPath) - toast({ - title: 'Masuk Akun', - description: <> - Masuk akun untuk dapat menambahkan promo ke keranjang belanja. {' '} - Klik disini - , - status: 'error', - duration: 4000, - isClosable: true, - position: 'top', - }) + router.push(`/login?next=${currentUrl}`) return } if (status === 'success') return -- cgit v1.2.3 From 0f78bc94e3b55a718e4ba620b02994afc866672c Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 11:37:15 +0700 Subject: Update promo item name tooltip --- src-migrate/modules/product-promo/components/Item.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/components/Item.tsx b/src-migrate/modules/product-promo/components/Item.tsx index 8012c17e..b396160f 100644 --- a/src-migrate/modules/product-promo/components/Item.tsx +++ b/src-migrate/modules/product-promo/components/Item.tsx @@ -1,8 +1,8 @@ import style from '../styles/item.module.css' -import React from 'react' -import Image from '~/components/ui/image' +import { Tooltip } from '@chakra-ui/react' +import Image from '~/components/ui/image' import { IProductVariantPromo } from '~/types/promotion' type Props = { @@ -22,7 +22,11 @@ const ProductPromoItem = ({ {variant.qty} pcs {isFree ? '(free)' : ''}

-
{variant.name}
+ +
+ {variant.name} +
+
) } -- cgit v1.2.3 From bb1451372cd847def47fcaed6669a72c664f68e3 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 13:53:53 +0700 Subject: Create and implement smooth render --- src-migrate/modules/product-promo/components/Section.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index 07a5df7b..b3c6e671 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -3,6 +3,7 @@ import style from "../styles/section.module.css" import { Button, Skeleton } from '@chakra-ui/react' import { useQuery } from 'react-query' +import SmoothRender from "~/components/ui/smooth-render" import clsxm from "~/libs/clsxm" import { IPromotion } from '~/types/promotion' import { useModalStore } from "../stores/useModalStore" @@ -24,9 +25,11 @@ const ProductPromoSection = ({ productId }: Props) => { const { openModal } = useModalStore() return ( -
0, - })}> + 0) || false} + height={450} + duration={700} + > {promotions?.data && promotions?.data.length > 0 && ( @@ -51,7 +54,7 @@ const ProductPromoSection = ({ productId }: Props) => {
))} -
+ ) } -- cgit v1.2.3 From 30da2d088793cabfb3c5b21d4560df53187e7047 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 16:30:43 +0700 Subject: Fix item url in cart item --- src-migrate/modules/cart/components/Item.tsx | 10 ++++++++-- src-migrate/modules/cart/components/ItemPromo.tsx | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index ba7f7cdf..d2bbdbdd 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -112,7 +112,10 @@ CartItem.Image = function CartItemImage({ item }: { item: CartItemProps }) { )} {item.cart_type === 'product' && ( - + {image && {item.name}} {!image &&
No Image
} @@ -129,7 +132,10 @@ CartItem.Name = function CartItemName({ item }: { item: CartItemProps }) { )} {item.cart_type === 'product' && ( - + {item.name} )} diff --git a/src-migrate/modules/cart/components/ItemPromo.tsx b/src-migrate/modules/cart/components/ItemPromo.tsx index d355c82a..878e17ac 100644 --- a/src-migrate/modules/cart/components/ItemPromo.tsx +++ b/src-migrate/modules/cart/components/ItemPromo.tsx @@ -13,12 +13,12 @@ type Props = { const CartItemPromo = ({ product }: Props) => { return (
- + {product?.image && {product.name}}
- + {product.display_name} -- cgit v1.2.3 From 5d3807a89596958a1e23e02ae11f73a2474c3432 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 22:27:06 +0700 Subject: Update smooth render function --- src-migrate/modules/product-promo/components/Section.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index b3c6e671..5fc0da4c 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -27,8 +27,8 @@ const ProductPromoSection = ({ productId }: Props) => { return ( 0) || false} - height={450} - duration={700} + height='450px' + duration='700ms' > -- cgit v1.2.3 From ab61908296a05d303cb9c0d019b5a92002e03972 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 21 Feb 2024 09:12:40 +0700 Subject: Fix weight on cart --- src-migrate/modules/cart/components/Item.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index d2bbdbdd..6ded6373 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -3,14 +3,13 @@ import style from '../styles/item.module.css' import { Skeleton, SkeletonProps, Tooltip } from '@chakra-ui/react' import { InfoIcon } from 'lucide-react' import Image from 'next/image' +import Link from 'next/link' import { PROMO_CATEGORY } from '~/constants/promotion' - import formatCurrency from '~/libs/formatCurrency' +import { createSlug } from '~/libs/slug' import { CartItem as CartItemProps } from '~/types/cart' -import Link from 'next/link' -import { createSlug } from '~/libs/slug' import CartItemAction from './ItemAction' import CartItemPromo from './ItemPromo' import CartItemSelect from './ItemSelect' @@ -81,7 +80,7 @@ const CartItem = ({ item, editable = true }: Props) => { )}
{item.cart_type === 'product' && item.code}
-
{item.weight} Kg
+
{Math.round(item.weight * 10) / 10} Kg
{editable && } -- cgit v1.2.3 From a698514b32353d8f6386ce8ba8c20941ab65f569 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 26 Feb 2024 15:12:12 +0700 Subject: Add qty append on upsert cart api --- src-migrate/modules/cart/components/ItemSelect.tsx | 8 +++++++- src-migrate/modules/product-detail/components/AddToCart.tsx | 10 +++++++++- src-migrate/modules/product-promo/components/AddToCart.tsx | 10 +++++++++- 3 files changed, 25 insertions(+), 3 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/ItemSelect.tsx b/src-migrate/modules/cart/components/ItemSelect.tsx index 1d8886a2..b904a1de 100644 --- a/src-migrate/modules/cart/components/ItemSelect.tsx +++ b/src-migrate/modules/cart/components/ItemSelect.tsx @@ -21,7 +21,13 @@ const CartItemSelect = ({ item }: Props) => { if (typeof auth !== 'object') return setIsLoad(true) - await upsertUserCart(auth.id, item.cart_type, item.id, item.quantity, e.target.checked) + await upsertUserCart({ + userId: auth.id, + type: item.cart_type, + id: item.id, + qty: item.quantity, + selected: e.target.checked + }) await loadCart(auth.id) setIsLoad(false) } diff --git a/src-migrate/modules/product-detail/components/AddToCart.tsx b/src-migrate/modules/product-detail/components/AddToCart.tsx index ebd6be7a..097db98a 100644 --- a/src-migrate/modules/product-detail/components/AddToCart.tsx +++ b/src-migrate/modules/product-detail/components/AddToCart.tsx @@ -36,7 +36,15 @@ const AddToCart = ({ ) return; toast.promise( - upsertUserCart(auth.id, 'product', variantId, quantity, true, source), + upsertUserCart({ + userId: auth.id, + type: 'product', + id: variantId, + qty: quantity, + selected: true, + source: source, + qtyAppend: true + }), { loading: { title: 'Menambahkan ke keranjang', description: 'Mohon tunggu...' }, success: { title: 'Menambahkan ke keranjang', description: 'Berhasil menambahkan ke keranjang belanja' }, diff --git a/src-migrate/modules/product-promo/components/AddToCart.tsx b/src-migrate/modules/product-promo/components/AddToCart.tsx index 95d275fc..192dd231 100644 --- a/src-migrate/modules/product-promo/components/AddToCart.tsx +++ b/src-migrate/modules/product-promo/components/AddToCart.tsx @@ -29,7 +29,15 @@ const ProductPromoAddToCart = ({ promotion }: Props) => { if (status === 'success') return setStatus('loading') - await upsertUserCart(auth.id, 'promotion', promotion.id, 1, true) + await upsertUserCart({ + userId: auth.id, + type: 'promotion', + id: promotion.id, + qty: 1, + selected: true, + source: 'add_to_cart', + qtyAppend: true + }) setStatus('idle') toast({ -- cgit v1.2.3 From 7a4d2462d05b2cc0e9c66551f1e2c16d4abace2f Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 26 Feb 2024 15:16:28 +0700 Subject: Update upsert user cart --- src-migrate/modules/cart/components/ItemAction.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/ItemAction.tsx b/src-migrate/modules/cart/components/ItemAction.tsx index 859c758c..e73d507b 100644 --- a/src-migrate/modules/cart/components/ItemAction.tsx +++ b/src-migrate/modules/cart/components/ItemAction.tsx @@ -51,7 +51,13 @@ const CartItemAction = ({ item }: Props) => { if (typeof auth !== 'object' || isNaN(debounceQty)) return setIsLoadQuantity(true) - await upsertUserCart(auth.id, item.cart_type, item.id, debounceQty, item.selected) + await upsertUserCart({ + userId: auth.id, + type: item.cart_type, + id: item.id, + qty: debounceQty, + selected: item.selected, + }) await loadCart(auth.id) setIsLoadQuantity(false) } -- cgit v1.2.3 From b5e19d709d27615f30466ee59014d372dac2335c Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 28 Feb 2024 11:24:19 +0700 Subject: Update popup information module --- src-migrate/modules/popup-information/index.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx index 3d537236..dd6da458 100644 --- a/src-migrate/modules/popup-information/index.tsx +++ b/src-migrate/modules/popup-information/index.tsx @@ -1,9 +1,9 @@ import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; -import { Modal } from "~/components/ui/modal" + +import { Modal } from "~/components/ui/modal"; import { getAuth } from '~/libs/auth'; import PageContent from '../page-content'; -import Link from 'next/link'; const PagePopupInformation = () => { const router = useRouter(); @@ -25,12 +25,7 @@ const PagePopupInformation = () => { mode='desktop' >
- - - - {/* - Daftar Sekarang - */} +
-- cgit v1.2.3 From 86d124a1c95a5e7c3367fd288303c9c9a07ca1fd Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 28 Feb 2024 13:17:36 +0700 Subject: Add close on click popup information --- src-migrate/modules/popup-information/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx index dd6da458..0d36f8e9 100644 --- a/src-migrate/modules/popup-information/index.tsx +++ b/src-migrate/modules/popup-information/index.tsx @@ -12,9 +12,7 @@ const PagePopupInformation = () => { const [active, setActive] = useState(false); useEffect(() => { - if (isHomePage && !auth) { - setActive(true); - } + if (isHomePage && !auth) setActive(true); }, [isHomePage, auth]); return (
@@ -24,7 +22,7 @@ const PagePopupInformation = () => { close={() => setActive(false)} mode='desktop' > -
+
setActive(false)}>
-- cgit v1.2.3 From 037c06c8c7ee0751105b5aea22819a5c4c41fdd8 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Thu, 29 Feb 2024 16:14:31 +0700 Subject: Add utm source function on product card --- src-migrate/modules/product-card/components/ProductCard.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx index 8487cd94..4ddebda5 100644 --- a/src-migrate/modules/product-card/components/ProductCard.tsx +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -1,8 +1,10 @@ import style from '../styles/product-card.module.css' +import clsx from 'clsx' import Link from 'next/link' -import React, { useMemo } from 'react' +import { useMemo } from 'react' import Image from '~/components/ui/image' +import useUtmSource from '~/hooks/useUtmSource' import clsxm from '~/libs/clsxm' import formatCurrency from '~/libs/formatCurrency' import { formatToShortText } from '~/libs/formatNumber' @@ -15,8 +17,10 @@ type Props = { } const ProductCard = ({ product, layout = 'vertical' }: Props) => { + const utmSource = useUtmSource() + const URL = { - product: createSlug('/shop/product/', product.name, product.id.toString()), + product: createSlug('/shop/product/', product.name, product.id.toString()) + `?utm_source=${utmSource}`, manufacture: createSlug('/shop/brands/', product.manufacture.name, product.manufacture.id.toString()), } -- cgit v1.2.3 From 87bc6b410b875d6f811e21e1e1d6f974e7cac653 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 5 Mar 2024 13:22:30 +0700 Subject: Update background flash sale --- src-migrate/modules/product-detail/components/Image.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx index b69cc87f..3d7777f8 100644 --- a/src-migrate/modules/product-detail/components/Image.tsx +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -67,7 +67,7 @@ const Image = ({ product }: Props) => {
Date: Fri, 8 Mar 2024 10:19:23 +0700 Subject: CR - Keranjang Mobile : informasi harga --- src-migrate/modules/cart/components/Summary.tsx | 110 +++++++++++++----------- 1 file changed, 61 insertions(+), 49 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Summary.tsx b/src-migrate/modules/cart/components/Summary.tsx index 2e55c8df..1e76d4fa 100644 --- a/src-migrate/modules/cart/components/Summary.tsx +++ b/src-migrate/modules/cart/components/Summary.tsx @@ -1,20 +1,20 @@ -import style from '../styles/summary.module.css' +import style from '../styles/summary.module.css'; -import React from 'react' -import formatCurrency from '~/libs/formatCurrency' -import clsxm from '~/libs/clsxm' -import { Skeleton } from '@chakra-ui/react' -import _ from 'lodash' +import React from 'react'; +import formatCurrency from '~/libs/formatCurrency'; +import clsxm from '~/libs/clsxm'; +import { Skeleton } from '@chakra-ui/react'; +import _ from 'lodash'; type Props = { - total?: number - discount?: number - subtotal?: number - tax?: number - shipping?: number - grandTotal?: number - isLoaded: boolean -} + total?: number; + discount?: number; + subtotal?: number; + tax?: number; + shipping?: number; + grandTotal?: number; + isLoaded: boolean; +}; const CartSummary = ({ total, @@ -27,49 +27,61 @@ const CartSummary = ({ }: Props) => { return ( <> -
Ringkasan Pesanan
- -
+
+ Ringkasan Pesanan +
-
- - Total Belanja - Rp {formatCurrency(subtotal || 0)} - +
- - Total Diskon - - Rp {formatCurrency(discount || 0)} - +
+
+ + Total Belanja + + Rp {formatCurrency(subtotal || 0)} + + -
+ + Total Diskon + + - Rp {formatCurrency(discount || 0)} + + - - Subtotal - Rp {formatCurrency(total || 0)} - +
- - Tax 11% - Rp {formatCurrency(tax || 0)} - + + Subtotal + Rp {formatCurrency(total || 0)} + - - Biaya Kirim - Rp {formatCurrency(shipping || 0)} - + + Tax 11% + Rp {formatCurrency(tax || 0)} + -
+ + Biaya Kirim + + Rp {formatCurrency(shipping || 0)} + + - - - Grand Total - - Rp {formatCurrency(grandTotal || 0)} - +
+
+ + + + Grand Total + + + Rp {formatCurrency(grandTotal || 0)} + + - ) -} + ); +}; -export default CartSummary \ No newline at end of file +export default CartSummary; -- cgit v1.2.3 From 9a52d9f835e2f30480142c6197fdf14b3fee5ead Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Tue, 12 Mar 2024 09:17:01 +0700 Subject: feedback kerajang di mobile --- .../modules/cart/components/CartSummaryMobile.tsx | 111 +++++++++++++++++++++ src-migrate/modules/cart/components/Summary.tsx | 110 +++++++++----------- 2 files changed, 160 insertions(+), 61 deletions(-) create mode 100644 src-migrate/modules/cart/components/CartSummaryMobile.tsx (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/CartSummaryMobile.tsx b/src-migrate/modules/cart/components/CartSummaryMobile.tsx new file mode 100644 index 00000000..d9f72e0e --- /dev/null +++ b/src-migrate/modules/cart/components/CartSummaryMobile.tsx @@ -0,0 +1,111 @@ +import style from '../styles/summary.module.css'; + +import React, { useState } from 'react'; +import formatCurrency from '~/libs/formatCurrency'; +import clsxm from '~/libs/clsxm'; +import { Button, Skeleton } from '@chakra-ui/react'; +import _ from 'lodash'; +import { ChevronDownIcon } from '@heroicons/react/24/outline'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import useDevice from '@/core/hooks/useDevice'; + +type Props = { + total?: number; + discount?: number; + subtotal?: number; + tax?: number; + shipping?: number; + grandTotal?: number; + isLoaded: boolean; +}; + +const CartSummaryMobile = ({ + total, + discount, + subtotal, + tax, + shipping, + grandTotal, + isLoaded = false, +}: Props) => { + const [showPopup, setShowPopup] = useState(false); + return ( + <> + setShowPopup(false)} + > +
+
+ + Total Belanja + + Rp {formatCurrency(subtotal || 0)} + + + + + Total Diskon + + - Rp {formatCurrency(discount || 0)} + + + +
+ + + Subtotal + + Rp {formatCurrency(total || 0)} + + + + + Tax 11% + Rp {formatCurrency(tax || 0)} + + + + Biaya Kirim + + Rp {formatCurrency(shipping || 0)} + + + +
+ + + Grand Total + + + Rp {formatCurrency(grandTotal || 0)} + + +
+
+ +
+ + + Grand Total + + + + + + Rp {formatCurrency(grandTotal || 0)} + + +
+ + ); +}; + +export default CartSummaryMobile; diff --git a/src-migrate/modules/cart/components/Summary.tsx b/src-migrate/modules/cart/components/Summary.tsx index 1e76d4fa..2e55c8df 100644 --- a/src-migrate/modules/cart/components/Summary.tsx +++ b/src-migrate/modules/cart/components/Summary.tsx @@ -1,20 +1,20 @@ -import style from '../styles/summary.module.css'; +import style from '../styles/summary.module.css' -import React from 'react'; -import formatCurrency from '~/libs/formatCurrency'; -import clsxm from '~/libs/clsxm'; -import { Skeleton } from '@chakra-ui/react'; -import _ from 'lodash'; +import React from 'react' +import formatCurrency from '~/libs/formatCurrency' +import clsxm from '~/libs/clsxm' +import { Skeleton } from '@chakra-ui/react' +import _ from 'lodash' type Props = { - total?: number; - discount?: number; - subtotal?: number; - tax?: number; - shipping?: number; - grandTotal?: number; - isLoaded: boolean; -}; + total?: number + discount?: number + subtotal?: number + tax?: number + shipping?: number + grandTotal?: number + isLoaded: boolean +} const CartSummary = ({ total, @@ -27,61 +27,49 @@ const CartSummary = ({ }: Props) => { return ( <> -
- Ringkasan Pesanan -
+
Ringkasan Pesanan
-
+
-
-
- - Total Belanja - - Rp {formatCurrency(subtotal || 0)} - - +
+ + Total Belanja + Rp {formatCurrency(subtotal || 0)} + - - Total Diskon - - - Rp {formatCurrency(discount || 0)} - - + + Total Diskon + - Rp {formatCurrency(discount || 0)} + -
+
- - Subtotal - Rp {formatCurrency(total || 0)} - + + Subtotal + Rp {formatCurrency(total || 0)} + - - Tax 11% - Rp {formatCurrency(tax || 0)} - + + Tax 11% + Rp {formatCurrency(tax || 0)} + - - Biaya Kirim - - Rp {formatCurrency(shipping || 0)} - - + + Biaya Kirim + Rp {formatCurrency(shipping || 0)} + -
-
-
+
- - - Grand Total - - - Rp {formatCurrency(grandTotal || 0)} - - + + + Grand Total + + Rp {formatCurrency(grandTotal || 0)} + +
- ); -}; + ) +} -export default CartSummary; +export default CartSummary \ No newline at end of file -- cgit v1.2.3 From 59e4c1cf1b45497bc98cbc13c57e33e1a256a22e Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Thu, 25 Apr 2024 11:33:09 +0700 Subject: Add side and bottom banner on search --- src-migrate/modules/footer-banner/index.tsx | 29 +++++++++++++++++++++++++++++ src-migrate/modules/side-banner/index.tsx | 29 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src-migrate/modules/footer-banner/index.tsx create mode 100644 src-migrate/modules/side-banner/index.tsx (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/footer-banner/index.tsx b/src-migrate/modules/footer-banner/index.tsx new file mode 100644 index 00000000..7db1363c --- /dev/null +++ b/src-migrate/modules/footer-banner/index.tsx @@ -0,0 +1,29 @@ +import Link from "next/link" +import { useQuery } from "react-query" +import Image from "~/components/ui/image" +import { getBanner } from "~/services/banner" + +const FooterBanner = () => { + const fetchFooterBanner = useQuery({ + queryKey: 'footerBanner', + queryFn: () => getBanner({ type: 'bottom-search-promotion' }) + }) + + const banner = fetchFooterBanner?.data?.[0] || false + + return banner && ( + <> + {banner.url && ( + + {banner.name} + + )} + + {!banner.url && ( + {banner.name} + )} + + ) +} + +export default FooterBanner \ No newline at end of file diff --git a/src-migrate/modules/side-banner/index.tsx b/src-migrate/modules/side-banner/index.tsx new file mode 100644 index 00000000..be52c554 --- /dev/null +++ b/src-migrate/modules/side-banner/index.tsx @@ -0,0 +1,29 @@ +import Link from "next/link" +import { useQuery } from "react-query" +import Image from "~/components/ui/image" +import { getBanner } from "~/services/banner" + +const SideBanner = () => { + const fetchSideBanner = useQuery({ + queryKey: 'sideBanner', + queryFn: () => getBanner({ type: 'side-banner-search' }) + }) + + const banner = fetchSideBanner?.data?.[0] || false + + return banner && ( + <> + {banner.url && ( + + {banner.name} + + )} + + {!banner.url && ( + {banner.name} + )} + + ) +} + +export default SideBanner \ No newline at end of file -- cgit v1.2.3 From 1850e105ce6578465a015e395fa4c38544afdafe Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Mon, 29 Apr 2024 10:17:54 +0700 Subject: CR - Price Product detail = variant yg ada harganya --- src-migrate/modules/product-detail/components/PriceAction.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index f25847a5..ad04de43 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -23,6 +23,16 @@ const PriceAction = ({ product }: Props) => { useEffect(() => { setActive(product.variants[0]) + 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]) + break; + } + } + } + }, [product, setActive]); return ( -- cgit v1.2.3 From 71e6f6b70601a5a6fa20645c2f01a6bf9f7b19ff Mon Sep 17 00:00:00 2001 From: "tri.susilo" Date: Mon, 29 Apr 2024 13:02:14 +0700 Subject: google tag --- src-migrate/modules/product-detail/components/ProductDetail.tsx | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 3b1bdbea..bfdf5b43 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -23,6 +23,8 @@ import SimilarBottom from './SimilarBottom' import SimilarSide from './SimilarSide' import VariantList from './VariantList' +import { gtagProductDetail } from '@/core/utils/googleTag' + type Props = { product: IProductDetail } @@ -34,6 +36,10 @@ const ProductDetail = ({ product }: Props) => { const router = useRouter() const { setAskAdminUrl, askAdminUrl, activeVariantId } = useProductDetail() + useEffect(() => { + gtagProductDetail(product); + },[product]) + useEffect(() => { const createdAskUrl = whatsappUrl({ template: 'product', -- cgit v1.2.3 From 1bb3f91f27db4db6a16a1ed3fe59016268ba3d44 Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Wed, 8 May 2024 13:23:10 +0700 Subject: change wa number --- src-migrate/modules/header/components/HeaderDesktop.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/header/components/HeaderDesktop.tsx b/src-migrate/modules/header/components/HeaderDesktop.tsx index 8f5a8efa..131fa7da 100644 --- a/src-migrate/modules/header/components/HeaderDesktop.tsx +++ b/src-migrate/modules/header/components/HeaderDesktop.tsx @@ -54,7 +54,7 @@ const HeaderDesktop = () => { Whatsapp
Whatsapp
- 0812 8080 622 (Chat) + 0817 1718 1922 (Chat)
-- cgit v1.2.3 From f9f1fdf83c2b6ed5c1d85d7418d45aeed9b05c77 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 5 Jun 2024 15:24:42 +0700 Subject: add feature SNI-TKDR --- src-migrate/modules/cart/components/Item.tsx | 59 ++++++++++++- .../product-card/components/ProductCard.tsx | 72 +++++++++++++++- .../modules/product-detail/components/Image.tsx | 89 +++++++++++++++++--- .../modules/product-promo/components/Item.tsx | 97 +++++++++++++++++----- 4 files changed, 280 insertions(+), 37 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index 6ded6373..fed11eb0 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -4,7 +4,8 @@ import { Skeleton, SkeletonProps, Tooltip } from '@chakra-ui/react' import { InfoIcon } from 'lucide-react' import Image from 'next/image' import Link from 'next/link' - +import ImageNext from 'next/image'; +import { useEffect, useState } from 'react'; import { PROMO_CATEGORY } from '~/constants/promotion' import formatCurrency from '~/libs/formatCurrency' import { createSlug } from '~/libs/slug' @@ -20,6 +21,31 @@ type Props = { } const CartItem = ({ item, editable = true }: Props) => { + const [isSni, setIsSni] = useState(false); + const [isTkdn, setTkdn] = useState(false); + + useEffect(() => { + const fetchData = async () => { + try { + const responseSni = await fetch('URL_API_SNI'); + const dataSni = await responseSni.json(); + setIsSni(dataSni && dataSni.sni); + + const responseTkdn = await fetch('URL_API_TKDN'); + const dataTkdn = await responseTkdn.json(); + setTkdn(dataTkdn && dataTkdn.tkdn); + } catch (error) { + console.error('Error fetching data:', error); + setIsSni(false); + setTkdn(false); + } + }; + + fetchData(); + + return () => {}; + }, []); + return (
{item.cart_type === 'promotion' && ( @@ -46,7 +72,7 @@ const CartItem = ({ item, editable = true }: Props) => { {editable && }
- +
@@ -98,7 +124,7 @@ const CartItem = ({ item, editable = true }: Props) => { ) } -CartItem.Image = function CartItemImage({ item }: { item: CartItemProps }) { +CartItem.Image = function CartItemImage({ item, isSni, isTkdn }: { item: CartItemProps, isSni: boolean, isTkdn: boolean }) { const image = item?.image || item?.parent?.image return ( @@ -116,6 +142,31 @@ CartItem.Image = function CartItemImage({ item }: { item: CartItemProps }) { className={style.image} > {image && {item.name}} + +
+ {/*
*/} + {!isSni && ( + + )} + {/*
*/} + {/*
*/} + {!isTkdn && ( + + )} + {/*
*/} +
{!image &&
No Image
} )} @@ -153,4 +204,4 @@ CartItem.Skeleton = function CartItemSkeleton(props: SkeletonProps & { count: nu )) } -export default CartItem \ No newline at end of file +export default CartItem diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx index 4ddebda5..c3f05176 100644 --- a/src-migrate/modules/product-card/components/ProductCard.tsx +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -1,8 +1,8 @@ import style from '../styles/product-card.module.css' - +import ImageNext from 'next/image'; import clsx from 'clsx' import Link from 'next/link' -import { useMemo } from 'react' +import React, { useEffect, useMemo, useState } from 'react' import Image from '~/components/ui/image' import useUtmSource from '~/hooks/useUtmSource' import clsxm from '~/libs/clsxm' @@ -18,6 +18,46 @@ type Props = { const ProductCard = ({ product, layout = 'vertical' }: Props) => { const utmSource = useUtmSource() + const [isSni, setIsSni] = useState(false); + const [isTkdn, setTkdn] = useState(false); + + useEffect(() => { + // Lakukan pemanggilan API untuk memeriksa isSni + const fetchSniData = async () => { + try { + const response = await fetch('URL_API_SNI'); + const data = await response.json(); + if (data && data.sni) { + setIsSni(true); + } else { + setIsSni(false); + } + } catch (error) { + console.error('Error fetching SNI data:', error); + setIsSni(false); + } + }; + + // Lakukan pemanggilan API untuk memeriksa isTkdn + const fetchTkdnData = async () => { + try { + const response = await fetch('URL_API_TKDN'); + const data = await response.json(); + if (data && data.tkdn) { + setTkdn(true); + } else { + setTkdn(false); + } + } catch (error) { + console.error('Error fetching TKDN data:', error); + setTkdn(false); + } + }; + fetchSniData(); + fetchTkdnData(); + return () => { + }; + }, []); const URL = { product: createSlug('/shop/product/', product.name, product.id.toString()) + `?utm_source=${utmSource}`, @@ -40,6 +80,8 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => { [style['image-h']]: layout === 'horizontal', })}> + +
{product.name} { height={128} className='object-contain object-center h-full w-full' /> +
+
+ {!isSni && ( + + )} +
+
+ {!isTkdn && ( + + )} +
+
+
+ {product.variant_total > 1 && (
{product.variant_total} Varian
)} diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx index 3d7777f8..1b80efa8 100644 --- a/src-migrate/modules/product-detail/components/Image.tsx +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -1,5 +1,5 @@ import style from '../styles/image.module.css'; - +import ImageNext from 'next/image'; import React, { useEffect, useMemo, useState } from 'react' import { InfoIcon } from 'lucide-react' import { Tooltip } from '@chakra-ui/react' @@ -17,6 +17,47 @@ const Image = ({ product }: Props) => { const [count, setCount] = useState(flashSale?.remaining_time || 0); + const [isSni, setIsSni] = useState(false); + const [isTkdn, setTkdn] = useState(false); + + useEffect(() => { + // Lakukan pemanggilan API untuk memeriksa isSni + const fetchSniData = async () => { + try { + const response = await fetch('URL_API_SNI'); + const data = await response.json(); + if (data && data.sni) { + setIsSni(true); + } else { + setIsSni(false); + } + } catch (error) { + console.error('Error fetching SNI data:', error); + setIsSni(false); + } + }; + + // Lakukan pemanggilan API untuk memeriksa isTkdn + const fetchTkdnData = async () => { + try { + const response = await fetch('URL_API_TKDN'); + const data = await response.json(); + if (data && data.tkdn) { + setTkdn(true); + } else { + setTkdn(false); + } + } catch (error) { + console.error('Error fetching TKDN data:', error); + setTkdn(false); + } + }; + fetchSniData(); + fetchTkdnData(); + return () => { + }; + }, []); + useEffect(() => { let interval: NodeJS.Timeout; @@ -42,15 +83,43 @@ const Image = ({ product }: Props) => { return (
- + {/*
*/} + +
+
+ {!isSni && ( + + )} +
+
+ {!isTkdn && ( + + )} +
+
+ {/*
*/} + +
{ + variant: IProductVariantPromo; + isFree?: boolean; +}; + +const ProductPromoItem = ({ variant, isFree = false }: Props) => { + const [isSni, setIsSni] = useState(false); + const [isTkdn, setTkdn] = useState(false); + + useEffect(() => { + // Lakukan pemanggilan API untuk memeriksa isSni dan isTkdn + const fetchData = async () => { + try { + const responseSni = await fetch('URL_API_SNI'); + const dataSni = await responseSni.json(); + setIsSni(dataSni && dataSni.sni); + + const responseTkdn = await fetch('URL_API_TKDN'); + const dataTkdn = await responseTkdn.json(); + setTkdn(dataTkdn && dataTkdn.tkdn); + } catch (error) { + console.error('Error fetching data:', error); + setIsSni(false); + setTkdn(false); + } + }; + + fetchData(); + + return () => {}; + }, []); + return (
- {variant.display_name} +
+ {variant.display_name} +
+ {!isSni && ( +
+ +
+ )} + {!isTkdn && ( +
+ +
+ )} +
+
+
{variant.qty} pcs {isFree ? '(free)' : ''}
- -
- {variant.name} -
+ +
{variant.name}
- ) -} + ); +}; -export default ProductPromoItem \ No newline at end of file +export default ProductPromoItem; -- cgit v1.2.3 From 755b61a8a7a082cc13f7ecb4a79807f90e60b3d6 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 5 Jun 2024 16:50:38 +0700 Subject: add feature SNI-TKDN --- .../product-card/components/ProductCard.tsx | 45 ++------------------- .../modules/product-detail/components/Image.tsx | 46 ++-------------------- 2 files changed, 6 insertions(+), 85 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx index c3f05176..0febfadb 100644 --- a/src-migrate/modules/product-card/components/ProductCard.tsx +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -18,46 +18,7 @@ type Props = { const ProductCard = ({ product, layout = 'vertical' }: Props) => { const utmSource = useUtmSource() - const [isSni, setIsSni] = useState(false); - const [isTkdn, setTkdn] = useState(false); - - useEffect(() => { - // Lakukan pemanggilan API untuk memeriksa isSni - const fetchSniData = async () => { - try { - const response = await fetch('URL_API_SNI'); - const data = await response.json(); - if (data && data.sni) { - setIsSni(true); - } else { - setIsSni(false); - } - } catch (error) { - console.error('Error fetching SNI data:', error); - setIsSni(false); - } - }; - - // Lakukan pemanggilan API untuk memeriksa isTkdn - const fetchTkdnData = async () => { - try { - const response = await fetch('URL_API_TKDN'); - const data = await response.json(); - if (data && data.tkdn) { - setTkdn(true); - } else { - setTkdn(false); - } - } catch (error) { - console.error('Error fetching TKDN data:', error); - setTkdn(false); - } - }; - fetchSniData(); - fetchTkdnData(); - return () => { - }; - }, []); + const URL = { product: createSlug('/shop/product/', product.name, product.id.toString()) + `?utm_source=${utmSource}`, @@ -91,7 +52,7 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => { />
- {!isSni && ( + {product.isSni && ( { )}
- {!isTkdn && ( + {product.isTkdn && ( { const flashSale = product.flash_sale - const [count, setCount] = useState(flashSale?.remaining_time || 0); - const [isSni, setIsSni] = useState(false); - const [isTkdn, setTkdn] = useState(false); - - useEffect(() => { - // Lakukan pemanggilan API untuk memeriksa isSni - const fetchSniData = async () => { - try { - const response = await fetch('URL_API_SNI'); - const data = await response.json(); - if (data && data.sni) { - setIsSni(true); - } else { - setIsSni(false); - } - } catch (error) { - console.error('Error fetching SNI data:', error); - setIsSni(false); - } - }; - - // Lakukan pemanggilan API untuk memeriksa isTkdn - const fetchTkdnData = async () => { - try { - const response = await fetch('URL_API_TKDN'); - const data = await response.json(); - if (data && data.tkdn) { - setTkdn(true); - } else { - setTkdn(false); - } - } catch (error) { - console.error('Error fetching TKDN data:', error); - setTkdn(false); - } - }; - fetchSniData(); - fetchTkdnData(); - return () => { - }; - }, []); + useEffect(() => { let interval: NodeJS.Timeout; @@ -95,7 +55,7 @@ const Image = ({ product }: Props) => { />
- {!isSni && ( + {product.isSni && ( { )}
- {!isTkdn && ( + {product.isTkdn && ( Date: Thu, 6 Jun 2024 09:42:23 +0700 Subject: add feature SNI-TKDN --- src-migrate/modules/cart/components/Item.tsx | 59 +------------ .../modules/product-promo/components/Item.tsx | 97 +++++----------------- 2 files changed, 25 insertions(+), 131 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index fed11eb0..6ded6373 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -4,8 +4,7 @@ import { Skeleton, SkeletonProps, Tooltip } from '@chakra-ui/react' import { InfoIcon } from 'lucide-react' import Image from 'next/image' import Link from 'next/link' -import ImageNext from 'next/image'; -import { useEffect, useState } from 'react'; + import { PROMO_CATEGORY } from '~/constants/promotion' import formatCurrency from '~/libs/formatCurrency' import { createSlug } from '~/libs/slug' @@ -21,31 +20,6 @@ type Props = { } const CartItem = ({ item, editable = true }: Props) => { - const [isSni, setIsSni] = useState(false); - const [isTkdn, setTkdn] = useState(false); - - useEffect(() => { - const fetchData = async () => { - try { - const responseSni = await fetch('URL_API_SNI'); - const dataSni = await responseSni.json(); - setIsSni(dataSni && dataSni.sni); - - const responseTkdn = await fetch('URL_API_TKDN'); - const dataTkdn = await responseTkdn.json(); - setTkdn(dataTkdn && dataTkdn.tkdn); - } catch (error) { - console.error('Error fetching data:', error); - setIsSni(false); - setTkdn(false); - } - }; - - fetchData(); - - return () => {}; - }, []); - return (
{item.cart_type === 'promotion' && ( @@ -72,7 +46,7 @@ const CartItem = ({ item, editable = true }: Props) => { {editable && }
- +
@@ -124,7 +98,7 @@ const CartItem = ({ item, editable = true }: Props) => { ) } -CartItem.Image = function CartItemImage({ item, isSni, isTkdn }: { item: CartItemProps, isSni: boolean, isTkdn: boolean }) { +CartItem.Image = function CartItemImage({ item }: { item: CartItemProps }) { const image = item?.image || item?.parent?.image return ( @@ -142,31 +116,6 @@ CartItem.Image = function CartItemImage({ item, isSni, isTkdn }: { item: CartIte className={style.image} > {image && {item.name}} - -
- {/*
*/} - {!isSni && ( - - )} - {/*
*/} - {/*
*/} - {!isTkdn && ( - - )} - {/*
*/} -
{!image &&
No Image
} )} @@ -204,4 +153,4 @@ CartItem.Skeleton = function CartItemSkeleton(props: SkeletonProps & { count: nu )) } -export default CartItem +export default CartItem \ No newline at end of file diff --git a/src-migrate/modules/product-promo/components/Item.tsx b/src-migrate/modules/product-promo/components/Item.tsx index 4d1808c2..b396160f 100644 --- a/src-migrate/modules/product-promo/components/Item.tsx +++ b/src-migrate/modules/product-promo/components/Item.tsx @@ -1,89 +1,34 @@ -import style from '../styles/item.module.css'; -import ImageNext from 'next/image'; -import { useEffect, useState } from 'react'; -import { Tooltip } from '@chakra-ui/react'; +import style from '../styles/item.module.css' -import Image from '~/components/ui/image'; -import { IProductVariantPromo } from '~/types/promotion'; +import { Tooltip } from '@chakra-ui/react' -type Props = { - variant: IProductVariantPromo; - isFree?: boolean; -}; - -const ProductPromoItem = ({ variant, isFree = false }: Props) => { - const [isSni, setIsSni] = useState(false); - const [isTkdn, setTkdn] = useState(false); - - useEffect(() => { - // Lakukan pemanggilan API untuk memeriksa isSni dan isTkdn - const fetchData = async () => { - try { - const responseSni = await fetch('URL_API_SNI'); - const dataSni = await responseSni.json(); - setIsSni(dataSni && dataSni.sni); - - const responseTkdn = await fetch('URL_API_TKDN'); - const dataTkdn = await responseTkdn.json(); - setTkdn(dataTkdn && dataTkdn.tkdn); - } catch (error) { - console.error('Error fetching data:', error); - setIsSni(false); - setTkdn(false); - } - }; - - fetchData(); - - return () => {}; - }, []); +import Image from '~/components/ui/image' +import { IProductVariantPromo } from '~/types/promotion' +type Props = { + variant: IProductVariantPromo, + isFree?: boolean +} + +const ProductPromoItem = ({ + variant, + isFree = false +}: Props) => { return (
-
- {variant.display_name} -
- {!isSni && ( -
- -
- )} - {!isTkdn && ( -
- -
- )} -
-
- + {variant.display_name}
{variant.qty} pcs {isFree ? '(free)' : ''}
- -
{variant.name}
+ +
+ {variant.name} +
- ); -}; + ) +} -export default ProductPromoItem; +export default ProductPromoItem \ No newline at end of file -- cgit v1.2.3 From c88d98f06a6301bad6dd6d2e58b4908d8562638c Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 7 Jun 2024 17:08:09 +0700 Subject: add promotion program --- .../modules/product-promo/components/Card.tsx | 144 ++++++++++++--------- 1 file changed, 80 insertions(+), 64 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx index 59110098..be7d5b6e 100644 --- a/src-migrate/modules/product-promo/components/Card.tsx +++ b/src-migrate/modules/product-promo/components/Card.tsx @@ -18,36 +18,47 @@ import ProductPromoCardCountdown from "./CardCountdown" type Props = { promotion: IPromotion + slug?: string } -const ProductPromoCard = ({ promotion }: Props) => { +const ProductPromoCard = ({ promotion, slug }: Props) => { const [products, setProducts] = useState([]) + const [freeProducts, setFreeProducts] = useState([]) + const [error, setError] = useState(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([]) - 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 +74,67 @@ const ProductPromoCard = ({ promotion }: Props) => { const allProducts = [...products, ...freeProducts] - return ( -
- + const shouldRender = !slug || promotion.type.value === slug -
-
-
{promotion.name}
+ return ( + shouldRender && ( +
+ + +
+
+
{promotion.name}
+ + +
+ Paket {PROMO_CATEGORY[promotion.type.value].alias} + +
+
+
- -
- Paket {PROMO_CATEGORY[promotion.type.value].alias} - + 0}> + {allProducts.map((product, index) => ( + + + products.length && promotion.type.value === 'merchandise'} + // isFree={index + 1 > products.length } + /> + + + {index + 1 < allProducts.length && ( +
+ +
+ )} +
+
+ ))} +
+ +
+
+ 0}> + Rp{formatCurrency(priceTotal)} + Hemat Rp {formatCurrency(priceTotal - promotion.price)} + + +
+ Rp{formatCurrency(promotion.price)} + (Total {promotion.total_qty} barang) +
- -
- - 0}> - {allProducts.map((product, index) => ( - <> - - products.length && promotion.type.value === 'merchandise'} - /> - - - {index + 1 < allProducts.length && ( -
- -
- )} -
- - ))} -
- -
-
- 0}> - Rp{formatCurrency(priceTotal)} - Hemat Rp {formatCurrency(priceTotal - promotion.price)} - - -
- Rp{formatCurrency(promotion.price)} - (Total {promotion.total_qty} barang) +
+
-
-
- -
+
-
+ ) ) } -export default ProductPromoCard \ No newline at end of file +export default ProductPromoCard -- cgit v1.2.3 From 9565ddf794165e297acf511a108f9a9643ee615d Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 11 Jun 2024 13:40:23 +0700 Subject: update promotion program --- src-migrate/modules/product-promo/components/Card.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx index be7d5b6e..0be27af2 100644 --- a/src-migrate/modules/product-promo/components/Card.tsx +++ b/src-migrate/modules/product-promo/components/Card.tsx @@ -77,7 +77,7 @@ const ProductPromoCard = ({ promotion, slug }: Props) => { const shouldRender = !slug || promotion.type.value === slug return ( - shouldRender && ( + // shouldRender && (
@@ -133,7 +133,7 @@ const ProductPromoCard = ({ promotion, slug }: Props) => {
- ) + // ) ) } -- cgit v1.2.3 From 0a87455727114468c216fbcd74266ea928eaec37 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 13 Jun 2024 09:34:24 +0700 Subject: update promotion-program --- src-migrate/modules/product-promo/components/Card.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx index 0be27af2..e927508f 100644 --- a/src-migrate/modules/product-promo/components/Card.tsx +++ b/src-migrate/modules/product-promo/components/Card.tsx @@ -18,10 +18,10 @@ import ProductPromoCardCountdown from "./CardCountdown" type Props = { promotion: IPromotion - slug?: string + } -const ProductPromoCard = ({ promotion, slug }: Props) => { +const ProductPromoCard = ({ promotion}: Props) => { const [products, setProducts] = useState([]) const [freeProducts, setFreeProducts] = useState([]) const [error, setError] = useState(null) @@ -74,7 +74,7 @@ const ProductPromoCard = ({ promotion, slug }: Props) => { const allProducts = [...products, ...freeProducts] - const shouldRender = !slug || promotion.type.value === slug + return ( // shouldRender && ( -- cgit v1.2.3 From e3e3fe8d87130fcd1872046de0160272b6ea9763 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 14 Jun 2024 15:15:30 +0700 Subject: update promotion-program --- src-migrate/modules/product-promo/components/Section.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index 5fc0da4c..4e8a7dd5 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -50,7 +50,7 @@ const ProductPromoSection = ({ productId }: Props) => { > {promotions?.data.map((promotion) => (
- +
))} -- cgit v1.2.3 From ba84659f27c84d0d2c0cc3275e211a865e416bf7 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 19 Jun 2024 11:31:02 +0700 Subject: update responsive promotion program --- .../modules/product-promo/components/AddToCart.tsx | 52 ++++++++++++----- .../modules/product-promo/components/Card.tsx | 68 +++++++++++++++++++++- .../modules/product-promo/styles/card.module.css | 12 ++++ 3 files changed, 117 insertions(+), 15 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/components/AddToCart.tsx b/src-migrate/modules/product-promo/components/AddToCart.tsx index 192dd231..87017c14 100644 --- a/src-migrate/modules/product-promo/components/AddToCart.tsx +++ b/src-migrate/modules/product-promo/components/AddToCart.tsx @@ -7,6 +7,9 @@ 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'; + type Props = { promotion: IPromotion } @@ -55,21 +58,42 @@ const ProductPromoAddToCart = ({ promotion }: Props) => { }, [status]) return ( - + + + + {status === 'success' && Berhasil} + {status !== 'success' && Keranjang} + + +
) } diff --git a/src-migrate/modules/product-promo/components/Card.tsx b/src-migrate/modules/product-promo/components/Card.tsx index e927508f..56e29e38 100644 --- a/src-migrate/modules/product-promo/components/Card.tsx +++ b/src-migrate/modules/product-promo/components/Card.tsx @@ -16,6 +16,9 @@ import ProductPromoItem from './Item' import ProductPromoAddToCart from "./AddToCart" import ProductPromoCardCountdown from "./CardCountdown" +import MobileView from '../../../../src/core/components/views/MobileView'; +import DesktopView from '../../../../src/core/components/views/DesktopView'; + type Props = { promotion: IPromotion @@ -77,7 +80,66 @@ const ProductPromoCard = ({ promotion}: Props) => { return ( - // shouldRender && ( +
+ +
+ + +
+
+
{promotion.name}
+ + + {/*
*/} + {/* Paket {PROMO_CATEGORY[promotion.type.value].alias} */} + + {/*
*/} +
+
+ + 0}> + {allProducts.map((product, index) => ( + + + products.length && promotion.type.value === 'merchandise'} + // isFree={index + 1 > products.length } + /> + + + {index + 1 < allProducts.length && ( +
+ +
+ )} +
+
+ ))} +
+ +
+
+ 0}> + Rp{formatCurrency(priceTotal)} + Hemat Rp {formatCurrency(priceTotal - promotion.price)} + + +
+ Rp{formatCurrency(promotion.price)} + (Total {promotion.total_qty} barang) +
+ +
+
+ +
+ +
+
+
+
+
@@ -133,6 +195,10 @@ const ProductPromoCard = ({ promotion}: Props) => {
+ +
+ // shouldRender && ( + // ) ) } diff --git a/src-migrate/modules/product-promo/styles/card.module.css b/src-migrate/modules/product-promo/styles/card.module.css index a2ad9af6..29db13f3 100644 --- a/src-migrate/modules/product-promo/styles/card.module.css +++ b/src-migrate/modules/product-promo/styles/card.module.css @@ -44,3 +44,15 @@ .totalItems { @apply text-gray_r-9; } + +/* @media only screen and (max-width: 384px) { + .basePrice { + @apply text-[13px]; + } + .price{ + @apply text-[15px]; + } + .totalItems{ + @apply text-[11px]; + } + } */ -- cgit v1.2.3 From 2de8d24f2d9850ae7578e5bf40f50c8157093625 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 19 Jun 2024 11:35:11 +0700 Subject: update responsive promotion program --- src-migrate/modules/product-promo/styles/card.module.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-promo/styles/card.module.css b/src-migrate/modules/product-promo/styles/card.module.css index 29db13f3..faa3b370 100644 --- a/src-migrate/modules/product-promo/styles/card.module.css +++ b/src-migrate/modules/product-promo/styles/card.module.css @@ -45,7 +45,7 @@ @apply text-gray_r-9; } -/* @media only screen and (max-width: 384px) { +@media only screen and (max-width: 384px) { .basePrice { @apply text-[13px]; } @@ -55,4 +55,4 @@ .totalItems{ @apply text-[11px]; } - } */ + } \ No newline at end of file -- cgit v1.2.3 From eead8eec2e37a2edf3c431664cacdcc502a64364 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 26 Jun 2024 11:19:27 +0700 Subject: bug fix side & footer banner --- src-migrate/modules/footer-banner/index.tsx | 9 ++++++--- src-migrate/modules/side-banner/index.tsx | 29 +++++++++++++++-------------- 2 files changed, 21 insertions(+), 17 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/footer-banner/index.tsx b/src-migrate/modules/footer-banner/index.tsx index 7db1363c..b214493d 100644 --- a/src-migrate/modules/footer-banner/index.tsx +++ b/src-migrate/modules/footer-banner/index.tsx @@ -1,15 +1,19 @@ import Link from "next/link" +import React, { useMemo } from "react"; import { useQuery } from "react-query" import Image from "~/components/ui/image" import { getBanner } from "~/services/banner" +import { getRandomInt } from '@/utils/getRandomInt' const FooterBanner = () => { const fetchFooterBanner = useQuery({ queryKey: 'footerBanner', queryFn: () => getBanner({ type: 'bottom-search-promotion' }) }) - - const banner = fetchFooterBanner?.data?.[0] || false + // ubah dari static menjadid dynamic dengan menggunakan random index + const length = useMemo(() => fetchFooterBanner.data?.length, [fetchFooterBanner.data]); + const randomIndex = useMemo(() => getRandomInt(length), [length]); + const banner = fetchFooterBanner?.data?.[randomIndex] || false; return banner && ( <> @@ -25,5 +29,4 @@ const FooterBanner = () => { ) } - export default FooterBanner \ No newline at end of file diff --git a/src-migrate/modules/side-banner/index.tsx b/src-migrate/modules/side-banner/index.tsx index be52c554..6214edfb 100644 --- a/src-migrate/modules/side-banner/index.tsx +++ b/src-migrate/modules/side-banner/index.tsx @@ -1,29 +1,30 @@ -import Link from "next/link" -import { useQuery } from "react-query" -import Image from "~/components/ui/image" -import { getBanner } from "~/services/banner" +import React, { useMemo } from "react"; +import Link from "next/link"; +import { useQuery } from "react-query"; +import Image from "~/components/ui/image"; +import { getBanner } from "~/services/banner"; +import { getRandomInt } from '@/utils/getRandomInt'; const SideBanner = () => { const fetchSideBanner = useQuery({ queryKey: 'sideBanner', queryFn: () => getBanner({ type: 'side-banner-search' }) - }) - - const banner = fetchSideBanner?.data?.[0] || false + }); + // ubah dari static menjadid dynamic dengan menggunakan random index + const length = useMemo(() => fetchSideBanner.data?.length, [fetchSideBanner.data]); + const randomIndex = useMemo(() => getRandomInt(length), [length]); + const banner = fetchSideBanner?.data?.[randomIndex] || false; return banner && ( <> - {banner.url && ( + {banner.url ? ( {banner.name} - )} - - {!banner.url && ( + ) : ( {banner.name} )} - ) + ); } - -export default SideBanner \ No newline at end of file +export default SideBanner; -- cgit v1.2.3 From 556cbc1e5ea1c1ef0170c9a1b8f470a3d92d888e Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Tue, 2 Jul 2024 10:40:06 +0700 Subject: IS SO APPROVAL --- .../product-detail/components/PriceAction.tsx | 75 +++++++++++++++------- .../product-detail/components/ProductDetail.tsx | 12 +++- .../product-detail/stores/useProductDetail.ts | 6 ++ 3 files changed, 68 insertions(+), 25 deletions(-) (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index f25847a5..d91ea49c 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -1,15 +1,16 @@ -import style from '../styles/price-action.module.css' +import style from '../styles/price-action.module.css'; -import React, { useEffect } from 'react' -import formatCurrency from '~/libs/formatCurrency' -import { IProductDetail } from '~/types/product' -import { useProductDetail } from '../stores/useProductDetail' -import AddToCart from './AddToCart' -import Link from 'next/link' +import React, { useEffect } from 'react'; +import formatCurrency from '~/libs/formatCurrency'; +import { IProductDetail } from '~/types/product'; +import { useProductDetail } from '../stores/useProductDetail'; +import AddToCart from './AddToCart'; +import Link from 'next/link'; +import { getAuth } from '~/libs/auth'; type Props = { - product: IProductDetail -} + product: IProductDetail; +}; const PriceAction = ({ product }: Props) => { const { @@ -18,15 +19,22 @@ const PriceAction = ({ product }: Props) => { activeVariantId, quantityInput, setQuantityInput, - askAdminUrl - } = useProductDetail() + askAdminUrl, + isApproval, + setIsApproval, + } = useProductDetail(); useEffect(() => { - setActive(product.variants[0]) + setActive(product.variants[0]); }, [product, setActive]); + + return ( -
+
{!!activePrice && activePrice.price > 0 && ( <>
@@ -46,8 +54,8 @@ const PriceAction = ({ product }: Props) => {
- Termasuk PPN: {' '} - Rp {formatCurrency(Math.round(activePrice.price_discount * 1.11))} + Termasuk PPN: Rp{' '} + {formatCurrency(Math.round(activePrice.price_discount * 1.11))}
)} @@ -55,7 +63,11 @@ const PriceAction = ({ product }: Props) => { {!!activePrice && activePrice.price === 0 && ( Hubungi kami untuk dapatkan harga terbaik,{' '} - + klik disini @@ -64,13 +76,30 @@ const PriceAction = ({ product }: Props) => {
- - setQuantityInput(e.target.value)} className={style['quantity-input']} /> - - + + setQuantityInput(e.target.value)} + className={style['quantity-input']} + /> + + {!isApproval && ( + + )}
- ) -} + ); +}; -export default PriceAction \ No newline at end of file +export default PriceAction; diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 3b1bdbea..6cd72353 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -22,6 +22,7 @@ import PriceAction from './PriceAction' import SimilarBottom from './SimilarBottom' import SimilarSide from './SimilarSide' import VariantList from './VariantList' +import { getAuth } from '~/libs/auth' type Props = { product: IProductDetail @@ -32,7 +33,8 @@ const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST const ProductDetail = ({ product }: Props) => { const { isDesktop, isMobile } = useDevice() const router = useRouter() - const { setAskAdminUrl, askAdminUrl, activeVariantId } = useProductDetail() + const auth = getAuth() + const { setAskAdminUrl, askAdminUrl, activeVariantId, setIsApproval, isApproval } = useProductDetail() useEffect(() => { const createdAskUrl = whatsappUrl({ @@ -48,6 +50,12 @@ const ProductDetail = ({ product }: Props) => { setAskAdminUrl(createdAskUrl) }, [router.asPath, product.manufacture.name, product.name, setAskAdminUrl]) + useEffect(() => { + if (typeof auth === 'object') { + setIsApproval(auth?.feature?.soApproval); + } + }, []); + return ( <>
@@ -115,7 +123,7 @@ const ProductDetail = ({ product }: Props) => { )}
- {!!activeVariantId && } + {!!activeVariantId && !isApproval && }

diff --git a/src-migrate/modules/product-detail/stores/useProductDetail.ts b/src-migrate/modules/product-detail/stores/useProductDetail.ts index 794f0346..2da8835d 100644 --- a/src-migrate/modules/product-detail/stores/useProductDetail.ts +++ b/src-migrate/modules/product-detail/stores/useProductDetail.ts @@ -6,12 +6,14 @@ type State = { activePrice: IProductVariantDetail['price'] | null; quantityInput: string; askAdminUrl: string; + isApproval : boolean; }; type Action = { setActive: (variant: IProductVariantDetail) => void; setQuantityInput: (value: string) => void; setAskAdminUrl: (url: string) => void; + setIsApproval : (value : boolean) => void; }; export const useProductDetail = create((set, get) => ({ @@ -19,6 +21,7 @@ export const useProductDetail = create((set, get) => ({ activePrice: null, quantityInput: '1', askAdminUrl: '', + isApproval : false, setActive: (variant) => { set({ activeVariantId: variant.id, activePrice: variant.price }); }, @@ -28,4 +31,7 @@ export const useProductDetail = create((set, get) => ({ setAskAdminUrl: (url: string) => { set({ askAdminUrl: url }); }, + setIsApproval : (value : boolean) => { + set({ isApproval : value }) + } })); -- cgit v1.2.3