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') 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') 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/common/components/elements/Modal.tsx | 4 ++-- src-migrate/modules/popup-information/index.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/common/components/elements/Modal.tsx b/src-migrate/common/components/elements/Modal.tsx index 9c5c73ce..b8491d2f 100644 --- a/src-migrate/common/components/elements/Modal.tsx +++ b/src-migrate/common/components/elements/Modal.tsx @@ -72,8 +72,8 @@ const Modal = ({ {title}
{close && ( - )}
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/common/libs/formatCurrency.ts | 5 + src-migrate/common/types/cart.ts | 70 ++++++++++++ src-migrate/common/types/productVariant.ts | 13 +++ src-migrate/common/types/promotion.ts | 28 +++++ src-migrate/common/types/solr.ts | 7 ++ 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 ++++++++ src-migrate/pages/api/product-variant/[id].tsx | 45 ++++++++ .../product-variant/[id]/promotion/highlight.tsx | 56 ++++++++++ src-migrate/services/auth.ts | 24 +---- src-migrate/services/banner.ts | 0 src-migrate/services/cart.ts | 29 +++++ src-migrate/services/pageContent.ts | 11 +- 30 files changed, 1130 insertions(+), 30 deletions(-) create mode 100644 src-migrate/common/libs/formatCurrency.ts create mode 100644 src-migrate/common/types/cart.ts create mode 100644 src-migrate/common/types/productVariant.ts create mode 100644 src-migrate/common/types/promotion.ts create mode 100644 src-migrate/common/types/solr.ts 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 create mode 100644 src-migrate/pages/api/product-variant/[id].tsx create mode 100644 src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx delete mode 100644 src-migrate/services/banner.ts create mode 100644 src-migrate/services/cart.ts (limited to 'src-migrate') diff --git a/src-migrate/common/libs/formatCurrency.ts b/src-migrate/common/libs/formatCurrency.ts new file mode 100644 index 00000000..41db4a6f --- /dev/null +++ b/src-migrate/common/libs/formatCurrency.ts @@ -0,0 +1,5 @@ +const formatCurrency = (value: number) => { + return value.toLocaleString('id-ID'); +}; + +export default formatCurrency; diff --git a/src-migrate/common/types/cart.ts b/src-migrate/common/types/cart.ts new file mode 100644 index 00000000..15e08093 --- /dev/null +++ b/src-migrate/common/types/cart.ts @@ -0,0 +1,70 @@ +type Price = { + price: number; + discount_percentage: number; + price_discount: number; +}; + +export type CartProduct = { + id: number; + image: string; + display_name: string; + name: string; + code: string; + price: Price; + qty: number; + weight: number; + package_weight: number; +}; + +export type CartItem = { + cart_id: number; + quantity: number; + selected: boolean; + can_buy: boolean; + cart_type: 'product' | 'promotion'; + id: number; + name: string; + stock: number; + weight: number; + attributes: string[]; + parent: { + id: number; + name: string; + image: string; + }; + price: Price; + manufacture: { + id: number; + name: string; + }; + has_flashsale: boolean; + subtotal: number; + + code?: string; + + image?: string; + remaining_time?: number; + promotion_type?: { + value?: string; + label?: string; + }; + limit_qty?: { + all?: number; + user?: number; + transaction?: number; + }; + remaining_qty?: { + all?: number; + user?: number; + transaction?: number; + }; + used_percentage?: number; + products?: CartProduct[]; + free_products?: CartProduct[]; + package_price?: number; +}; + +export type CartProps = { + product_total: number; + products: CartItem[]; +}; diff --git a/src-migrate/common/types/productVariant.ts b/src-migrate/common/types/productVariant.ts new file mode 100644 index 00000000..c4aa9534 --- /dev/null +++ b/src-migrate/common/types/productVariant.ts @@ -0,0 +1,13 @@ +export interface IProductVariant { + id: number; + parent_id: number; + display_name: string; + image: string; + name: string; + default_code: string; + price: { + price: number; + discount_percentage: number; + price_discount: number; + }; +} diff --git a/src-migrate/common/types/promotion.ts b/src-migrate/common/types/promotion.ts new file mode 100644 index 00000000..9e62cc3f --- /dev/null +++ b/src-migrate/common/types/promotion.ts @@ -0,0 +1,28 @@ +import { IProductVariant } from './productVariant'; + +export interface IPromotion { + id: number; + program_id: number; + name: string; + type: { + value: string; + label: string; + }; + limit: number; + limit_user: number; + limit_trx: number; + price: number; + total_qty: number; + products: { + product_id: number; + qty: number; + }[]; + free_products: { + product_id: number; + qty: number; + }[]; +} + +export interface IProductVariantPromo extends IProductVariant { + qty: number; +} diff --git a/src-migrate/common/types/solr.ts b/src-migrate/common/types/solr.ts new file mode 100644 index 00000000..d231c305 --- /dev/null +++ b/src-migrate/common/types/solr.ts @@ -0,0 +1,7 @@ +export type SolrResponse = { + response: { + numFound: number; + start: number; + docs: T; + }; +}; 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 diff --git a/src-migrate/pages/api/product-variant/[id].tsx b/src-migrate/pages/api/product-variant/[id].tsx new file mode 100644 index 00000000..ec95714d --- /dev/null +++ b/src-migrate/pages/api/product-variant/[id].tsx @@ -0,0 +1,45 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { SolrResponse } from "~/common/types/solr"; + +const SOLR_HOST = process.env.SOLR_HOST as string + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const variantId = req.query.id as string + let price_tier = 'tier1' + + let auth = req.cookies.auth ? JSON.parse(req.cookies.auth) : null + if (auth?.pricelist) price_tier = auth.pricelist + + if (req.method === 'GET') { + const queryParams = new URLSearchParams({ q: `id:${variantId}` }) + const response = await fetch(`${SOLR_HOST}/solr/variants/select?${queryParams.toString()}`) + const data: SolrResponse = await response.json() + + if (data.response.numFound === 0) { + res.status(404).json({ error: 'Variant not found' }) + return + } + + const variant = await extractVariant(data.response.docs[0], price_tier) + + res.status(200).json({ data: variant }) + } +} + +const extractVariant = async (variant: any, price_tier: string) => { + const data: any = {} + + data.id = parseInt(variant.id) + data.parent_id = variant.template_id_i + data.display_name = variant.display_name_s + data.image = variant.image_s + data.name = variant.name_s + data.default_code = variant.default_code_s + data.price = { + price: variant.price_v2_f, + discount_percentage: variant[`discount_${price_tier}_v2_f`] || 0, + price_discount: variant[`price_${price_tier}_v2_f`] || 0, + } + + return data +} \ No newline at end of file diff --git a/src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx b/src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx new file mode 100644 index 00000000..0fe8fd1b --- /dev/null +++ b/src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx @@ -0,0 +1,56 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { SolrResponse } from "~/common/types/solr"; + +const SOLR_HOST = process.env.SOLR_HOST as string + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const productId = req.query.id as string + + if (req.method === 'GET') { + const types = ['bundling', 'discount_loading', 'merchandise'] + const queryParams = new URLSearchParams({ + q: `product_ids:${productId}`, + rows: '1' + }) + + let programs: any[] = [] + + for (const type of types) { + queryParams.set('fq', `type_value_s:${type}`) + const response = await fetch(`${SOLR_HOST}/solr/promotion_program_lines/select?${queryParams.toString()}`) + const data: SolrResponse = await response.json() + programs.push(...data.response.docs) + } + + programs = await extractPrograms(programs) + res.status(200).json({ data: programs }) + } +} + +const extractPrograms = async (programs: any[]) => { + const result = [] + + for (const program of programs) { + const data: any = {} + + data.id = program.id + data.program_id = program.program_id_i + data.name = program.name_s + data.type = { + value: program.type_value_s, + label: program.type_label_s, + } + data.limit = program.package_limit_i + data.limit_user = program.package_limit_user_i + data.limit_trx = program.package_limit_trx_i + data.price = program.price_f + data.total_qty = program.total_qty_i + + data.products = JSON.parse(program.products_s) + data.free_products = JSON.parse(program.free_products_s) + + result.push(data) + } + + return result +} \ No newline at end of file diff --git a/src-migrate/services/auth.ts b/src-migrate/services/auth.ts index a5d02754..1cc09c10 100644 --- a/src-migrate/services/auth.ts +++ b/src-migrate/services/auth.ts @@ -15,39 +15,23 @@ const BASE_PATH = '/api/v1/user'; export const registerUser = async ( data: RegisterProps ): Promise => { - const response = await odooApi('POST', `${BASE_PATH}/register`, data); - - return response; + return await odooApi('POST', `${BASE_PATH}/register`, data); }; export const activationUserToken = async ( params: ActivationTokenProps ): Promise => { - const response = await odooApi( - 'POST', - `${BASE_PATH}/activation-token`, - params - ); - - return response; + return await odooApi('POST', `${BASE_PATH}/activation-token`, params); }; export const activationUserOTP = async ( params: ActivationOtpProps ): Promise => { - const response = await odooApi('POST', `${BASE_PATH}/activation-otp`, params); - - return response; + return await odooApi('POST', `${BASE_PATH}/activation-otp`, params); }; export const activationReq = async ( params: ActivationReqProps ): Promise => { - const response = await odooApi( - 'POST', - `${BASE_PATH}/activation-request`, - params - ); - - return response; + return await odooApi('POST', `${BASE_PATH}/activation-request`, params); }; diff --git a/src-migrate/services/banner.ts b/src-migrate/services/banner.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/src-migrate/services/cart.ts b/src-migrate/services/cart.ts new file mode 100644 index 00000000..b238be3d --- /dev/null +++ b/src-migrate/services/cart.ts @@ -0,0 +1,29 @@ +import odooApi from '~/common/libs/odooApi'; + +export const getUserCart = async (userId: number) => { + return await odooApi('GET', `/api/v1/user/${userId}/cart`); +}; + +export const upsertUserCart = async ( + userId: number, + type: 'product' | 'promotion', + id: number, + qty: number, + selected: boolean, + source: 'buy' | 'add_to_cart' = 'add_to_cart' +) => { + return await odooApi('POST', `/api/v1/user/${userId}/cart/create-or-update`, { + product_id: type === 'product' ? id : null, + qty, + selected, + program_line_id: type === 'promotion' ? id : null, + source, + }); +}; + +export const deleteUserCart = async (userId: number, ids: number[]) => { + return await odooApi( + 'DELETE', + `/api/v1/user/${userId}/cart?ids=${ids.join(',')}` + ); +}; diff --git a/src-migrate/services/pageContent.ts b/src-migrate/services/pageContent.ts index 24f2c2f0..16146059 100644 --- a/src-migrate/services/pageContent.ts +++ b/src-migrate/services/pageContent.ts @@ -1,14 +1,7 @@ import odooApi from '~/common/libs/odooApi'; export const getPageContent = async ({ path }: { path: string }) => { - const params = new URLSearchParams({ - url_path: path, - }); + const params = new URLSearchParams({ url_path: path }); - const pageContent = await odooApi( - 'GET', - `/api/v1/page-content?${params.toString()}` - ); - - return pageContent; + return await odooApi('GET', `/api/v1/page-content?${params.toString()}`); }; -- 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/common/libs/parse | 0 src-migrate/common/types/cart.ts | 4 +- src-migrate/common/types/checkout.ts | 16 +++ src-migrate/common/types/promotion.ts | 9 +- src-migrate/common/types/promotionProgram.ts | 8 ++ src-migrate/constants/promotion.ts | 17 +++ 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 -------- src-migrate/pages/api/product-variant/[id].tsx | 4 +- .../product-variant/[id]/promotion/[category].tsx | 51 +++++++++ src-migrate/pages/api/promotion-program/[id].tsx | 43 ++++++++ src-migrate/services/checkout.ts | 7 ++ src-migrate/services/promotionProgram.ts | 8 ++ src-migrate/services/variant.ts | 14 +++ 55 files changed, 1327 insertions(+), 814 deletions(-) create mode 100644 src-migrate/common/libs/parse create mode 100644 src-migrate/common/types/checkout.ts create mode 100644 src-migrate/common/types/promotionProgram.ts create mode 100644 src-migrate/constants/promotion.ts 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 create mode 100644 src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx create mode 100644 src-migrate/pages/api/promotion-program/[id].tsx create mode 100644 src-migrate/services/checkout.ts create mode 100644 src-migrate/services/promotionProgram.ts create mode 100644 src-migrate/services/variant.ts (limited to 'src-migrate') diff --git a/src-migrate/common/libs/parse b/src-migrate/common/libs/parse new file mode 100644 index 00000000..e69de29b diff --git a/src-migrate/common/types/cart.ts b/src-migrate/common/types/cart.ts index 15e08093..3aceeac4 100644 --- a/src-migrate/common/types/cart.ts +++ b/src-migrate/common/types/cart.ts @@ -1,3 +1,5 @@ +import { CategoryPromo } from "./promotion"; + type Price = { price: number; discount_percentage: number; @@ -45,7 +47,7 @@ export type CartItem = { image?: string; remaining_time?: number; promotion_type?: { - value?: string; + value?: CategoryPromo; label?: string; }; limit_qty?: { diff --git a/src-migrate/common/types/checkout.ts b/src-migrate/common/types/checkout.ts new file mode 100644 index 00000000..dc1365d8 --- /dev/null +++ b/src-migrate/common/types/checkout.ts @@ -0,0 +1,16 @@ +import { CartItem } from './cart'; + +export interface ICheckout { + total_purchase: number; + total_discount: number; + discount_voucher: number; + subtotal: number; + tax: number; + grand_total: number; + total_weight: { + kg: number; + g: number; + }; + has_product_without_weight: boolean; + products: CartItem[]; +} diff --git a/src-migrate/common/types/promotion.ts b/src-migrate/common/types/promotion.ts index 9e62cc3f..1f8316cf 100644 --- a/src-migrate/common/types/promotion.ts +++ b/src-migrate/common/types/promotion.ts @@ -5,7 +5,7 @@ export interface IPromotion { program_id: number; name: string; type: { - value: string; + value: CategoryPromo; label: string; }; limit: number; @@ -26,3 +26,10 @@ export interface IPromotion { export interface IProductVariantPromo extends IProductVariant { qty: number; } + +export type CategoryPromo = 'bundling' | 'discount_loading' | 'merchandise'; + +export interface ICategoryPromo { + value: CategoryPromo; + label: string; +} diff --git a/src-migrate/common/types/promotionProgram.ts b/src-migrate/common/types/promotionProgram.ts new file mode 100644 index 00000000..205884b6 --- /dev/null +++ b/src-migrate/common/types/promotionProgram.ts @@ -0,0 +1,8 @@ +export type IPromotionProgram = { + id: number; + name: string; + start_time: string; + end_time: string; + applies_to: string; + time_left: number; +}; diff --git a/src-migrate/constants/promotion.ts b/src-migrate/constants/promotion.ts new file mode 100644 index 00000000..e6dfcc9b --- /dev/null +++ b/src-migrate/constants/promotion.ts @@ -0,0 +1,17 @@ +export const PROMO_CATEGORY = { + bundling: { + name: 'Bundling', + alias: 'Silat', + description: 'Kombinasi Kilat (SiLat)', + }, + discount_loading: { + name: 'Discount Loading', + alias: 'Barong', + description: 'Barang Borong (BaRong)', + }, + merchandise: { + name: 'Merchandise', + alias: 'Angklung', + description: 'Menang Langsung (Angklung)', + }, +}; 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 diff --git a/src-migrate/pages/api/product-variant/[id].tsx b/src-migrate/pages/api/product-variant/[id].tsx index ec95714d..b3bd4096 100644 --- a/src-migrate/pages/api/product-variant/[id].tsx +++ b/src-migrate/pages/api/product-variant/[id].tsx @@ -20,13 +20,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return } - const variant = await extractVariant(data.response.docs[0], price_tier) + const variant = await map(data.response.docs[0], price_tier) res.status(200).json({ data: variant }) } } -const extractVariant = async (variant: any, price_tier: string) => { +const map = async (variant: any, price_tier: string) => { const data: any = {} data.id = parseInt(variant.id) diff --git a/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx b/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx new file mode 100644 index 00000000..b1207c5e --- /dev/null +++ b/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx @@ -0,0 +1,51 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { SolrResponse } from "~/common/types/solr"; + +const SOLR_HOST = process.env.SOLR_HOST as string + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const productId = req.query.id as string + const category = req.query.category as string + + if (req.method === 'GET') { + const queryParams = new URLSearchParams({ + q: `product_ids:${productId}`, + fq: `type_value_s:${category}`, + rows: '1' + }) + + const response = await fetch(`${SOLR_HOST}/solr/promotion_program_lines/select?${queryParams.toString()}`) + const data: SolrResponse = await response.json() + + const promotions = await map(data.response.docs) + res.status(200).json({ data: promotions }) + } +} + +const map = async (promotions: any[]) => { + const result = [] + + for (const promotion of promotions) { + const data: any = {} + + data.id = promotion.id + data.program_id = promotion.program_id_i + data.name = promotion.name_s + data.type = { + value: promotion.type_value_s, + label: promotion.type_label_s, + } + data.limit = promotion.package_limit_i + data.limit_user = promotion.package_limit_user_i + data.limit_trx = promotion.package_limit_trx_i + data.price = promotion.price_f + data.total_qty = promotion.total_qty_i + + data.products = JSON.parse(promotion.products_s) + data.free_products = JSON.parse(promotion.free_products_s) + + result.push(data) + } + + return result +} \ No newline at end of file diff --git a/src-migrate/pages/api/promotion-program/[id].tsx b/src-migrate/pages/api/promotion-program/[id].tsx new file mode 100644 index 00000000..ba716e85 --- /dev/null +++ b/src-migrate/pages/api/promotion-program/[id].tsx @@ -0,0 +1,43 @@ +import { NextApiRequest, NextApiResponse } from "next"; +import { SolrResponse } from "~/common/types/solr"; +import moment from 'moment' + +const SOLR_HOST = process.env.SOLR_HOST as string + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const id = req.query.id as string + + if (req.method === 'GET') { + const queryParams = new URLSearchParams({ q: `id:${id}` }) + const response = await fetch(`${SOLR_HOST}/solr/promotion_programs/select?${queryParams.toString()}`) + const data: SolrResponse = await response.json() + + if (data.response.numFound === 0) { + res.status(404).json({ error: 'Program not found' }) + return + } + + const program = await map(data.response.docs[0]) + + res.status(200).json({ data: program }) + } +} + +const map = async (program: any) => { + const data: any = {} + + data.id = program.id + data.name = program.name_s + data.start_time = program.start_time_s + data.end_time = program.end_time_s + data.applies_to = program.applies_to_s + data.time_left = (new Date(data.end_time).getTime() - new Date().getTime()) / 1000 + + // const duration = moment.duration(data.time_left, 'seconds') + // const days = duration.days() + // const hours = duration.hours() + // const minutes = duration.minutes() + // const seconds = duration.seconds() + + return data +} \ No newline at end of file diff --git a/src-migrate/services/checkout.ts b/src-migrate/services/checkout.ts new file mode 100644 index 00000000..3dd1c8e8 --- /dev/null +++ b/src-migrate/services/checkout.ts @@ -0,0 +1,7 @@ +import odooApi from '~/common/libs/odooApi'; + +export const getUserCheckout = async (userId: number) => { + return await odooApi('GET', `/api/v1/user/${userId}/sale_order/checkout`); +}; + +// /api/v1/user/${id}/sale_order/checkout?voucher=${voucher} \ No newline at end of file diff --git a/src-migrate/services/promotionProgram.ts b/src-migrate/services/promotionProgram.ts new file mode 100644 index 00000000..a5026c71 --- /dev/null +++ b/src-migrate/services/promotionProgram.ts @@ -0,0 +1,8 @@ +import { IPromotionProgram } from '~/common/types/promotionProgram'; + +export const getPromotionProgram = async ( + programId: number +): Promise<{ data: IPromotionProgram }> => { + const url = `/api/promotion-program/${programId}`; + return await fetch(url).then((res) => res.json()); +}; diff --git a/src-migrate/services/variant.ts b/src-migrate/services/variant.ts new file mode 100644 index 00000000..213187d2 --- /dev/null +++ b/src-migrate/services/variant.ts @@ -0,0 +1,14 @@ +import { CategoryPromo, IPromotion } from '~/common/types/promotion'; + +export const getVariantById = async (variantId: number) => { + const url = `/api/product-variant/${variantId}`; + return await fetch(url).then((res) => res.json()); +}; + +export const getVariantPromoByCategory = async ( + variantId: number, + type: CategoryPromo +): Promise<{ data: IPromotion[] }> => { + const url = `/api/product-variant/${variantId}/promotion/${type}`; + return await fetch(url).then((res) => res.json()); +}; -- 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/common/components/elements/Modal.tsx | 2 +- 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 ++-- .../api/product-variant/[id]/promotion/[category].tsx | 3 +-- 13 files changed, 60 insertions(+), 30 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/common/components/elements/Modal.tsx b/src-migrate/common/components/elements/Modal.tsx index 8e488a3a..ea95c0b1 100644 --- a/src-migrate/common/components/elements/Modal.tsx +++ b/src-migrate/common/components/elements/Modal.tsx @@ -37,7 +37,7 @@ const Modal = ({ const modalClassNames = clsxm( "fixed bg-white max-h-[80vh] overflow-auto p-4 pt-0 z-[60] border-gray_r-6", { - "left-1/2 -translate-x-1/2 translate-y-1/2 bottom-1/2 w-11/12 md:w-1/4 lg:w-1/3 border rounded-xl": mode === 'desktop', + "left-1/2 -translate-x-1/2 translate-y-1/2 bottom-1/2 w-11/12 md:w-[500px] border rounded-xl": mode === 'desktop', "left-0 w-full border-t bottom-0 rounded-t-xl": mode === 'mobile' }, className 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 { diff --git a/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx b/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx index b1207c5e..745f9944 100644 --- a/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx +++ b/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx @@ -10,8 +10,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) if (req.method === 'GET') { const queryParams = new URLSearchParams({ q: `product_ids:${productId}`, - fq: `type_value_s:${category}`, - rows: '1' + fq: `type_value_s:${category}` }) const response = await fetch(`${SOLR_HOST}/solr/promotion_program_lines/select?${queryParams.toString()}`) -- 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 +- src-migrate/pages/shop/cart.module.css | 31 ++++++++ src-migrate/pages/shop/cart.tsx | 91 ++++++++++++++++++++++ 6 files changed, 125 insertions(+), 88 deletions(-) delete mode 100644 src-migrate/modules/cart/components/Detail.tsx delete mode 100644 src-migrate/modules/cart/styles/detail.module.css create mode 100644 src-migrate/pages/shop/cart.module.css create mode 100644 src-migrate/pages/shop/cart.tsx (limited to 'src-migrate') 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 && ( { + 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 CartPage \ No newline at end of file -- cgit v1.2.3 From fb14d51cccd4c5c3c8aed57bcaee920a2e6f3355 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly 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') 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 --- src-migrate/common/components/elements/Modal.tsx | 90 --- .../common/components/elements/ReCaptcha.tsx | 17 - src-migrate/common/components/elements/Seo.tsx | 34 -- .../components/skeleton/PageContentSkeleton.tsx | 19 - src-migrate/common/constants/menu.ts | 20 - src-migrate/common/libs/auth.ts | 26 - src-migrate/common/libs/clsxm.ts | 6 - src-migrate/common/libs/formatCurrency.ts | 5 - src-migrate/common/libs/odooApi.ts | 81 --- src-migrate/common/libs/parse | 0 src-migrate/common/stores/useRegisterStore.ts | 60 -- .../common/styles/fonts/Inter/Inter-Black.woff | Bin 138764 -> 0 bytes .../common/styles/fonts/Inter/Inter-Black.woff2 | Bin 102868 -> 0 bytes .../styles/fonts/Inter/Inter-BlackItalic.woff | Bin 146824 -> 0 bytes .../styles/fonts/Inter/Inter-BlackItalic.woff2 | Bin 108752 -> 0 bytes .../common/styles/fonts/Inter/Inter-Bold.woff | Bin 143208 -> 0 bytes .../common/styles/fonts/Inter/Inter-Bold.woff2 | Bin 106140 -> 0 bytes .../styles/fonts/Inter/Inter-BoldItalic.woff | Bin 151052 -> 0 bytes .../styles/fonts/Inter/Inter-BoldItalic.woff2 | Bin 111808 -> 0 bytes .../common/styles/fonts/Inter/Inter-ExtraBold.woff | Bin 142920 -> 0 bytes .../styles/fonts/Inter/Inter-ExtraBold.woff2 | Bin 106108 -> 0 bytes .../styles/fonts/Inter/Inter-ExtraBoldItalic.woff | Bin 150628 -> 0 bytes .../styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 | Bin 111708 -> 0 bytes .../styles/fonts/Inter/Inter-ExtraLight.woff | Bin 140724 -> 0 bytes .../styles/fonts/Inter/Inter-ExtraLight.woff2 | Bin 104232 -> 0 bytes .../styles/fonts/Inter/Inter-ExtraLightItalic.woff | Bin 149996 -> 0 bytes .../fonts/Inter/Inter-ExtraLightItalic.woff2 | Bin 111392 -> 0 bytes .../common/styles/fonts/Inter/Inter-Italic.woff | Bin 144372 -> 0 bytes .../common/styles/fonts/Inter/Inter-Italic.woff2 | Bin 106876 -> 0 bytes .../common/styles/fonts/Inter/Inter-Light.woff | Bin 140632 -> 0 bytes .../common/styles/fonts/Inter/Inter-Light.woff2 | Bin 104332 -> 0 bytes .../styles/fonts/Inter/Inter-LightItalic.woff | Bin 150092 -> 0 bytes .../styles/fonts/Inter/Inter-LightItalic.woff2 | Bin 111332 -> 0 bytes .../common/styles/fonts/Inter/Inter-Medium.woff | Bin 142552 -> 0 bytes .../common/styles/fonts/Inter/Inter-Medium.woff2 | Bin 105924 -> 0 bytes .../styles/fonts/Inter/Inter-MediumItalic.woff | Bin 150988 -> 0 bytes .../styles/fonts/Inter/Inter-MediumItalic.woff2 | Bin 112184 -> 0 bytes .../common/styles/fonts/Inter/Inter-Regular.woff | Bin 133844 -> 0 bytes .../common/styles/fonts/Inter/Inter-Regular.woff2 | Bin 98868 -> 0 bytes .../common/styles/fonts/Inter/Inter-SemiBold.woff | Bin 142932 -> 0 bytes .../common/styles/fonts/Inter/Inter-SemiBold.woff2 | Bin 105804 -> 0 bytes .../styles/fonts/Inter/Inter-SemiBoldItalic.woff | Bin 151180 -> 0 bytes .../styles/fonts/Inter/Inter-SemiBoldItalic.woff2 | Bin 112048 -> 0 bytes .../common/styles/fonts/Inter/Inter-Thin.woff | Bin 135920 -> 0 bytes .../common/styles/fonts/Inter/Inter-Thin.woff2 | Bin 99632 -> 0 bytes .../styles/fonts/Inter/Inter-ThinItalic.woff | Bin 145480 -> 0 bytes .../styles/fonts/Inter/Inter-ThinItalic.woff2 | Bin 106496 -> 0 bytes .../styles/fonts/Inter/Inter-italic.var.woff2 | Bin 245036 -> 0 bytes .../styles/fonts/Inter/Inter-roman.var.woff2 | Bin 227180 -> 0 bytes .../common/styles/fonts/Inter/Inter.var.woff2 | Bin 324864 -> 0 bytes src-migrate/common/styles/fonts/Inter/inter.css | 199 ------ src-migrate/common/styles/globals.css | 674 --------------------- src-migrate/common/types/auth.ts | 58 -- src-migrate/common/types/cart.ts | 72 --- src-migrate/common/types/checkout.ts | 16 - src-migrate/common/types/nav.ts | 4 - src-migrate/common/types/odoo.ts | 6 - src-migrate/common/types/pageContent.ts | 5 - src-migrate/common/types/productVariant.ts | 13 - src-migrate/common/types/promotion.ts | 35 -- src-migrate/common/types/promotionProgram.ts | 8 - src-migrate/common/types/solr.ts | 7 - src-migrate/common/validations/auth.ts | 17 - src-migrate/components/seo.tsx | 32 + src-migrate/components/ui/image.tsx | 46 ++ src-migrate/components/ui/modal.tsx | 87 +++ src-migrate/components/ui/re-captcha.tsx | 15 + src-migrate/constants/menu.ts | 20 + src-migrate/libs/auth.ts | 26 + src-migrate/libs/clsxm.ts | 6 + src-migrate/libs/formatCurrency.ts | 5 + src-migrate/libs/formatNumber.ts | 8 + src-migrate/libs/odooApi.ts | 81 +++ src-migrate/libs/slug.ts | 34 ++ src-migrate/libs/toTitleCase.ts | 5 + .../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 ++ src-migrate/pages/_app.tsx | 4 +- src-migrate/pages/api/product-variant/[id].tsx | 2 +- .../product-variant/[id]/promotion/[category].tsx | 2 +- .../product-variant/[id]/promotion/highlight.tsx | 2 +- src-migrate/pages/api/promotion-program/[id].tsx | 3 +- src-migrate/pages/register.tsx | 2 +- src-migrate/pages/shop/cart.module.css | 31 - src-migrate/pages/shop/cart.tsx | 91 --- src-migrate/pages/shop/cart/cart.module.css | 31 + src-migrate/pages/shop/cart/index.tsx | 93 +++ src-migrate/pages/shop/product/[slug].tsx | 73 +++ src-migrate/pages/shop/product/product.module.css | 0 src-migrate/services/auth.ts | 4 +- src-migrate/services/cart.ts | 2 +- src-migrate/services/checkout.ts | 2 +- src-migrate/services/pageContent.ts | 2 +- src-migrate/services/product.ts | 59 ++ src-migrate/services/productVariant.ts | 23 + src-migrate/services/promotionProgram.ts | 2 +- src-migrate/services/variant.ts | 14 - src-migrate/styles/fonts/Inter/Inter-Black.woff | Bin 0 -> 138764 bytes src-migrate/styles/fonts/Inter/Inter-Black.woff2 | Bin 0 -> 102868 bytes .../styles/fonts/Inter/Inter-BlackItalic.woff | Bin 0 -> 146824 bytes .../styles/fonts/Inter/Inter-BlackItalic.woff2 | Bin 0 -> 108752 bytes src-migrate/styles/fonts/Inter/Inter-Bold.woff | Bin 0 -> 143208 bytes src-migrate/styles/fonts/Inter/Inter-Bold.woff2 | Bin 0 -> 106140 bytes .../styles/fonts/Inter/Inter-BoldItalic.woff | Bin 0 -> 151052 bytes .../styles/fonts/Inter/Inter-BoldItalic.woff2 | Bin 0 -> 111808 bytes .../styles/fonts/Inter/Inter-ExtraBold.woff | Bin 0 -> 142920 bytes .../styles/fonts/Inter/Inter-ExtraBold.woff2 | Bin 0 -> 106108 bytes .../styles/fonts/Inter/Inter-ExtraBoldItalic.woff | Bin 0 -> 150628 bytes .../styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 | Bin 0 -> 111708 bytes .../styles/fonts/Inter/Inter-ExtraLight.woff | Bin 0 -> 140724 bytes .../styles/fonts/Inter/Inter-ExtraLight.woff2 | Bin 0 -> 104232 bytes .../styles/fonts/Inter/Inter-ExtraLightItalic.woff | Bin 0 -> 149996 bytes .../fonts/Inter/Inter-ExtraLightItalic.woff2 | Bin 0 -> 111392 bytes src-migrate/styles/fonts/Inter/Inter-Italic.woff | Bin 0 -> 144372 bytes src-migrate/styles/fonts/Inter/Inter-Italic.woff2 | Bin 0 -> 106876 bytes src-migrate/styles/fonts/Inter/Inter-Light.woff | Bin 0 -> 140632 bytes src-migrate/styles/fonts/Inter/Inter-Light.woff2 | Bin 0 -> 104332 bytes .../styles/fonts/Inter/Inter-LightItalic.woff | Bin 0 -> 150092 bytes .../styles/fonts/Inter/Inter-LightItalic.woff2 | Bin 0 -> 111332 bytes src-migrate/styles/fonts/Inter/Inter-Medium.woff | Bin 0 -> 142552 bytes src-migrate/styles/fonts/Inter/Inter-Medium.woff2 | Bin 0 -> 105924 bytes .../styles/fonts/Inter/Inter-MediumItalic.woff | Bin 0 -> 150988 bytes .../styles/fonts/Inter/Inter-MediumItalic.woff2 | Bin 0 -> 112184 bytes src-migrate/styles/fonts/Inter/Inter-Regular.woff | Bin 0 -> 133844 bytes src-migrate/styles/fonts/Inter/Inter-Regular.woff2 | Bin 0 -> 98868 bytes src-migrate/styles/fonts/Inter/Inter-SemiBold.woff | Bin 0 -> 142932 bytes .../styles/fonts/Inter/Inter-SemiBold.woff2 | Bin 0 -> 105804 bytes .../styles/fonts/Inter/Inter-SemiBoldItalic.woff | Bin 0 -> 151180 bytes .../styles/fonts/Inter/Inter-SemiBoldItalic.woff2 | Bin 0 -> 112048 bytes src-migrate/styles/fonts/Inter/Inter-Thin.woff | Bin 0 -> 135920 bytes src-migrate/styles/fonts/Inter/Inter-Thin.woff2 | Bin 0 -> 99632 bytes .../styles/fonts/Inter/Inter-ThinItalic.woff | Bin 0 -> 145480 bytes .../styles/fonts/Inter/Inter-ThinItalic.woff2 | Bin 0 -> 106496 bytes .../styles/fonts/Inter/Inter-italic.var.woff2 | Bin 0 -> 245036 bytes .../styles/fonts/Inter/Inter-roman.var.woff2 | Bin 0 -> 227180 bytes src-migrate/styles/fonts/Inter/Inter.var.woff2 | Bin 0 -> 324864 bytes src-migrate/styles/fonts/Inter/inter.css | 199 ++++++ src-migrate/styles/globals.css | 674 +++++++++++++++++++++ src-migrate/types/auth.ts | 58 ++ src-migrate/types/cart.ts | 72 +++ src-migrate/types/checkout.ts | 16 + src-migrate/types/nav.ts | 4 + src-migrate/types/odoo.ts | 6 + src-migrate/types/pageContent.ts | 5 + src-migrate/types/product.ts | 39 ++ src-migrate/types/productVariant.ts | 33 + src-migrate/types/promotion.ts | 44 ++ src-migrate/types/promotionProgram.ts | 8 + src-migrate/types/solr.ts | 7 + src-migrate/validations/auth.ts | 17 + 195 files changed, 2808 insertions(+), 1667 deletions(-) delete mode 100644 src-migrate/common/components/elements/Modal.tsx delete mode 100644 src-migrate/common/components/elements/ReCaptcha.tsx delete mode 100644 src-migrate/common/components/elements/Seo.tsx delete mode 100644 src-migrate/common/components/skeleton/PageContentSkeleton.tsx delete mode 100644 src-migrate/common/constants/menu.ts delete mode 100644 src-migrate/common/libs/auth.ts delete mode 100644 src-migrate/common/libs/clsxm.ts delete mode 100644 src-migrate/common/libs/formatCurrency.ts delete mode 100644 src-migrate/common/libs/odooApi.ts delete mode 100644 src-migrate/common/libs/parse delete mode 100644 src-migrate/common/stores/useRegisterStore.ts delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Black.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Black.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Bold.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Italic.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Light.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Light.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Medium.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Regular.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Thin.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/Inter.var.woff2 delete mode 100644 src-migrate/common/styles/fonts/Inter/inter.css delete mode 100644 src-migrate/common/styles/globals.css delete mode 100644 src-migrate/common/types/auth.ts delete mode 100644 src-migrate/common/types/cart.ts delete mode 100644 src-migrate/common/types/checkout.ts delete mode 100644 src-migrate/common/types/nav.ts delete mode 100644 src-migrate/common/types/odoo.ts delete mode 100644 src-migrate/common/types/pageContent.ts delete mode 100644 src-migrate/common/types/productVariant.ts delete mode 100644 src-migrate/common/types/promotion.ts delete mode 100644 src-migrate/common/types/promotionProgram.ts delete mode 100644 src-migrate/common/types/solr.ts delete mode 100644 src-migrate/common/validations/auth.ts create mode 100644 src-migrate/components/seo.tsx create mode 100644 src-migrate/components/ui/image.tsx create mode 100644 src-migrate/components/ui/modal.tsx create mode 100644 src-migrate/components/ui/re-captcha.tsx create mode 100644 src-migrate/constants/menu.ts create mode 100644 src-migrate/libs/auth.ts create mode 100644 src-migrate/libs/clsxm.ts create mode 100644 src-migrate/libs/formatCurrency.ts create mode 100644 src-migrate/libs/formatNumber.ts create mode 100644 src-migrate/libs/odooApi.ts create mode 100644 src-migrate/libs/slug.ts create mode 100644 src-migrate/libs/toTitleCase.ts 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 delete mode 100644 src-migrate/pages/shop/cart.module.css delete mode 100644 src-migrate/pages/shop/cart.tsx create mode 100644 src-migrate/pages/shop/cart/cart.module.css create mode 100644 src-migrate/pages/shop/cart/index.tsx create mode 100644 src-migrate/pages/shop/product/[slug].tsx create mode 100644 src-migrate/pages/shop/product/product.module.css create mode 100644 src-migrate/services/product.ts create mode 100644 src-migrate/services/productVariant.ts delete mode 100644 src-migrate/services/variant.ts create mode 100644 src-migrate/styles/fonts/Inter/Inter-Black.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-Black.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-Bold.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-Bold.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-Italic.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-Italic.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-Light.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-Light.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-LightItalic.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-LightItalic.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-Medium.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-Medium.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-Regular.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-Regular.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-SemiBold.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-SemiBold.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-Thin.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-Thin.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff create mode 100644 src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-italic.var.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter-roman.var.woff2 create mode 100644 src-migrate/styles/fonts/Inter/Inter.var.woff2 create mode 100644 src-migrate/styles/fonts/Inter/inter.css create mode 100644 src-migrate/styles/globals.css create mode 100644 src-migrate/types/auth.ts create mode 100644 src-migrate/types/cart.ts create mode 100644 src-migrate/types/checkout.ts create mode 100644 src-migrate/types/nav.ts create mode 100644 src-migrate/types/odoo.ts create mode 100644 src-migrate/types/pageContent.ts create mode 100644 src-migrate/types/product.ts create mode 100644 src-migrate/types/productVariant.ts create mode 100644 src-migrate/types/promotion.ts create mode 100644 src-migrate/types/promotionProgram.ts create mode 100644 src-migrate/types/solr.ts create mode 100644 src-migrate/validations/auth.ts (limited to 'src-migrate') diff --git a/src-migrate/common/components/elements/Modal.tsx b/src-migrate/common/components/elements/Modal.tsx deleted file mode 100644 index c9c621e0..00000000 --- a/src-migrate/common/components/elements/Modal.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { XMarkIcon } from "@heroicons/react/24/outline"; -import { AnimatePresence, motion } from "framer-motion" -import { useRouter } from "next/router"; -import { useEffect, useState } from "react"; -import ReactDOM from "react-dom"; -import { useWindowSize } from "usehooks-ts"; -import clsxm from "~/common/libs/clsxm"; - - -type Props = { - children: React.ReactNode - active: boolean - title?: string - close?: () => void, - className?: string, - mode?: "mobile" | "desktop" -} - -const Modal = ({ - children, - active = false, - title, - close, - className, - mode -}: Props) => { - const router = useRouter() - const { width } = useWindowSize() - const [rendered, setRendered] = useState(false) - - mode = mode || width >= 768 ? "desktop" : "mobile" - - useEffect(() => { - setRendered(true) - }, []) - - const modalClassNames = clsxm( - "fixed bg-white max-h-[80vh] overflow-auto p-4 pt-0 z-[60] border-gray_r-6", - { - "left-1/2 -translate-x-1/2 translate-y-1/2 bottom-1/2 w-11/12 md:w-[500px] border rounded-xl": mode === 'desktop', - "left-0 w-full border-t bottom-0 rounded-t-xl": mode === 'mobile' - }, - className - ) - - const variant = { - initial: { bottom: mode === 'desktop' ? '45%' : '-100%', opacity: 0 }, - animate: { bottom: mode === 'desktop' ? '50%' : 0, opacity: 1 }, - exit: { bottom: mode === 'desktop' ? '55%' : '-100%', opacity: 0 }, - transition: { ease: 'linear', duration: 0.25 } - } - - return rendered && ReactDOM.createPortal( - - {active && ( - - )} - - {active && ( - -
-
- {title} -
- {close && ( - - )} -
- - {children} -
- )} - -
, - document.querySelector('body')! - ) -} - -export default Modal \ No newline at end of file diff --git a/src-migrate/common/components/elements/ReCaptcha.tsx b/src-migrate/common/components/elements/ReCaptcha.tsx deleted file mode 100644 index 1bc31d90..00000000 --- a/src-migrate/common/components/elements/ReCaptcha.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import ReCAPTCHA, { ReCAPTCHAProps } from "react-google-recaptcha" - -const GOOGLE_RECAPTCHA_KEY = process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE || '' - -type Props = Omit & { - sitekey?: string; -} - -const ReCaptcha = (props: Props) => { - const { sitekey, ...rest } = props - - return ( - - ) -} - -export default ReCaptcha \ No newline at end of file diff --git a/src-migrate/common/components/elements/Seo.tsx b/src-migrate/common/components/elements/Seo.tsx deleted file mode 100644 index 2245663a..00000000 --- a/src-migrate/common/components/elements/Seo.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { useRouter } from 'next/router' -import React from 'react' -import { NextSeo } from "next-seo" -import { MetaTag, NextSeoProps } from 'next-seo/lib/types'; - -const Seo = (props: NextSeoProps) => { - const router = useRouter() - - const additionalMetaTags: MetaTag[] = [ - { - property: 'fb:app_id', - content: '270830718811' - }, - { - property: 'fb:page_id', - content: '101759953569' - }, - ] - - if (!!props.additionalMetaTags) additionalMetaTags.push(...props.additionalMetaTags) - - return ( - - ) -} - -export default Seo \ No newline at end of file diff --git a/src-migrate/common/components/skeleton/PageContentSkeleton.tsx b/src-migrate/common/components/skeleton/PageContentSkeleton.tsx deleted file mode 100644 index bf85cff1..00000000 --- a/src-migrate/common/components/skeleton/PageContentSkeleton.tsx +++ /dev/null @@ -1,19 +0,0 @@ -const PageContentSkeleton = () => { - return ( -
-
-
-
-
-
-
-
-
-
-
-
-
- ) -} - -export default PageContentSkeleton \ No newline at end of file diff --git a/src-migrate/common/constants/menu.ts b/src-migrate/common/constants/menu.ts deleted file mode 100644 index 853da507..00000000 --- a/src-migrate/common/constants/menu.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { SecondaryNavItemProps } from '../types/nav' - -export const SECONDARY_MENU_ITEMS: SecondaryNavItemProps[] = [ - { - label: 'Semua Brand', - href: '/shop/brands' - }, - { - label: 'Ready Stock', - href: '/shop/search?orderBy=stock' - }, - { - label: 'Blog Indoteknik', - href: 'https://blog.indoteknik.com/' - }, - { - label: 'Indoteknik TV', - href: '/video' - } -] diff --git a/src-migrate/common/libs/auth.ts b/src-migrate/common/libs/auth.ts deleted file mode 100644 index fb4e836a..00000000 --- a/src-migrate/common/libs/auth.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { deleteCookie, getCookie, setCookie } from 'cookies-next'; -import { AuthProps } from '../types/auth'; - -const COOKIE_KEY = 'auth'; - -export const getAuth = (): AuthProps | boolean => { - const auth = getCookie(COOKIE_KEY); - - if (typeof auth === 'string') { - return JSON.parse(auth); - } - - return false; -}; - -export const setAuth = (user: AuthProps): boolean => { - setCookie(COOKIE_KEY, JSON.stringify(user)); - - return true; -}; - -export const deleteAuth = (): boolean => { - deleteCookie(COOKIE_KEY); - - return true; -}; diff --git a/src-migrate/common/libs/clsxm.ts b/src-migrate/common/libs/clsxm.ts deleted file mode 100644 index 0fc10317..00000000 --- a/src-migrate/common/libs/clsxm.ts +++ /dev/null @@ -1,6 +0,0 @@ -import clsx, { ClassValue } from 'clsx'; -import { twMerge } from 'tw-merge'; - -export default function clsxm(...classes: ClassValue[]) { - return twMerge(clsx(...classes)); -} diff --git a/src-migrate/common/libs/formatCurrency.ts b/src-migrate/common/libs/formatCurrency.ts deleted file mode 100644 index 41db4a6f..00000000 --- a/src-migrate/common/libs/formatCurrency.ts +++ /dev/null @@ -1,5 +0,0 @@ -const formatCurrency = (value: number) => { - return value.toLocaleString('id-ID'); -}; - -export default formatCurrency; diff --git a/src-migrate/common/libs/odooApi.ts b/src-migrate/common/libs/odooApi.ts deleted file mode 100644 index 2dbc18d3..00000000 --- a/src-migrate/common/libs/odooApi.ts +++ /dev/null @@ -1,81 +0,0 @@ -import axios, { AxiosRequestConfig, Method } from 'axios'; -import { getCookie, setCookie } from 'cookies-next'; -import { getAuth } from './auth'; -import { AuthApiProps, AuthProps } from '../types/auth'; - -const ODOO_HOST = process.env.NEXT_PUBLIC_ODOO_API_HOST as string; - -const renewToken = async () => { - let token = await axios.get(`${ODOO_HOST}/api/token`); - setCookie('token', token.data.result); - - return token.data.result; -}; - -const getToken = async () => { - let token = getCookie('token'); - if (token == undefined) token = await renewToken(); - - return token; -}; - -const maxConnectionAttempt = 15; -let connectionAttempt = 0; - -const odooApi = async ( - method: Method, - url: string, - data = {}, - headers = {} -): Promise => { - connectionAttempt++; - - try { - let token = await getToken(); - const auth = getAuth(); - - let axiosParameter: AxiosRequestConfig = { - method, - url: process.env.NEXT_PUBLIC_ODOO_API_HOST + url, - headers: { Authorization: token, ...headers }, - }; - - if (typeof auth === 'object' && 'token' in auth) { - axiosParameter.headers = { - ...axiosParameter.headers, - Token: auth.token, - }; - } - - if (method.toUpperCase() === 'POST') { - axiosParameter.headers = { - ...axiosParameter.headers, - 'Content-Type': 'application/x-www-form-urlencoded', - }; - } - - if (Object.keys(data).length > 0) { - axiosParameter.data = new URLSearchParams( - Object.entries(data) - ).toString(); - } - - let res = await axios(axiosParameter); - const authResponse: AuthApiProps = res.data; - - if ( - authResponse.status.code == 401 && - connectionAttempt < maxConnectionAttempt - ) { - await renewToken(); - return odooApi(method, url, data, headers); - } - - return authResponse.result || null; - } catch (error) { - console.log(error); - return null; - } -}; - -export default odooApi; diff --git a/src-migrate/common/libs/parse b/src-migrate/common/libs/parse deleted file mode 100644 index e69de29b..00000000 diff --git a/src-migrate/common/stores/useRegisterStore.ts b/src-migrate/common/stores/useRegisterStore.ts deleted file mode 100644 index 90ce8a2b..00000000 --- a/src-migrate/common/stores/useRegisterStore.ts +++ /dev/null @@ -1,60 +0,0 @@ -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 })), -})); diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Black.woff b/src-migrate/common/styles/fonts/Inter/Inter-Black.woff deleted file mode 100644 index a18593a0..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Black.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Black.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Black.woff2 deleted file mode 100644 index 68f64c9e..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Black.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff deleted file mode 100644 index b6b01943..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2 deleted file mode 100644 index 1c9c7ca8..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff b/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff deleted file mode 100644 index eaf3d4bf..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2 deleted file mode 100644 index 2846f29c..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff deleted file mode 100644 index 32750761..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2 deleted file mode 100644 index 0b1fe8e1..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff deleted file mode 100644 index c2c17ede..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2 deleted file mode 100644 index c24c2bdc..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff deleted file mode 100644 index c42f7052..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 deleted file mode 100644 index 4a81dc79..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff deleted file mode 100644 index d0de5f39..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2 deleted file mode 100644 index f2ea706f..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff deleted file mode 100644 index 81f1a28e..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 deleted file mode 100644 index 9af717ba..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff b/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff deleted file mode 100644 index a806b382..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2 deleted file mode 100644 index a619fc54..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Light.woff b/src-migrate/common/styles/fonts/Inter/Inter-Light.woff deleted file mode 100644 index c496464d..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Light.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Light.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Light.woff2 deleted file mode 100644 index bc4be665..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Light.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff deleted file mode 100644 index f84a9de3..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2 deleted file mode 100644 index 842b2dfc..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff b/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff deleted file mode 100644 index d546843f..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2 deleted file mode 100644 index f92498a2..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff deleted file mode 100644 index 459a6568..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2 deleted file mode 100644 index 0e3019f4..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff b/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff deleted file mode 100644 index 62d3a618..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2 deleted file mode 100644 index 6c2b6893..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff b/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff deleted file mode 100644 index a815f43a..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2 deleted file mode 100644 index 611e90c9..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff deleted file mode 100644 index 909e43a9..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 deleted file mode 100644 index 545685bd..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff b/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff deleted file mode 100644 index 62bc58cd..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2 deleted file mode 100644 index abbc3a5c..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff b/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff deleted file mode 100644 index 700a7f06..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2 deleted file mode 100644 index ab0b2002..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2 deleted file mode 100644 index b826d5af..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2 b/src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2 deleted file mode 100644 index 6a256a06..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/Inter.var.woff2 b/src-migrate/common/styles/fonts/Inter/Inter.var.woff2 deleted file mode 100644 index 365eedc5..00000000 Binary files a/src-migrate/common/styles/fonts/Inter/Inter.var.woff2 and /dev/null differ diff --git a/src-migrate/common/styles/fonts/Inter/inter.css b/src-migrate/common/styles/fonts/Inter/inter.css deleted file mode 100644 index de6ce273..00000000 --- a/src-migrate/common/styles/fonts/Inter/inter.css +++ /dev/null @@ -1,199 +0,0 @@ -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url('Inter-Thin.woff2?v=3.19') format('woff2'), - url('Inter-Thin.woff?v=3.19') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 100; - font-display: swap; - src: url('Inter-ThinItalic.woff2?v=3.19') format('woff2'), - url('Inter-ThinItalic.woff?v=3.19') format('woff'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 200; - font-display: swap; - src: url('Inter-ExtraLight.woff2?v=3.19') format('woff2'), - url('Inter-ExtraLight.woff?v=3.19') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 200; - font-display: swap; - src: url('Inter-ExtraLightItalic.woff2?v=3.19') format('woff2'), - url('Inter-ExtraLightItalic.woff?v=3.19') format('woff'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url('Inter-Light.woff2?v=3.19') format('woff2'), - url('Inter-Light.woff?v=3.19') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url('Inter-LightItalic.woff2?v=3.19') format('woff2'), - url('Inter-LightItalic.woff?v=3.19') format('woff'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url('Inter-Regular.woff2?v=3.19') format('woff2'), - url('Inter-Regular.woff?v=3.19') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url('Inter-Italic.woff2?v=3.19') format('woff2'), - url('Inter-Italic.woff?v=3.19') format('woff'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url('Inter-Medium.woff2?v=3.19') format('woff2'), - url('Inter-Medium.woff?v=3.19') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url('Inter-MediumItalic.woff2?v=3.19') format('woff2'), - url('Inter-MediumItalic.woff?v=3.19') format('woff'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url('Inter-SemiBold.woff2?v=3.19') format('woff2'), - url('Inter-SemiBold.woff?v=3.19') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url('Inter-SemiBoldItalic.woff2?v=3.19') format('woff2'), - url('Inter-SemiBoldItalic.woff?v=3.19') format('woff'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url('Inter-Bold.woff2?v=3.19') format('woff2'), - url('Inter-Bold.woff?v=3.19') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url('Inter-BoldItalic.woff2?v=3.19') format('woff2'), - url('Inter-BoldItalic.woff?v=3.19') format('woff'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url('Inter-ExtraBold.woff2?v=3.19') format('woff2'), - url('Inter-ExtraBold.woff?v=3.19') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url('Inter-ExtraBoldItalic.woff2?v=3.19') format('woff2'), - url('Inter-ExtraBoldItalic.woff?v=3.19') format('woff'); -} - -@font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 900; - font-display: swap; - src: url('Inter-Black.woff2?v=3.19') format('woff2'), - url('Inter-Black.woff?v=3.19') format('woff'); -} -@font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 900; - font-display: swap; - src: url('Inter-BlackItalic.woff2?v=3.19') format('woff2'), - url('Inter-BlackItalic.woff?v=3.19') format('woff'); -} - -/* ------------------------------------------------------- -Variable font. -Usage: - - html { font-family: 'Inter', sans-serif; } - @supports (font-variation-settings: normal) { - html { font-family: 'Inter var', sans-serif; } - } -*/ -@font-face { - font-family: 'Inter var'; - font-weight: 100 900; - font-display: swap; - font-style: normal; - font-named-instance: 'Regular'; - src: url('Inter-roman.var.woff2?v=3.19') format('woff2'); -} -@font-face { - font-family: 'Inter var'; - font-weight: 100 900; - font-display: swap; - font-style: italic; - font-named-instance: 'Italic'; - src: url('Inter-italic.var.woff2?v=3.19') format('woff2'); -} - -/* -------------------------------------------------------------------------- -[EXPERIMENTAL] Multi-axis, single variable font. - -Slant axis is not yet widely supported (as of February 2019) and thus this -multi-axis single variable font is opt-in rather than the default. - -When using this, you will probably need to set font-variation-settings -explicitly, e.g. - - * { font-variation-settings: "slnt" 0deg } - .italic { font-variation-settings: "slnt" 10deg } - -*/ -@font-face { - font-family: 'Inter var experimental'; - font-weight: 100 900; - font-display: swap; - font-style: oblique 0deg 10deg; - src: url('Inter.var.woff2?v=3.19') format('woff2'); -} diff --git a/src-migrate/common/styles/globals.css b/src-migrate/common/styles/globals.css deleted file mode 100644 index ea20b247..00000000 --- a/src-migrate/common/styles/globals.css +++ /dev/null @@ -1,674 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -* { - -webkit-tap-highlight-color: transparent; -} - -html, -body { - @apply w-screen - text-body-2 - text-gray_r-12 - bg-gray_r-1 - overflow-x-clip; -} - -#__next main { - @apply min-h-screen; -} - -button { - @apply block; -} - -@layer base { - input[type='number']::-webkit-inner-spin-button, - input[type='number']::-webkit-outer-spin-button { - -webkit-appearance: none; - margin: 0; - } - - input[type='number'] { - -moz-appearance: textfield; - } -} - -@layer components { - .badge-red, - .badge-solid-red, - .badge-gray, - .badge-yellow, - .badge-blue, - .badge-green, - .badge-solid-green { - @apply text-[11px] - leading-none - font-medium - px-1 - py-1 - rounded - w-fit; - } - - .badge-red { - @apply bg-danger-100 - text-danger-600; - } - - .badge-solid-red { - @apply bg-danger-500 - text-white; - } - - .badge-gray { - @apply bg-gray_r-5 - text-gray_r-10; - } - - .badge-yellow { - @apply bg-warning-500 - text-warning-900; - } - - .badge-blue { - @apply bg-blue-200 - text-blue-600; - } - - .badge-green { - @apply bg-success-100 - text-success-600; - } - - .badge-solid-green { - @apply bg-success-500 - text-white; - } - - .form-label { - @apply font-medium - block; - } - - .form-input { - @apply p-3 - rounded - border - text-gray_r-12 - border-gray_r-7 - !bg-white - bg-transparent - w-full - leading-none - focus:outline-none - focus:border-warning-500 - disabled:bg-gray_r-5; - } - - .form-input[aria-invalid] { - @apply border-danger-500 - focus:border-danger-500; - } - - .form-input[type='file'] { - @apply py-2; - } - - .btn-yellow, - .btn-light, - .btn-red, - .btn-solid-red { - @apply block - w-fit - py-3 - px-6 - rounded - border - text-center - font-medium - ease-linear - duration-150; - } - - .btn-yellow { - @apply bg-warning-500 - border-warning-500 - hover:bg-warning-500/80 - disabled:text-gray_r-10 - disabled:bg-warning-200 - disabled:border-warning-200; - } - - .btn-red { - @apply bg-danger-100 - border-danger-300 - text-danger-500 - disabled:text-danger-400 - disabled:bg-danger-200; - } - - .btn-solid-red { - @apply bg-danger-500 - border-danger-500 - text-gray_r-1 - hover:bg-danger-500/80 - disabled:text-gray_r-1 - disabled:bg-danger-200 - disabled:border-danger-200; - } - - .btn-light { - @apply bg-gray_r-3 - border-gray_r-6 - disabled:text-gray_r-10 - disabled:bg-gray_r-6; - } - - .product-card { - @apply w-full - h-full - border - border-gray_r-3 - shadow - bg-white - rounded - relative - flex - flex-col; - } - - .product-card__image { - @apply w-full - h-[160px] - object-contain - object-center - border-b - border-gray_r-6; - } - - .product-card__content { - @apply p-2 - pb-3 - flex-1; - } - - .product-card__title { - @apply text-caption-1 - text-gray_r-12 - leading-5; - } - - .product-card__brand { - @apply text-caption-1 - mb-1 - block; - } - - .product__description { - @apply text-gray_r-12/90; - } - - .product__description br { - @apply block my-1; - } - - .product__description b { - @apply font-semibold; - } -} - -@layer utilities { - .wrap-line-ellipsis-1, - .wrap-line-ellipsis-2, - .wrap-line-ellipsis-3 { - display: -webkit-box; - -webkit-box-orient: vertical; - overflow: hidden; - text-overflow: ellipsis; - } - - .wrap-line-ellipsis-1 { - -webkit-line-clamp: 1; - } - - .wrap-line-ellipsis-2 { - -webkit-line-clamp: 2; - } - - .wrap-line-ellipsis-3 { - -webkit-line-clamp: 3; - } -} - -.menu-wrapper { - @apply fixed - top-0 - left-0 - bg-white - w-[80%] - h-full - z-[60] - overflow-y-auto - translate-x-[-100%] - ease-linear - duration-150; -} - -.menu-wrapper.active { - @apply translate-x-0; -} - -.overlay { - @apply fixed - top-0 - left-0 - w-full - h-full - z-[55] - bg-gray_r-12/40; -} - -.sticky-header { - @apply px-4 - py-3 - bg-gray_r-1/90 - backdrop-blur-lg - sticky - top-0 - border-b - border-gray_r-7 - z-50; -} - -.content-container { - @apply max-w-full - overflow-x-hidden; -} - -#indoteknik_toast { - @apply fixed - bottom-4 - translate-y-[200%] - left-[50%] - translate-x-[-50%] - z-[100] - flex - items-center - p-4 - mb-4 - w-[90%] - text-gray-500 - bg-white - border - border-gray-300 - rounded-lg - shadow - ease-linear - duration-300; -} - -#indoteknik_toast.active { - @apply translate-y-0; -} - -.category-menu { - @apply hidden; -} - -.swiper-slide { - @apply !h-auto; -} - -.lazy-load-image-background { - @apply !block - w-full; -} - -.swiper-pagination-bullet-active { - @apply !bg-danger-500; -} - -.pagination { - @apply flex - justify-center - gap-x-1; -} - -.pagination-item { - @apply p-1 - flex - justify-center - items-center - w-10 - rounded - ease-linear - duration-150 - border - border-gray_r-6 - bg-gray_r-3 - hover:bg-gray_r-5 - text-gray_r-12; -} - -.pagination-item--active { - @apply border-warning-500 - bg-warning-500 - hover:bg-warning-500; -} - -.pagination-dots { - @apply p-1 - flex - justify-center - items-end - w-10 - rounded - ease-linear - bg-gray_r-3 - text-caption-2; -} - -.idt-transition { - @apply transition-all - ease-out - duration-200; -} - -.form-select__placeholder { - @apply !text-gray_r-9; -} - -.form-select__control { - @apply !shadow-none - !border-gray_r-7; -} - -.form-select__control--menu-is-open { - @apply !border-warning-500; -} - -.table-specification { - @apply max-h-[500px] overflow-y-auto border border-gray_r-6; -} - -.table-specification > table { - @apply table-auto - border-collapse - w-full; -} - -.table-specification > table > thead > tr > th:last-child { - @apply w-3/12; -} - -.table-specification > table > thead { - @apply sticky top-0 border-b; -} - -.table-specification > table > thead > tr { - @apply bg-gray_r-1/80 backdrop-blur-lg; -} - -.table-specification th { - @apply font-semibold; -} - -.table-specification th, -.table-specification td { - @apply p-4 text-center; -} - -.table-specification > table > tbody > tr { - @apply odd:bg-gray_r-3 even:bg-gray_r-1; -} - -.table-cart, -.table-checkout { - @apply w-full - table-auto - border-collapse; -} - -.table-cart tr, -.table-checkout tr { - @apply border-y - border-gray_r-6 - first:border-t-0; -} - -.table-cart th, -.table-cart td, -.table-checkout th, -.table-checkout td { - @apply py-4 - px-3 - text-center - text-gray_r-12/90; -} - -.table-cart th, -.table-cart td { - @apply first:w-12; -} - -.table-cart th, -.table-checkout th { - @apply font-medium; -} - -.table-data { - @apply w-full - table-auto - border-collapse; -} - -.table-data thead tr { - @apply bg-gray_r-3; -} - -.table-data thead th { - @apply font-medium whitespace-nowrap; -} - -.table-data thead th, -.table-data tbody td { - @apply px-3 - py-4 - text-center; -} - -.table-data tbody td { - @apply text-gray_r-12/90; -} - -.table-data tbody tr { - @apply border-b - border-gray_r-6; -} - -.navbar-user-dropdown-button { - @apply flex-1 - flex - gap-x-2 - p-4 - items-center - bg-danger-500 - font-medium - !text-gray_r-1 - rounded-none - rounded-t-xl; -} - -.navbar-user-dropdown-button span { - @apply line-clamp-1; -} - -.navbar-user-dropdown-wrapper a, -.navbar-user-dropdown-wrapper button { - @apply text-gray_r-12/80 hover:bg-gray_r-5 font-medium py-2 px-4 w-full text-left; -} - -.navbar-user-dropdown { - @apply bg-white - border - border-gray_r-6 - py-2 - w-full - shadow; -} - -.category-mega-box-wrapper, -.navbar-user-dropdown-wrapper { - @apply absolute - opacity-0 - left-0 - top-[125%] - flex - w-full - z-10 - transition-all - ease-in - duration-200 - pointer-events-none - text-left; -} - -.category-mega-box-wrapper.show, -.navbar-user-dropdown-button:hover ~ .navbar-user-dropdown-wrapper, -.navbar-user-dropdown-wrapper:hover { - @apply top-[100%] - opacity-100 - pointer-events-auto; -} - -.category-mega-box { - @apply relative - py-2 - border - border-t-0 - bg-white - border-gray_r-6 - h-full - w-full; -} - -.category-mega-box > div { - @apply text-gray_r-12/80; -} - -.category-mega-box > div:hover .category-mega-box__parent { - @apply bg-gray_r-5; -} - -.category-mega-box > div:hover .category-mega-box__child-wrapper { - @apply opacity-100 - top-0 - pointer-events-auto; -} - -.category-mega-box .category-mega-box__parent { - @apply py-2.5 - px-4 - text-gray_r-12/80 - font-normal; -} - -.category-mega-box__child-wrapper { - @apply absolute - left-[100%] - top-12 - w-[40vw] - bg-gray_r-1/90 - backdrop-blur-md - border - border-gray_r-6 - p-6 - opacity-0 - h-full - transition-all - ease-in - duration-200 - pointer-events-none - z-50; -} - -.category-mega-box__child-one { - @apply text-gray_r-12/80 - hover:text-danger-500 - transition-colors - ease-linear - duration-100 - font-semibold; -} - -.category-mega-box__child-two { - @apply text-gray_r-12/80 - hover:text-danger-500 - transition-colors - ease-linear - duration-100 - font-normal; -} - -@keyframes page-loader { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} - -.page-loader { - animation-name: page-loader; - animation-duration: 1000ms; - animation-delay: 50ms; - animation-timing-function: ease-in-out; - animation-iteration-count: infinite; -} - -@keyframes shake { - 0% { - transform: translateX(0); - } - 10%, - 90% { - transform: translateX(-10px); - } - 20%, - 80% { - transform: translateX(10px); - } - 30%, - 50%, - 70% { - transform: translateX(-10px); - } - 40%, - 60% { - transform: translateX(10px); - } - 100% { - transform: translateX(0); - } -} - -.blink-color-flash-sale { - @apply text-body-1 md:text-title-sm; - transform: rotateY(180deg) rotateZ(120deg); - animation-name: blink-color-flash-sale; - animation-duration: 300ms; - animation-iteration-count: infinite; - animation-timing-function: linear; -} - -@keyframes blink-color-flash-sale { - from { - @apply text-danger-500; - } - to { - @apply text-warning-500; - } -} diff --git a/src-migrate/common/types/auth.ts b/src-migrate/common/types/auth.ts deleted file mode 100644 index 65fd06c7..00000000 --- a/src-migrate/common/types/auth.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { registerSchema } from '../validations/auth'; -import { OdooApiProps } from './odoo'; -import { z } from 'zod'; - -export type AuthProps = { - id: number; - parent_id: number; - parent_name: string; - partner_id: number; - name: string; - email: string; - phone: string; - mobile: string; - external: boolean; - company: boolean; - pricelist: string | null; - token: string; -}; - -export type AuthApiProps = OdooApiProps & { result: AuthProps }; - -export type RegisterProps = z.infer; - -export type RegisterResApiProps = { - register: boolean; - reason: 'EMAIL_USED' | 'NOT_ACTIVE' | null; -}; - -type ActivationResProps = { - activation: boolean; - user: AuthProps | null; -}; - -export type ActivationTokenProps = { - token: string; -}; - -export type ActivationTokenResApiProps = ActivationResProps & { - reason: 'INVALID_TOKEN' | null; -}; - -export type ActivationOtpProps = { - email: string; - otp: string; -}; - -export type ActivationOtpResApiProps = ActivationResProps & { - reason: 'INVALID_OTP' | null; -}; - -export type ActivationReqProps = { - email: string; -}; - -export type ActivationReqResApiProps = { - activation_request: boolean; - reason: 'NOT_FOUND' | 'ACTIVE' | null; -}; diff --git a/src-migrate/common/types/cart.ts b/src-migrate/common/types/cart.ts deleted file mode 100644 index 3aceeac4..00000000 --- a/src-migrate/common/types/cart.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { CategoryPromo } from "./promotion"; - -type Price = { - price: number; - discount_percentage: number; - price_discount: number; -}; - -export type CartProduct = { - id: number; - image: string; - display_name: string; - name: string; - code: string; - price: Price; - qty: number; - weight: number; - package_weight: number; -}; - -export type CartItem = { - cart_id: number; - quantity: number; - selected: boolean; - can_buy: boolean; - cart_type: 'product' | 'promotion'; - id: number; - name: string; - stock: number; - weight: number; - attributes: string[]; - parent: { - id: number; - name: string; - image: string; - }; - price: Price; - manufacture: { - id: number; - name: string; - }; - has_flashsale: boolean; - subtotal: number; - - code?: string; - - image?: string; - remaining_time?: number; - promotion_type?: { - value?: CategoryPromo; - label?: string; - }; - limit_qty?: { - all?: number; - user?: number; - transaction?: number; - }; - remaining_qty?: { - all?: number; - user?: number; - transaction?: number; - }; - used_percentage?: number; - products?: CartProduct[]; - free_products?: CartProduct[]; - package_price?: number; -}; - -export type CartProps = { - product_total: number; - products: CartItem[]; -}; diff --git a/src-migrate/common/types/checkout.ts b/src-migrate/common/types/checkout.ts deleted file mode 100644 index dc1365d8..00000000 --- a/src-migrate/common/types/checkout.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { CartItem } from './cart'; - -export interface ICheckout { - total_purchase: number; - total_discount: number; - discount_voucher: number; - subtotal: number; - tax: number; - grand_total: number; - total_weight: { - kg: number; - g: number; - }; - has_product_without_weight: boolean; - products: CartItem[]; -} diff --git a/src-migrate/common/types/nav.ts b/src-migrate/common/types/nav.ts deleted file mode 100644 index ba97b1bf..00000000 --- a/src-migrate/common/types/nav.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type SecondaryNavItemProps = { - label: string - href: string -} diff --git a/src-migrate/common/types/odoo.ts b/src-migrate/common/types/odoo.ts deleted file mode 100644 index b34bc667..00000000 --- a/src-migrate/common/types/odoo.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type OdooApiProps = { - status: { - code: number; - description: string; - }; -}; diff --git a/src-migrate/common/types/pageContent.ts b/src-migrate/common/types/pageContent.ts deleted file mode 100644 index 4361deb7..00000000 --- a/src-migrate/common/types/pageContent.ts +++ /dev/null @@ -1,5 +0,0 @@ -export type PageContentProps = { - id: number; - url_path: string; - content: string; -} | null; diff --git a/src-migrate/common/types/productVariant.ts b/src-migrate/common/types/productVariant.ts deleted file mode 100644 index c4aa9534..00000000 --- a/src-migrate/common/types/productVariant.ts +++ /dev/null @@ -1,13 +0,0 @@ -export interface IProductVariant { - id: number; - parent_id: number; - display_name: string; - image: string; - name: string; - default_code: string; - price: { - price: number; - discount_percentage: number; - price_discount: number; - }; -} diff --git a/src-migrate/common/types/promotion.ts b/src-migrate/common/types/promotion.ts deleted file mode 100644 index 1f8316cf..00000000 --- a/src-migrate/common/types/promotion.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { IProductVariant } from './productVariant'; - -export interface IPromotion { - id: number; - program_id: number; - name: string; - type: { - value: CategoryPromo; - label: string; - }; - limit: number; - limit_user: number; - limit_trx: number; - price: number; - total_qty: number; - products: { - product_id: number; - qty: number; - }[]; - free_products: { - product_id: number; - qty: number; - }[]; -} - -export interface IProductVariantPromo extends IProductVariant { - qty: number; -} - -export type CategoryPromo = 'bundling' | 'discount_loading' | 'merchandise'; - -export interface ICategoryPromo { - value: CategoryPromo; - label: string; -} diff --git a/src-migrate/common/types/promotionProgram.ts b/src-migrate/common/types/promotionProgram.ts deleted file mode 100644 index 205884b6..00000000 --- a/src-migrate/common/types/promotionProgram.ts +++ /dev/null @@ -1,8 +0,0 @@ -export type IPromotionProgram = { - id: number; - name: string; - start_time: string; - end_time: string; - applies_to: string; - time_left: number; -}; diff --git a/src-migrate/common/types/solr.ts b/src-migrate/common/types/solr.ts deleted file mode 100644 index d231c305..00000000 --- a/src-migrate/common/types/solr.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type SolrResponse = { - response: { - numFound: number; - start: number; - docs: T; - }; -}; diff --git a/src-migrate/common/validations/auth.ts b/src-migrate/common/validations/auth.ts deleted file mode 100644 index 78fc5e71..00000000 --- a/src-migrate/common/validations/auth.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { z } from 'zod'; - -export const registerSchema = z.object({ - name: z.string().min(1, { message: 'Nama harus diisi' }), - email: z - .string() - .min(1, { message: 'Email harus diisi' }) - .email({ message: 'Email harus menggunakan format example@mail.com' }), - password: z.string().min(6, { message: 'Password minimal 6 karakter' }), - company: z.string().optional(), - phone: z - .string() - .min(1, { message: 'Nomor telepon harus diisi' }) - .refine((val) => /^\d{10,12}$/.test(val), { - message: 'Format nomor telepon tidak valid, contoh: 081234567890', - }), -}); diff --git a/src-migrate/components/seo.tsx b/src-migrate/components/seo.tsx new file mode 100644 index 00000000..1e78ed4d --- /dev/null +++ b/src-migrate/components/seo.tsx @@ -0,0 +1,32 @@ +import { useRouter } from 'next/router' +import React from 'react' +import { NextSeo } from "next-seo" +import { MetaTag, NextSeoProps } from 'next-seo/lib/types'; + +export const Seo = (props: NextSeoProps) => { + const router = useRouter() + + const additionalMetaTags: MetaTag[] = [ + { + property: 'fb:app_id', + content: '270830718811' + }, + { + property: 'fb:page_id', + content: '101759953569' + }, + ] + + if (!!props.additionalMetaTags) additionalMetaTags.push(...props.additionalMetaTags) + + return ( + + ) +} \ No newline at end of file diff --git a/src-migrate/components/ui/image.tsx b/src-migrate/components/ui/image.tsx new file mode 100644 index 00000000..a91b2a9d --- /dev/null +++ b/src-migrate/components/ui/image.tsx @@ -0,0 +1,46 @@ +import clsx from 'clsx'; +import NextImage, { ImageProps as NextImageProps } from 'next/image'; +import { useState } from 'react'; + +import clsxm from '~/libs/clsxm'; + +type ImageProps = { + rounded?: string; + classNames?: { + wrapper: string + } +} & NextImageProps; + +const Image = (props: ImageProps) => { + const { alt, src, className, classNames, rounded, ...rest } = props; + const [isLoading, setLoading] = useState(true); + + return ( +
+ setLoading(false)} + {...rest} + /> +
+ ); +}; +export default Image; \ No newline at end of file diff --git a/src-migrate/components/ui/modal.tsx b/src-migrate/components/ui/modal.tsx new file mode 100644 index 00000000..34e1d1c3 --- /dev/null +++ b/src-migrate/components/ui/modal.tsx @@ -0,0 +1,87 @@ +import { useEffect, useState } from "react"; +import ReactDOM from "react-dom"; +import { useRouter } from "next/router"; +import { AnimatePresence, motion } from "framer-motion" +import { useWindowSize } from "usehooks-ts"; +import { XMarkIcon } from "@heroicons/react/24/outline"; +import clsxm from "~/libs/clsxm"; + +export interface ModalProps { + children: React.ReactNode + active: boolean + title?: string + close?: () => void, + className?: string, + mode?: "mobile" | "desktop" +} + +export const Modal = ({ + children, + active = false, + title, + close, + className, + mode +}: ModalProps) => { + const router = useRouter() + const { width } = useWindowSize() + const [rendered, setRendered] = useState(false) + + mode = mode || width >= 768 ? "desktop" : "mobile" + + useEffect(() => { + setRendered(true) + }, []) + + const modalClassNames = clsxm( + "fixed bg-white max-h-[80vh] overflow-auto p-4 pt-0 z-[60] border-gray_r-6", + { + "left-1/2 -translate-x-1/2 translate-y-1/2 bottom-1/2 w-11/12 md:w-[500px] border rounded-xl": mode === 'desktop', + "left-0 w-full border-t bottom-0 rounded-t-xl": mode === 'mobile' + }, + className + ) + + const variant = { + initial: { bottom: mode === 'desktop' ? '45%' : '-100%', opacity: 0 }, + animate: { bottom: mode === 'desktop' ? '50%' : 0, opacity: 1 }, + exit: { bottom: mode === 'desktop' ? '55%' : '-100%', opacity: 0 }, + transition: { ease: 'linear', duration: 0.25 } + } + + return rendered && ReactDOM.createPortal( + + {active && ( + + )} + + {active && ( + +
+
+ {title} +
+ {close && ( + + )} +
+ + {children} +
+ )} + +
, + document.querySelector('body')! + ) +} \ No newline at end of file diff --git a/src-migrate/components/ui/re-captcha.tsx b/src-migrate/components/ui/re-captcha.tsx new file mode 100644 index 00000000..e31aa1e3 --- /dev/null +++ b/src-migrate/components/ui/re-captcha.tsx @@ -0,0 +1,15 @@ +import ReCAPTCHA, { ReCAPTCHAProps } from "react-google-recaptcha" + +const GOOGLE_RECAPTCHA_KEY = process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE || '' + +export interface ReCaptchaProps extends Omit { + sitekey?: string; +} + +export const ReCaptcha = (props: ReCaptchaProps) => { + const { sitekey, ...rest } = props + + return ( + + ) +} diff --git a/src-migrate/constants/menu.ts b/src-migrate/constants/menu.ts new file mode 100644 index 00000000..d1adebca --- /dev/null +++ b/src-migrate/constants/menu.ts @@ -0,0 +1,20 @@ +import { SecondaryNavItemProps } from '~/types/nav'; + +export const SECONDARY_MENU_ITEMS: SecondaryNavItemProps[] = [ + { + label: 'Semua Brand', + href: '/shop/brands', + }, + { + label: 'Ready Stock', + href: '/shop/search?orderBy=stock', + }, + { + label: 'Blog Indoteknik', + href: 'https://blog.indoteknik.com/', + }, + { + label: 'Indoteknik TV', + href: '/video', + }, +]; diff --git a/src-migrate/libs/auth.ts b/src-migrate/libs/auth.ts new file mode 100644 index 00000000..86ce26e1 --- /dev/null +++ b/src-migrate/libs/auth.ts @@ -0,0 +1,26 @@ +import { deleteCookie, getCookie, setCookie } from 'cookies-next'; +import { AuthProps } from '~/types/auth'; + +const COOKIE_KEY = 'auth'; + +export const getAuth = (): AuthProps | boolean => { + const auth = getCookie(COOKIE_KEY); + + if (typeof auth === 'string') { + return JSON.parse(auth); + } + + return false; +}; + +export const setAuth = (user: AuthProps): boolean => { + setCookie(COOKIE_KEY, JSON.stringify(user)); + + return true; +}; + +export const deleteAuth = (): boolean => { + deleteCookie(COOKIE_KEY); + + return true; +}; diff --git a/src-migrate/libs/clsxm.ts b/src-migrate/libs/clsxm.ts new file mode 100644 index 00000000..0fc10317 --- /dev/null +++ b/src-migrate/libs/clsxm.ts @@ -0,0 +1,6 @@ +import clsx, { ClassValue } from 'clsx'; +import { twMerge } from 'tw-merge'; + +export default function clsxm(...classes: ClassValue[]) { + return twMerge(clsx(...classes)); +} diff --git a/src-migrate/libs/formatCurrency.ts b/src-migrate/libs/formatCurrency.ts new file mode 100644 index 00000000..41db4a6f --- /dev/null +++ b/src-migrate/libs/formatCurrency.ts @@ -0,0 +1,5 @@ +const formatCurrency = (value: number) => { + return value.toLocaleString('id-ID'); +}; + +export default formatCurrency; diff --git a/src-migrate/libs/formatNumber.ts b/src-migrate/libs/formatNumber.ts new file mode 100644 index 00000000..da243418 --- /dev/null +++ b/src-migrate/libs/formatNumber.ts @@ -0,0 +1,8 @@ +export const formatToShortText = (number: number) => { + if (number > 1000) { + return `${Math.floor(number / 1000)}rb+`; + } else if (number > 100) { + return `${Math.floor(number / 100) * 100}+`; + } + return number.toString(); +}; diff --git a/src-migrate/libs/odooApi.ts b/src-migrate/libs/odooApi.ts new file mode 100644 index 00000000..9482542b --- /dev/null +++ b/src-migrate/libs/odooApi.ts @@ -0,0 +1,81 @@ +import axios, { AxiosRequestConfig, Method } from 'axios'; +import { getCookie, setCookie } from 'cookies-next'; +import { getAuth } from './auth'; +import { AuthApiProps } from '~/types/auth'; + +const ODOO_HOST = process.env.NEXT_PUBLIC_ODOO_API_HOST as string; + +const renewToken = async () => { + let token = await axios.get(`${ODOO_HOST}/api/token`); + setCookie('token', token.data.result); + + return token.data.result; +}; + +const getToken = async () => { + let token = getCookie('token'); + if (token == undefined) token = await renewToken(); + + return token; +}; + +const maxConnectionAttempt = 15; +let connectionAttempt = 0; + +const odooApi = async ( + method: Method, + url: string, + data = {}, + headers = {} +): Promise => { + connectionAttempt++; + + try { + let token = await getToken(); + const auth = getAuth(); + + let axiosParameter: AxiosRequestConfig = { + method, + url: process.env.NEXT_PUBLIC_ODOO_API_HOST + url, + headers: { Authorization: token, ...headers }, + }; + + if (typeof auth === 'object' && 'token' in auth) { + axiosParameter.headers = { + ...axiosParameter.headers, + Token: auth.token, + }; + } + + if (method.toUpperCase() === 'POST') { + axiosParameter.headers = { + ...axiosParameter.headers, + 'Content-Type': 'application/x-www-form-urlencoded', + }; + } + + if (Object.keys(data).length > 0) { + axiosParameter.data = new URLSearchParams( + Object.entries(data) + ).toString(); + } + + let res = await axios(axiosParameter); + const authResponse: AuthApiProps = res.data; + + if ( + authResponse.status.code == 401 && + connectionAttempt < maxConnectionAttempt + ) { + await renewToken(); + return odooApi(method, url, data, headers); + } + + return authResponse.result || null; + } catch (error) { + console.log(error); + return null; + } +}; + +export default odooApi; diff --git a/src-migrate/libs/slug.ts b/src-migrate/libs/slug.ts new file mode 100644 index 00000000..5ab3b3dd --- /dev/null +++ b/src-migrate/libs/slug.ts @@ -0,0 +1,34 @@ +import { toTitleCase } from './toTitleCase'; + +export const createSlug = ( + prefix: string, + name: string, + id: string, + withHost = false +) => { + const cleanName = name + .trim() + .replace(new RegExp(/[^A-Za-z0-9]/, 'g'), '-') + .toLowerCase(); + + let slug = `${cleanName}-${id}`; + const splitSlug = slug.split('-'); + const filterSlug = splitSlug.filter((x) => x !== ''); + + slug = `${prefix}${filterSlug.join('-')}`; + + if (withHost) slug = process.env.NEXT_PUBLIC_SELF_HOST + slug; + + return slug; +}; + +export const getIdFromSlug = (slug: string) => { + let id = slug.split('-'); + return id[id.length - 1]; +}; + +export const getNameFromSlug = (slug: string) => { + let name = slug.split('-'); + name.pop(); + return toTitleCase(name.join(' ')); +}; diff --git a/src-migrate/libs/toTitleCase.ts b/src-migrate/libs/toTitleCase.ts new file mode 100644 index 00000000..dad66813 --- /dev/null +++ b/src-migrate/libs/toTitleCase.ts @@ -0,0 +1,5 @@ +export const toTitleCase = (val: string) => { + return val.replace(/\w\S*/g, function (txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); +}; 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 })), +})); diff --git a/src-migrate/pages/_app.tsx b/src-migrate/pages/_app.tsx index 2dc82559..36640c04 100644 --- a/src-migrate/pages/_app.tsx +++ b/src-migrate/pages/_app.tsx @@ -1,5 +1,5 @@ -import '~/common/styles/fonts/Inter/inter.css' -import '~/common/styles/globals.css' +import '~/styles/fonts/Inter/inter.css' +import '~/styles/globals.css' import type { AppProps } from "next/app" export default function MyApp({ Component, pageProps }: AppProps) { diff --git a/src-migrate/pages/api/product-variant/[id].tsx b/src-migrate/pages/api/product-variant/[id].tsx index b3bd4096..c25c10ac 100644 --- a/src-migrate/pages/api/product-variant/[id].tsx +++ b/src-migrate/pages/api/product-variant/[id].tsx @@ -1,5 +1,5 @@ import { NextApiRequest, NextApiResponse } from "next"; -import { SolrResponse } from "~/common/types/solr"; +import { SolrResponse } from "~/types/solr"; const SOLR_HOST = process.env.SOLR_HOST as string diff --git a/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx b/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx index 745f9944..50671afd 100644 --- a/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx +++ b/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx @@ -1,5 +1,5 @@ import { NextApiRequest, NextApiResponse } from "next"; -import { SolrResponse } from "~/common/types/solr"; +import { SolrResponse } from "~/types/solr"; const SOLR_HOST = process.env.SOLR_HOST as string diff --git a/src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx b/src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx index 0fe8fd1b..8153f346 100644 --- a/src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx +++ b/src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx @@ -1,5 +1,5 @@ import { NextApiRequest, NextApiResponse } from "next"; -import { SolrResponse } from "~/common/types/solr"; +import { SolrResponse } from "~/types/solr"; const SOLR_HOST = process.env.SOLR_HOST as string diff --git a/src-migrate/pages/api/promotion-program/[id].tsx b/src-migrate/pages/api/promotion-program/[id].tsx index ba716e85..c509b802 100644 --- a/src-migrate/pages/api/promotion-program/[id].tsx +++ b/src-migrate/pages/api/promotion-program/[id].tsx @@ -1,6 +1,5 @@ import { NextApiRequest, NextApiResponse } from "next"; -import { SolrResponse } from "~/common/types/solr"; -import moment from 'moment' +import { SolrResponse } from "~/types/solr"; const SOLR_HOST = process.env.SOLR_HOST as string diff --git a/src-migrate/pages/register.tsx b/src-migrate/pages/register.tsx index 1246c6f5..136eaa3b 100644 --- a/src-migrate/pages/register.tsx +++ b/src-migrate/pages/register.tsx @@ -1,7 +1,7 @@ import BasicLayout from "@/core/components/layouts/BasicLayout" import { useWindowSize } from "usehooks-ts" -import Seo from "~/common/components/elements/Seo" +import { Seo } from "~/components/seo" import Register from "~/modules/register" const RegisterPage = () => { diff --git a/src-migrate/pages/shop/cart.module.css b/src-migrate/pages/shop/cart.module.css deleted file mode 100644 index d523a55a..00000000 --- a/src-migrate/pages/shop/cart.module.css +++ /dev/null @@ -1,31 +0,0 @@ -.title { - @apply text-h-lg font-semibold; -} - -.content { - @apply flex flex-wrap; -} - -.item-wrapper { - @apply w-full md:w-3/4; -} - -.item-skeleton { - @apply grid grid-cols-1 gap-y-4; -} - -.items { - @apply flex flex-col gap-y-6 border-t border-gray-300 pt-6; -} - -.summary-wrapper { - @apply w-full md:w-1/4 md:pl-6 mt-6 md:mt-0; -} - -.summary { - @apply border border-gray-300 p-4 rounded-md sticky top-[180px]; -} - -.summary-buttons { - @apply grid grid-cols-2 gap-x-3 mt-6; -} diff --git a/src-migrate/pages/shop/cart.tsx b/src-migrate/pages/shop/cart.tsx deleted file mode 100644 index 5016c9b5..00000000 --- a/src-migrate/pages/shop/cart.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import style from './cart.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 '~/modules/cart/stores/useCartStore' - -import CartItem from '~/modules/cart/components/Item' -import CartSummary from '~/modules/cart/components/Summary' - -const CartPage = () => { - 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 CartPage \ No newline at end of file diff --git a/src-migrate/pages/shop/cart/cart.module.css b/src-migrate/pages/shop/cart/cart.module.css new file mode 100644 index 00000000..d523a55a --- /dev/null +++ b/src-migrate/pages/shop/cart/cart.module.css @@ -0,0 +1,31 @@ +.title { + @apply text-h-lg font-semibold; +} + +.content { + @apply flex flex-wrap; +} + +.item-wrapper { + @apply w-full md:w-3/4; +} + +.item-skeleton { + @apply grid grid-cols-1 gap-y-4; +} + +.items { + @apply flex flex-col gap-y-6 border-t border-gray-300 pt-6; +} + +.summary-wrapper { + @apply w-full md:w-1/4 md:pl-6 mt-6 md:mt-0; +} + +.summary { + @apply border border-gray-300 p-4 rounded-md sticky top-[180px]; +} + +.summary-buttons { + @apply grid grid-cols-2 gap-x-3 mt-6; +} diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx new file mode 100644 index 00000000..397852f9 --- /dev/null +++ b/src-migrate/pages/shop/cart/index.tsx @@ -0,0 +1,93 @@ +import style from './cart.module.css' + +import React, { useEffect, useMemo } from 'react' +import Link from 'next/link' +import { Button, Tooltip } from '@chakra-ui/react' + +import { getAuth } from '~/libs/auth' +import { useCartStore } from '~/modules/cart/stores/useCartStore' + +import CartItem from '~/modules/cart/components/Item' +import CartSummary from '~/modules/cart/components/Summary' + +const CartPage = () => { + 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 CartPage \ No newline at end of file diff --git a/src-migrate/pages/shop/product/[slug].tsx b/src-migrate/pages/shop/product/[slug].tsx new file mode 100644 index 00000000..883532ed --- /dev/null +++ b/src-migrate/pages/shop/product/[slug].tsx @@ -0,0 +1,73 @@ +import style from './product.module.css' + +import { GetServerSideProps, NextPage } from 'next' +import React from 'react' +import dynamic from 'next/dynamic' +import cookie from 'cookie' + +import { getProductById } from '~/services/product' +import { getIdFromSlug } from '~/libs/slug' +import { IProductDetail } from '~/types/product' + +import { Seo } from '~/components/seo' + +const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout'), { ssr: false }) +const ProductDetail = dynamic(() => import('~/modules/product-detail'), { ssr: false }) + +type PageProps = { + product: IProductDetail +} + +export const getServerSideProps: GetServerSideProps = (async (context) => { + const { slug } = context.query + const cookieString = context.req.headers.cookie; + const cookies = cookieString ? cookie.parse(cookieString) : {}; + const auth = cookies?.auth ? JSON.parse(cookies.auth) : {}; + const tier = auth?.pricelist || '' + + const productId = getIdFromSlug(slug as string) + + const product = await getProductById(productId, tier) + + if (!product) return { notFound: true } + + return { + props: { product } + } +}) + +const ProductDetailPage: NextPage = ({ product }) => { + return ( + + + +
+ +
+
+ ) +} + +export default ProductDetailPage \ No newline at end of file diff --git a/src-migrate/pages/shop/product/product.module.css b/src-migrate/pages/shop/product/product.module.css new file mode 100644 index 00000000..e69de29b diff --git a/src-migrate/services/auth.ts b/src-migrate/services/auth.ts index 1cc09c10..35ba290a 100644 --- a/src-migrate/services/auth.ts +++ b/src-migrate/services/auth.ts @@ -1,4 +1,4 @@ -import odooApi from '~/common/libs/odooApi'; +import odooApi from '~/libs/odooApi'; import { RegisterResApiProps, RegisterProps, @@ -8,7 +8,7 @@ import { ActivationOtpResApiProps, ActivationReqProps, ActivationReqResApiProps, -} from '~/common/types/auth'; +} from '~/types/auth'; const BASE_PATH = '/api/v1/user'; diff --git a/src-migrate/services/cart.ts b/src-migrate/services/cart.ts index b238be3d..73967073 100644 --- a/src-migrate/services/cart.ts +++ b/src-migrate/services/cart.ts @@ -1,4 +1,4 @@ -import odooApi from '~/common/libs/odooApi'; +import odooApi from '~/libs/odooApi'; export const getUserCart = async (userId: number) => { return await odooApi('GET', `/api/v1/user/${userId}/cart`); diff --git a/src-migrate/services/checkout.ts b/src-migrate/services/checkout.ts index 3dd1c8e8..3eff95a8 100644 --- a/src-migrate/services/checkout.ts +++ b/src-migrate/services/checkout.ts @@ -1,4 +1,4 @@ -import odooApi from '~/common/libs/odooApi'; +import odooApi from '~/libs/odooApi'; export const getUserCheckout = async (userId: number) => { return await odooApi('GET', `/api/v1/user/${userId}/sale_order/checkout`); diff --git a/src-migrate/services/pageContent.ts b/src-migrate/services/pageContent.ts index 16146059..516b4bed 100644 --- a/src-migrate/services/pageContent.ts +++ b/src-migrate/services/pageContent.ts @@ -1,4 +1,4 @@ -import odooApi from '~/common/libs/odooApi'; +import odooApi from '~/libs/odooApi'; export const getPageContent = async ({ path }: { path: string }) => { const params = new URLSearchParams({ url_path: path }); diff --git a/src-migrate/services/product.ts b/src-migrate/services/product.ts new file mode 100644 index 00000000..c9e93396 --- /dev/null +++ b/src-migrate/services/product.ts @@ -0,0 +1,59 @@ +import { IProduct, IProductDetail } from '~/types/product'; +import snakeCase from 'snakecase-keys'; + +const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST; + +export const getProductById = async ( + id: string, + tier: string +): Promise => { + const url = `${SELF_HOST}/api/shop/product-detail`; + const params = new URLSearchParams({ id, auth: tier }); + return await fetch(`${url}?${params.toString()}`) + .then((res) => res.json()) + .then((res) => { + if (res.length > 0) return snakeCase(res[0]) as IProductDetail; + + return null; + }); +}; + +export interface GetProductSimilarProps { + name: string; + except?: { + productId?: number; + manufactureId?: number; + }; + limit?: number; +} + +export interface GetProductSimilarRes { + products: IProduct[]; + num_found: number; + num_found_exact: boolean; + start: number; +} + +export const getProductSimilar = async ({ + name, + except, + limit = 30, +}: GetProductSimilarProps): Promise => { + const query = [ + `q=${name}`, + 'page=1', + 'orderBy=popular-weekly', + 'operation=OR', + 'priceFrom=1', + ]; + + if (except?.productId) query.push(`fq=-product_id_i:${except.productId}`); + if (except?.manufactureId) + query.push(`fq=-manufacture_id_i:${except.manufactureId}`); + + const url = `${SELF_HOST}/api/shop/search?${query.join('&')}`; + + return await fetch(url) + .then((res) => res.json()) + .then((res) => snakeCase(res.response)); +}; diff --git a/src-migrate/services/productVariant.ts b/src-migrate/services/productVariant.ts new file mode 100644 index 00000000..9fec4d1f --- /dev/null +++ b/src-migrate/services/productVariant.ts @@ -0,0 +1,23 @@ +import odooApi from '~/libs/odooApi'; +import { IProductVariantSLA } from '~/types/productVariant'; +import { CategoryPromo, IPromotion } from '~/types/promotion'; + +export const getVariantById = async (variantId: number) => { + const url = `/api/product-variant/${variantId}`; + return await fetch(url).then((res) => res.json()); +}; + +export const getVariantPromoByCategory = async ( + variantId: number, + type: CategoryPromo +): Promise<{ data: IPromotion[] }> => { + const url = `/api/product-variant/${variantId}/promotion/${type}`; + return await fetch(url).then((res) => res.json()); +}; + +export const getVariantSLA = async ( + variantId: number +): Promise => { + const url = `/api/v1/product_variant/${variantId}/stock`; + return await odooApi('GET', url); +}; diff --git a/src-migrate/services/promotionProgram.ts b/src-migrate/services/promotionProgram.ts index a5026c71..c8c46c65 100644 --- a/src-migrate/services/promotionProgram.ts +++ b/src-migrate/services/promotionProgram.ts @@ -1,4 +1,4 @@ -import { IPromotionProgram } from '~/common/types/promotionProgram'; +import { IPromotionProgram } from '~/types/promotionProgram'; export const getPromotionProgram = async ( programId: number diff --git a/src-migrate/services/variant.ts b/src-migrate/services/variant.ts deleted file mode 100644 index 213187d2..00000000 --- a/src-migrate/services/variant.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { CategoryPromo, IPromotion } from '~/common/types/promotion'; - -export const getVariantById = async (variantId: number) => { - const url = `/api/product-variant/${variantId}`; - return await fetch(url).then((res) => res.json()); -}; - -export const getVariantPromoByCategory = async ( - variantId: number, - type: CategoryPromo -): Promise<{ data: IPromotion[] }> => { - const url = `/api/product-variant/${variantId}/promotion/${type}`; - return await fetch(url).then((res) => res.json()); -}; diff --git a/src-migrate/styles/fonts/Inter/Inter-Black.woff b/src-migrate/styles/fonts/Inter/Inter-Black.woff new file mode 100644 index 00000000..a18593a0 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Black.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Black.woff2 b/src-migrate/styles/fonts/Inter/Inter-Black.woff2 new file mode 100644 index 00000000..68f64c9e Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Black.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff b/src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff new file mode 100644 index 00000000..b6b01943 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff2 new file mode 100644 index 00000000..1c9c7ca8 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Bold.woff b/src-migrate/styles/fonts/Inter/Inter-Bold.woff new file mode 100644 index 00000000..eaf3d4bf Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Bold.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Bold.woff2 b/src-migrate/styles/fonts/Inter/Inter-Bold.woff2 new file mode 100644 index 00000000..2846f29c Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Bold.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff b/src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff new file mode 100644 index 00000000..32750761 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff2 new file mode 100644 index 00000000..0b1fe8e1 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff b/src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff new file mode 100644 index 00000000..c2c17ede Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff2 b/src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff2 new file mode 100644 index 00000000..c24c2bdc Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff b/src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff new file mode 100644 index 00000000..c42f7052 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 new file mode 100644 index 00000000..4a81dc79 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff b/src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff new file mode 100644 index 00000000..d0de5f39 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff2 b/src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff2 new file mode 100644 index 00000000..f2ea706f Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff b/src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff new file mode 100644 index 00000000..81f1a28e Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 new file mode 100644 index 00000000..9af717ba Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Italic.woff b/src-migrate/styles/fonts/Inter/Inter-Italic.woff new file mode 100644 index 00000000..a806b382 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Italic.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Italic.woff2 b/src-migrate/styles/fonts/Inter/Inter-Italic.woff2 new file mode 100644 index 00000000..a619fc54 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Italic.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Light.woff b/src-migrate/styles/fonts/Inter/Inter-Light.woff new file mode 100644 index 00000000..c496464d Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Light.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Light.woff2 b/src-migrate/styles/fonts/Inter/Inter-Light.woff2 new file mode 100644 index 00000000..bc4be665 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Light.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-LightItalic.woff b/src-migrate/styles/fonts/Inter/Inter-LightItalic.woff new file mode 100644 index 00000000..f84a9de3 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-LightItalic.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-LightItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-LightItalic.woff2 new file mode 100644 index 00000000..842b2dfc Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-LightItalic.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Medium.woff b/src-migrate/styles/fonts/Inter/Inter-Medium.woff new file mode 100644 index 00000000..d546843f Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Medium.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Medium.woff2 b/src-migrate/styles/fonts/Inter/Inter-Medium.woff2 new file mode 100644 index 00000000..f92498a2 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Medium.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff b/src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff new file mode 100644 index 00000000..459a6568 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff2 new file mode 100644 index 00000000..0e3019f4 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Regular.woff b/src-migrate/styles/fonts/Inter/Inter-Regular.woff new file mode 100644 index 00000000..62d3a618 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Regular.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Regular.woff2 b/src-migrate/styles/fonts/Inter/Inter-Regular.woff2 new file mode 100644 index 00000000..6c2b6893 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Regular.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-SemiBold.woff b/src-migrate/styles/fonts/Inter/Inter-SemiBold.woff new file mode 100644 index 00000000..a815f43a Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-SemiBold.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-SemiBold.woff2 b/src-migrate/styles/fonts/Inter/Inter-SemiBold.woff2 new file mode 100644 index 00000000..611e90c9 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-SemiBold.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff b/src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff new file mode 100644 index 00000000..909e43a9 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 new file mode 100644 index 00000000..545685bd Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Thin.woff b/src-migrate/styles/fonts/Inter/Inter-Thin.woff new file mode 100644 index 00000000..62bc58cd Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Thin.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-Thin.woff2 b/src-migrate/styles/fonts/Inter/Inter-Thin.woff2 new file mode 100644 index 00000000..abbc3a5c Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-Thin.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff b/src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff new file mode 100644 index 00000000..700a7f06 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff differ diff --git a/src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff2 new file mode 100644 index 00000000..ab0b2002 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-italic.var.woff2 b/src-migrate/styles/fonts/Inter/Inter-italic.var.woff2 new file mode 100644 index 00000000..b826d5af Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-italic.var.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter-roman.var.woff2 b/src-migrate/styles/fonts/Inter/Inter-roman.var.woff2 new file mode 100644 index 00000000..6a256a06 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter-roman.var.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/Inter.var.woff2 b/src-migrate/styles/fonts/Inter/Inter.var.woff2 new file mode 100644 index 00000000..365eedc5 Binary files /dev/null and b/src-migrate/styles/fonts/Inter/Inter.var.woff2 differ diff --git a/src-migrate/styles/fonts/Inter/inter.css b/src-migrate/styles/fonts/Inter/inter.css new file mode 100644 index 00000000..de6ce273 --- /dev/null +++ b/src-migrate/styles/fonts/Inter/inter.css @@ -0,0 +1,199 @@ +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: url('Inter-Thin.woff2?v=3.19') format('woff2'), + url('Inter-Thin.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: url('Inter-ThinItalic.woff2?v=3.19') format('woff2'), + url('Inter-ThinItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 200; + font-display: swap; + src: url('Inter-ExtraLight.woff2?v=3.19') format('woff2'), + url('Inter-ExtraLight.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 200; + font-display: swap; + src: url('Inter-ExtraLightItalic.woff2?v=3.19') format('woff2'), + url('Inter-ExtraLightItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: url('Inter-Light.woff2?v=3.19') format('woff2'), + url('Inter-Light.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: url('Inter-LightItalic.woff2?v=3.19') format('woff2'), + url('Inter-LightItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url('Inter-Regular.woff2?v=3.19') format('woff2'), + url('Inter-Regular.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: url('Inter-Italic.woff2?v=3.19') format('woff2'), + url('Inter-Italic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: url('Inter-Medium.woff2?v=3.19') format('woff2'), + url('Inter-Medium.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: url('Inter-MediumItalic.woff2?v=3.19') format('woff2'), + url('Inter-MediumItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: url('Inter-SemiBold.woff2?v=3.19') format('woff2'), + url('Inter-SemiBold.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: url('Inter-SemiBoldItalic.woff2?v=3.19') format('woff2'), + url('Inter-SemiBoldItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: url('Inter-Bold.woff2?v=3.19') format('woff2'), + url('Inter-Bold.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: url('Inter-BoldItalic.woff2?v=3.19') format('woff2'), + url('Inter-BoldItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: url('Inter-ExtraBold.woff2?v=3.19') format('woff2'), + url('Inter-ExtraBold.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 800; + font-display: swap; + src: url('Inter-ExtraBoldItalic.woff2?v=3.19') format('woff2'), + url('Inter-ExtraBoldItalic.woff?v=3.19') format('woff'); +} + +@font-face { + font-family: 'Inter'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: url('Inter-Black.woff2?v=3.19') format('woff2'), + url('Inter-Black.woff?v=3.19') format('woff'); +} +@font-face { + font-family: 'Inter'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: url('Inter-BlackItalic.woff2?v=3.19') format('woff2'), + url('Inter-BlackItalic.woff?v=3.19') format('woff'); +} + +/* ------------------------------------------------------- +Variable font. +Usage: + + html { font-family: 'Inter', sans-serif; } + @supports (font-variation-settings: normal) { + html { font-family: 'Inter var', sans-serif; } + } +*/ +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: normal; + font-named-instance: 'Regular'; + src: url('Inter-roman.var.woff2?v=3.19') format('woff2'); +} +@font-face { + font-family: 'Inter var'; + font-weight: 100 900; + font-display: swap; + font-style: italic; + font-named-instance: 'Italic'; + src: url('Inter-italic.var.woff2?v=3.19') format('woff2'); +} + +/* -------------------------------------------------------------------------- +[EXPERIMENTAL] Multi-axis, single variable font. + +Slant axis is not yet widely supported (as of February 2019) and thus this +multi-axis single variable font is opt-in rather than the default. + +When using this, you will probably need to set font-variation-settings +explicitly, e.g. + + * { font-variation-settings: "slnt" 0deg } + .italic { font-variation-settings: "slnt" 10deg } + +*/ +@font-face { + font-family: 'Inter var experimental'; + font-weight: 100 900; + font-display: swap; + font-style: oblique 0deg 10deg; + src: url('Inter.var.woff2?v=3.19') format('woff2'); +} diff --git a/src-migrate/styles/globals.css b/src-migrate/styles/globals.css new file mode 100644 index 00000000..ea20b247 --- /dev/null +++ b/src-migrate/styles/globals.css @@ -0,0 +1,674 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +* { + -webkit-tap-highlight-color: transparent; +} + +html, +body { + @apply w-screen + text-body-2 + text-gray_r-12 + bg-gray_r-1 + overflow-x-clip; +} + +#__next main { + @apply min-h-screen; +} + +button { + @apply block; +} + +@layer base { + input[type='number']::-webkit-inner-spin-button, + input[type='number']::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } + + input[type='number'] { + -moz-appearance: textfield; + } +} + +@layer components { + .badge-red, + .badge-solid-red, + .badge-gray, + .badge-yellow, + .badge-blue, + .badge-green, + .badge-solid-green { + @apply text-[11px] + leading-none + font-medium + px-1 + py-1 + rounded + w-fit; + } + + .badge-red { + @apply bg-danger-100 + text-danger-600; + } + + .badge-solid-red { + @apply bg-danger-500 + text-white; + } + + .badge-gray { + @apply bg-gray_r-5 + text-gray_r-10; + } + + .badge-yellow { + @apply bg-warning-500 + text-warning-900; + } + + .badge-blue { + @apply bg-blue-200 + text-blue-600; + } + + .badge-green { + @apply bg-success-100 + text-success-600; + } + + .badge-solid-green { + @apply bg-success-500 + text-white; + } + + .form-label { + @apply font-medium + block; + } + + .form-input { + @apply p-3 + rounded + border + text-gray_r-12 + border-gray_r-7 + !bg-white + bg-transparent + w-full + leading-none + focus:outline-none + focus:border-warning-500 + disabled:bg-gray_r-5; + } + + .form-input[aria-invalid] { + @apply border-danger-500 + focus:border-danger-500; + } + + .form-input[type='file'] { + @apply py-2; + } + + .btn-yellow, + .btn-light, + .btn-red, + .btn-solid-red { + @apply block + w-fit + py-3 + px-6 + rounded + border + text-center + font-medium + ease-linear + duration-150; + } + + .btn-yellow { + @apply bg-warning-500 + border-warning-500 + hover:bg-warning-500/80 + disabled:text-gray_r-10 + disabled:bg-warning-200 + disabled:border-warning-200; + } + + .btn-red { + @apply bg-danger-100 + border-danger-300 + text-danger-500 + disabled:text-danger-400 + disabled:bg-danger-200; + } + + .btn-solid-red { + @apply bg-danger-500 + border-danger-500 + text-gray_r-1 + hover:bg-danger-500/80 + disabled:text-gray_r-1 + disabled:bg-danger-200 + disabled:border-danger-200; + } + + .btn-light { + @apply bg-gray_r-3 + border-gray_r-6 + disabled:text-gray_r-10 + disabled:bg-gray_r-6; + } + + .product-card { + @apply w-full + h-full + border + border-gray_r-3 + shadow + bg-white + rounded + relative + flex + flex-col; + } + + .product-card__image { + @apply w-full + h-[160px] + object-contain + object-center + border-b + border-gray_r-6; + } + + .product-card__content { + @apply p-2 + pb-3 + flex-1; + } + + .product-card__title { + @apply text-caption-1 + text-gray_r-12 + leading-5; + } + + .product-card__brand { + @apply text-caption-1 + mb-1 + block; + } + + .product__description { + @apply text-gray_r-12/90; + } + + .product__description br { + @apply block my-1; + } + + .product__description b { + @apply font-semibold; + } +} + +@layer utilities { + .wrap-line-ellipsis-1, + .wrap-line-ellipsis-2, + .wrap-line-ellipsis-3 { + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + } + + .wrap-line-ellipsis-1 { + -webkit-line-clamp: 1; + } + + .wrap-line-ellipsis-2 { + -webkit-line-clamp: 2; + } + + .wrap-line-ellipsis-3 { + -webkit-line-clamp: 3; + } +} + +.menu-wrapper { + @apply fixed + top-0 + left-0 + bg-white + w-[80%] + h-full + z-[60] + overflow-y-auto + translate-x-[-100%] + ease-linear + duration-150; +} + +.menu-wrapper.active { + @apply translate-x-0; +} + +.overlay { + @apply fixed + top-0 + left-0 + w-full + h-full + z-[55] + bg-gray_r-12/40; +} + +.sticky-header { + @apply px-4 + py-3 + bg-gray_r-1/90 + backdrop-blur-lg + sticky + top-0 + border-b + border-gray_r-7 + z-50; +} + +.content-container { + @apply max-w-full + overflow-x-hidden; +} + +#indoteknik_toast { + @apply fixed + bottom-4 + translate-y-[200%] + left-[50%] + translate-x-[-50%] + z-[100] + flex + items-center + p-4 + mb-4 + w-[90%] + text-gray-500 + bg-white + border + border-gray-300 + rounded-lg + shadow + ease-linear + duration-300; +} + +#indoteknik_toast.active { + @apply translate-y-0; +} + +.category-menu { + @apply hidden; +} + +.swiper-slide { + @apply !h-auto; +} + +.lazy-load-image-background { + @apply !block + w-full; +} + +.swiper-pagination-bullet-active { + @apply !bg-danger-500; +} + +.pagination { + @apply flex + justify-center + gap-x-1; +} + +.pagination-item { + @apply p-1 + flex + justify-center + items-center + w-10 + rounded + ease-linear + duration-150 + border + border-gray_r-6 + bg-gray_r-3 + hover:bg-gray_r-5 + text-gray_r-12; +} + +.pagination-item--active { + @apply border-warning-500 + bg-warning-500 + hover:bg-warning-500; +} + +.pagination-dots { + @apply p-1 + flex + justify-center + items-end + w-10 + rounded + ease-linear + bg-gray_r-3 + text-caption-2; +} + +.idt-transition { + @apply transition-all + ease-out + duration-200; +} + +.form-select__placeholder { + @apply !text-gray_r-9; +} + +.form-select__control { + @apply !shadow-none + !border-gray_r-7; +} + +.form-select__control--menu-is-open { + @apply !border-warning-500; +} + +.table-specification { + @apply max-h-[500px] overflow-y-auto border border-gray_r-6; +} + +.table-specification > table { + @apply table-auto + border-collapse + w-full; +} + +.table-specification > table > thead > tr > th:last-child { + @apply w-3/12; +} + +.table-specification > table > thead { + @apply sticky top-0 border-b; +} + +.table-specification > table > thead > tr { + @apply bg-gray_r-1/80 backdrop-blur-lg; +} + +.table-specification th { + @apply font-semibold; +} + +.table-specification th, +.table-specification td { + @apply p-4 text-center; +} + +.table-specification > table > tbody > tr { + @apply odd:bg-gray_r-3 even:bg-gray_r-1; +} + +.table-cart, +.table-checkout { + @apply w-full + table-auto + border-collapse; +} + +.table-cart tr, +.table-checkout tr { + @apply border-y + border-gray_r-6 + first:border-t-0; +} + +.table-cart th, +.table-cart td, +.table-checkout th, +.table-checkout td { + @apply py-4 + px-3 + text-center + text-gray_r-12/90; +} + +.table-cart th, +.table-cart td { + @apply first:w-12; +} + +.table-cart th, +.table-checkout th { + @apply font-medium; +} + +.table-data { + @apply w-full + table-auto + border-collapse; +} + +.table-data thead tr { + @apply bg-gray_r-3; +} + +.table-data thead th { + @apply font-medium whitespace-nowrap; +} + +.table-data thead th, +.table-data tbody td { + @apply px-3 + py-4 + text-center; +} + +.table-data tbody td { + @apply text-gray_r-12/90; +} + +.table-data tbody tr { + @apply border-b + border-gray_r-6; +} + +.navbar-user-dropdown-button { + @apply flex-1 + flex + gap-x-2 + p-4 + items-center + bg-danger-500 + font-medium + !text-gray_r-1 + rounded-none + rounded-t-xl; +} + +.navbar-user-dropdown-button span { + @apply line-clamp-1; +} + +.navbar-user-dropdown-wrapper a, +.navbar-user-dropdown-wrapper button { + @apply text-gray_r-12/80 hover:bg-gray_r-5 font-medium py-2 px-4 w-full text-left; +} + +.navbar-user-dropdown { + @apply bg-white + border + border-gray_r-6 + py-2 + w-full + shadow; +} + +.category-mega-box-wrapper, +.navbar-user-dropdown-wrapper { + @apply absolute + opacity-0 + left-0 + top-[125%] + flex + w-full + z-10 + transition-all + ease-in + duration-200 + pointer-events-none + text-left; +} + +.category-mega-box-wrapper.show, +.navbar-user-dropdown-button:hover ~ .navbar-user-dropdown-wrapper, +.navbar-user-dropdown-wrapper:hover { + @apply top-[100%] + opacity-100 + pointer-events-auto; +} + +.category-mega-box { + @apply relative + py-2 + border + border-t-0 + bg-white + border-gray_r-6 + h-full + w-full; +} + +.category-mega-box > div { + @apply text-gray_r-12/80; +} + +.category-mega-box > div:hover .category-mega-box__parent { + @apply bg-gray_r-5; +} + +.category-mega-box > div:hover .category-mega-box__child-wrapper { + @apply opacity-100 + top-0 + pointer-events-auto; +} + +.category-mega-box .category-mega-box__parent { + @apply py-2.5 + px-4 + text-gray_r-12/80 + font-normal; +} + +.category-mega-box__child-wrapper { + @apply absolute + left-[100%] + top-12 + w-[40vw] + bg-gray_r-1/90 + backdrop-blur-md + border + border-gray_r-6 + p-6 + opacity-0 + h-full + transition-all + ease-in + duration-200 + pointer-events-none + z-50; +} + +.category-mega-box__child-one { + @apply text-gray_r-12/80 + hover:text-danger-500 + transition-colors + ease-linear + duration-100 + font-semibold; +} + +.category-mega-box__child-two { + @apply text-gray_r-12/80 + hover:text-danger-500 + transition-colors + ease-linear + duration-100 + font-normal; +} + +@keyframes page-loader { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +.page-loader { + animation-name: page-loader; + animation-duration: 1000ms; + animation-delay: 50ms; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; +} + +@keyframes shake { + 0% { + transform: translateX(0); + } + 10%, + 90% { + transform: translateX(-10px); + } + 20%, + 80% { + transform: translateX(10px); + } + 30%, + 50%, + 70% { + transform: translateX(-10px); + } + 40%, + 60% { + transform: translateX(10px); + } + 100% { + transform: translateX(0); + } +} + +.blink-color-flash-sale { + @apply text-body-1 md:text-title-sm; + transform: rotateY(180deg) rotateZ(120deg); + animation-name: blink-color-flash-sale; + animation-duration: 300ms; + animation-iteration-count: infinite; + animation-timing-function: linear; +} + +@keyframes blink-color-flash-sale { + from { + @apply text-danger-500; + } + to { + @apply text-warning-500; + } +} diff --git a/src-migrate/types/auth.ts b/src-migrate/types/auth.ts new file mode 100644 index 00000000..464bc12a --- /dev/null +++ b/src-migrate/types/auth.ts @@ -0,0 +1,58 @@ +import { registerSchema } from '~/validations/auth'; +import { OdooApiProps } from './odoo'; +import { z } from 'zod'; + +export type AuthProps = { + id: number; + parent_id: number; + parent_name: string; + partner_id: number; + name: string; + email: string; + phone: string; + mobile: string; + external: boolean; + company: boolean; + pricelist: string | null; + token: string; +}; + +export type AuthApiProps = OdooApiProps & { result: AuthProps }; + +export type RegisterProps = z.infer; + +export type RegisterResApiProps = { + register: boolean; + reason: 'EMAIL_USED' | 'NOT_ACTIVE' | null; +}; + +type ActivationResProps = { + activation: boolean; + user: AuthProps | null; +}; + +export type ActivationTokenProps = { + token: string; +}; + +export type ActivationTokenResApiProps = ActivationResProps & { + reason: 'INVALID_TOKEN' | null; +}; + +export type ActivationOtpProps = { + email: string; + otp: string; +}; + +export type ActivationOtpResApiProps = ActivationResProps & { + reason: 'INVALID_OTP' | null; +}; + +export type ActivationReqProps = { + email: string; +}; + +export type ActivationReqResApiProps = { + activation_request: boolean; + reason: 'NOT_FOUND' | 'ACTIVE' | null; +}; diff --git a/src-migrate/types/cart.ts b/src-migrate/types/cart.ts new file mode 100644 index 00000000..3aceeac4 --- /dev/null +++ b/src-migrate/types/cart.ts @@ -0,0 +1,72 @@ +import { CategoryPromo } from "./promotion"; + +type Price = { + price: number; + discount_percentage: number; + price_discount: number; +}; + +export type CartProduct = { + id: number; + image: string; + display_name: string; + name: string; + code: string; + price: Price; + qty: number; + weight: number; + package_weight: number; +}; + +export type CartItem = { + cart_id: number; + quantity: number; + selected: boolean; + can_buy: boolean; + cart_type: 'product' | 'promotion'; + id: number; + name: string; + stock: number; + weight: number; + attributes: string[]; + parent: { + id: number; + name: string; + image: string; + }; + price: Price; + manufacture: { + id: number; + name: string; + }; + has_flashsale: boolean; + subtotal: number; + + code?: string; + + image?: string; + remaining_time?: number; + promotion_type?: { + value?: CategoryPromo; + label?: string; + }; + limit_qty?: { + all?: number; + user?: number; + transaction?: number; + }; + remaining_qty?: { + all?: number; + user?: number; + transaction?: number; + }; + used_percentage?: number; + products?: CartProduct[]; + free_products?: CartProduct[]; + package_price?: number; +}; + +export type CartProps = { + product_total: number; + products: CartItem[]; +}; diff --git a/src-migrate/types/checkout.ts b/src-migrate/types/checkout.ts new file mode 100644 index 00000000..dc1365d8 --- /dev/null +++ b/src-migrate/types/checkout.ts @@ -0,0 +1,16 @@ +import { CartItem } from './cart'; + +export interface ICheckout { + total_purchase: number; + total_discount: number; + discount_voucher: number; + subtotal: number; + tax: number; + grand_total: number; + total_weight: { + kg: number; + g: number; + }; + has_product_without_weight: boolean; + products: CartItem[]; +} diff --git a/src-migrate/types/nav.ts b/src-migrate/types/nav.ts new file mode 100644 index 00000000..ba97b1bf --- /dev/null +++ b/src-migrate/types/nav.ts @@ -0,0 +1,4 @@ +export type SecondaryNavItemProps = { + label: string + href: string +} diff --git a/src-migrate/types/odoo.ts b/src-migrate/types/odoo.ts new file mode 100644 index 00000000..b34bc667 --- /dev/null +++ b/src-migrate/types/odoo.ts @@ -0,0 +1,6 @@ +export type OdooApiProps = { + status: { + code: number; + description: string; + }; +}; diff --git a/src-migrate/types/pageContent.ts b/src-migrate/types/pageContent.ts new file mode 100644 index 00000000..4361deb7 --- /dev/null +++ b/src-migrate/types/pageContent.ts @@ -0,0 +1,5 @@ +export type PageContentProps = { + id: number; + url_path: string; + content: string; +} | null; diff --git a/src-migrate/types/product.ts b/src-migrate/types/product.ts new file mode 100644 index 00000000..b411224b --- /dev/null +++ b/src-migrate/types/product.ts @@ -0,0 +1,39 @@ +import { IProductVariantDetail } from './productVariant'; + +export interface IProduct { + id: number; + image: string; + code: string; + display_name: string; + name: string; + weight: number; + qty_sold: number; + stock_total: number; + variant_total: number; + description: string; + categories: { + id: string; + name: string; + }[]; + flash_sale: { + id: string; + remaining_time: { + remaining_time: number; + is_flashsale: boolean; + }; + tag: string; + }; + lowest_price: { + price: number; + price_discount: number; + discount_percentage: number; + }; + manufacture: { + id: number; + name: string; + }; +} + +export interface IProductDetail extends IProduct { + variants: IProductVariantDetail[]; +} diff --git a/src-migrate/types/productVariant.ts b/src-migrate/types/productVariant.ts new file mode 100644 index 00000000..861b216a --- /dev/null +++ b/src-migrate/types/productVariant.ts @@ -0,0 +1,33 @@ +export interface IProductVariantDetail { + id: number; + image: string; + code: string; + name: string; + weight: number; + is_flashsale: { + remaining_time: number; + is_flashsale: boolean; + }; + price: { + price: number; + price_discount: number; + discount_percentage: number; + }; + manufacture: + | { + id: string; + name: string; + } + | {}; + parent: { + id: string; + name: string; + image: string; + }; + attributes: string[]; +} + +export interface IProductVariantSLA { + qty: number; + sla_date: string; +} diff --git a/src-migrate/types/promotion.ts b/src-migrate/types/promotion.ts new file mode 100644 index 00000000..85190aad --- /dev/null +++ b/src-migrate/types/promotion.ts @@ -0,0 +1,44 @@ +export interface IPromotion { + id: number; + program_id: number; + name: string; + type: { + value: CategoryPromo; + label: string; + }; + limit: number; + limit_user: number; + limit_trx: number; + price: number; + total_qty: number; + products: { + product_id: number; + qty: number; + }[]; + free_products: { + product_id: number; + qty: number; + }[]; +} + +export interface IProductVariantPromo { + id: number; + parent_id: number; + display_name: string; + image: string; + name: string; + default_code: string; + price: { + price: number; + discount_percentage: number; + price_discount: number; + }; + qty: number; +} + +export type CategoryPromo = 'bundling' | 'discount_loading' | 'merchandise'; + +export interface ICategoryPromo { + value: CategoryPromo; + label: string; +} diff --git a/src-migrate/types/promotionProgram.ts b/src-migrate/types/promotionProgram.ts new file mode 100644 index 00000000..205884b6 --- /dev/null +++ b/src-migrate/types/promotionProgram.ts @@ -0,0 +1,8 @@ +export type IPromotionProgram = { + id: number; + name: string; + start_time: string; + end_time: string; + applies_to: string; + time_left: number; +}; diff --git a/src-migrate/types/solr.ts b/src-migrate/types/solr.ts new file mode 100644 index 00000000..d231c305 --- /dev/null +++ b/src-migrate/types/solr.ts @@ -0,0 +1,7 @@ +export type SolrResponse = { + response: { + numFound: number; + start: number; + docs: T; + }; +}; diff --git a/src-migrate/validations/auth.ts b/src-migrate/validations/auth.ts new file mode 100644 index 00000000..78fc5e71 --- /dev/null +++ b/src-migrate/validations/auth.ts @@ -0,0 +1,17 @@ +import { z } from 'zod'; + +export const registerSchema = z.object({ + name: z.string().min(1, { message: 'Nama harus diisi' }), + email: z + .string() + .min(1, { message: 'Email harus diisi' }) + .email({ message: 'Email harus menggunakan format example@mail.com' }), + password: z.string().min(6, { message: 'Password minimal 6 karakter' }), + company: z.string().optional(), + phone: z + .string() + .min(1, { message: 'Nomor telepon harus diisi' }) + .refine((val) => /^\d{10,12}$/.test(val), { + message: 'Format nomor telepon tidak valid, contoh: 081234567890', + }), +}); -- 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 --- src-migrate/libs/whatsappUrl.ts | 46 ++++++++++++++++++++++ .../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 ++++- src-migrate/types/auth.ts | 6 +-- 8 files changed, 135 insertions(+), 24 deletions(-) create mode 100644 src-migrate/libs/whatsappUrl.ts (limited to 'src-migrate') diff --git a/src-migrate/libs/whatsappUrl.ts b/src-migrate/libs/whatsappUrl.ts new file mode 100644 index 00000000..02c36cef --- /dev/null +++ b/src-migrate/libs/whatsappUrl.ts @@ -0,0 +1,46 @@ +import { getAuth } from './auth'; + +const TEMPLATES = { + default: 'Bisa tolong bantu kebutuhan saya?', + product: + 'Saya mencari barang berikut:\n\n{{url}}\n\n```Brand: {{manufacture}}\nName: {{productName}}```', +}; + +interface WhatsappUrlProps { + template: keyof typeof TEMPLATES; + payload: any; + greeting?: boolean; + needLogin?: boolean; + fallbackUrl?: string; +} + +export const whatsappUrl = ({ + template, + payload, + greeting = true, + needLogin = true, + fallbackUrl, +}: WhatsappUrlProps) => { + const auth = getAuth(); + + let greetingText = ''; + + if (needLogin && !auth) { + return fallbackUrl ? `/login=next?${fallbackUrl}` : '/login'; + } + + let result = TEMPLATES[template].replace( + /{{(.*?)}}/g, + (match, key) => payload[key] || '' + ); + + if (greeting && typeof auth === 'object') { + greetingText = `Halo Indoteknik.com, Saya ${auth.name} `; + if (auth.parentName) greetingText += `dari ${auth.parentName}`; + greetingText += '.\n\n'; + + result = greetingText + result; + } + + return `https://wa.me/628128080622?text=${encodeURIComponent(result)}`; +}; 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) => (
diff --git a/src-migrate/types/auth.ts b/src-migrate/types/auth.ts index 464bc12a..4f69bb96 100644 --- a/src-migrate/types/auth.ts +++ b/src-migrate/types/auth.ts @@ -4,9 +4,9 @@ import { z } from 'zod'; export type AuthProps = { id: number; - parent_id: number; - parent_name: string; - partner_id: number; + parentId: number; + parentName: string; + partnerId: number; name: string; email: string; phone: string; -- 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') 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 && '-'}
diff --git a/src-migrate/pages/shop/product/[slug].tsx b/src-migrate/pages/shop/product/[slug].tsx index 883532ed..733e10d9 100644 --- a/src-migrate/pages/shop/product/[slug].tsx +++ b/src-migrate/pages/shop/product/[slug].tsx @@ -1,5 +1,3 @@ -import style from './product.module.css' - import { GetServerSideProps, NextPage } from 'next' import React from 'react' import dynamic from 'next/dynamic' @@ -10,6 +8,7 @@ import { getIdFromSlug } from '~/libs/slug' import { IProductDetail } from '~/types/product' import { Seo } from '~/components/seo' +import { useRouter } from 'next/router' const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout'), { ssr: false }) const ProductDetail = dynamic(() => import('~/modules/product-detail'), { ssr: false }) @@ -36,14 +35,18 @@ export const getServerSideProps: GetServerSideProps = (async (context } }) +const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST + const ProductDetailPage: NextPage = ({ product }) => { + const router = useRouter(); + return ( = ({ product }) => { content: `${product?.name}, Harga ${product?.name}, Beli ${product?.name}, Spesifikasi ${product?.name}`, } ]} - // canonical='' + canonical={SELF_HOST + router.asPath} /> -
+
diff --git a/src-migrate/pages/shop/product/product.module.css b/src-migrate/pages/shop/product/product.module.css deleted file mode 100644 index e69de29b..00000000 diff --git a/src-migrate/services/product.ts b/src-migrate/services/product.ts index c9e93396..4ef027e1 100644 --- a/src-migrate/services/product.ts +++ b/src-migrate/services/product.ts @@ -1,5 +1,7 @@ import { IProduct, IProductDetail } from '~/types/product'; import snakeCase from 'snakecase-keys'; +import odooApi from '~/libs/odooApi'; +import { ICategoryBreadcrumb } from '~/types/category'; const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST; @@ -57,3 +59,9 @@ export const getProductSimilar = async ({ .then((res) => res.json()) .then((res) => snakeCase(res.response)); }; + +export const getProductCategoryBreadcrumb = async ( + id: number +): Promise => { + return await odooApi('GET', `/api/v1/product/${id}/category-breadcrumb`); +}; diff --git a/src-migrate/types/auth.ts b/src-migrate/types/auth.ts index 4f69bb96..02e3623d 100644 --- a/src-migrate/types/auth.ts +++ b/src-migrate/types/auth.ts @@ -1,5 +1,5 @@ import { registerSchema } from '~/validations/auth'; -import { OdooApiProps } from './odoo'; +import { OdooApiRes } from './odoo'; import { z } from 'zod'; export type AuthProps = { @@ -17,7 +17,7 @@ export type AuthProps = { token: string; }; -export type AuthApiProps = OdooApiProps & { result: AuthProps }; +export type AuthApiProps = OdooApiRes; export type RegisterProps = z.infer; diff --git a/src-migrate/types/category.ts b/src-migrate/types/category.ts new file mode 100644 index 00000000..1037b5f9 --- /dev/null +++ b/src-migrate/types/category.ts @@ -0,0 +1,4 @@ +export interface ICategoryBreadcrumb { + id: number; + name: string; +} diff --git a/src-migrate/types/odoo.ts b/src-migrate/types/odoo.ts index b34bc667..73a029e9 100644 --- a/src-migrate/types/odoo.ts +++ b/src-migrate/types/odoo.ts @@ -1,6 +1,7 @@ -export type OdooApiProps = { +export interface OdooApiRes { status: { code: number; description: string; }; -}; + result: T; +} -- 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/components/ui/image.tsx | 43 ++++++++-------------- .../product-card/components/ProductCard.tsx | 1 - .../modules/product-detail/components/Image.tsx | 1 - 3 files changed, 15 insertions(+), 30 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/components/ui/image.tsx b/src-migrate/components/ui/image.tsx index c7ed0b0e..c1dde170 100644 --- a/src-migrate/components/ui/image.tsx +++ b/src-migrate/components/ui/image.tsx @@ -1,4 +1,3 @@ -import clsx from 'clsx'; import NextImage, { ImageProps as NextImageProps } from 'next/image'; import { useState } from 'react'; @@ -6,41 +5,29 @@ import clsxm from '~/libs/clsxm'; type ImageProps = { rounded?: string; - classNames?: { - wrapper: string - } } & NextImageProps; const Image = (props: ImageProps) => { - const { alt, src, className, classNames, rounded, ...rest } = props; + const { alt, src, className, rounded, ...rest } = props; const [isLoading, setLoading] = useState(true); return ( -
- setLoading(false)} - {...rest} - /> -
+ src={src} + alt={alt} + loading='lazy' + quality={100} + onLoadingComplete={() => setLoading(false)} + {...rest} + /> ); }; export default Image; \ No newline at end of file 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') 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 - + => { + const url = `/api/v1/user/${userId}/wishlist`; + const searchParamsObj = new URLSearchParams(searchParams); + + return await odooApi('GET', url + '?' + searchParamsObj.toString()); +}; + +export const upsertUserWishlist = async ( + userId: number, + productId: number +): Promise<{ id: number }> => { + const url = `/api/v1/user/${userId}/wishlist/create-or-delete`; + const data = { product_id: productId }; + + return await odooApi('POST', url, data); +}; -- cgit v1.2.3 From 7298d8e811a68cb92c02a7d810f412498d1609d8 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly 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') 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 +++ src-migrate/services/product.ts | 1 - src-migrate/types/product.ts | 5 +- 8 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 src-migrate/modules/product-detail/styles/image.module.css (limited to 'src-migrate') 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}
diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx index 397852f9..9ec58a48 100644 --- a/src-migrate/pages/shop/cart/index.tsx +++ b/src-migrate/pages/shop/cart/index.tsx @@ -9,6 +9,7 @@ import { useCartStore } from '~/modules/cart/stores/useCartStore' import CartItem from '~/modules/cart/components/Item' import CartSummary from '~/modules/cart/components/Summary' +import clsxm from '~/libs/clsxm' const CartPage = () => { const auth = getAuth() @@ -35,6 +36,14 @@ const CartPage = () => { return false }, [cart]) + const hasSelectNoPrice = useMemo(() => { + if (!cart) return false + for (const item of cart.products) { + if (item.selected && item.price.price_discount == 0) return true + } + return false + }, [cart]) + return ( <>
@@ -71,11 +80,14 @@ const CartPage = () => { - + + + + + 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 diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx index 9ec58a48..6c37b434 100644 --- a/src-migrate/pages/shop/cart/index.tsx +++ b/src-migrate/pages/shop/cart/index.tsx @@ -10,11 +10,15 @@ import { useCartStore } from '~/modules/cart/stores/useCartStore' import CartItem from '~/modules/cart/components/Item' import CartSummary from '~/modules/cart/components/Summary' import clsxm from '~/libs/clsxm' +import useDevice from '@/core/hooks/useDevice' +import CartSummaryMobile from '~/modules/cart/components/CartSummaryMobile' const CartPage = () => { const auth = getAuth() const { loadCart, cart, summary } = useCartStore() + + const useDivvice = useDevice(); useEffect(() => { if (typeof auth === 'object' && !cart) loadCart(auth.id) @@ -65,7 +69,8 @@ const CartPage = () => {
- + {useDivvice.isMobile && } + {!useDivvice.isMobile && }
-- cgit v1.2.3 From 5234b7c85ac984bc76d8945934a90b75f4a0d8c0 Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Tue, 12 Mar 2024 15:07:56 +0700 Subject: feeback review --- src-migrate/pages/shop/cart/cart.module.css | 4 +- src-migrate/pages/shop/cart/index.tsx | 129 +++++++++++++++++----------- 2 files changed, 82 insertions(+), 51 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/pages/shop/cart/cart.module.css b/src-migrate/pages/shop/cart/cart.module.css index 353b131a..98a6ac86 100644 --- a/src-migrate/pages/shop/cart/cart.module.css +++ b/src-migrate/pages/shop/cart/cart.module.css @@ -3,11 +3,11 @@ } .content { - @apply flex flex-wrap; + @apply flex flex-wrap ; } .item-wrapper { - @apply w-full md:w-3/4; + @apply w-full md:w-3/4 min-h-screen; } .item-skeleton { diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx index d1a6a6ff..4b4de92b 100644 --- a/src-migrate/pages/shop/cart/index.tsx +++ b/src-migrate/pages/shop/cart/index.tsx @@ -1,59 +1,57 @@ -import style from './cart.module.css' +import style from './cart.module.css'; -import React, { useEffect, useMemo } from 'react' -import Link from 'next/link' -import { Button, Tooltip } from '@chakra-ui/react' +import React, { useEffect, useMemo } from 'react'; +import Link from 'next/link'; +import { Button, Tooltip } from '@chakra-ui/react'; -import { getAuth } from '~/libs/auth' -import { useCartStore } from '~/modules/cart/stores/useCartStore' +import { getAuth } from '~/libs/auth'; +import { useCartStore } from '~/modules/cart/stores/useCartStore'; -import CartItem from '~/modules/cart/components/Item' -import CartSummary from '~/modules/cart/components/Summary' -import clsxm from '~/libs/clsxm' -import useDevice from '@/core/hooks/useDevice' -import CartSummaryMobile from '~/modules/cart/components/CartSummaryMobile' -import Image from '~/components/ui/image' +import CartItem from '~/modules/cart/components/Item'; +import CartSummary from '~/modules/cart/components/Summary'; +import clsxm from '~/libs/clsxm'; +import useDevice from '@/core/hooks/useDevice'; +import CartSummaryMobile from '~/modules/cart/components/CartSummaryMobile'; +import Image from '~/components/ui/image'; const CartPage = () => { - const auth = getAuth() + const auth = getAuth(); + + const { loadCart, cart, summary } = useCartStore(); - const { loadCart, cart, summary } = useCartStore() - const useDivvice = useDevice(); useEffect(() => { - if (typeof auth === 'object' && !cart) loadCart(auth.id) - }, [auth, loadCart, cart]) + if (typeof auth === 'object' && !cart) loadCart(auth.id); + }, [auth, loadCart, cart]); const hasSelectedPromo = useMemo(() => { - if (!cart) return false + if (!cart) return false; for (const item of cart.products) { - if (item.cart_type === 'promotion' && item.selected) return true + if (item.cart_type === 'promotion' && item.selected) return true; } - return false - }, [cart]) + return false; + }, [cart]); const hasSelected = useMemo(() => { - if (!cart) return false + if (!cart) return false; for (const item of cart.products) { - if (item.selected) return true + if (item.selected) return true; } - return false - }, [cart]) + return false; + }, [cart]); const hasSelectNoPrice = useMemo(() => { - if (!cart) return false + if (!cart) return false; for (const item of cart.products) { - if (item.selected && item.price.price_discount == 0) return true + if (item.selected && item.price.price_discount == 0) return true; } - return false - }, [cart]) + return false; + }, [cart]); return ( <> -
- Keranjang Belanja -
+
Keranjang Belanja
@@ -64,26 +62,57 @@ const CartPage = () => {
- {cart?.products.map((item) => )} + {cart?.products.map((item) => ( + + ))} {cart?.products?.length === 0 && (
- Empty Cart -
Keranjangnya masih kosong nih
-
Yuk, tambahin barang-barang yang kamu mau ke keranjang sekarang!
Ada banyak potongan belanjanya pakai kode voucher
- Mulai Belanja + Empty Cart +
+ Keranjangnya masih kosong nih +
+
+ Yuk, tambahin barang-barang yang kamu mau ke keranjang + sekarang! +
+ Ada banyak potongan belanjanya pakai kode voucher +
+ + Mulai Belanja +
)}
- -
+
- {useDivvice.isMobile && } - {!useDivvice.isMobile && } + {useDivvice.isMobile && ( + + )} + {!useDivvice.isMobile && ( + + )}
- + - +
- ) -} + ); +}; -export default CartPage \ No newline at end of file +export default CartPage; -- cgit v1.2.3 From 8a671dd0ecf1f91f06d6913538742085fefe9990 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 23 Apr 2024 16:13:43 +0700 Subject: Fix whatsapp url on basic layout --- src-migrate/pages/shop/product/[slug].tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src-migrate') diff --git a/src-migrate/pages/shop/product/[slug].tsx b/src-migrate/pages/shop/product/[slug].tsx index 733e10d9..fc72a6b0 100644 --- a/src-migrate/pages/shop/product/[slug].tsx +++ b/src-migrate/pages/shop/product/[slug].tsx @@ -1,5 +1,5 @@ import { GetServerSideProps, NextPage } from 'next' -import React from 'react' +import React, { useEffect } from 'react' import dynamic from 'next/dynamic' import cookie from 'cookie' @@ -9,6 +9,7 @@ import { IProductDetail } from '~/types/product' import { Seo } from '~/components/seo' import { useRouter } from 'next/router' +import { useProductContext } from '@/contexts/ProductContext' const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout'), { ssr: false }) const ProductDetail = dynamic(() => import('~/modules/product-detail'), { ssr: false }) @@ -40,6 +41,12 @@ const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST const ProductDetailPage: NextPage = ({ product }) => { const router = useRouter(); + const { setProduct } = useProductContext(); + + useEffect(() => { + if (product) setProduct(product); + }, [product, setProduct]); + return ( 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 +++++++++++++++++++++++++++++ src-migrate/services/banner.ts | 11 +++++++++++ src-migrate/types/banner.ts | 8 ++++++++ 4 files changed, 77 insertions(+) create mode 100644 src-migrate/modules/footer-banner/index.tsx create mode 100644 src-migrate/modules/side-banner/index.tsx create mode 100644 src-migrate/services/banner.ts create mode 100644 src-migrate/types/banner.ts (limited to 'src-migrate') 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 diff --git a/src-migrate/services/banner.ts b/src-migrate/services/banner.ts new file mode 100644 index 00000000..1b46ba06 --- /dev/null +++ b/src-migrate/services/banner.ts @@ -0,0 +1,11 @@ +import odooApi from '~/libs/odooApi'; +import { IBanner } from '~/types/banner'; + +export const getBanner = async ({ + type, +}: { + type: string; +}): Promise => { + const searchParams = new URLSearchParams({ type }); + return await odooApi('GET', `/api/v1/banner?${searchParams.toString()}`); +}; diff --git a/src-migrate/types/banner.ts b/src-migrate/types/banner.ts new file mode 100644 index 00000000..dbccc378 --- /dev/null +++ b/src-migrate/types/banner.ts @@ -0,0 +1,8 @@ +export interface IBanner { + background_color: string | false; + group_by_week: number | false; + image: string; + name: string; + sequence: number; + url: string; +} -- cgit v1.2.3 From ed0d0293f88adf3c5312cb556bc464e330b3672a Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Fri, 26 Apr 2024 09:44:31 +0700 Subject: hide button checkout if is step approval is true --- src-migrate/pages/shop/cart/index.tsx | 39 ++++++++++++++++++++--------------- src-migrate/types/auth.ts | 4 ++++ 2 files changed, 26 insertions(+), 17 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx index 4b4de92b..9866be46 100644 --- a/src-migrate/pages/shop/cart/index.tsx +++ b/src-migrate/pages/shop/cart/index.tsx @@ -16,13 +16,17 @@ import Image from '~/components/ui/image'; const CartPage = () => { const auth = getAuth(); + const [isStepApproval, setIsStepApproval] = React.useState(false); const { loadCart, cart, summary } = useCartStore(); const useDivvice = useDevice(); useEffect(() => { - if (typeof auth === 'object' && !cart) loadCart(auth.id); + if (typeof auth === 'object' && !cart) { + loadCart(auth.id); + setIsStepApproval(auth?.feature?.soApproval); + } }, [auth, loadCart, cart]); const hasSelectedPromo = useMemo(() => { @@ -123,23 +127,24 @@ const CartPage = () => { Quotation - - - - + + + )}
diff --git a/src-migrate/types/auth.ts b/src-migrate/types/auth.ts index 02e3623d..e93a475a 100644 --- a/src-migrate/types/auth.ts +++ b/src-migrate/types/auth.ts @@ -15,6 +15,10 @@ export type AuthProps = { company: boolean; pricelist: string | null; token: string; + feature : { + onlyReadyStock : boolean, + soApproval : boolean + } }; export type AuthApiProps = OdooApiRes; -- cgit v1.2.3 From 145f9edfd32b385771483b9b95a9fa0fa24c2dec Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Fri, 26 Apr 2024 13:57:55 +0700 Subject: css --- src-migrate/pages/shop/cart/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src-migrate') diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx index 9866be46..e101b5ad 100644 --- a/src-migrate/pages/shop/cart/index.tsx +++ b/src-migrate/pages/shop/cart/index.tsx @@ -110,7 +110,7 @@ const CartPage = () => { )} -
+
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') 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') 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/libs/whatsappUrl.ts | 2 +- src-migrate/modules/header/components/HeaderDesktop.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/libs/whatsappUrl.ts b/src-migrate/libs/whatsappUrl.ts index 66879585..a3fcf8ad 100644 --- a/src-migrate/libs/whatsappUrl.ts +++ b/src-migrate/libs/whatsappUrl.ts @@ -44,5 +44,5 @@ export const whatsappUrl = ({ result = greetingText + result; } - return `https://wa.me/628128080622?text=${encodeURIComponent(result)}`; + return `https://wa.me/6281717181922?text=${encodeURIComponent(result)}`; }; 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') 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 ++-------------------- src-migrate/types/product.ts | 2 + 3 files changed, 8 insertions(+), 85 deletions(-) (limited to 'src-migrate') 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') 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 6ac1792ee37e5a5a9438f61e708966c944b61914 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 6 Jun 2024 10:21:39 +0700 Subject: " add feature SNI-TKDN" --- src-migrate/types/product.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src-migrate') diff --git a/src-migrate/types/product.ts b/src-migrate/types/product.ts index 428b6e45..681cdc8e 100644 --- a/src-migrate/types/product.ts +++ b/src-migrate/types/product.ts @@ -11,8 +11,8 @@ export interface IProduct { stock_total: number; variant_total: number; description: string; - is_sni: boolean; - is_tkdn: boolean; + isSni: boolean; + isTkdn: boolean; categories: { id: string; name: string; -- 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') 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') 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') 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') 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') 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') 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') 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 ++ src-migrate/pages/shop/cart/cart.module.css | 4 ++ src-migrate/pages/shop/cart/index.tsx | 2 +- 5 files changed, 73 insertions(+), 26 deletions(-) (limited to 'src-migrate') 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 }) + } })); diff --git a/src-migrate/pages/shop/cart/cart.module.css b/src-migrate/pages/shop/cart/cart.module.css index 98a6ac86..806104be 100644 --- a/src-migrate/pages/shop/cart/cart.module.css +++ b/src-migrate/pages/shop/cart/cart.module.css @@ -29,3 +29,7 @@ .summary-buttons { @apply grid grid-cols-2 gap-x-3 mt-6; } + +.summary-buttons-step-approval { + @apply grid grid-cols-1 gap-y-3 mt-6; +} diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx index e101b5ad..d89707d2 100644 --- a/src-migrate/pages/shop/cart/index.tsx +++ b/src-migrate/pages/shop/cart/index.tsx @@ -110,7 +110,7 @@ const CartPage = () => { )} -
+