From f62b2345f463695ef0f8f79830cd76b6e0332821 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Sat, 13 Jan 2024 10:35:22 +0700 Subject: Refactor src migrate folder --- .../account-activation/components/FormEmail.tsx | 6 +- .../account-activation/components/FormOTP.tsx | 6 +- .../account-activation/components/FormToken.tsx | 6 +- src-migrate/modules/cart/components/Item.tsx | 4 +- src-migrate/modules/cart/components/ItemAction.tsx | 4 +- src-migrate/modules/cart/components/ItemPromo.tsx | 2 +- src-migrate/modules/cart/components/ItemSelect.tsx | 4 +- src-migrate/modules/cart/components/Summary.tsx | 4 +- src-migrate/modules/cart/stores/useCartStore.ts | 2 +- .../modules/header/components/HeaderDesktop.tsx | 2 +- src-migrate/modules/page-content/index.tsx | 19 +++- src-migrate/modules/popup-information/index.tsx | 4 +- .../product-card/components/ProductCard.tsx | 104 +++++++++++++++++ src-migrate/modules/product-card/index.tsx | 3 + .../product-card/styles/product-card.module.css | 50 ++++++++ .../product-detail/components/AddToCart.tsx | 79 +++++++++++++ .../product-detail/components/AddToWishlist.tsx | 17 +++ .../modules/product-detail/components/Image.tsx | 37 ++++++ .../product-detail/components/Information.tsx | 84 ++++++++++++++ .../product-detail/components/PriceAction.tsx | 53 +++++++++ .../product-detail/components/ProductDetail.tsx | 126 +++++++++++++++++++++ .../product-detail/components/SimilarBottom.tsx | 21 ++++ .../product-detail/components/SimilarSide.tsx | 34 ++++++ .../product-detail/components/VariantList.tsx | 85 ++++++++++++++ src-migrate/modules/product-detail/index.ts | 3 + .../product-detail/stores/useProductDetail.ts | 25 ++++ .../product-detail/styles/information.module.css | 19 ++++ .../product-detail/styles/price-action.module.css | 12 ++ .../styles/product-detail.module.css | 15 +++ .../product-detail/styles/side-similar.module.css | 3 + .../product-detail/styles/variant-list.module.css | 19 ++++ .../modules/product-promo/components/AddToCart.tsx | 4 +- .../modules/product-promo/components/Card.tsx | 8 +- .../product-promo/components/CardCountdown.tsx | 4 +- .../product-promo/components/CategoryTab.tsx | 4 +- .../modules/product-promo/components/Item.tsx | 2 +- .../modules/product-promo/components/Modal.tsx | 2 +- .../product-promo/components/ModalContent.tsx | 2 +- .../modules/product-promo/components/Section.tsx | 2 +- .../modules/product-promo/stores/useModalStore.ts | 2 +- .../product-similar/hooks/useProductSimilar.tsx | 15 +++ .../product-slider/components/ProductSlider.tsx | 42 +++++++ src-migrate/modules/product-slider/index.ts | 3 + src-migrate/modules/register/components/Form.tsx | 4 +- .../modules/register/components/FormCaptcha.tsx | 4 +- .../modules/register/components/TermCondition.tsx | 4 +- .../modules/register/stores/useRegisterStore.ts | 60 ++++++++++ 47 files changed, 969 insertions(+), 45 deletions(-) create mode 100644 src-migrate/modules/product-card/components/ProductCard.tsx create mode 100644 src-migrate/modules/product-card/index.tsx create mode 100644 src-migrate/modules/product-card/styles/product-card.module.css create mode 100644 src-migrate/modules/product-detail/components/AddToCart.tsx create mode 100644 src-migrate/modules/product-detail/components/AddToWishlist.tsx create mode 100644 src-migrate/modules/product-detail/components/Image.tsx create mode 100644 src-migrate/modules/product-detail/components/Information.tsx create mode 100644 src-migrate/modules/product-detail/components/PriceAction.tsx create mode 100644 src-migrate/modules/product-detail/components/ProductDetail.tsx create mode 100644 src-migrate/modules/product-detail/components/SimilarBottom.tsx create mode 100644 src-migrate/modules/product-detail/components/SimilarSide.tsx create mode 100644 src-migrate/modules/product-detail/components/VariantList.tsx create mode 100644 src-migrate/modules/product-detail/index.ts create mode 100644 src-migrate/modules/product-detail/stores/useProductDetail.ts create mode 100644 src-migrate/modules/product-detail/styles/information.module.css create mode 100644 src-migrate/modules/product-detail/styles/price-action.module.css create mode 100644 src-migrate/modules/product-detail/styles/product-detail.module.css create mode 100644 src-migrate/modules/product-detail/styles/side-similar.module.css create mode 100644 src-migrate/modules/product-detail/styles/variant-list.module.css create mode 100644 src-migrate/modules/product-similar/hooks/useProductSimilar.tsx create mode 100644 src-migrate/modules/product-slider/components/ProductSlider.tsx create mode 100644 src-migrate/modules/product-slider/index.ts create mode 100644 src-migrate/modules/register/stores/useRegisterStore.ts (limited to 'src-migrate/modules') diff --git a/src-migrate/modules/account-activation/components/FormEmail.tsx b/src-migrate/modules/account-activation/components/FormEmail.tsx index ec300ba4..f7925481 100644 --- a/src-migrate/modules/account-activation/components/FormEmail.tsx +++ b/src-migrate/modules/account-activation/components/FormEmail.tsx @@ -3,9 +3,9 @@ import Link from "next/link" import { useRouter } from "next/router" import { ChangeEvent, useEffect, useState } from "react" import { useMutation } from "react-query" -import Modal from "~/common/components/elements/Modal" -import { useRegisterStore } from "~/common/stores/useRegisterStore" -import { ActivationReqProps } from "~/common/types/auth" +import { Modal } from "~/components/ui/modal" +import { useRegisterStore } from "~/modules/register/stores/useRegisterStore" +import { ActivationReqProps } from "~/types/auth" import { activationReq } from "~/services/auth" const FormEmail = () => { diff --git a/src-migrate/modules/account-activation/components/FormOTP.tsx b/src-migrate/modules/account-activation/components/FormOTP.tsx index 6815a088..cf4da2db 100644 --- a/src-migrate/modules/account-activation/components/FormOTP.tsx +++ b/src-migrate/modules/account-activation/components/FormOTP.tsx @@ -3,9 +3,9 @@ import { useRouter } from "next/router" import { useEffect, useState } from "react" import { useMutation } from "react-query" import { useCountdown } from "usehooks-ts" -import Modal from '~/common/components/elements/Modal' -import { setAuth } from "~/common/libs/auth" -import { ActivationOtpProps, ActivationReqProps } from "~/common/types/auth" +import { Modal } from "~/components/ui/modal" +import { setAuth } from "~/libs/auth" +import { ActivationOtpProps, ActivationReqProps } from "~/types/auth" import { activationReq, activationUserOTP } from "~/services/auth" const FormOTP = () => { diff --git a/src-migrate/modules/account-activation/components/FormToken.tsx b/src-migrate/modules/account-activation/components/FormToken.tsx index b68b244f..2835ec0e 100644 --- a/src-migrate/modules/account-activation/components/FormToken.tsx +++ b/src-migrate/modules/account-activation/components/FormToken.tsx @@ -4,10 +4,10 @@ import { useEffect, useState } from "react" import Link from "next/link" import { useMutation } from "react-query" -import Modal from "~/common/components/elements/Modal" -import { ActivationTokenProps } from "~/common/types/auth" +import { Modal } from "~/components/ui/modal" +import { ActivationTokenProps } from "~/types/auth" import { activationUserToken } from "~/services/auth" -import { setAuth } from "~/common/libs/auth" +import { setAuth } from "~/libs/auth" const FormToken = () => { const router = useRouter() diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index baf48bb6..08823d19 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -7,8 +7,8 @@ import { InfoIcon } from 'lucide-react' import { PROMO_CATEGORY } from '~/constants/promotion' -import formatCurrency from '~/common/libs/formatCurrency' -import { CartItem as CartItemProps } from '~/common/types/cart' +import formatCurrency from '~/libs/formatCurrency' +import { CartItem as CartItemProps } from '~/types/cart' import CartItemPromo from './ItemPromo' import CartItemAction from './ItemAction' diff --git a/src-migrate/modules/cart/components/ItemAction.tsx b/src-migrate/modules/cart/components/ItemAction.tsx index 3e264aef..859c758c 100644 --- a/src-migrate/modules/cart/components/ItemAction.tsx +++ b/src-migrate/modules/cart/components/ItemAction.tsx @@ -5,8 +5,8 @@ import React, { useEffect, useState } from 'react' import { Spinner, Tooltip } from '@chakra-ui/react' import { MinusIcon, PlusIcon, Trash2Icon } from 'lucide-react' -import { CartItem } from '~/common/types/cart' -import { getAuth } from '~/common/libs/auth' +import { CartItem } from '~/types/cart' +import { getAuth } from '~/libs/auth' import { deleteUserCart, upsertUserCart } from '~/services/cart' import { useDebounce } from 'usehooks-ts' diff --git a/src-migrate/modules/cart/components/ItemPromo.tsx b/src-migrate/modules/cart/components/ItemPromo.tsx index bb286e8b..bc507578 100644 --- a/src-migrate/modules/cart/components/ItemPromo.tsx +++ b/src-migrate/modules/cart/components/ItemPromo.tsx @@ -3,7 +3,7 @@ import style from '../styles/item-promo.module.css' import Image from 'next/image' import React from 'react' -import { CartProduct } from '~/common/types/cart' +import { CartProduct } from '~/types/cart' type Props = { product: CartProduct diff --git a/src-migrate/modules/cart/components/ItemSelect.tsx b/src-migrate/modules/cart/components/ItemSelect.tsx index 10d7493a..1d8886a2 100644 --- a/src-migrate/modules/cart/components/ItemSelect.tsx +++ b/src-migrate/modules/cart/components/ItemSelect.tsx @@ -1,8 +1,8 @@ import { Checkbox, Spinner } from '@chakra-ui/react' import React, { useState } from 'react' -import { getAuth } from '~/common/libs/auth' -import { CartItem } from '~/common/types/cart' +import { getAuth } from '~/libs/auth' +import { CartItem } from '~/types/cart' import { upsertUserCart } from '~/services/cart' import { useCartStore } from '../stores/useCartStore' diff --git a/src-migrate/modules/cart/components/Summary.tsx b/src-migrate/modules/cart/components/Summary.tsx index a835bca9..2e55c8df 100644 --- a/src-migrate/modules/cart/components/Summary.tsx +++ b/src-migrate/modules/cart/components/Summary.tsx @@ -1,8 +1,8 @@ import style from '../styles/summary.module.css' import React from 'react' -import formatCurrency from '~/common/libs/formatCurrency' -import clsxm from '~/common/libs/clsxm' +import formatCurrency from '~/libs/formatCurrency' +import clsxm from '~/libs/clsxm' import { Skeleton } from '@chakra-ui/react' import _ from 'lodash' diff --git a/src-migrate/modules/cart/stores/useCartStore.ts b/src-migrate/modules/cart/stores/useCartStore.ts index 0643b8e6..3d9a0aed 100644 --- a/src-migrate/modules/cart/stores/useCartStore.ts +++ b/src-migrate/modules/cart/stores/useCartStore.ts @@ -1,5 +1,5 @@ import { create } from 'zustand'; -import { CartProps } from '~/common/types/cart'; +import { CartProps } from '~/types/cart'; import { getUserCart } from '~/services/cart'; type State = { diff --git a/src-migrate/modules/header/components/HeaderDesktop.tsx b/src-migrate/modules/header/components/HeaderDesktop.tsx index 3860bded..8f5a8efa 100644 --- a/src-migrate/modules/header/components/HeaderDesktop.tsx +++ b/src-migrate/modules/header/components/HeaderDesktop.tsx @@ -8,7 +8,7 @@ import Link from 'next/link' import SearchBar from "./SearchBar"; // Constants -import { SECONDARY_MENU_ITEMS } from "~/common/constants/menu"; +import { SECONDARY_MENU_ITEMS } from "~/constants/menu"; const LOGO_WIDTH = 210; const LOGO_HEIGHT = LOGO_WIDTH / 3; diff --git a/src-migrate/modules/page-content/index.tsx b/src-migrate/modules/page-content/index.tsx index 608079f8..547b1957 100644 --- a/src-migrate/modules/page-content/index.tsx +++ b/src-migrate/modules/page-content/index.tsx @@ -1,7 +1,6 @@ import { useMemo } from "react" import { useQuery } from "react-query" -import PageContentSkeleton from "~/common/components/skeleton/PageContentSkeleton" -import { PageContentProps } from "~/common/types/pageContent" +import { PageContentProps } from "~/types/pageContent" import { getPageContent } from "~/services/pageContent" type Props = { @@ -26,4 +25,20 @@ const PageContent = ({ path }: Props) => { ) } +const PageContentSkeleton = () => ( +
+
+
+
+
+
+
+
+
+
+
+
+
+) + export default PageContent \ No newline at end of file diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx index cd1fd5f2..3d537236 100644 --- a/src-migrate/modules/popup-information/index.tsx +++ b/src-migrate/modules/popup-information/index.tsx @@ -1,7 +1,7 @@ import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; -import Modal from '~/common/components/elements/Modal'; -import { getAuth } from '~/common/libs/auth'; +import { Modal } from "~/components/ui/modal" +import { getAuth } from '~/libs/auth'; import PageContent from '../page-content'; import Link from 'next/link'; diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx new file mode 100644 index 00000000..8cd96ce8 --- /dev/null +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -0,0 +1,104 @@ +import style from '../styles/product-card.module.css' + +import Link from 'next/link' +import React from 'react' +import Image from '~/components/ui/image' +import clsxm from '~/libs/clsxm' +import formatCurrency from '~/libs/formatCurrency' +import { formatToShortText } from '~/libs/formatNumber' +import { createSlug } from '~/libs/slug' +import { IProduct } from '~/types/product' + +type Props = { + product: IProduct + layout?: 'vertical' | 'horizontal' +} + +const ProductCard = ({ product, layout = 'vertical' }: Props) => { + const URL = { + product: createSlug('/shop/product/', product.name, product.id.toString()), + manufacture: createSlug('/shop/brands/', product.manufacture.name, product.manufacture.id.toString()), + } + + return ( +
+
+ + {product.name} + +
+ +
+ + {product.manufacture.name} + + +
+ + + {product.name} + +
+ +
+ Rp {formatCurrency(product.lowest_price.price)} +
+ +
+ +
+ Inc PPN: + Rp {formatCurrency(Math.round(product.lowest_price.price * 1.11))} +
+ +
+ +
+ {product.stock_total > 0 && ( +
+ Ready Stock +
+ )} + {product.qty_sold > 0 && ( +
+ {formatToShortText(product.qty_sold)} Terjual +
+ )} +
+ +
+
+ ) +} + +const classPrefix = ({ layout }: Props) => { + +} + +export default ProductCard \ No newline at end of file diff --git a/src-migrate/modules/product-card/index.tsx b/src-migrate/modules/product-card/index.tsx new file mode 100644 index 00000000..c87167bc --- /dev/null +++ b/src-migrate/modules/product-card/index.tsx @@ -0,0 +1,3 @@ +import ProductCard from "./components/ProductCard"; + +export default ProductCard \ No newline at end of file diff --git a/src-migrate/modules/product-card/styles/product-card.module.css b/src-migrate/modules/product-card/styles/product-card.module.css new file mode 100644 index 00000000..38b895f9 --- /dev/null +++ b/src-migrate/modules/product-card/styles/product-card.module.css @@ -0,0 +1,50 @@ +.wrapper { + @apply w-full flex; +} +.wrapper-v { + @apply flex-col border border-gray-300 rounded-md h-[350px]; +} +.wrapper-h { + @apply flex-row gap-x-2 pt-4; +} + +.image-v { + @apply w-full h-48 px-4 border-b border-gray-300; +} +.image-h { + @apply w-4/12 h-24 px-1; +} + +.content-v { + @apply w-full p-2; +} +.content-h { + @apply w-8/12; +} + +.brand { + @apply text-danger-500 font-medium block; +} + +.name { + @apply text-gray-700 font-medium line-clamp-3; +} +.name-v { + @apply min-h-[64px]; +} +.name-h { + @apply min-h-[32px]; +} + +.price { + @apply text-danger-500 font-medium; +} + +.ready-stock { + @apply bg-danger-500 text-white text-[11px] px-2 py-1 rounded-md; +} + +.price-inc, +.sold { + @apply text-gray-600 text-[11px]; +} diff --git a/src-migrate/modules/product-detail/components/AddToCart.tsx b/src-migrate/modules/product-detail/components/AddToCart.tsx new file mode 100644 index 00000000..4accab17 --- /dev/null +++ b/src-migrate/modules/product-detail/components/AddToCart.tsx @@ -0,0 +1,79 @@ +import React from 'react' +import { Button, useToast } from '@chakra-ui/react' +import { getAuth } from '~/libs/auth' +import { useRouter } from 'next/router' +import Link from 'next/link' +import { upsertUserCart } from '~/services/cart' + +type Props = { + variantId: number | null, + quantity?: number; + source?: 'buy' | 'add_to_cart'; +} + +const AddToCart = ({ + variantId, + quantity = 1, + source = 'add_to_cart' +}: Props) => { + const auth = getAuth() + const router = useRouter() + const toast = useToast({ + position: 'top', + isClosable: true + }) + + const handleClick = async () => { + if (typeof auth !== 'object') { + const currentUrl = encodeURIComponent(router.asPath) + toast({ + title: 'Masuk Akun', + description: <> + Masuk akun untuk dapat menambahkan barang ke keranjang belanja. {' '} + Klik disini + , + status: 'error', + duration: 4000, + }) + return; + } + + if ( + !variantId || + isNaN(quantity) || + typeof auth !== 'object' + ) return; + + toast.promise( + upsertUserCart(auth.id, 'product', variantId, quantity, true, source), + { + loading: { title: 'Menambahkan ke keranjang', description: 'Mohon tunggu...' }, + success: { title: 'Menambahkan ke keranjang', description: 'Berhasil menambahkan ke keranjang belanja' }, + error: { title: 'Menambahkan ke keranjang', description: 'Gagal menambahkan ke keranjang belanja' }, + } + ) + + if (source === 'buy') { + router.push('/shop/checkout?source=buy') + } + } + + const btnConfig = { + 'add_to_cart': { + colorScheme: 'yellow', + text: 'Keranjang' + }, + 'buy': { + colorScheme: 'red', + text: 'Beli' + } + } + + return ( + + ) +} + +export default AddToCart \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/AddToWishlist.tsx b/src-migrate/modules/product-detail/components/AddToWishlist.tsx new file mode 100644 index 00000000..eab3c7be --- /dev/null +++ b/src-migrate/modules/product-detail/components/AddToWishlist.tsx @@ -0,0 +1,17 @@ +import { Button } from '@chakra-ui/react' +import { HeartIcon } from 'lucide-react' +import React from 'react' + +const AddToWishlist = () => { + return ( + + ) +} + +export default AddToWishlist \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx new file mode 100644 index 00000000..361580ea --- /dev/null +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -0,0 +1,37 @@ +import React from 'react' +import { InfoIcon } from 'lucide-react' +import { Tooltip } from '@chakra-ui/react' + +import { IProductDetail } from '~/types/product' +import ImageUI from '~/components/ui/image' + +type Props = { + product: IProductDetail +} + +const Image = ({ product }: Props) => { + return ( +
+ +
+ +
+ +
+
+
+
+ ) +} + +export default Image \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx new file mode 100644 index 00000000..fd0e0b3c --- /dev/null +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -0,0 +1,84 @@ +import style from '../styles/information.module.css' + +import React from 'react' +import dynamic from 'next/dynamic' +import Link from 'next/link' +import { useQuery } from 'react-query' + +import { IProductDetail } from '~/types/product' +import { IProductVariantSLA } from '~/types/productVariant' +import { createSlug } from '~/libs/slug' +import { getVariantSLA } from '~/services/productVariant' + +const Skeleton = dynamic(() => import('@chakra-ui/react').then((mod) => mod.Skeleton)) + +type Props = { + product: IProductDetail +} + +const Information = ({ product }: Props) => { + const querySLA = useQuery({ + queryKey: ['variant-sla', product.variants[0].id], + queryFn: () => getVariantSLA(product.variants[0].id), + enabled: product.variant_total === 1 + }) + + const sla = querySLA?.data + + return ( +
+
+
SKU Number
+
SKU-{product.id}
+
+ {/*
+
Part Number
+
{product.code || '-'}
+
*/} +
+
Manufacture
+
+ {!!product.manufacture.name ? ( + + {product.manufacture.name} + + ) : '-'} +
+
+ {/*
+
Preparation Time
+
+ {product.variant_total > 1 && 'Lihat Variant'} + {product.variant_total === 1 && ( + + {sla?.sla_date} + + )} +
+
+
+
Stock
+
+ {product.variant_total > 1 && 'Lihat Variant'} + {product.variant_total === 1 && ( + + {sla?.qty && sla.qty > 0 ? sla?.qty : '-'} + + )} +
+
+
+
Weight
+
+ {product.variant_total > 1 && 'Lihat Variant'} + {product.variant_total === 1 && (product.weight > 0 ? `${product.weight} kg` : '-')} +
+
*/} +
+ ) +} + +export default Information \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx new file mode 100644 index 00000000..8189e5bd --- /dev/null +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -0,0 +1,53 @@ +import style from '../styles/price-action.module.css' + +import React, { useEffect } from 'react' +import formatCurrency from '~/libs/formatCurrency' +import { formatToShortText } from '~/libs/formatNumber' +import { IProductDetail } from '~/types/product' +import { useProductDetail } from '../stores/useProductDetail' +import AddToCart from './AddToCart' + +type Props = { + product: IProductDetail +} + +const PriceAction = ({ product }: Props) => { + const { activePrice, setActive, activeVariantId, quantityInput, setQuantityInput } = useProductDetail() + + useEffect(() => { + setActive(product.variants[0]) + }, [product, setActive]); + + return ( +
+ {product.qty_sold > 0 && ( +
+ {formatToShortText(product.qty_sold)} Terjual +
+ )} +
+
+ Rp {formatCurrency(activePrice?.price || 0)} +
+
+
+ {!!activePrice && ( + <> + Termasuk PPN: {' '} + Rp {formatCurrency(Math.round(activePrice?.price * 1.11))} + + )} +
+ +
+ +
+ setQuantityInput(e.target.value)} className={style['quantity-input']} /> + + +
+
+ ) +} + +export default PriceAction \ No newline at end of file diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx new file mode 100644 index 00000000..b752a138 --- /dev/null +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -0,0 +1,126 @@ +import style from '../styles/product-detail.module.css' + +import React from 'react' +import Link from 'next/link' +import { MessageCircleIcon } from 'lucide-react' +import { Button } from '@chakra-ui/react' + +import { IProductDetail } from '~/types/product' + +import ProductImage from './Image' +import Information from './Information' +import AddToWishlist from './AddToWishlist' +import VariantList from './VariantList' +import SimilarSide from './SimilarSide' +import SimilarBottom from './SimilarBottom' +import useDevice from '@/core/hooks/useDevice' +import PriceAction from './PriceAction' + +type Props = { + product: IProductDetail +} + +const ProductDetail = ({ product }: Props) => { + const { isDesktop, isMobile } = useDevice() + + return ( + <> +
+
+
+
+ +
+ +
+
+ +

+ {product.name} +

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

+ Variant ({product.variant_total}) +

+
+ +
+ +
+ +
+

+ Informasi Produk +

+
+

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