summaryrefslogtreecommitdiff
path: root/src-migrate
diff options
context:
space:
mode:
authorRafi Zadanly <zadanlyr@gmail.com>2024-01-13 10:35:22 +0700
committerRafi Zadanly <zadanlyr@gmail.com>2024-01-13 10:35:22 +0700
commitf62b2345f463695ef0f8f79830cd76b6e0332821 (patch)
treec06ff12a8312e3a02b0203f588db0f4da044c911 /src-migrate
parentee0b5893ac039ab05fe8247647364a923d707da3 (diff)
Refactor src migrate folder
Diffstat (limited to 'src-migrate')
-rw-r--r--src-migrate/common/components/skeleton/PageContentSkeleton.tsx19
-rw-r--r--src-migrate/common/types/productVariant.ts13
-rw-r--r--src-migrate/components/seo.tsx (renamed from src-migrate/common/components/elements/Seo.tsx)6
-rw-r--r--src-migrate/components/ui/image.tsx46
-rw-r--r--src-migrate/components/ui/modal.tsx (renamed from src-migrate/common/components/elements/Modal.tsx)19
-rw-r--r--src-migrate/components/ui/re-captcha.tsx (renamed from src-migrate/common/components/elements/ReCaptcha.tsx)6
-rw-r--r--src-migrate/constants/menu.ts (renamed from src-migrate/common/constants/menu.ts)14
-rw-r--r--src-migrate/libs/auth.ts (renamed from src-migrate/common/libs/auth.ts)2
-rw-r--r--src-migrate/libs/clsxm.ts (renamed from src-migrate/common/libs/clsxm.ts)0
-rw-r--r--src-migrate/libs/formatCurrency.ts (renamed from src-migrate/common/libs/formatCurrency.ts)0
-rw-r--r--src-migrate/libs/formatNumber.ts8
-rw-r--r--src-migrate/libs/odooApi.ts (renamed from src-migrate/common/libs/odooApi.ts)2
-rw-r--r--src-migrate/libs/slug.ts34
-rw-r--r--src-migrate/libs/toTitleCase.ts5
-rw-r--r--src-migrate/modules/account-activation/components/FormEmail.tsx6
-rw-r--r--src-migrate/modules/account-activation/components/FormOTP.tsx6
-rw-r--r--src-migrate/modules/account-activation/components/FormToken.tsx6
-rw-r--r--src-migrate/modules/cart/components/Item.tsx4
-rw-r--r--src-migrate/modules/cart/components/ItemAction.tsx4
-rw-r--r--src-migrate/modules/cart/components/ItemPromo.tsx2
-rw-r--r--src-migrate/modules/cart/components/ItemSelect.tsx4
-rw-r--r--src-migrate/modules/cart/components/Summary.tsx4
-rw-r--r--src-migrate/modules/cart/stores/useCartStore.ts2
-rw-r--r--src-migrate/modules/header/components/HeaderDesktop.tsx2
-rw-r--r--src-migrate/modules/page-content/index.tsx19
-rw-r--r--src-migrate/modules/popup-information/index.tsx4
-rw-r--r--src-migrate/modules/product-card/components/ProductCard.tsx104
-rw-r--r--src-migrate/modules/product-card/index.tsx3
-rw-r--r--src-migrate/modules/product-card/styles/product-card.module.css50
-rw-r--r--src-migrate/modules/product-detail/components/AddToCart.tsx79
-rw-r--r--src-migrate/modules/product-detail/components/AddToWishlist.tsx17
-rw-r--r--src-migrate/modules/product-detail/components/Image.tsx37
-rw-r--r--src-migrate/modules/product-detail/components/Information.tsx84
-rw-r--r--src-migrate/modules/product-detail/components/PriceAction.tsx53
-rw-r--r--src-migrate/modules/product-detail/components/ProductDetail.tsx126
-rw-r--r--src-migrate/modules/product-detail/components/SimilarBottom.tsx21
-rw-r--r--src-migrate/modules/product-detail/components/SimilarSide.tsx34
-rw-r--r--src-migrate/modules/product-detail/components/VariantList.tsx85
-rw-r--r--src-migrate/modules/product-detail/index.ts3
-rw-r--r--src-migrate/modules/product-detail/stores/useProductDetail.ts25
-rw-r--r--src-migrate/modules/product-detail/styles/information.module.css19
-rw-r--r--src-migrate/modules/product-detail/styles/price-action.module.css12
-rw-r--r--src-migrate/modules/product-detail/styles/product-detail.module.css15
-rw-r--r--src-migrate/modules/product-detail/styles/side-similar.module.css3
-rw-r--r--src-migrate/modules/product-detail/styles/variant-list.module.css19
-rw-r--r--src-migrate/modules/product-promo/components/AddToCart.tsx4
-rw-r--r--src-migrate/modules/product-promo/components/Card.tsx8
-rw-r--r--src-migrate/modules/product-promo/components/CardCountdown.tsx4
-rw-r--r--src-migrate/modules/product-promo/components/CategoryTab.tsx4
-rw-r--r--src-migrate/modules/product-promo/components/Item.tsx2
-rw-r--r--src-migrate/modules/product-promo/components/Modal.tsx2
-rw-r--r--src-migrate/modules/product-promo/components/ModalContent.tsx2
-rw-r--r--src-migrate/modules/product-promo/components/Section.tsx2
-rw-r--r--src-migrate/modules/product-promo/stores/useModalStore.ts2
-rw-r--r--src-migrate/modules/product-similar/hooks/useProductSimilar.tsx15
-rw-r--r--src-migrate/modules/product-slider/components/ProductSlider.tsx42
-rw-r--r--src-migrate/modules/product-slider/index.ts3
-rw-r--r--src-migrate/modules/register/components/Form.tsx4
-rw-r--r--src-migrate/modules/register/components/FormCaptcha.tsx4
-rw-r--r--src-migrate/modules/register/components/TermCondition.tsx4
-rw-r--r--src-migrate/modules/register/stores/useRegisterStore.ts (renamed from src-migrate/common/stores/useRegisterStore.ts)4
-rw-r--r--src-migrate/pages/_app.tsx4
-rw-r--r--src-migrate/pages/api/product-variant/[id].tsx2
-rw-r--r--src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx2
-rw-r--r--src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx2
-rw-r--r--src-migrate/pages/api/promotion-program/[id].tsx3
-rw-r--r--src-migrate/pages/register.tsx2
-rw-r--r--src-migrate/pages/shop/cart/cart.module.css (renamed from src-migrate/pages/shop/cart.module.css)0
-rw-r--r--src-migrate/pages/shop/cart/index.tsx (renamed from src-migrate/pages/shop/cart.tsx)4
-rw-r--r--src-migrate/pages/shop/product/[slug].tsx73
-rw-r--r--src-migrate/pages/shop/product/product.module.css (renamed from src-migrate/common/libs/parse)0
-rw-r--r--src-migrate/services/auth.ts4
-rw-r--r--src-migrate/services/cart.ts2
-rw-r--r--src-migrate/services/checkout.ts2
-rw-r--r--src-migrate/services/pageContent.ts2
-rw-r--r--src-migrate/services/product.ts59
-rw-r--r--src-migrate/services/productVariant.ts (renamed from src-migrate/services/variant.ts)11
-rw-r--r--src-migrate/services/promotionProgram.ts2
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Black.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-Black.woff)bin138764 -> 138764 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Black.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-Black.woff2)bin102868 -> 102868 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff)bin146824 -> 146824 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2)bin108752 -> 108752 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Bold.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-Bold.woff)bin143208 -> 143208 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Bold.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2)bin106140 -> 106140 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff)bin151052 -> 151052 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2)bin111808 -> 111808 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff)bin142920 -> 142920 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2)bin106108 -> 106108 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff)bin150628 -> 150628 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2)bin111708 -> 111708 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff)bin140724 -> 140724 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2)bin104232 -> 104232 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff)bin149996 -> 149996 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2)bin111392 -> 111392 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Italic.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-Italic.woff)bin144372 -> 144372 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Italic.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2)bin106876 -> 106876 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Light.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-Light.woff)bin140632 -> 140632 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Light.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-Light.woff2)bin104332 -> 104332 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-LightItalic.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff)bin150092 -> 150092 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-LightItalic.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2)bin111332 -> 111332 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Medium.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-Medium.woff)bin142552 -> 142552 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Medium.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2)bin105924 -> 105924 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff)bin150988 -> 150988 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2)bin112184 -> 112184 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Regular.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-Regular.woff)bin133844 -> 133844 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Regular.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2)bin98868 -> 98868 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-SemiBold.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff)bin142932 -> 142932 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-SemiBold.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2)bin105804 -> 105804 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff)bin151180 -> 151180 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2)bin112048 -> 112048 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Thin.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-Thin.woff)bin135920 -> 135920 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-Thin.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2)bin99632 -> 99632 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff (renamed from src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff)bin145480 -> 145480 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2)bin106496 -> 106496 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-italic.var.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2)bin245036 -> 245036 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter-roman.var.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2)bin227180 -> 227180 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/Inter.var.woff2 (renamed from src-migrate/common/styles/fonts/Inter/Inter.var.woff2)bin324864 -> 324864 bytes
-rw-r--r--src-migrate/styles/fonts/Inter/inter.css (renamed from src-migrate/common/styles/fonts/Inter/inter.css)0
-rw-r--r--src-migrate/styles/globals.css (renamed from src-migrate/common/styles/globals.css)0
-rw-r--r--src-migrate/types/auth.ts (renamed from src-migrate/common/types/auth.ts)2
-rw-r--r--src-migrate/types/cart.ts (renamed from src-migrate/common/types/cart.ts)0
-rw-r--r--src-migrate/types/checkout.ts (renamed from src-migrate/common/types/checkout.ts)0
-rw-r--r--src-migrate/types/nav.ts (renamed from src-migrate/common/types/nav.ts)0
-rw-r--r--src-migrate/types/odoo.ts (renamed from src-migrate/common/types/odoo.ts)0
-rw-r--r--src-migrate/types/pageContent.ts (renamed from src-migrate/common/types/pageContent.ts)0
-rw-r--r--src-migrate/types/product.ts39
-rw-r--r--src-migrate/types/productVariant.ts33
-rw-r--r--src-migrate/types/promotion.ts (renamed from src-migrate/common/types/promotion.ts)15
-rw-r--r--src-migrate/types/promotionProgram.ts (renamed from src-migrate/common/types/promotionProgram.ts)0
-rw-r--r--src-migrate/types/solr.ts (renamed from src-migrate/common/types/solr.ts)0
-rw-r--r--src-migrate/validations/auth.ts (renamed from src-migrate/common/validations/auth.ts)0
131 files changed, 1268 insertions, 127 deletions
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 (
- <div className="animate-pulse grid gap-y-4">
- <div className="w-full h-10 bg-gray-300 rounded" />
- <div className="h-2" />
- <div className="w-full h-4 bg-gray-300 rounded" />
- <div className="w-full h-4 bg-gray-300 rounded" />
- <div className="w-full h-4 bg-gray-300 rounded" />
- <div className="w-8/12 h-4 bg-gray-300 rounded" />
- <div className="h-2" />
- <div className="w-full h-4 bg-gray-300 rounded" />
- <div className="w-full h-4 bg-gray-300 rounded" />
- <div className="w-full h-4 bg-gray-300 rounded" />
- <div className="w-1/2 h-4 bg-gray-300 rounded" />
- </div>
- )
-}
-
-export default PageContentSkeleton \ No newline at end of file
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/components/elements/Seo.tsx b/src-migrate/components/seo.tsx
index 2245663a..1e78ed4d 100644
--- a/src-migrate/common/components/elements/Seo.tsx
+++ b/src-migrate/components/seo.tsx
@@ -3,7 +3,7 @@ import React from 'react'
import { NextSeo } from "next-seo"
import { MetaTag, NextSeoProps } from 'next-seo/lib/types';
-const Seo = (props: NextSeoProps) => {
+export const Seo = (props: NextSeoProps) => {
const router = useRouter()
const additionalMetaTags: MetaTag[] = [
@@ -29,6 +29,4 @@ const Seo = (props: NextSeoProps) => {
additionalMetaTags={additionalMetaTags}
/>
)
-}
-
-export default Seo \ No newline at end of file
+} \ 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 (
+ <div
+ className={clsx(
+ 'overflow-hidden',
+ isLoading ? 'animate-pulse' : '',
+ rounded,
+ classNames?.wrapper
+ )}
+ >
+ <NextImage
+ className={clsxm(
+ 'duration-700 ease-in-out',
+ isLoading
+ ? 'scale-[1.02] blur-xl grayscale'
+ : 'scale-100 blur-0 grayscale-0',
+ rounded,
+ className
+ )}
+ src={src}
+ alt={alt}
+ loading='lazy'
+ quality={100}
+ onLoadingComplete={() => setLoading(false)}
+ {...rest}
+ />
+ </div>
+ );
+};
+export default Image; \ No newline at end of file
diff --git a/src-migrate/common/components/elements/Modal.tsx b/src-migrate/components/ui/modal.tsx
index c9c621e0..34e1d1c3 100644
--- a/src-migrate/common/components/elements/Modal.tsx
+++ b/src-migrate/components/ui/modal.tsx
@@ -1,13 +1,12 @@
-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 { useRouter } from "next/router";
+import { AnimatePresence, motion } from "framer-motion"
import { useWindowSize } from "usehooks-ts";
-import clsxm from "~/common/libs/clsxm";
-
+import { XMarkIcon } from "@heroicons/react/24/outline";
+import clsxm from "~/libs/clsxm";
-type Props = {
+export interface ModalProps {
children: React.ReactNode
active: boolean
title?: string
@@ -16,14 +15,14 @@ type Props = {
mode?: "mobile" | "desktop"
}
-const Modal = ({
+export const Modal = ({
children,
active = false,
title,
close,
className,
mode
-}: Props) => {
+}: ModalProps) => {
const router = useRouter()
const { width } = useWindowSize()
const [rendered, setRendered] = useState<boolean>(false)
@@ -85,6 +84,4 @@ const Modal = ({
</AnimatePresence>,
document.querySelector('body')!
)
-}
-
-export default Modal \ No newline at end of file
+} \ No newline at end of file
diff --git a/src-migrate/common/components/elements/ReCaptcha.tsx b/src-migrate/components/ui/re-captcha.tsx
index 1bc31d90..e31aa1e3 100644
--- a/src-migrate/common/components/elements/ReCaptcha.tsx
+++ b/src-migrate/components/ui/re-captcha.tsx
@@ -2,16 +2,14 @@ import ReCAPTCHA, { ReCAPTCHAProps } from "react-google-recaptcha"
const GOOGLE_RECAPTCHA_KEY = process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE || ''
-type Props = Omit<ReCAPTCHAProps, 'sitekey'> & {
+export interface ReCaptchaProps extends Omit<ReCAPTCHAProps, 'sitekey'> {
sitekey?: string;
}
-const ReCaptcha = (props: Props) => {
+export const ReCaptcha = (props: ReCaptchaProps) => {
const { sitekey, ...rest } = props
return (
<ReCAPTCHA sitekey={sitekey || GOOGLE_RECAPTCHA_KEY} {...rest} />
)
}
-
-export default ReCaptcha \ No newline at end of file
diff --git a/src-migrate/common/constants/menu.ts b/src-migrate/constants/menu.ts
index 853da507..d1adebca 100644
--- a/src-migrate/common/constants/menu.ts
+++ b/src-migrate/constants/menu.ts
@@ -1,20 +1,20 @@
-import { SecondaryNavItemProps } from '../types/nav'
+import { SecondaryNavItemProps } from '~/types/nav';
export const SECONDARY_MENU_ITEMS: SecondaryNavItemProps[] = [
{
label: 'Semua Brand',
- href: '/shop/brands'
+ href: '/shop/brands',
},
{
label: 'Ready Stock',
- href: '/shop/search?orderBy=stock'
+ href: '/shop/search?orderBy=stock',
},
{
label: 'Blog Indoteknik',
- href: 'https://blog.indoteknik.com/'
+ href: 'https://blog.indoteknik.com/',
},
{
label: 'Indoteknik TV',
- href: '/video'
- }
-]
+ href: '/video',
+ },
+];
diff --git a/src-migrate/common/libs/auth.ts b/src-migrate/libs/auth.ts
index fb4e836a..86ce26e1 100644
--- a/src-migrate/common/libs/auth.ts
+++ b/src-migrate/libs/auth.ts
@@ -1,5 +1,5 @@
import { deleteCookie, getCookie, setCookie } from 'cookies-next';
-import { AuthProps } from '../types/auth';
+import { AuthProps } from '~/types/auth';
const COOKIE_KEY = 'auth';
diff --git a/src-migrate/common/libs/clsxm.ts b/src-migrate/libs/clsxm.ts
index 0fc10317..0fc10317 100644
--- a/src-migrate/common/libs/clsxm.ts
+++ b/src-migrate/libs/clsxm.ts
diff --git a/src-migrate/common/libs/formatCurrency.ts b/src-migrate/libs/formatCurrency.ts
index 41db4a6f..41db4a6f 100644
--- a/src-migrate/common/libs/formatCurrency.ts
+++ b/src-migrate/libs/formatCurrency.ts
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/common/libs/odooApi.ts b/src-migrate/libs/odooApi.ts
index 2dbc18d3..9482542b 100644
--- a/src-migrate/common/libs/odooApi.ts
+++ b/src-migrate/libs/odooApi.ts
@@ -1,7 +1,7 @@
import axios, { AxiosRequestConfig, Method } from 'axios';
import { getCookie, setCookie } from 'cookies-next';
import { getAuth } from './auth';
-import { AuthApiProps, AuthProps } from '../types/auth';
+import { AuthApiProps } from '~/types/auth';
const ODOO_HOST = process.env.NEXT_PUBLIC_ODOO_API_HOST as string;
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 = () => (
+ <div className="animate-pulse grid gap-y-4">
+ <div className="w-full h-10 bg-gray-300 rounded" />
+ <div className="h-2" />
+ <div className="w-full h-4 bg-gray-300 rounded" />
+ <div className="w-full h-4 bg-gray-300 rounded" />
+ <div className="w-full h-4 bg-gray-300 rounded" />
+ <div className="w-8/12 h-4 bg-gray-300 rounded" />
+ <div className="h-2" />
+ <div className="w-full h-4 bg-gray-300 rounded" />
+ <div className="w-full h-4 bg-gray-300 rounded" />
+ <div className="w-full h-4 bg-gray-300 rounded" />
+ <div className="w-1/2 h-4 bg-gray-300 rounded" />
+ </div>
+)
+
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 (
+ <div className={clsxm(style['wrapper'], {
+ [style['wrapper-v']]: layout === 'vertical',
+ [style['wrapper-h']]: layout === 'horizontal',
+ })}
+ >
+ <div className={clsxm({
+ [style['image-v']]: layout === 'vertical',
+ [style['image-h']]: layout === 'horizontal',
+ })}>
+ <Link href={URL.product}>
+ <Image
+ src={product.image || '/images/noimage.jpeg'}
+ alt={product.name}
+ width={128}
+ height={128}
+ className='object-contain object-center h-full w-full'
+ classNames={{ wrapper: 'h-full' }}
+ />
+ </Link>
+ </div>
+
+ <div className={clsxm({
+ [style['content-v']]: layout === 'vertical',
+ [style['content-h']]: layout === 'horizontal',
+ })}>
+ <Link
+ href={URL.manufacture}
+ className={style['brand']}
+ >
+ {product.manufacture.name}
+ </Link>
+
+ <div className='h-0.5' />
+
+ <Link
+ href={URL.product}
+ className={clsxm(style['name'], {
+ [style['name-v']]: layout === 'vertical',
+ [style['name-h']]: layout === 'horizontal',
+ })}
+ >
+ {product.name}
+ </Link>
+ <div className='h-1.5' />
+
+ <div className={style['price']}>
+ Rp {formatCurrency(product.lowest_price.price)}
+ </div>
+
+ <div className='h-1.5' />
+
+ <div className={style['price-inc']}>
+ Inc PPN:
+ Rp {formatCurrency(Math.round(product.lowest_price.price * 1.11))}
+ </div>
+
+ <div className='h-1' />
+
+ <div className='flex items-center gap-x-2.5'>
+ {product.stock_total > 0 && (
+ <div className={style['ready-stock']}>
+ Ready Stock
+ </div>
+ )}
+ {product.qty_sold > 0 && (
+ <div className={style['sold']}>
+ {formatToShortText(product.qty_sold)} Terjual
+ </div>
+ )}
+ </div>
+
+ </div>
+ </div>
+ )
+}
+
+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. {' '}
+ <Link className='underline' href={`/login?next=${currentUrl}`}>Klik disini</Link>
+ </>,
+ 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 (
+ <Button onClick={handleClick} colorScheme={btnConfig[source].colorScheme} className='w-full'>
+ {btnConfig[source].text}
+ </Button>
+ )
+}
+
+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 (
+ <Button
+ variant='link'
+ className='!text-gray-500 !font-medium'
+ leftIcon={<HeartIcon size={18} />}
+ >
+ Wishlist
+ </Button>
+ )
+}
+
+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 (
+ <div className='h-[340px] border border-gray-200 rounded-lg p-2 relative'>
+ <ImageUI
+ src={product.image || '/images/noimage.jpeg'}
+ alt={product.name}
+ width={512}
+ height={512}
+ className='object-contain object-center h-full'
+ classNames={{ wrapper: 'h-full' }}
+ />
+ <div className='absolute hidden md:block top-4 right-4'>
+ <Tooltip
+ placement='bottom-end'
+ label='Gambar atau foto berperan sebagai ilustrasi produk. Kadang tidak sesuai dengan kondisi terbaru dengan berbagai perubahan dan perbaikan. Hubungi admin kami untuk informasi yang lebih baik perihal gambar.'
+ >
+ <div className="text-gray-600">
+ <InfoIcon size={20} />
+ </div>
+ </Tooltip>
+ </div>
+ </div>
+ )
+}
+
+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<IProductVariantSLA>({
+ queryKey: ['variant-sla', product.variants[0].id],
+ queryFn: () => getVariantSLA(product.variants[0].id),
+ enabled: product.variant_total === 1
+ })
+
+ const sla = querySLA?.data
+
+ return (
+ <div className={style['wrapper']}>
+ <div className={style['row']}>
+ <div className={style['label']}>SKU Number</div>
+ <div className={style['value']}>SKU-{product.id}</div>
+ </div>
+ {/* <div className={style['row']}>
+ <div className={style['label']}>Part Number</div>
+ <div className={style['value']}>{product.code || '-'}</div>
+ </div> */}
+ <div className={style['row']}>
+ <div className={style['label']}>Manufacture</div>
+ <div className={style['value']}>
+ {!!product.manufacture.name ? (
+ <Link
+ href={createSlug('/shop/brands/', product.manufacture.name, product.manufacture.id.toString())}
+ className='text-danger-500 hover:underline'
+ >
+ {product.manufacture.name}
+ </Link>
+ ) : '-'}
+ </div>
+ </div>
+ {/* <div className={style['row']}>
+ <div className={style['label']}>Preparation Time</div>
+ <div className={style['value']}>
+ {product.variant_total > 1 && 'Lihat Variant'}
+ {product.variant_total === 1 && (
+ <Skeleton isLoaded={querySLA.isSuccess} w={querySLA.isSuccess ? '100%' : '40px'} h='100%'>
+ {sla?.sla_date}
+ </Skeleton>
+ )}
+ </div>
+ </div>
+ <div className={style['row']}>
+ <div className={style['label']}>Stock</div>
+ <div className={style['value']}>
+ {product.variant_total > 1 && 'Lihat Variant'}
+ {product.variant_total === 1 && (
+ <Skeleton isLoaded={querySLA.isSuccess} w={querySLA.isSuccess ? '100%' : '10px'} h='100%'>
+ {sla?.qty && sla.qty > 0 ? sla?.qty : '-'}
+ </Skeleton>
+ )}
+ </div>
+ </div>
+ <div className={style['row']}>
+ <div className={style['label']}>Weight</div>
+ <div className={style['value']}>
+ {product.variant_total > 1 && 'Lihat Variant'}
+ {product.variant_total === 1 && (product.weight > 0 ? `${product.weight} kg` : '-')}
+ </div>
+ </div> */}
+ </div>
+ )
+}
+
+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 (
+ <div className='block md:sticky top-[150px] bg-white py-0 md:py-6 z-10'>
+ {product.qty_sold > 0 && (
+ <div className={style['secondary-text']}>
+ {formatToShortText(product.qty_sold)} Terjual
+ </div>
+ )}
+ <div className='h-2' />
+ <div className={style['main-price']}>
+ Rp {formatCurrency(activePrice?.price || 0)}
+ </div>
+ <div className='h-1' />
+ <div className={style['secondary-text']}>
+ {!!activePrice && (
+ <>
+ Termasuk PPN: {' '}
+ Rp {formatCurrency(Math.round(activePrice?.price * 1.11))}
+ </>
+ )}
+ </div>
+
+ <div className='h-4' />
+
+ <div className={style['action-wrapper']}>
+ <input type='number' value={quantityInput} onChange={(e) => setQuantityInput(e.target.value)} className={style['quantity-input']} />
+ <AddToCart variantId={activeVariantId} quantity={Number(quantityInput)} />
+ <AddToCart source='buy' variantId={activeVariantId} quantity={Number(quantityInput)} />
+ </div>
+ </div>
+ )
+}
+
+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 (
+ <>
+ <div className='md:flex md:flex-wrap'>
+ <div className='md:w-9/12 md:flex md:flex-col md:pr-4'>
+ <div className='md:flex md:flex-wrap'>
+ <div className="md:w-4/12">
+ <ProductImage product={product} />
+ </div>
+
+ <div className='md:w-8/12 px-4 md:pl-6'>
+ <div className='h-6 md:h-0' />
+
+ <h1 className={style['title']}>
+ {product.name}
+ </h1>
+
+ <div className='h-6 md:h-8' />
+
+ <Information product={product} />
+
+ <div className='h-4' />
+
+ <Button
+ as={Link}
+ href=''
+ variant='link'
+ colorScheme='red'
+ leftIcon={<MessageCircleIcon size={18} />}
+ >
+ Ask Admin
+ </Button>
+ </div>
+ </div>
+
+ <div className='h-full'>
+ {isMobile && (
+ <div className='px-4 pt-6'>
+ <PriceAction product={product} />
+ </div>
+ )}
+
+ <div className='h-4 md:h-10'></div>
+
+ <div className={style['section-card']}>
+ <h2 className={style['heading']}>
+ Variant ({product.variant_total})
+ </h2>
+ <div className='h-4' />
+ <VariantList variants={product.variants} />
+ </div>
+
+ <div className='h-0 md:h-6'></div>
+
+ <div className={style['section-card']}>
+ <h2 className={style['heading']}>
+ Informasi Produk
+ </h2>
+ <div className='h-4' />
+ <div
+ className={style['description']}
+ dangerouslySetInnerHTML={{ __html: !product.description || product.description == '<p><br></p>' ? 'Belum ada deskripsi' : product.description }}
+ />
+ </div>
+ </div>
+ </div>
+
+ {isDesktop && (
+ <div className="md:w-3/12">
+ <PriceAction product={product} />
+
+ <AddToWishlist />
+
+ <div className='h-8' />
+
+ <div className={style['heading']}>
+ Produk Serupa
+ </div>
+
+ <div className='h-4' />
+
+ <SimilarSide product={product} />
+ </div>
+ )}
+
+ <div className='md:w-full py-0 md:py-10 px-4 md:px-0'>
+ <div className={style['heading']}>
+ Kamu Mungkin Juga Suka
+ </div>
+
+ <div className='h-6' />
+
+ <SimilarBottom product={product} />
+ </div>
+
+ <div className='h-6 md:h-0' />
+ </div>
+ </>
+ )
+}
+
+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 <ProductSlider products={products} productLayout='vertical' />;
+}
+
+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 (
+ <div className={style['wrapper']}>
+ {products.map((product) => (
+ <ProductCard
+ key={product.id}
+ product={product}
+ layout='horizontal'
+ />
+ ))}
+ </div>
+ )
+}
+
+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 (
+ <div className='overflow-auto'>
+ <div className={style['wrapper']}>
+ <div className={style['header']}>
+ <div className="w-2/12 sticky left-0 bg-gray-200">Part Number</div>
+ <div className="w-2/12">Variant</div>
+ <div className="w-1/12">Stock</div>
+ <div className="w-2/12">Time</div>
+ <div className="w-1/12">Weight</div>
+ <div className="w-2/12">Price</div>
+ </div>
+ {variants.map((variant) => (
+ <LazyLoadComponent key={variant.id}>
+ <Row variant={variant} />
+ </LazyLoadComponent>
+ ))}
+ </div>
+ </div>
+ )
+}
+
+const Row = ({ variant }: { variant: IProductVariantDetail }) => {
+ const { activeVariantId, setActive } = useProductDetail()
+ const querySLA = useQuery<IProductVariantSLA>({
+ queryKey: ['variant-sla', variant.id],
+ queryFn: () => getVariantSLA(variant.id),
+ })
+
+ const sla = querySLA?.data
+
+ return (
+ <div className={style['row']}>
+ <div className='w-2/12 sticky left-0 bg-white'>{variant.code}</div>
+ <div className='w-2/12'>{variant.attributes.join(', ')}</div>
+ <div className='w-1/12'>
+ <Skeleton isLoaded={querySLA.isSuccess} h='21px' w={16}>
+ {sla?.qty}
+ </Skeleton>
+ </div>
+ <div className='w-2/12'>
+ <Skeleton isLoaded={querySLA.isSuccess} h='21px' w={16}>
+ {sla?.sla_date}
+ </Skeleton>
+ </div>
+ <div className='w-1/12'>
+ {variant.weight > 0 ? `${variant.weight} Kg` : '-'}
+ </div>
+ <div className='w-2/12'>
+ Rp {formatCurrency(variant.price.price)}
+ </div>
+ <div className='w-2/12'>
+ <Button
+ onClick={() => setActive(variant)}
+ size='sm'
+ w='100%'
+ className={clsxm(style['select-btn'], {
+ [style['select-btn--active']]: variant.id === activeVariantId
+ })}
+ >
+ Pilih
+ </Button>
+ </div>
+ </div>
+ )
+}
+
+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<State & Action>((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 (
+ <div>
+ <Swiper
+ slidesPerView={isDesktop ? 6.7 : 1.85}
+ spaceBetween={isDesktop ? 16 : 12}
+ prefix='product-slider'
+ modules={[FreeMode]}
+ freeMode={{ enabled: true, sticky: false }}
+ className='!pb-0.5'
+ >
+ {products.map((product) => (
+ <SwiperSlide key={product.id}>
+ <ProductCard
+ product={product}
+ layout={productLayout}
+ />
+ </SwiperSlide>
+ ))}
+ </Swiper>
+ </div >
+ )
+}
+
+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/common/stores/useRegisterStore.ts b/src-migrate/modules/register/stores/useRegisterStore.ts
index 90ce8a2b..d8abf52b 100644
--- a/src-migrate/common/stores/useRegisterStore.ts
+++ b/src-migrate/modules/register/stores/useRegisterStore.ts
@@ -1,6 +1,6 @@
import { create } from 'zustand';
-import { RegisterProps } from '../types/auth';
-import { registerSchema } from '../validations/auth';
+import { RegisterProps } from '~/types/auth';
+import { registerSchema } from '~/validations/auth';
import { ZodError } from 'zod';
type State = {
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/cart.module.css
index d523a55a..d523a55a 100644
--- a/src-migrate/pages/shop/cart.module.css
+++ b/src-migrate/pages/shop/cart/cart.module.css
diff --git a/src-migrate/pages/shop/cart.tsx b/src-migrate/pages/shop/cart/index.tsx
index 5016c9b5..397852f9 100644
--- a/src-migrate/pages/shop/cart.tsx
+++ b/src-migrate/pages/shop/cart/index.tsx
@@ -4,7 +4,7 @@ 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 { getAuth } from '~/libs/auth'
import { useCartStore } from '~/modules/cart/stores/useCartStore'
import CartItem from '~/modules/cart/components/Item'
@@ -64,6 +64,8 @@ const CartPage = () => {
colorScheme='yellow'
w='full'
isDisabled={hasSelectedPromo || !hasSelected}
+ as={Link}
+ href='/shop/quotation'
>
Quotation
</Button>
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<PageProps> = (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<PageProps> = ({ product }) => {
+ return (
+ <BasicLayout>
+ <Seo
+ title={`${product.name} - Indoteknik.com`}
+ description='Temukan pilihan produk B2B Industri &amp; Alat Teknik untuk Perusahaan, UMKM &amp; Pemerintah dengan lengkap, mudah dan transparan.'
+ openGraph={{
+ // url: process.env.NEXT_PUBLIC_SELF_HOST + router.asPath,
+ images: [
+ {
+ url: product?.image,
+ width: 800,
+ height: 800,
+ alt: product?.name,
+ },
+ ],
+ type: 'product',
+ }}
+ additionalMetaTags={[
+ {
+ name: 'keywords',
+ content: `${product?.name}, Harga ${product?.name}, Beli ${product?.name}, Spesifikasi ${product?.name}`,
+ }
+ ]}
+ // canonical=''
+ />
+
+ <div className='md:container pt-10'>
+ <ProductDetail product={product} />
+ </div>
+ </BasicLayout>
+ )
+}
+
+export default ProductDetailPage \ No newline at end of file
diff --git a/src-migrate/common/libs/parse b/src-migrate/pages/shop/product/product.module.css
index e69de29b..e69de29b 100644
--- a/src-migrate/common/libs/parse
+++ b/src-migrate/pages/shop/product/product.module.css
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<IProductDetail | null> => {
+ 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<GetProductSimilarRes> => {
+ 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/variant.ts b/src-migrate/services/productVariant.ts
index 213187d2..9fec4d1f 100644
--- a/src-migrate/services/variant.ts
+++ b/src-migrate/services/productVariant.ts
@@ -1,4 +1,6 @@
-import { CategoryPromo, IPromotion } from '~/common/types/promotion';
+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}`;
@@ -12,3 +14,10 @@ export const getVariantPromoByCategory = async (
const url = `/api/product-variant/${variantId}/promotion/${type}`;
return await fetch(url).then((res) => res.json());
};
+
+export const getVariantSLA = async (
+ variantId: number
+): Promise<IProductVariantSLA> => {
+ 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/common/styles/fonts/Inter/Inter-Black.woff b/src-migrate/styles/fonts/Inter/Inter-Black.woff
index a18593a0..a18593a0 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Black.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-Black.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Black.woff2 b/src-migrate/styles/fonts/Inter/Inter-Black.woff2
index 68f64c9e..68f64c9e 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Black.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-Black.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff b/src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff
index b6b01943..b6b01943 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff2
index 1c9c7ca8..1c9c7ca8 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-BlackItalic.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-BlackItalic.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff b/src-migrate/styles/fonts/Inter/Inter-Bold.woff
index eaf3d4bf..eaf3d4bf 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-Bold.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2 b/src-migrate/styles/fonts/Inter/Inter-Bold.woff2
index 2846f29c..2846f29c 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Bold.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-Bold.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff b/src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff
index 32750761..32750761 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff2
index 0b1fe8e1..0b1fe8e1 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-BoldItalic.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-BoldItalic.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff b/src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff
index c2c17ede..c2c17ede 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2 b/src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff2
index c24c2bdc..c24c2bdc 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBold.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-ExtraBold.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff b/src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff
index c42f7052..c42f7052 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2
index 4a81dc79..4a81dc79 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-ExtraBoldItalic.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff b/src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff
index d0de5f39..d0de5f39 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2 b/src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff2
index f2ea706f..f2ea706f 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLight.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-ExtraLight.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff b/src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff
index 81f1a28e..81f1a28e 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff2
index 9af717ba..9af717ba 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-ExtraLightItalic.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-ExtraLightItalic.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff b/src-migrate/styles/fonts/Inter/Inter-Italic.woff
index a806b382..a806b382 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-Italic.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2 b/src-migrate/styles/fonts/Inter/Inter-Italic.woff2
index a619fc54..a619fc54 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Italic.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-Italic.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Light.woff b/src-migrate/styles/fonts/Inter/Inter-Light.woff
index c496464d..c496464d 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Light.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-Light.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Light.woff2 b/src-migrate/styles/fonts/Inter/Inter-Light.woff2
index bc4be665..bc4be665 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Light.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-Light.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff b/src-migrate/styles/fonts/Inter/Inter-LightItalic.woff
index f84a9de3..f84a9de3 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-LightItalic.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-LightItalic.woff2
index 842b2dfc..842b2dfc 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-LightItalic.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-LightItalic.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff b/src-migrate/styles/fonts/Inter/Inter-Medium.woff
index d546843f..d546843f 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-Medium.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2 b/src-migrate/styles/fonts/Inter/Inter-Medium.woff2
index f92498a2..f92498a2 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Medium.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-Medium.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff b/src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff
index 459a6568..459a6568 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff2
index 0e3019f4..0e3019f4 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-MediumItalic.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-MediumItalic.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff b/src-migrate/styles/fonts/Inter/Inter-Regular.woff
index 62d3a618..62d3a618 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-Regular.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2 b/src-migrate/styles/fonts/Inter/Inter-Regular.woff2
index 6c2b6893..6c2b6893 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Regular.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-Regular.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff b/src-migrate/styles/fonts/Inter/Inter-SemiBold.woff
index a815f43a..a815f43a 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-SemiBold.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2 b/src-migrate/styles/fonts/Inter/Inter-SemiBold.woff2
index 611e90c9..611e90c9 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-SemiBold.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-SemiBold.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff b/src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff
index 909e43a9..909e43a9 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff2
index 545685bd..545685bd 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-SemiBoldItalic.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-SemiBoldItalic.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff b/src-migrate/styles/fonts/Inter/Inter-Thin.woff
index 62bc58cd..62bc58cd 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-Thin.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2 b/src-migrate/styles/fonts/Inter/Inter-Thin.woff2
index abbc3a5c..abbc3a5c 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-Thin.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-Thin.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff b/src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff
index 700a7f06..700a7f06 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff
+++ b/src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2 b/src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff2
index ab0b2002..ab0b2002 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-ThinItalic.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-ThinItalic.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2 b/src-migrate/styles/fonts/Inter/Inter-italic.var.woff2
index b826d5af..b826d5af 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-italic.var.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-italic.var.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2 b/src-migrate/styles/fonts/Inter/Inter-roman.var.woff2
index 6a256a06..6a256a06 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter-roman.var.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter-roman.var.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/Inter.var.woff2 b/src-migrate/styles/fonts/Inter/Inter.var.woff2
index 365eedc5..365eedc5 100644
--- a/src-migrate/common/styles/fonts/Inter/Inter.var.woff2
+++ b/src-migrate/styles/fonts/Inter/Inter.var.woff2
Binary files differ
diff --git a/src-migrate/common/styles/fonts/Inter/inter.css b/src-migrate/styles/fonts/Inter/inter.css
index de6ce273..de6ce273 100644
--- a/src-migrate/common/styles/fonts/Inter/inter.css
+++ b/src-migrate/styles/fonts/Inter/inter.css
diff --git a/src-migrate/common/styles/globals.css b/src-migrate/styles/globals.css
index ea20b247..ea20b247 100644
--- a/src-migrate/common/styles/globals.css
+++ b/src-migrate/styles/globals.css
diff --git a/src-migrate/common/types/auth.ts b/src-migrate/types/auth.ts
index 65fd06c7..464bc12a 100644
--- a/src-migrate/common/types/auth.ts
+++ b/src-migrate/types/auth.ts
@@ -1,4 +1,4 @@
-import { registerSchema } from '../validations/auth';
+import { registerSchema } from '~/validations/auth';
import { OdooApiProps } from './odoo';
import { z } from 'zod';
diff --git a/src-migrate/common/types/cart.ts b/src-migrate/types/cart.ts
index 3aceeac4..3aceeac4 100644
--- a/src-migrate/common/types/cart.ts
+++ b/src-migrate/types/cart.ts
diff --git a/src-migrate/common/types/checkout.ts b/src-migrate/types/checkout.ts
index dc1365d8..dc1365d8 100644
--- a/src-migrate/common/types/checkout.ts
+++ b/src-migrate/types/checkout.ts
diff --git a/src-migrate/common/types/nav.ts b/src-migrate/types/nav.ts
index ba97b1bf..ba97b1bf 100644
--- a/src-migrate/common/types/nav.ts
+++ b/src-migrate/types/nav.ts
diff --git a/src-migrate/common/types/odoo.ts b/src-migrate/types/odoo.ts
index b34bc667..b34bc667 100644
--- a/src-migrate/common/types/odoo.ts
+++ b/src-migrate/types/odoo.ts
diff --git a/src-migrate/common/types/pageContent.ts b/src-migrate/types/pageContent.ts
index 4361deb7..4361deb7 100644
--- a/src-migrate/common/types/pageContent.ts
+++ b/src-migrate/types/pageContent.ts
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/common/types/promotion.ts b/src-migrate/types/promotion.ts
index 1f8316cf..85190aad 100644
--- a/src-migrate/common/types/promotion.ts
+++ b/src-migrate/types/promotion.ts
@@ -1,5 +1,3 @@
-import { IProductVariant } from './productVariant';
-
export interface IPromotion {
id: number;
program_id: number;
@@ -23,7 +21,18 @@ export interface IPromotion {
}[];
}
-export interface IProductVariantPromo extends IProductVariant {
+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;
}
diff --git a/src-migrate/common/types/promotionProgram.ts b/src-migrate/types/promotionProgram.ts
index 205884b6..205884b6 100644
--- a/src-migrate/common/types/promotionProgram.ts
+++ b/src-migrate/types/promotionProgram.ts
diff --git a/src-migrate/common/types/solr.ts b/src-migrate/types/solr.ts
index d231c305..d231c305 100644
--- a/src-migrate/common/types/solr.ts
+++ b/src-migrate/types/solr.ts
diff --git a/src-migrate/common/validations/auth.ts b/src-migrate/validations/auth.ts
index 78fc5e71..78fc5e71 100644
--- a/src-migrate/common/validations/auth.ts
+++ b/src-migrate/validations/auth.ts