From 41f5aa212c69b61c2b101b682e77c8106849076e Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Fri, 9 Feb 2024 09:35:26 +0700 Subject: Add record activity on product --- src/core/components/layouts/BasicLayout.jsx | 39 ++++++++++++++++++----------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx index b6e2c59f..fa41a8ed 100644 --- a/src/core/components/layouts/BasicLayout.jsx +++ b/src/core/components/layouts/BasicLayout.jsx @@ -1,8 +1,9 @@ import dynamic from 'next/dynamic'; import Image from 'next/image'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { useProductContext } from '@/contexts/ProductContext'; +import odooApi from '@/core/api/odooApi'; import whatsappUrl from '@/core/utils/whatsappUrl'; import { useRouter } from 'next/router'; @@ -41,20 +42,28 @@ const BasicLayout = ({ children }) => { } }, [product, router]); - // useEffect(() => { - // const getIP = async () => { - // const ip = await odooApi('GET', '/api/ip-address'); - // const data = { - // page_title: document.title, - // url: window.location.href, - // ip: ip, - // }; - // axios.get( - // `/api/user-activity?page_title=${data.page_title}&url=${data.url}&ip=${data.ip}` - // ); - // }; - // getIP(); - // }, []); + const recordActivity = useCallback(async () => { + const recordedPath = [ + '/shop/product/[slug]', + '/shop/product/variant/[slug]', + ]; + + if (!recordedPath.includes(router.pathname)) return; + + const ip = await odooApi('GET', '/api/ip-address'); + const data = new URLSearchParams({ + page_title: document.title, + url: window.location.href, + ip, + }); + + fetch(`/api/user-activity?${data.toString()}`); + }, [router.pathname]); + + useEffect(() => { + recordActivity(); + }, [recordActivity]); + return ( <> -- cgit v1.2.3 From 2e38ec15d42201c26c48f9bcf856750204db0582 Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Fri, 9 Feb 2024 10:19:28 +0700 Subject: fixing error form form sales --- src/lib/form/components/KunjunganSales.jsx | 4 ++-- src/lib/form/components/KunjunganService.jsx | 4 ++-- src/lib/form/components/Merchant.jsx | 4 ++-- src/lib/form/components/PembayaranTempo.jsx | 4 ++-- src/lib/form/components/RequestForQuotation.jsx | 4 ++-- src/lib/form/components/SuratDukungan.jsx | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib/form/components/KunjunganSales.jsx b/src/lib/form/components/KunjunganSales.jsx index 29940edf..ffa8f135 100644 --- a/src/lib/form/components/KunjunganSales.jsx +++ b/src/lib/form/components/KunjunganSales.jsx @@ -35,7 +35,7 @@ const KunjunganSales = () => { const recaptchaRef = useRef(null) useEffect(() => { - if(!auth) { + if(auth == false) { router.push('/login') } const loadCities = async () => { @@ -50,7 +50,7 @@ const KunjunganSales = () => { loadCompanyTypes() loadCities() - }, []) + }, [auth]) const onSubmitHandler = async (values) => { const recaptchaValue = recaptchaRef.current.getValue() diff --git a/src/lib/form/components/KunjunganService.jsx b/src/lib/form/components/KunjunganService.jsx index 0e1d1ab3..5720d14e 100644 --- a/src/lib/form/components/KunjunganService.jsx +++ b/src/lib/form/components/KunjunganService.jsx @@ -33,7 +33,7 @@ const CreateKunjunganService = () => { const recaptchaRef = useRef(null) useEffect(() => { - if(!auth) { + if(auth == false) { router.push('/login') } const loadCities = async () => { @@ -42,7 +42,7 @@ const CreateKunjunganService = () => { setCities(dataCities) } loadCities() - }, []) + }, [auth]) const onSubmitHandler = async (values) => { const recaptchaValue = recaptchaRef.current.getValue() diff --git a/src/lib/form/components/Merchant.jsx b/src/lib/form/components/Merchant.jsx index 770bfb82..85f72bf8 100644 --- a/src/lib/form/components/Merchant.jsx +++ b/src/lib/form/components/Merchant.jsx @@ -58,7 +58,7 @@ const CreateMerchant = () => { const auth = useAuth() useEffect(() => { - if(!auth) { + if(auth == false) { router.push('/login') } const loadCities = async () => { @@ -70,7 +70,7 @@ const CreateMerchant = () => { setCities(dataCities); }; loadCities(); - }, []); + }, [auth]); const onSubmitHandler = async (values) => { const recaptchaValue = recaptchaRef.current.getValue(); diff --git a/src/lib/form/components/PembayaranTempo.jsx b/src/lib/form/components/PembayaranTempo.jsx index 73ceee49..9591a0b0 100644 --- a/src/lib/form/components/PembayaranTempo.jsx +++ b/src/lib/form/components/PembayaranTempo.jsx @@ -29,10 +29,10 @@ const PembayaranTempo = () => { const auth = useAuth() useEffect(() => { - if(!auth) { + if(auth) { router.push('/login') } - },[]) + },[auth]) const onSubmitHandler = async (values) => { const recaptchaValue = recaptchaRef.current.getValue() diff --git a/src/lib/form/components/RequestForQuotation.jsx b/src/lib/form/components/RequestForQuotation.jsx index 34434dd5..68b7fa17 100644 --- a/src/lib/form/components/RequestForQuotation.jsx +++ b/src/lib/form/components/RequestForQuotation.jsx @@ -35,7 +35,7 @@ const RequestForQuotation = () => { useEffect(() => { - if(!auth) { + if(auth == false) { router.push('/login') } const loadCities = async () => { @@ -44,7 +44,7 @@ const RequestForQuotation = () => { setCities(dataCities) } loadCities() - }, []) + }, [auth]) const onSubmitHandler = async (values) => { const recaptchaValue = recaptchaRef.current.getValue() diff --git a/src/lib/form/components/SuratDukungan.jsx b/src/lib/form/components/SuratDukungan.jsx index 0b3b650e..31e7ee83 100644 --- a/src/lib/form/components/SuratDukungan.jsx +++ b/src/lib/form/components/SuratDukungan.jsx @@ -33,7 +33,7 @@ const CreateSuratDukungan = () => { const auth = useAuth() useEffect(() => { - if(!auth) { + if(auth == false) { router.push('/login') } const loadCities = async () => { @@ -45,7 +45,7 @@ const CreateSuratDukungan = () => { setCities(dataCities); }; loadCities(); - }, []); + }, [auth]); const onSubmitHandler = async (values) => { const recaptchaValue = recaptchaRef.current.getValue(); -- cgit v1.2.3 From 34ee25921623818ba7b03b8542d351fe51bc8076 Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Fri, 9 Feb 2024 10:28:39 +0700 Subject: if auth false di pembayaran tempo --- src/lib/form/components/PembayaranTempo.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/form/components/PembayaranTempo.jsx b/src/lib/form/components/PembayaranTempo.jsx index 9591a0b0..fc4d248a 100644 --- a/src/lib/form/components/PembayaranTempo.jsx +++ b/src/lib/form/components/PembayaranTempo.jsx @@ -29,7 +29,7 @@ const PembayaranTempo = () => { const auth = useAuth() useEffect(() => { - if(auth) { + if(auth == false) { router.push('/login') } },[auth]) -- cgit v1.2.3 From de452faa701939225b76d4344a3d04748f03d571 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Sat, 17 Feb 2024 10:24:15 +0700 Subject: Fix product variant api price --- src-migrate/pages/api/product-variant/[id].tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src-migrate/pages/api/product-variant/[id].tsx b/src-migrate/pages/api/product-variant/[id].tsx index c25c10ac..39a871b7 100644 --- a/src-migrate/pages/api/product-variant/[id].tsx +++ b/src-migrate/pages/api/product-variant/[id].tsx @@ -1,3 +1,4 @@ +import moment from "moment"; import { NextApiRequest, NextApiResponse } from "next"; import { SolrResponse } from "~/types/solr"; @@ -28,6 +29,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const map = async (variant: any, price_tier: string) => { const data: any = {} + const price = variant[`price_${price_tier}_v2_f`] || 0 + const isFlashsale = checkIsFlashsale(variant) data.id = parseInt(variant.id) data.parent_id = variant.template_id_i @@ -36,10 +39,20 @@ const map = async (variant: any, price_tier: string) => { data.name = variant.name_s data.default_code = variant.default_code_s data.price = { - price: variant.price_v2_f, - discount_percentage: variant[`discount_${price_tier}_v2_f`] || 0, - price_discount: variant[`price_${price_tier}_v2_f`] || 0, + discount_percentage: isFlashsale ? variant.flashsale_discount_f : 0, + price: isFlashsale ? Math.round(variant.flashsale_base_price_f) : price, + price_discount: isFlashsale ? Math.round(variant.flashsale_price_f) : price } return data +} + +const checkIsFlashsale = (variant: any) => { + const endDateStr = variant.flashsale_end_date_s || null + if (!endDateStr) return false + + const now = moment() + const endDate = moment(endDateStr, 'YYYY-MM-DD HH:mm:ss') + + return variant.flashsale_id_i > 0 && now.isSameOrBefore(endDate) } \ No newline at end of file -- cgit v1.2.3 From 14a25de72d80eab600d5481342b763d5db8ecd5f Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 19 Feb 2024 10:28:44 +0700 Subject: Update format currency to Math.round --- src-migrate/libs/formatCurrency.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-migrate/libs/formatCurrency.ts b/src-migrate/libs/formatCurrency.ts index 41db4a6f..d683acf3 100644 --- a/src-migrate/libs/formatCurrency.ts +++ b/src-migrate/libs/formatCurrency.ts @@ -1,5 +1,5 @@ const formatCurrency = (value: number) => { - return value.toLocaleString('id-ID'); + return Math.round(value).toLocaleString('id-ID'); }; export default formatCurrency; -- cgit v1.2.3 From f1eba397ba49db8c1a3310c0b12d01d3ee7fc478 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 19 Feb 2024 10:29:42 +0700 Subject: Update promotion program spacing on product detail --- src-migrate/modules/product-detail/components/ProductDetail.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 2bd3c901..4b514944 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -117,9 +117,8 @@ const ProductDetail = ({ product }: Props) => { )}
- {!!activeVariantId && ( - - )} + {!!activeVariantId && } + {!!activeVariantId && !isMobile &&
}

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

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

diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index 5000e737..07a5df7b 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -11,10 +11,9 @@ import ProductPromoModal from "./Modal" type Props = { productId: number; - height: number; } -const ProductPromoSection = ({ productId, height = 435 }: Props) => { +const ProductPromoSection = ({ productId }: Props) => { const promotionsQuery = useQuery({ queryKey: [`promotions.highlight`, productId], queryFn: async () => await fetch(`/api/product-variant/${productId}/promotion/highlight`).then((res) => res.json()) as { data: IPromotion[] } @@ -26,7 +25,7 @@ const ProductPromoSection = ({ productId, height = 435 }: Props) => { return (
0, + 'h-[450px] opacity-100': promotions?.data && promotions?.data.length > 0, })}> -- cgit v1.2.3 From de4e52bd63a85b8251d8369866e0b3ffd37b6059 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 10:06:07 +0700 Subject: Add smooth animation on top banner --- .../components/elements/Navbar/NavbarDesktop.jsx | 6 +-- src/core/components/elements/Navbar/TopBanner.jsx | 43 +++++++++++++--------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx index d1ff2d58..05ef0fb0 100644 --- a/src/core/components/elements/Navbar/NavbarDesktop.jsx +++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx @@ -18,13 +18,9 @@ import { useEffect, useState } from 'react'; import DesktopView from '../../views/DesktopView'; import Link from '../Link/Link'; import NavbarUserDropdown from './NavbarUserDropdown'; -import { TopBannerSkeleton } from '../Skeleton/TopBannerSkeleton'; const Search = dynamic(() => import('./Search'), { ssr: false }); -const TopBanner = dynamic(() => import('./TopBanner'), { - ssr: false, - loading: () => , -}); +const TopBanner = dynamic(() => import('./TopBanner'), { ssr: false }); const NavbarDesktop = () => { const [isOpenCategory, setIsOpenCategory] = useState(false); diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx index a757c260..4342149d 100644 --- a/src/core/components/elements/Navbar/TopBanner.jsx +++ b/src/core/components/elements/Navbar/TopBanner.jsx @@ -1,21 +1,28 @@ -import odooApi from '@/core/api/odooApi' -import { useQuery } from 'react-query' -import Image from 'next/image' -import Link from '../Link/Link' -import { TopBannerSkeleton } from '../Skeleton/TopBannerSkeleton' +import odooApi from '@/core/api/odooApi'; +import Image from 'next/image'; +import { useQuery } from 'react-query'; +import clsxm from '~/libs/clsxm'; +import Link from '../Link/Link'; const TopBanner = () => { - const fetchTopBanner = async () => await odooApi('GET', '/api/v1/banner?type=top-banner') - const topBanner = useQuery('topBanner', fetchTopBanner, { refetchOnWindowFocus: false }) + const topBanner = useQuery({ + queryKey: 'topBanner', + queryFn: async () => await odooApi('GET', '/api/v1/banner?type=top-banner'), + refetchOnWindowFocus: false, + }); - if (topBanner.isLoading) { - return - } + const backgroundColor = topBanner.data?.[0]?.backgroundColor || 'transparent'; + const hasData = topBanner.data?.length > 0; return ( - topBanner.isFetched && - topBanner.data?.length > 0 && ( -
+
+ {hasData && ( { className='object-cover object-center h-full mx-auto' /> -
- ) - ) -} + )} +
+ ); +}; -export default TopBanner +export default TopBanner; -- cgit v1.2.3 From 6e396f4a0ebd3d8ed394ae55a6fb55f295fc9a11 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 10:31:36 +0700 Subject: Update add to cart button redirect to login page --- .../product-detail/components/AddToCart.tsx | 15 +++----------- .../modules/product-promo/components/AddToCart.tsx | 24 +++++++--------------- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/src-migrate/modules/product-detail/components/AddToCart.tsx b/src-migrate/modules/product-detail/components/AddToCart.tsx index 4accab17..ebd6be7a 100644 --- a/src-migrate/modules/product-detail/components/AddToCart.tsx +++ b/src-migrate/modules/product-detail/components/AddToCart.tsx @@ -1,8 +1,7 @@ -import React from 'react' import { Button, useToast } from '@chakra-ui/react' -import { getAuth } from '~/libs/auth' import { useRouter } from 'next/router' -import Link from 'next/link' + +import { getAuth } from '~/libs/auth' import { upsertUserCart } from '~/services/cart' type Props = { @@ -26,15 +25,7 @@ const AddToCart = ({ const handleClick = async () => { if (typeof auth !== 'object') { const currentUrl = encodeURIComponent(router.asPath) - toast({ - title: 'Masuk Akun', - description: <> - Masuk akun untuk dapat menambahkan barang ke keranjang belanja. {' '} - Klik disini - , - status: 'error', - duration: 4000, - }) + router.push(`/login?next=${currentUrl}`) return; } diff --git a/src-migrate/modules/product-promo/components/AddToCart.tsx b/src-migrate/modules/product-promo/components/AddToCart.tsx index 3bac3c66..95d275fc 100644 --- a/src-migrate/modules/product-promo/components/AddToCart.tsx +++ b/src-migrate/modules/product-promo/components/AddToCart.tsx @@ -1,11 +1,11 @@ -import React, { useEffect, useState } from 'react' -import { CheckIcon, PlusIcon } from 'lucide-react' -import { IPromotion } from '~/types/promotion' -import { upsertUserCart } from '~/services/cart' -import { getAuth } from '~/libs/auth' import { Button, Spinner, useToast } from '@chakra-ui/react' -import Link from 'next/link' +import { CheckIcon, PlusIcon } from 'lucide-react' import { useRouter } from 'next/router' +import { useEffect, useState } from 'react' + +import { getAuth } from '~/libs/auth' +import { upsertUserCart } from '~/services/cart' +import { IPromotion } from '~/types/promotion' type Props = { promotion: IPromotion @@ -23,17 +23,7 @@ const ProductPromoAddToCart = ({ promotion }: Props) => { const handleButton = async () => { if (typeof auth !== 'object') { const currentUrl = encodeURIComponent(router.asPath) - toast({ - title: 'Masuk Akun', - description: <> - Masuk akun untuk dapat menambahkan promo ke keranjang belanja. {' '} - Klik disini - , - status: 'error', - duration: 4000, - isClosable: true, - position: 'top', - }) + router.push(`/login?next=${currentUrl}`) return } if (status === 'success') return -- cgit v1.2.3 From 0f78bc94e3b55a718e4ba620b02994afc866672c Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 11:37:15 +0700 Subject: Update promo item name tooltip --- src-migrate/modules/product-promo/components/Item.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src-migrate/modules/product-promo/components/Item.tsx b/src-migrate/modules/product-promo/components/Item.tsx index 8012c17e..b396160f 100644 --- a/src-migrate/modules/product-promo/components/Item.tsx +++ b/src-migrate/modules/product-promo/components/Item.tsx @@ -1,8 +1,8 @@ import style from '../styles/item.module.css' -import React from 'react' -import Image from '~/components/ui/image' +import { Tooltip } from '@chakra-ui/react' +import Image from '~/components/ui/image' import { IProductVariantPromo } from '~/types/promotion' type Props = { @@ -22,7 +22,11 @@ const ProductPromoItem = ({ {variant.qty} pcs {isFree ? '(free)' : ''}

-
{variant.name}
+ +
+ {variant.name} +
+
) } -- cgit v1.2.3 From bb1451372cd847def47fcaed6669a72c664f68e3 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 13:53:53 +0700 Subject: Create and implement smooth render --- src-migrate/components/ui/smooth-render.tsx | 41 ++++++++++++++++++++++ .../modules/product-promo/components/Section.tsx | 11 +++--- src/core/components/elements/Navbar/TopBanner.jsx | 35 +++++++++--------- 3 files changed, 65 insertions(+), 22 deletions(-) create mode 100644 src-migrate/components/ui/smooth-render.tsx diff --git a/src-migrate/components/ui/smooth-render.tsx b/src-migrate/components/ui/smooth-render.tsx new file mode 100644 index 00000000..0e9a4096 --- /dev/null +++ b/src-migrate/components/ui/smooth-render.tsx @@ -0,0 +1,41 @@ +import React from 'react' +import clsxm from '~/libs/clsxm' + +type Props = { + children: React.ReactNode, + isLoaded: boolean, + height: number, + duration?: number + delay?: number +} & React.HTMLProps + +const SmoothRender = (props: Props) => { + const { + children, + isLoaded, + height, + duration = 0, + delay = 0, + style, + className, + ...rest + } = props + + return ( +
+ {isLoaded && children} +
+ ) +} + +export default SmoothRender \ No newline at end of file diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index 07a5df7b..b3c6e671 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -3,6 +3,7 @@ import style from "../styles/section.module.css" import { Button, Skeleton } from '@chakra-ui/react' import { useQuery } from 'react-query' +import SmoothRender from "~/components/ui/smooth-render" import clsxm from "~/libs/clsxm" import { IPromotion } from '~/types/promotion' import { useModalStore } from "../stores/useModalStore" @@ -24,9 +25,11 @@ const ProductPromoSection = ({ productId }: Props) => { const { openModal } = useModalStore() return ( -
0, - })}> + 0) || false} + height={450} + duration={700} + > {promotions?.data && promotions?.data.length > 0 && ( @@ -51,7 +54,7 @@ const ProductPromoSection = ({ productId }: Props) => {
))} -
+ ) } diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx index 4342149d..ff5b1d90 100644 --- a/src/core/components/elements/Navbar/TopBanner.jsx +++ b/src/core/components/elements/Navbar/TopBanner.jsx @@ -1,8 +1,8 @@ import odooApi from '@/core/api/odooApi'; import Image from 'next/image'; import { useQuery } from 'react-query'; -import clsxm from '~/libs/clsxm'; import Link from '../Link/Link'; +import SmoothRender from '~/components/ui/smooth-render'; const TopBanner = () => { const topBanner = useQuery({ @@ -13,27 +13,26 @@ const TopBanner = () => { const backgroundColor = topBanner.data?.[0]?.backgroundColor || 'transparent'; const hasData = topBanner.data?.length > 0; + const data = topBanner.data?.[0] || null; return ( -
- {hasData && ( - - {topBanner.data[0].name} - - )} -
+ + {data?.name} + + ); }; -- cgit v1.2.3 From 055bf8322dce7b7fba1b2e1d6176946067ff5bbe Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 16:10:10 +0700 Subject: Update scrollbar --- src/styles/globals.css | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/styles/globals.css b/src/styles/globals.css index bf9fec10..f6561b00 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -107,7 +107,7 @@ button { disabled:bg-gray_r-5; } - .form-input[aria-invalid="true"] { + .form-input[aria-invalid='true'] { @apply border-danger-500 focus:border-danger-500; } @@ -676,3 +676,21 @@ button { @apply text-warning-500; } } + +::-webkit-scrollbar { + width: 12px; +} + +::-webkit-scrollbar-track { + background-color: transparent; +} + +::-webkit-scrollbar-thumb { + background-color: #888; + border-radius: 6px; + border: 3px solid #f5f5f5; +} + +::-webkit-scrollbar-thumb:hover { + background-color: #555; +} -- cgit v1.2.3 From 30da2d088793cabfb3c5b21d4560df53187e7047 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 16:30:43 +0700 Subject: Fix item url in cart item --- src-migrate/modules/cart/components/Item.tsx | 10 ++++++++-- src-migrate/modules/cart/components/ItemPromo.tsx | 4 ++-- src-migrate/types/cart.ts | 4 ++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index ba7f7cdf..d2bbdbdd 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -112,7 +112,10 @@ CartItem.Image = function CartItemImage({ item }: { item: CartItemProps }) { )} {item.cart_type === 'product' && ( - + {image && {item.name}} {!image &&
No Image
} @@ -129,7 +132,10 @@ CartItem.Name = function CartItemName({ item }: { item: CartItemProps }) { )} {item.cart_type === 'product' && ( - + {item.name} )} diff --git a/src-migrate/modules/cart/components/ItemPromo.tsx b/src-migrate/modules/cart/components/ItemPromo.tsx index d355c82a..878e17ac 100644 --- a/src-migrate/modules/cart/components/ItemPromo.tsx +++ b/src-migrate/modules/cart/components/ItemPromo.tsx @@ -13,12 +13,12 @@ type Props = { const CartItemPromo = ({ product }: Props) => { return (
- + {product?.image && {product.name}}
- + {product.display_name} diff --git a/src-migrate/types/cart.ts b/src-migrate/types/cart.ts index 3aceeac4..5a2cf4a9 100644 --- a/src-migrate/types/cart.ts +++ b/src-migrate/types/cart.ts @@ -9,6 +9,10 @@ type Price = { export type CartProduct = { id: number; image: string; + parent: { + id: number; + name: string; + }; display_name: string; name: string; code: string; -- cgit v1.2.3 From 5d3807a89596958a1e23e02ae11f73a2474c3432 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 20 Feb 2024 22:27:06 +0700 Subject: Update smooth render function --- src-migrate/components/ui/smooth-render.tsx | 12 ++-- .../modules/product-promo/components/Section.tsx | 4 +- src/components/ui/HeroBanner.jsx | 78 ++++++++++++---------- src/core/components/elements/Navbar/TopBanner.jsx | 6 +- 4 files changed, 53 insertions(+), 47 deletions(-) diff --git a/src-migrate/components/ui/smooth-render.tsx b/src-migrate/components/ui/smooth-render.tsx index 0e9a4096..5de3b28d 100644 --- a/src-migrate/components/ui/smooth-render.tsx +++ b/src-migrate/components/ui/smooth-render.tsx @@ -4,9 +4,9 @@ import clsxm from '~/libs/clsxm' type Props = { children: React.ReactNode, isLoaded: boolean, - height: number, - duration?: number - delay?: number + height: string, + duration?: string + delay?: string } & React.HTMLProps const SmoothRender = (props: Props) => { @@ -26,9 +26,9 @@ const SmoothRender = (props: Props) => { className={clsxm('overflow-y-hidden transition-all', className)} style={{ opacity: isLoaded ? 1 : 0, - height: isLoaded ? `${height}px` : 0, - transitionDuration: `${duration}ms`, - transitionDelay: `${delay}ms`, + height: isLoaded ? height : 0, + transitionDuration: duration || '', + transitionDelay: delay || '', ...style }} {...rest} diff --git a/src-migrate/modules/product-promo/components/Section.tsx b/src-migrate/modules/product-promo/components/Section.tsx index b3c6e671..5fc0da4c 100644 --- a/src-migrate/modules/product-promo/components/Section.tsx +++ b/src-migrate/modules/product-promo/components/Section.tsx @@ -27,8 +27,8 @@ const ProductPromoSection = ({ productId }: Props) => { return ( 0) || false} - height={450} - duration={700} + height='450px' + duration='700ms' > diff --git a/src/components/ui/HeroBanner.jsx b/src/components/ui/HeroBanner.jsx index 6cf7902c..9a62465d 100644 --- a/src/components/ui/HeroBanner.jsx +++ b/src/components/ui/HeroBanner.jsx @@ -1,42 +1,43 @@ // Swiper -import { Swiper, SwiperSlide } from 'swiper/react' -import { Pagination, Autoplay } from 'swiper' -import 'swiper/css' -import 'swiper/css/pagination' -import 'swiper/css/autoplay' +import { Autoplay, Pagination } from 'swiper'; +import 'swiper/css'; +import 'swiper/css/autoplay'; +import 'swiper/css/pagination'; +import { Swiper, SwiperSlide } from 'swiper/react'; -import DesktopView from '@/core/components/views/DesktopView' -import MobileView from '@/core/components/views/MobileView' -import { useMemo } from 'react' -import Link from '@/core/components/elements/Link/Link' -import Image from 'next/image' -import { useQuery } from 'react-query' -import { bannerApi } from '@/api/bannerApi' -import { HeroBannerSkeleton } from '../skeleton/BannerSkeleton' +import Image from 'next/image'; +import { useMemo } from 'react'; +import { useQuery } from 'react-query'; + +import { bannerApi } from '@/api/bannerApi'; +import Link from '@/core/components/elements/Link/Link'; +import DesktopView from '@/core/components/views/DesktopView'; +import MobileView from '@/core/components/views/MobileView'; +import SmoothRender from '~/components/ui/smooth-render'; const swiperBanner = { autoplay: { delay: 6000, - disableOnInteraction: false + disableOnInteraction: false, }, modules: [Pagination, Autoplay], loop: true, className: 'border border-gray_r-6 min-h-full', - slidesPerView: 1 -} + slidesPerView: 1, +}; const HeroBanner = () => { - const heroBanner = useQuery('heroBanner', bannerApi({ type: 'index-a-1' })) + const heroBanner = useQuery('heroBanner', bannerApi({ type: 'index-a-1' })); const swiperBannerMobile = { ...swiperBanner, - pagination: { dynamicBullets: true, clickable: true } - } + pagination: { dynamicBullets: true, clickable: true }, + }; const swiperBannerDesktop = { ...swiperBanner, - pagination: { dynamicBullets: false, clickable: true } - } + pagination: { dynamicBullets: false, clickable: true }, + }; const BannerComponent = useMemo(() => { return heroBanner.data?.map((banner, index) => ( @@ -51,24 +52,29 @@ const HeroBanner = () => { /> - )) - }, [heroBanner.data]) - - if (heroBanner.isLoading) return + )); + }, [heroBanner.data]); return ( - heroBanner.data && ( - <> - + <> + + 0} + height='68vw' + duration='750ms' + delay='100ms' + > {BannerComponent} - + + - + + {heroBanner.data?.length > 0 && ( {BannerComponent} - - - ) - ) -} + )} + + + ); +}; -export default HeroBanner +export default HeroBanner; diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx index ff5b1d90..ded2857f 100644 --- a/src/core/components/elements/Navbar/TopBanner.jsx +++ b/src/core/components/elements/Navbar/TopBanner.jsx @@ -18,9 +18,9 @@ const TopBanner = () => { return ( -- cgit v1.2.3 From ab61908296a05d303cb9c0d019b5a92002e03972 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 21 Feb 2024 09:12:40 +0700 Subject: Fix weight on cart --- src-migrate/modules/cart/components/Item.tsx | 7 +++---- src/core/components/elements/Navbar/TopBanner.jsx | 5 +++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index d2bbdbdd..6ded6373 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -3,14 +3,13 @@ import style from '../styles/item.module.css' import { Skeleton, SkeletonProps, Tooltip } from '@chakra-ui/react' import { InfoIcon } from 'lucide-react' import Image from 'next/image' +import Link from 'next/link' import { PROMO_CATEGORY } from '~/constants/promotion' - import formatCurrency from '~/libs/formatCurrency' +import { createSlug } from '~/libs/slug' import { CartItem as CartItemProps } from '~/types/cart' -import Link from 'next/link' -import { createSlug } from '~/libs/slug' import CartItemAction from './ItemAction' import CartItemPromo from './ItemPromo' import CartItemSelect from './ItemSelect' @@ -81,7 +80,7 @@ const CartItem = ({ item, editable = true }: Props) => { )}
{item.cart_type === 'product' && item.code}
-
{item.weight} Kg
+
{Math.round(item.weight * 10) / 10} Kg
{editable && } diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx index ded2857f..f9ac7032 100644 --- a/src/core/components/elements/Navbar/TopBanner.jsx +++ b/src/core/components/elements/Navbar/TopBanner.jsx @@ -1,8 +1,9 @@ -import odooApi from '@/core/api/odooApi'; import Image from 'next/image'; import { useQuery } from 'react-query'; -import Link from '../Link/Link'; + +import odooApi from '@/core/api/odooApi'; import SmoothRender from '~/components/ui/smooth-render'; +import Link from '../Link/Link'; const TopBanner = () => { const topBanner = useQuery({ -- cgit v1.2.3 From 4bac265bf958d67dfbd584fd524d661287661368 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Thu, 22 Feb 2024 16:29:01 +0700 Subject: Improve render on checkout page --- src/core/components/elements/Navbar/TopBanner.jsx | 2 +- src/lib/checkout/components/Checkout.jsx | 58 +++++++++++++---------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx index f9ac7032..722a7501 100644 --- a/src/core/components/elements/Navbar/TopBanner.jsx +++ b/src/core/components/elements/Navbar/TopBanner.jsx @@ -21,7 +21,7 @@ const TopBanner = () => { isLoaded={hasData} height='36px' duration='700ms' - delay='500ms' + delay='300ms' style={{ backgroundColor }} > diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index 85eda80b..52edbd05 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -1,36 +1,36 @@ -import Alert from '@/core/components/elements/Alert/Alert'; -import Divider from '@/core/components/elements/Divider/Divider'; -import Link from '@/core/components/elements/Link/Link'; -import useAuth from '@/core/hooks/useAuth'; -import { getItemAddress } from '@/core/utils/address'; -import addressesApi from '@/lib/address/api/addressesApi'; +import { Skeleton, Spinner } from '@chakra-ui/react'; import { BanknotesIcon, ChevronLeftIcon, ClockIcon, ExclamationCircleIcon, } from '@heroicons/react/24/outline'; +import axios from 'axios'; +import { AnimatePresence, motion } from 'framer-motion'; +import { useRouter } from 'next/router'; import React, { useEffect, useMemo, useRef, useState } from 'react'; -import _ from 'lodash'; -import { deleteItemCart } from '@/core/utils/cart'; -import currencyFormat from '@/core/utils/currencyFormat'; import { toast } from 'react-hot-toast'; -import getFileBase64 from '@/core/utils/getFileBase64'; -import { useRouter } from 'next/router'; -import axios from 'axios'; +import { useQuery } from 'react-query'; +import snakecaseKeys from 'snakecase-keys'; + +import Alert from '@/core/components/elements/Alert/Alert'; +import Divider from '@/core/components/elements/Divider/Divider'; import Image from '@/core/components/elements/Image/Image'; -import MobileView from '@/core/components/views/MobileView'; -import DesktopView from '@/core/components/views/DesktopView'; -import ExpedisiList from '../api/ExpedisiList'; -import whatsappUrl from '@/core/utils/whatsappUrl'; +import Link from '@/core/components/elements/Link/Link'; import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; -import { useQuery } from 'react-query'; +import DesktopView from '@/core/components/views/DesktopView'; +import MobileView from '@/core/components/views/MobileView'; +import useAuth from '@/core/hooks/useAuth'; +import { getItemAddress } from '@/core/utils/address'; +import { deleteItemCart } from '@/core/utils/cart'; +import currencyFormat from '@/core/utils/currencyFormat'; +import getFileBase64 from '@/core/utils/getFileBase64'; import { gtagPurchase } from '@/core/utils/googleTag'; -import { findVoucher, getVoucher } from '../api/getVoucher'; -import { Spinner } from '@chakra-ui/react'; -import { AnimatePresence, motion } from 'framer-motion'; -import snakecaseKeys from 'snakecase-keys'; +import whatsappUrl from '@/core/utils/whatsappUrl'; +import addressesApi from '@/lib/address/api/addressesApi'; import CartItem from '~/modules/cart/components/Item.tsx'; +import ExpedisiList from '../api/ExpedisiList'; +import { findVoucher, getVoucher } from '../api/getVoucher'; const SELF_PICKUP_ID = 32; @@ -773,7 +773,10 @@ const Checkout = () => { )} {selectedCarrierId != SELF_PICKUP_ID && ( - <> + { label='Alamat Penagihan' url='/my/address?select=invoice' /> - + )} @@ -1056,7 +1059,12 @@ const Checkout = () => { )} {selectedCarrierId != SELF_PICKUP_ID && ( - <> + { label='Alamat Penagihan' url='/my/address?select=invoice' /> - + )} -- cgit v1.2.3 From 55d476a4666150a013a308deb6cb99513778ac22 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Fri, 23 Feb 2024 09:16:24 +0700 Subject: Update variant image without watermark --- .../components/Product/ProductDesktopVariant.jsx | 275 +++++++++++++-------- .../components/Product/ProductMobileVariant.jsx | 240 +++++++++++------- src/pages/google_merchant/products/[page].js | 130 +++++----- src/pages/shop/product/variant/[slug].jsx | 86 ++++--- 4 files changed, 434 insertions(+), 297 deletions(-) diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx index ef61bafd..bae00b87 100644 --- a/src/lib/product/components/Product/ProductDesktopVariant.jsx +++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx @@ -1,137 +1,154 @@ -import Image from '@/core/components/elements/Image/Image' -import Link from '@/core/components/elements/Link/Link' -import DesktopView from '@/core/components/views/DesktopView' -import currencyFormat from '@/core/utils/currencyFormat' -import { HeartIcon } from '@heroicons/react/24/outline' -import { useCallback, useEffect, useRef, useState } from 'react' -import LazyLoad from 'react-lazy-load' -import ProductSimilar from '../ProductSimilar' -import { toast } from 'react-hot-toast' -import { updateItemCart } from '@/core/utils/cart' -import { useRouter } from 'next/router' -import { createSlug } from '@/core/utils/slug' -import BottomPopup from '@/core/components/elements/Popup/BottomPopup' -import ProductCard from '../ProductCard' -import productSimilarApi from '../../api/productSimilarApi' -import whatsappUrl from '@/core/utils/whatsappUrl' -import useAuth from '@/core/hooks/useAuth' -import odooApi from '@/core/api/odooApi' -import { useProductCartContext } from '@/contexts/ProductCartContext' -import { Box, Skeleton, Tooltip } from '@chakra-ui/react' -import { Info } from 'lucide-react' - -const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant }) => { - const router = useRouter() - const auth = useAuth() - const { slug } = router.query - - const [lowestPrice, setLowestPrice] = useState(null) - - const [addCartAlert, setAddCartAlert] = useState(false) - const [isLoadingSLA, setIsLoadingSLA] = useState(true) - - const { setRefreshCart } = useProductCartContext() +import { Box, Skeleton, Tooltip } from '@chakra-ui/react'; +import { HeartIcon } from '@heroicons/react/24/outline'; +import { Info } from 'lucide-react'; +import { useRouter } from 'next/router'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { toast } from 'react-hot-toast'; +import LazyLoad from 'react-lazy-load'; + +import { useProductCartContext } from '@/contexts/ProductCartContext'; +import odooApi from '@/core/api/odooApi'; +import Image from '@/core/components/elements/Image/Image'; +import Link from '@/core/components/elements/Link/Link'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import DesktopView from '@/core/components/views/DesktopView'; +import useAuth from '@/core/hooks/useAuth'; +import { updateItemCart } from '@/core/utils/cart'; +import currencyFormat from '@/core/utils/currencyFormat'; +import { createSlug } from '@/core/utils/slug'; +import whatsappUrl from '@/core/utils/whatsappUrl'; + +import productSimilarApi from '../../api/productSimilarApi'; +import ProductCard from '../ProductCard'; +import ProductSimilar from '../ProductSimilar'; + +const ProductDesktopVariant = ({ + product, + wishlist, + toggleWishlist, + isVariant, +}) => { + const router = useRouter(); + const auth = useAuth(); + const { slug } = router.query; + + const [lowestPrice, setLowestPrice] = useState(null); + + const [addCartAlert, setAddCartAlert] = useState(false); + const [isLoadingSLA, setIsLoadingSLA] = useState(true); + + const { setRefreshCart } = useProductCartContext(); const getLowestPrice = useCallback(() => { - const lowest = product.price + const lowest = product.price; /* const lowest = prices.reduce((lowest, price) => { return price.priceDiscount < lowest.priceDiscount ? price : lowest }, prices[0])*/ - return lowest - }, [product]) + return lowest; + }, [product]); useEffect(() => { - const lowest = getLowestPrice() - setLowestPrice(lowest) - }, [getLowestPrice]) + const lowest = getLowestPrice(); + setLowestPrice(lowest); + }, [getLowestPrice]); - const [informationTab, setInformationTab] = useState(informationTabOptions[0].value) + const [informationTab, setInformationTab] = useState( + informationTabOptions[0].value + ); - const variantQuantityRefs = useRef([]) + const variantQuantityRefs = useRef([]); const setVariantQuantityRef = (variantId) => (element) => { - variantQuantityRefs.current[variantId] = element - } + variantQuantityRefs.current[variantId] = element; + }; const validQuantity = (quantity) => { - let isValid = true + let isValid = true; if (!quantity || quantity < 1 || isNaN(parseInt(quantity))) { - toast.error('Jumlah barang minimal 1') - isValid = false + toast.error('Jumlah barang minimal 1'); + isValid = false; } - return isValid - } + return isValid; + }; const handleAddToCart = (variant) => { if (!auth) { - router.push(`/login?next=/shop/product/${slug}`) - return + router.push(`/login?next=/shop/product/${slug}`); + return; } - const quantity = variantQuantityRefs.current[product.id].value - if (!validQuantity(quantity)) return + const quantity = variantQuantityRefs.current[product.id].value; + if (!validQuantity(quantity)) return; updateItemCart({ productId: product.id, quantity, programLineId: null, selected: true, - source: null + source: null, }).then(() => { - setRefreshCart(true) - }) - setAddCartAlert(true) - } + setRefreshCart(true); + }); + setAddCartAlert(true); + }; const handleBuy = (variant) => { - const quantity = variantQuantityRefs.current[product.id].value - if (!validQuantity(quantity)) return + const quantity = variantQuantityRefs.current[product.id].value; + if (!validQuantity(quantity)) return; updateItemCart({ productId: variant, quantity, programLineId: null, selected: true, - source: 'buy' - }) - router.push(`/shop/checkout?source=buy`) - } + source: 'buy', + }); + router.push(`/shop/checkout?source=buy`); + }; - const variantSectionRef = useRef(null) + const variantSectionRef = useRef(null); const goToVariantSection = () => { if (variantSectionRef.current) { - const position = variantSectionRef.current.getBoundingClientRect() + const position = variantSectionRef.current.getBoundingClientRect(); window.scrollTo({ top: position.top - 120 + window.pageYOffset, - behavior: 'smooth' - }) + behavior: 'smooth', + }); } - } + }; const productSimilarQuery = [ product?.name, `fq=-product_id_i:${product.id}`, - `fq=-manufacture_id_i:${product.manufacture?.id || 0}` - ].join('&') + `fq=-manufacture_id_i:${product.manufacture?.id || 0}`, + ].join('&'); - const [productSimilarInBrand, setProductSimilarInBrand] = useState(null) + const [productSimilarInBrand, setProductSimilarInBrand] = useState(null); useEffect(() => { const loadProductSimilarInBrand = async () => { - const productSimilarQuery = [product?.name, `fq=-product_id_i:${product.id}`].join('&') - const dataProductSimilar = await productSimilarApi({ query: productSimilarQuery }) - setProductSimilarInBrand(dataProductSimilar.products) - } - if (!productSimilarInBrand) loadProductSimilarInBrand() - }, [product, productSimilarInBrand]) + const productSimilarQuery = [ + product?.name, + `fq=-product_id_i:${product.id}`, + ].join('&'); + const dataProductSimilar = await productSimilarApi({ + query: productSimilarQuery, + }); + setProductSimilarInBrand(dataProductSimilar.products); + }; + if (!productSimilarInBrand) loadProductSimilarInBrand(); + }, [product, productSimilarInBrand]); useEffect(() => { const fetchData = async () => { - const dataSLA = await odooApi('GET', `/api/v1/product_variant/${product.id}/stock`) - product.sla = dataSLA + const dataSLA = await odooApi( + 'GET', + `/api/v1/product_variant/${product.id}/stock` + ); + product.sla = dataSLA; - setIsLoadingSLA(false) - } - fetchData() - }, [product]) + setIsLoadingSLA(false); + }; + fetchData(); + }, [product]); return ( @@ -140,14 +157,16 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant })
{product.name}
-

{product?.name}

+

+ {product?.name} +

Nomor SKU
@@ -177,7 +196,9 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant })
-
Persiapan Barang
+
+ Persiapan Barang +
{!product?.sla && } {product?.sla && ( @@ -203,8 +224,13 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant }) @@ -221,7 +247,12 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant }) @@ -270,7 +301,8 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant })
*/}
- {product?.isFlashsale > 0 && product?.price?.discountPercentage > 0? ( + {product?.isFlashsale > 0 && + product?.price?.discountPercentage > 0 ? ( <>
@@ -285,7 +317,9 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant })
Termasuk PPN:{' '} - {currencyFormat(product?.price?.priceDiscount * process.env.NEXT_PUBLIC_PPN)} + {currencyFormat( + product?.price?.priceDiscount * process.env.NEXT_PUBLIC_PPN + )}
) : ( @@ -295,7 +329,9 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant }) {currencyFormat(product?.price?.price)}
Termasuk PPN:{' '} - {currencyFormat(product?.price?.price * process.env.NEXT_PUBLIC_PPN)} + {currencyFormat( + product?.price?.price * process.env.NEXT_PUBLIC_PPN + )}
) : ( @@ -305,7 +341,12 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant }) href={whatsappUrl('product', { name: product.name, manufacture: product.manufacture?.name, - url: createSlug('/shop/product/', product.name, product.id, true) + url: createSlug( + '/shop/product/', + product.name, + product.id, + true + ), })} className='text-danger-500 underline' rel='noopener noreferrer' @@ -340,7 +381,10 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant })
-
-
Kamu Mungkin Juga Suka
+
+ Kamu Mungkin Juga Suka +
@@ -381,21 +427,28 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant })
{product.name}
-
{product.name}
+
+ {product.name} +
- + Lihat Keranjang
-
Kamu Mungkin Juga Suka
+
+ Kamu Mungkin Juga Suka +
@@ -403,29 +456,33 @@ const ProductDesktopVariant = ({ product, wishlist, toggleWishlist, isVariant })
- ) -} + ); +}; const informationTabOptions = [ { value: 'description', label: 'Deskripsi' }, - { value: 'information', label: 'Info Penting' } -] + { value: 'information', label: 'Info Penting' }, +]; const TabButton = ({ children, active, ...props }) => { const activeClassName = active ? 'text-danger-500 underline underline-offset-4' - : 'text-gray_r-12/80' + : 'text-gray_r-12/80'; return ( - - ) -} + ); +}; const TabContent = ({ children, active, className = '', ...props }) => (
{children}
-) +); -export default ProductDesktopVariant +export default ProductDesktopVariant; diff --git a/src/lib/product/components/Product/ProductMobileVariant.jsx b/src/lib/product/components/Product/ProductMobileVariant.jsx index 9888e482..af9e52bb 100644 --- a/src/lib/product/components/Product/ProductMobileVariant.jsx +++ b/src/lib/product/components/Product/ProductMobileVariant.jsx @@ -1,37 +1,40 @@ -import Divider from '@/core/components/elements/Divider/Divider' -import Image from '@/core/components/elements/Image/Image' -import Link from '@/core/components/elements/Link/Link' -import currencyFormat from '@/core/utils/currencyFormat' -import { useEffect, useState } from 'react' -import Select from 'react-select' -import ProductSimilar from '../ProductSimilar' -import LazyLoad from 'react-lazy-load' -import { updateItemCart } from '@/core/utils/cart' -import { HeartIcon } from '@heroicons/react/24/outline' -import { useRouter } from 'next/router' -import MobileView from '@/core/components/views/MobileView' -import { toast } from 'react-hot-toast' -import { createSlug } from '@/core/utils/slug' -import BottomPopup from '@/core/components/elements/Popup/BottomPopup' -import whatsappUrl from '@/core/utils/whatsappUrl' -import { gtagAddToCart } from '@/core/utils/googleTag' -import odooApi from '@/core/api/odooApi' -import { Skeleton } from '@chakra-ui/react' +import { Skeleton } from '@chakra-ui/react'; +import { HeartIcon } from '@heroicons/react/24/outline'; +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import { toast } from 'react-hot-toast'; +import LazyLoad from 'react-lazy-load'; + +import odooApi from '@/core/api/odooApi'; +import Divider from '@/core/components/elements/Divider/Divider'; +import Image from '@/core/components/elements/Image/Image'; +import Link from '@/core/components/elements/Link/Link'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import MobileView from '@/core/components/views/MobileView'; +import { updateItemCart } from '@/core/utils/cart'; +import currencyFormat from '@/core/utils/currencyFormat'; +import { gtagAddToCart } from '@/core/utils/googleTag'; +import { createSlug } from '@/core/utils/slug'; +import whatsappUrl from '@/core/utils/whatsappUrl'; + +import ProductSimilar from '../ProductSimilar'; const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { - const router = useRouter() + const router = useRouter(); - const [quantity, setQuantity] = useState('1') - const [selectedVariant, setSelectedVariant] = useState(product.id) - const [informationTab, setInformationTab] = useState(informationTabOptions[0].value) - const [addCartAlert, setAddCartAlert] = useState(false) + const [quantity, setQuantity] = useState('1'); + const [selectedVariant, setSelectedVariant] = useState(product.id); + const [informationTab, setInformationTab] = useState( + informationTabOptions[0].value + ); + const [addCartAlert, setAddCartAlert] = useState(false); - const [isLoadingSLA, setIsLoadingSLA] = useState(true) + const [isLoadingSLA, setIsLoadingSLA] = useState(true); const getLowestPrice = () => { - const lowest = product.lowestPrice - return lowest - } + const lowest = product.lowestPrice; + return lowest; + }; const [activeVariant, setActiveVariant] = useState({ id: null, @@ -40,8 +43,8 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { price: getLowestPrice(), stock: product.stockTotal, weight: product.weight, - isFlashSale: product.isFlashSale - }) + isFlashSale: product.isFlashSale, + }); useEffect(() => { if (selectedVariant) { @@ -52,70 +55,73 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { price: product.price, stock: product.stockTotal, weight: product.weight, - isFlashSale: product.isFlashSale - }) + isFlashSale: product.isFlashSale, + }); } - }, [selectedVariant, product]) + }, [selectedVariant, product]); const validAction = () => { - let isValid = true + let isValid = true; if (!selectedVariant) { - toast.error('Pilih varian terlebih dahulu') - isValid = false + toast.error('Pilih varian terlebih dahulu'); + isValid = false; } if (!quantity || quantity < 1 || isNaN(parseInt(quantity))) { - toast.error('Jumlah barang minimal 1') - isValid = false + toast.error('Jumlah barang minimal 1'); + isValid = false; } - return isValid - } + return isValid; + }; const handleClickCart = () => { - if (!validAction()) return - gtagAddToCart(activeVariant, quantity) + if (!validAction()) return; + gtagAddToCart(activeVariant, quantity); updateItemCart({ productId: variant, quantity, programLineId: null, selected: true, - source: null - }) - setAddCartAlert(true) - } + source: null, + }); + setAddCartAlert(true); + }; const handleClickBuy = () => { - if (!validAction()) return + if (!validAction()) return; updateItemCart({ productId: product.id, quantity, programLineId: null, selected: true, - source: 'buy' - }) - router.push(`/shop/checkout?source=buy`) - } + source: 'buy', + }); + router.push(`/shop/checkout?source=buy`); + }; const productSimilarQuery = [ product?.name, `fq=-product_id_i:${product.id}`, - `fq=-manufacture_id_i:${product.manufacture?.id || 0}` - ].join('&') + `fq=-manufacture_id_i:${product.manufacture?.id || 0}`, + ].join('&'); useEffect(() => { const fetchData = async () => { - const dataSLA = await odooApi('GET', `/api/v1/product_variant/${product.id}/stock`) - product.sla = dataSLA + const dataSLA = await odooApi( + 'GET', + `/api/v1/product_variant/${product.id}/stock` + ); + product.sla = dataSLA; - setIsLoadingSLA(false) - } - fetchData() - }, [product]) + setIsLoadingSLA(false); + }; + fetchData(); + }, [product]); return ( {product.name} @@ -124,7 +130,11 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {
{product.manufacture?.name ? ( {product.manufacture?.name} @@ -141,10 +151,13 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {

{activeVariant?.name}

- {activeVariant.isFlashSale && activeVariant?.price?.discountPercentage > 0 ? ( + {activeVariant.isFlashSale && + activeVariant?.price?.discountPercentage > 0 ? ( <>
-
{activeVariant?.price?.discountPercentage}%
+
+ {activeVariant?.price?.discountPercentage}% +
{currencyFormat(activeVariant?.price?.price)}
@@ -154,7 +167,9 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {
Termasuk PPN:{' '} - {currencyFormat(activeVariant?.price.priceDiscount * process.env.NEXT_PUBLIC_PPN)} + {currencyFormat( + activeVariant?.price.priceDiscount * process.env.NEXT_PUBLIC_PPN + )}
) : ( @@ -164,7 +179,9 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { {currencyFormat(activeVariant?.price?.price)}
Termasuk PPN:{' '} - {currencyFormat(activeVariant?.price.price * process.env.NEXT_PUBLIC_PPN)} + {currencyFormat( + activeVariant?.price.price * process.env.NEXT_PUBLIC_PPN + )}
) : ( @@ -173,7 +190,12 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {
@@ -199,10 +221,18 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { onChange={(e) => setQuantity(e.target.value)} />
- -
@@ -238,7 +268,9 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { type='button' title={`Masa Persiapan Barang ${product?.sla?.slaDate}`} className={`flex gap-x-1 items-center p-2 h-8 rounded-lg w-full ${ - product?.sla?.slaDate === 'indent' ? 'bg-indigo-900' : 'btn-light' + product?.sla?.slaDate === 'indent' + ? 'bg-indigo-900' + : 'btn-light' }`} >
{ {activeVariant?.stock > 0 && (
Ready Stock
-
{activeVariant?.stock > 5 ? '> 5' : '< 5'}
+
+ {activeVariant?.stock > 5 ? '> 5' : '< 5'} +
)} {activeVariant?.stock == 0 && (
@@ -297,12 +336,19 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { )} - {activeVariant?.weight > 0 && {activeVariant?.weight} KG} + {activeVariant?.weight > 0 && ( + {activeVariant?.weight} KG + )} {activeVariant?.weight == 0 && ( @@ -316,7 +362,10 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => { active={informationTab == 'description'} className='leading-6 text-gray_r-11' dangerouslySetInnerHTML={{ - __html: product.description != '' ? product.description : 'Belum ada deskripsi produk.' + __html: + product.description != '' + ? product.description + : 'Belum ada deskripsi produk.', }} />
@@ -338,55 +387,68 @@ const ProductMobileVariant = ({ product, wishlist, toggleWishlist }) => {
{product.name}
-
{product.name}
+
+ {product.name} +
- + Lihat Keranjang
-
Kamu Mungkin Juga Suka
+
+ Kamu Mungkin Juga Suka +
- ) -} + ); +}; const informationTabOptions = [ - { value: 'specification', label: 'Spesifikasi' } + { value: 'specification', label: 'Spesifikasi' }, // { value: 'description', label: 'Deskripsi' }, // { value: 'information', label: 'Info Penting' } -] +]; const TabButton = ({ children, active, ...props }) => { - const activeClassName = active ? 'text-danger-500 underline underline-offset-4' : 'text-gray_r-11' + const activeClassName = active + ? 'text-danger-500 underline underline-offset-4' + : 'text-gray_r-11'; return ( - - ) -} + ); +}; const TabContent = ({ children, active, className, ...props }) => (
{children}
-) +); const SpecificationContent = ({ children, label }) => (
{label} {children}
-) +); -export default ProductMobileVariant +export default ProductMobileVariant; diff --git a/src/pages/google_merchant/products/[page].js b/src/pages/google_merchant/products/[page].js index c8b4079b..6e0eb703 100644 --- a/src/pages/google_merchant/products/[page].js +++ b/src/pages/google_merchant/products/[page].js @@ -1,94 +1,102 @@ -import { createSlug } from '@/core/utils/slug' -import toTitleCase from '@/core/utils/toTitleCase' -import productSearchApi from '@/lib/product/api/productSearchApi' -import variantSearchApi from '@/lib/product/api/variantSearchApi' -import axios from 'axios' -import _ from 'lodash-contrib' -import { create } from 'xmlbuilder' +import { createSlug } from '@/core/utils/slug'; +import toTitleCase from '@/core/utils/toTitleCase'; +import variantSearchApi from '@/lib/product/api/variantSearchApi'; +import axios from 'axios'; +import _ from 'lodash-contrib'; +import { create } from 'xmlbuilder'; export async function getServerSideProps({ res, query }) { - const titleContent = 'Indoteknik.com: B2B Industrial Supply & Solution' + const titleContent = 'Indoteknik.com: B2B Industrial Supply & Solution'; const descriptionContent = - 'Temukan pilihan produk B2B Industri & Alat Teknik untuk Perusahaan, UMKM & Pemerintah dengan lengkap, mudah dan transparan.' + 'Temukan pilihan produk B2B Industri & Alat Teknik untuk Perusahaan, UMKM & Pemerintah dengan lengkap, mudah dan transparan.'; - const { page } = query - const limit = 5000 + const { page } = query; + const limit = 5000; const queries = { limit, page: page.replace('.xml', ''), priceFrom: 1, orderBy: 'popular', - fq: 'image_s:["" TO *]' - } - const products = await variantSearchApi({ query: _.toQuery(queries) }) + fq: 'image_s:["" TO *]', + }; + const products = await variantSearchApi({ query: _.toQuery(queries) }); - const brandsData = {} - const categoriesData = {} + const brandsData = {}; + const categoriesData = {}; - const productItems = [] + const productItems = []; for (const product of products.response.products) { - const productUrl = createSlug('/shop/product/variant/', product.name, product.id, true) - const productId = product.code != '' ? product.code : product.id - const regexHtmlTags = /(<([^>]+)>)/gi - product.description = product.description?.replace(regexHtmlTags, ' ').trim() + const productUrl = createSlug( + '/shop/product/variant/', + product.name, + product.id, + true + ); + const productId = product.code != '' ? product.code : product.id; + const regexHtmlTags = /(<([^>]+)>)/gi; + product.description = product.description + ?.replace(regexHtmlTags, ' ') + .trim(); const defaultProductDescription = - 'Indoteknik.com menawarkan berbagai produk industri, konstruksi, dan teknik terpercaya. Temukan mesin industri, peralatan listrik, alat pengukur, dan banyak lagi. Pengalaman berbelanja mudah dengan deskripsi produk lengkap, spesifikasi teknis, dan gambar jelas. Pembayaran aman, pengiriman cepat ke seluruh Indonesia. Solusi lengkap untuk kebutuhan Kantor, Industri & Teknik Anda.' + 'Indoteknik.com menawarkan berbagai produk industri, konstruksi, dan teknik terpercaya. Temukan mesin industri, peralatan listrik, alat pengukur, dan banyak lagi. Pengalaman berbelanja mudah dengan deskripsi produk lengkap, spesifikasi teknis, dan gambar jelas. Pembayaran aman, pengiriman cepat ke seluruh Indonesia. Solusi lengkap untuk kebutuhan Kantor, Industri & Teknik Anda.'; if (!product.description) { - product.description = defaultProductDescription + product.description = defaultProductDescription; } - let categoryName = null + let categoryName = null; - let brandId = product.manufacture?.id ?? null - let categoryId = null + let brandId = product.manufacture?.id ?? null; + let categoryId = null; if (brandId && brandId in brandsData) { - categoryId = brandsData[brandId].category_ids?.[0] ?? null + categoryId = brandsData[brandId].category_ids?.[0] ?? null; } else { - const solrBrand = await getBrandById(brandId) - brandsData[brandId] = solrBrand - categoryId = solrBrand?.category_ids?.[0] ?? null + const solrBrand = await getBrandById(brandId); + brandsData[brandId] = solrBrand; + categoryId = solrBrand?.category_ids?.[0] ?? null; } if (categoryId && categoryId in categoriesData) { - categoryName = categoriesData[categoryId].name_s ?? null + categoryName = categoriesData[categoryId].name_s ?? null; } else { - const solrCategory = await getCategoryById(categoryId) - categoriesData[categoryId] = solrCategory - categoryName = solrCategory?.name_s ?? null + const solrCategory = await getCategoryById(categoryId); + categoriesData[categoryId] = solrCategory; + categoryName = solrCategory?.name_s ?? null; } - const availability = 'in_stock' + const availability = 'in_stock'; const item = { 'g:id': { '#text': productId }, 'g:title': { '#text': toTitleCase(product.name) }, 'g:description': { '#text': product.description }, 'g:link': { '#text': productUrl }, - 'g:image_link': { '#text': product.image }, + 'g:image_link': { '#text': product.image + '?variant=True' }, 'g:condition': { '#text': 'new' }, 'g:availability': { '#text': availability }, 'g:brand': { '#text': product.manufacture?.name || '' }, - 'g:price': { '#text': `${Math.round(product.lowestPrice.price * 1.11)} IDR` } - } + 'g:price': { + '#text': `${Math.round(product.lowestPrice.price * 1.11)} IDR`, + }, + }; if (product.stockTotal == 0) { - item['g:custom_label_0'] = { '#text': 'Stok Tidak Tersedia' } + item['g:custom_label_0'] = { '#text': 'Stok Tidak Tersedia' }; } else { - item['g:custom_label_1'] = { '#text': 'Stok Tersedia' } + item['g:custom_label_1'] = { '#text': 'Stok Tersedia' }; } if (categoryName) { - item['g:custom_label_2'] = { '#text': categoryName } + item['g:custom_label_2'] = { '#text': categoryName }; } if (product.lowestPrice.discountPercentage > 0) { item['g:sale_price'] = { - '#text': `${Math.round(product.lowestPrice.priceDiscount * 1.11)} IDR` - } + '#text': `${Math.round(product.lowestPrice.priceDiscount * 1.11)} IDR`, + }; } - productItems.push(item) + productItems.push(item); } const googleMerchant = { @@ -99,28 +107,32 @@ export async function getServerSideProps({ res, query }) { title: { '#text': `` }, link: { '#text': process.env.SELF_HOST }, description: { '#text': `` }, - item: productItems - } - } - } + item: productItems, + }, + }, + }; - res.setHeader('Content-Type', 'text/xml;charset=iso-8859-1') - res.write(create(googleMerchant).end()) - res.end() + res.setHeader('Content-Type', 'text/xml;charset=iso-8859-1'); + res.write(create(googleMerchant).end()); + res.end(); - return { props: {} } + return { props: {} }; } const getBrandById = async (id) => { - const brand = await axios(`${process.env.SOLR_HOST}/solr/brands/select?q=id:${id}`) - return brand.data.response.docs[0] ?? null -} + const brand = await axios( + `${process.env.SOLR_HOST}/solr/brands/select?q=id:${id}` + ); + return brand.data.response.docs[0] ?? null; +}; const getCategoryById = async (id) => { - const category = await axios(`${process.env.SOLR_HOST}/solr/categories/select?q=id:${id}`) - return category.data.response.docs[0] ?? null -} + const category = await axios( + `${process.env.SOLR_HOST}/solr/categories/select?q=id:${id}` + ); + return category.data.response.docs[0] ?? null; +}; export default function GoogleMerchantPage() { - return null + return null; } diff --git a/src/pages/shop/product/variant/[slug].jsx b/src/pages/shop/product/variant/[slug].jsx index 455b248b..cb335e0a 100644 --- a/src/pages/shop/product/variant/[slug].jsx +++ b/src/pages/shop/product/variant/[slug].jsx @@ -1,62 +1,68 @@ -import Seo from '@/core/components/Seo' -import LogoSpinner from '@/core/components/elements/Spinner/LogoSpinner' -import { getIdFromSlug } from '@/core/utils/slug' -import PageNotFound from '@/pages/404' -import dynamic from 'next/dynamic' -import { useRouter } from 'next/router' -import cookie from 'cookie' -import variantApi from '@/lib/product/api/variantApi' -import axios from 'axios' -import { useProductContext } from '@/contexts/ProductContext' -import { useEffect } from 'react' +import axios from 'axios'; +import cookie from 'cookie'; +import dynamic from 'next/dynamic'; +import { useRouter } from 'next/router'; +import { useEffect } from 'react'; -const BasicLayout = dynamic(() => import('@/core/components/layouts/BasicLayout')) -const Product = dynamic(() => import('@/lib/product/components/Product/Product')) +import { useProductContext } from '@/contexts/ProductContext'; +import Seo from '@/core/components/Seo'; +import LogoSpinner from '@/core/components/elements/Spinner/LogoSpinner'; +import { getIdFromSlug } from '@/core/utils/slug'; +import PageNotFound from '@/pages/404'; -export async function getServerSideProps(context) { - const { slug } = context.query - const cookies = context.req.headers.cookie - const cookieObj = cookies ? cookie.parse(cookies) : {} - const auth = cookieObj.auth ? JSON.parse(cookieObj.auth) : {} - const tier = auth.pricelist ? auth.pricelist : false - const authToken = auth?.token || '' +const BasicLayout = dynamic(() => + import('@/core/components/layouts/BasicLayout') +); +const Product = dynamic(() => + import('@/lib/product/components/Product/Product') +); +export async function getServerSideProps(context) { + const { slug } = context.query; + const cookies = context.req.headers.cookie; + const cookieObj = cookies ? cookie.parse(cookies) : {}; + const auth = cookieObj.auth ? JSON.parse(cookieObj.auth) : {}; + const tier = auth.pricelist ? auth.pricelist : false; + const authToken = auth?.token || ''; let response = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/variant-detail?id=` + getIdFromSlug(slug) +'&auth=' + tier - ) - let product = response.data + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/variant-detail?id=` + + getIdFromSlug(slug) + + '&auth=' + + tier + ); + let product = response.data; // let product = await variantApi({ id: getIdFromSlug(slug), headers: { Token: authToken } }) - + if (product?.length == 1) { - product = product[0] - /* const regexHtmlTags = /(<([^>]+)>)/gi + product = product[0]; + /* const regexHtmlTags = /(<([^>]+)>)/gi const regexHtmlTagsExceptP = /<\/?(?!p\b)[^>]*>/g product.description = product.description .replace(regexHtmlTagsExceptP, ' ') .replace(regexHtmlTags, ' ') .trim()*/ } else { - product = null + product = null; } return { - props: { product } - } + props: { product }, + }; } export default function ProductDetail({ product }) { - const router = useRouter() + const router = useRouter(); - const { setProduct } = useProductContext() + const { setProduct } = useProductContext(); useEffect(() => { if (product) { - setProduct(product) + setProduct(product); } - }, [product, setProduct]) + }, [product, setProduct]); - if (!product) return + if (!product) return ; return ( @@ -70,16 +76,16 @@ export default function ProductDetail({ product }) { url: product?.image, width: 800, height: 800, - alt: product?.name - } + alt: product?.name, + }, ], - type: 'product' + type: 'product', }} additionalMetaTags={[ { name: 'keywords', - content: `${product?.name}, Harga ${product?.name}, Beli ${product?.name}, Spesifikasi ${product?.name}` - } + content: `${product?.name}, Harga ${product?.name}, Beli ${product?.name}, Spesifikasi ${product?.name}`, + }, ]} /> {!product && ( @@ -89,5 +95,5 @@ export default function ProductDetail({ product }) { )} {product && } - ) + ); } -- cgit v1.2.3 From a698514b32353d8f6386ce8ba8c20941ab65f569 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 26 Feb 2024 15:12:12 +0700 Subject: Add qty append on upsert cart api --- src-migrate/modules/cart/components/ItemSelect.tsx | 8 ++++++- .../product-detail/components/AddToCart.tsx | 10 +++++++- .../modules/product-promo/components/AddToCart.tsx | 10 +++++++- src-migrate/services/cart.ts | 28 +++++++++++++++------- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src-migrate/modules/cart/components/ItemSelect.tsx b/src-migrate/modules/cart/components/ItemSelect.tsx index 1d8886a2..b904a1de 100644 --- a/src-migrate/modules/cart/components/ItemSelect.tsx +++ b/src-migrate/modules/cart/components/ItemSelect.tsx @@ -21,7 +21,13 @@ const CartItemSelect = ({ item }: Props) => { if (typeof auth !== 'object') return setIsLoad(true) - await upsertUserCart(auth.id, item.cart_type, item.id, item.quantity, e.target.checked) + await upsertUserCart({ + userId: auth.id, + type: item.cart_type, + id: item.id, + qty: item.quantity, + selected: e.target.checked + }) await loadCart(auth.id) setIsLoad(false) } diff --git a/src-migrate/modules/product-detail/components/AddToCart.tsx b/src-migrate/modules/product-detail/components/AddToCart.tsx index ebd6be7a..097db98a 100644 --- a/src-migrate/modules/product-detail/components/AddToCart.tsx +++ b/src-migrate/modules/product-detail/components/AddToCart.tsx @@ -36,7 +36,15 @@ const AddToCart = ({ ) return; toast.promise( - upsertUserCart(auth.id, 'product', variantId, quantity, true, source), + upsertUserCart({ + userId: auth.id, + type: 'product', + id: variantId, + qty: quantity, + selected: true, + source: source, + qtyAppend: true + }), { loading: { title: 'Menambahkan ke keranjang', description: 'Mohon tunggu...' }, success: { title: 'Menambahkan ke keranjang', description: 'Berhasil menambahkan ke keranjang belanja' }, diff --git a/src-migrate/modules/product-promo/components/AddToCart.tsx b/src-migrate/modules/product-promo/components/AddToCart.tsx index 95d275fc..192dd231 100644 --- a/src-migrate/modules/product-promo/components/AddToCart.tsx +++ b/src-migrate/modules/product-promo/components/AddToCart.tsx @@ -29,7 +29,15 @@ const ProductPromoAddToCart = ({ promotion }: Props) => { if (status === 'success') return setStatus('loading') - await upsertUserCart(auth.id, 'promotion', promotion.id, 1, true) + await upsertUserCart({ + userId: auth.id, + type: 'promotion', + id: promotion.id, + qty: 1, + selected: true, + source: 'add_to_cart', + qtyAppend: true + }) setStatus('idle') toast({ diff --git a/src-migrate/services/cart.ts b/src-migrate/services/cart.ts index 73967073..11f87125 100644 --- a/src-migrate/services/cart.ts +++ b/src-migrate/services/cart.ts @@ -4,20 +4,32 @@ export const getUserCart = async (userId: number) => { return await odooApi('GET', `/api/v1/user/${userId}/cart`); }; -export const upsertUserCart = async ( - userId: number, - type: 'product' | 'promotion', - id: number, - qty: number, - selected: boolean, - source: 'buy' | 'add_to_cart' = 'add_to_cart' -) => { +interface UpsertUserCartProps { + userId: number; + type: 'product' | 'promotion'; + id: number; + qty: number; + selected: boolean; + source?: 'buy' | 'add_to_cart'; + qtyAppend?: boolean; +} + +export const upsertUserCart = async ({ + userId, + type, + id, + qty, + selected, + source = 'add_to_cart', + qtyAppend = false, +}: UpsertUserCartProps) => { return await odooApi('POST', `/api/v1/user/${userId}/cart/create-or-update`, { product_id: type === 'product' ? id : null, qty, selected, program_line_id: type === 'promotion' ? id : null, source, + qty_append: qtyAppend, }); }; -- cgit v1.2.3 From 7a4d2462d05b2cc0e9c66551f1e2c16d4abace2f Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Mon, 26 Feb 2024 15:16:28 +0700 Subject: Update upsert user cart --- src-migrate/modules/cart/components/ItemAction.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src-migrate/modules/cart/components/ItemAction.tsx b/src-migrate/modules/cart/components/ItemAction.tsx index 859c758c..e73d507b 100644 --- a/src-migrate/modules/cart/components/ItemAction.tsx +++ b/src-migrate/modules/cart/components/ItemAction.tsx @@ -51,7 +51,13 @@ const CartItemAction = ({ item }: Props) => { if (typeof auth !== 'object' || isNaN(debounceQty)) return setIsLoadQuantity(true) - await upsertUserCart(auth.id, item.cart_type, item.id, debounceQty, item.selected) + await upsertUserCart({ + userId: auth.id, + type: item.cart_type, + id: item.id, + qty: debounceQty, + selected: item.selected, + }) await loadCart(auth.id) setIsLoadQuantity(false) } -- cgit v1.2.3 From 756f93807a33bb1398931ae894b071504dfec3b8 Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Tue, 27 Feb 2024 13:16:57 +0700 Subject: fixing pencarian brand di mobile & active button tab header --- src/core/components/elements/Navbar/NavbarDesktop.jsx | 6 +++--- src/lib/product/components/ProductSearch.jsx | 15 ++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx index 05ef0fb0..e11ad214 100644 --- a/src/core/components/elements/Navbar/NavbarDesktop.jsx +++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx @@ -166,7 +166,7 @@ const NavbarDesktop = () => {
@@ -174,7 +174,7 @@ const NavbarDesktop = () => { @@ -190,7 +190,7 @@ const NavbarDesktop = () => { diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index b3fdf888..ee4ec2de 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -268,11 +268,16 @@ const ProductSearch = ({

Brand Pencarian {q}

- + + +
)}

Produk

-- cgit v1.2.3 From 910b0eb1f7b8721db995094e49da7ed67b995146 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 28 Feb 2024 11:23:36 +0700 Subject: Add id on flashsale section --- src/lib/flashSale/components/FlashSale.jsx | 64 ++++++++++++++++-------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx index 3d5c4e0e..1a15ba8d 100644 --- a/src/lib/flashSale/components/FlashSale.jsx +++ b/src/lib/flashSale/components/FlashSale.jsx @@ -1,35 +1,39 @@ -import { useEffect, useState } from 'react' -import flashSaleApi from '../api/flashSaleApi' -import Image from 'next/image' -import CountDown from '@/core/components/elements/CountDown/CountDown' -import productSearchApi from '@/lib/product/api/productSearchApi' -import ProductSlider from '@/lib/product/components/ProductSlider' -import { FlashSaleSkeleton } from '../skeleton/FlashSaleSkeleton' +import Image from 'next/image'; +import { useEffect, useState } from 'react'; + +import CountDown from '@/core/components/elements/CountDown/CountDown'; +import productSearchApi from '@/lib/product/api/productSearchApi'; +import ProductSlider from '@/lib/product/components/ProductSlider'; + +import flashSaleApi from '../api/flashSaleApi'; +import { FlashSaleSkeleton } from '../skeleton/FlashSaleSkeleton'; const FlashSale = () => { - const [flashSales, setFlashSales] = useState(null) - const [isLoading, setIsLoading] = useState(true) + const [flashSales, setFlashSales] = useState(null); + const [isLoading, setIsLoading] = useState(true); useEffect(() => { const loadFlashSales = async () => { - const dataFlashSales = await flashSaleApi() - setFlashSales(dataFlashSales) - setIsLoading(false) - } - loadFlashSales() - }, []) + const dataFlashSales = await flashSaleApi(); + setFlashSales(dataFlashSales); + setIsLoading(false); + }; + loadFlashSales(); + }, []); if (isLoading) { - return + return ; } return ( flashSales?.length > 0 && ( -
+
{flashSales.map((flashSale, index) => (
-
{flashSale.name}
+
+ {flashSale.name} +
@@ -54,24 +58,24 @@ const FlashSale = () => { ))}
) - ) -} + ); +}; const FlashSaleProduct = ({ flashSaleId }) => { - const [products, setProducts] = useState(null) + const [products, setProducts] = useState(null); useEffect(() => { const loadProducts = async () => { const dataProducts = await productSearchApi({ query: `fq=flashsale_id_i:${flashSaleId}&fq=flashsale_price_f:[1 TO *]&limit=500&orderBy=flashsale-price-asc`, - operation: 'AND' - }) - setProducts(dataProducts.response) - } - loadProducts() - }, [flashSaleId]) + operation: 'AND', + }); + setProducts(dataProducts.response); + }; + loadProducts(); + }, [flashSaleId]); - return -} + return ; +}; -export default FlashSale +export default FlashSale; -- cgit v1.2.3 From 191f6592375ebac9d1530b67e7100a13a6155239 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 28 Feb 2024 11:23:54 +0700 Subject: Enable record activity log on all page --- src/core/components/layouts/BasicLayout.jsx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx index fa41a8ed..13f4af76 100644 --- a/src/core/components/layouts/BasicLayout.jsx +++ b/src/core/components/layouts/BasicLayout.jsx @@ -42,13 +42,10 @@ const BasicLayout = ({ children }) => { } }, [product, router]); - const recordActivity = useCallback(async () => { - const recordedPath = [ - '/shop/product/[slug]', - '/shop/product/variant/[slug]', - ]; - - if (!recordedPath.includes(router.pathname)) return; + const recordActivity = async (pathname) => { + const ONLY_ON_PATH = false; + const recordedPath = []; + if (ONLY_ON_PATH && !recordedPath.includes(pathname)) return; const ip = await odooApi('GET', '/api/ip-address'); const data = new URLSearchParams({ @@ -58,11 +55,11 @@ const BasicLayout = ({ children }) => { }); fetch(`/api/user-activity?${data.toString()}`); - }, [router.pathname]); + }; useEffect(() => { - recordActivity(); - }, [recordActivity]); + recordActivity(router.pathname); + }, [router.pathname]); return ( <> -- cgit v1.2.3 From b5e19d709d27615f30466ee59014d372dac2335c Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 28 Feb 2024 11:24:19 +0700 Subject: Update popup information module --- src-migrate/modules/popup-information/index.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx index 3d537236..dd6da458 100644 --- a/src-migrate/modules/popup-information/index.tsx +++ b/src-migrate/modules/popup-information/index.tsx @@ -1,9 +1,9 @@ import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; -import { Modal } from "~/components/ui/modal" + +import { Modal } from "~/components/ui/modal"; import { getAuth } from '~/libs/auth'; import PageContent from '../page-content'; -import Link from 'next/link'; const PagePopupInformation = () => { const router = useRouter(); @@ -25,12 +25,7 @@ const PagePopupInformation = () => { mode='desktop' >
- - - - {/* - Daftar Sekarang - */} +
-- cgit v1.2.3 From 86d124a1c95a5e7c3367fd288303c9c9a07ca1fd Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Wed, 28 Feb 2024 13:17:36 +0700 Subject: Add close on click popup information --- src-migrate/modules/popup-information/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src-migrate/modules/popup-information/index.tsx b/src-migrate/modules/popup-information/index.tsx index dd6da458..0d36f8e9 100644 --- a/src-migrate/modules/popup-information/index.tsx +++ b/src-migrate/modules/popup-information/index.tsx @@ -12,9 +12,7 @@ const PagePopupInformation = () => { const [active, setActive] = useState(false); useEffect(() => { - if (isHomePage && !auth) { - setActive(true); - } + if (isHomePage && !auth) setActive(true); }, [isHomePage, auth]); return (
@@ -24,7 +22,7 @@ const PagePopupInformation = () => { close={() => setActive(false)} mode='desktop' > -
+
setActive(false)}>
-- cgit v1.2.3 From b841b265fe697e28e4d5ce47017910bf9b536de5 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Thu, 29 Feb 2024 11:13:58 +0700 Subject: Update get program line API only active is true --- .../pages/api/product-variant/[id]/promotion/[category].tsx | 7 +++---- src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx | 4 +++- 2 files changed, 6 insertions(+), 5 deletions(-) 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 50671afd..8da0d9a3 100644 --- a/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx +++ b/src-migrate/pages/api/product-variant/[id]/promotion/[category].tsx @@ -8,10 +8,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const category = req.query.category as string if (req.method === 'GET') { - const queryParams = new URLSearchParams({ - q: `product_ids:${productId}`, - fq: `type_value_s:${category}` - }) + const queryParams = new URLSearchParams({ q: `product_ids:${productId}` }) + queryParams.append('fq', `type_value_s:${category}`) + queryParams.append('fq', `active_b:true`) const response = await fetch(`${SOLR_HOST}/solr/promotion_program_lines/select?${queryParams.toString()}`) const data: SolrResponse = await response.json() 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 8153f346..c4acacf1 100644 --- a/src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx +++ b/src-migrate/pages/api/product-variant/[id]/promotion/highlight.tsx @@ -16,7 +16,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) let programs: any[] = [] for (const type of types) { - queryParams.set('fq', `type_value_s:${type}`) + queryParams.set('fq', `type_value_s:${type}`) + queryParams.append('fq', `active_b:true`) + const response = await fetch(`${SOLR_HOST}/solr/promotion_program_lines/select?${queryParams.toString()}`) const data: SolrResponse = await response.json() programs.push(...data.response.docs) -- cgit v1.2.3 From 58267593b6214ecc37489802729a5702116794af Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Thu, 29 Feb 2024 15:31:16 +0700 Subject: Update flashsale section --- src/core/components/layouts/BasicLayout.jsx | 9 +++------ src/lib/flashSale/components/FlashSale.jsx | 2 +- src/pages/index.jsx | 17 ++++++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx index 13f4af76..a4f3a856 100644 --- a/src/core/components/layouts/BasicLayout.jsx +++ b/src/core/components/layouts/BasicLayout.jsx @@ -1,16 +1,13 @@ import dynamic from 'next/dynamic'; import Image from 'next/image'; -import { useCallback, useEffect, useState } from 'react'; +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; import { useProductContext } from '@/contexts/ProductContext'; import odooApi from '@/core/api/odooApi'; import whatsappUrl from '@/core/utils/whatsappUrl'; -import { useRouter } from 'next/router'; +import Navbar from '../elements/Navbar/Navbar'; -const Navbar = dynamic(() => import('../elements/Navbar/Navbar'), { - ssr: false, - loading: () =>
, -}); const AnimationLayout = dynamic(() => import('./AnimationLayout'), { ssr: false, }); diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx index 1a15ba8d..5be6d4e3 100644 --- a/src/lib/flashSale/components/FlashSale.jsx +++ b/src/lib/flashSale/components/FlashSale.jsx @@ -27,7 +27,7 @@ const FlashSale = () => { return ( flashSales?.length > 0 && ( -
+
{flashSales.map((flashSale, index) => (
diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 65d953d2..4bdccdb4 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -1,14 +1,15 @@ import dynamic from 'next/dynamic'; -import MobileView from '@/core/components/views/MobileView'; -import DesktopView from '@/core/components/views/DesktopView'; import { useRef } from 'react'; -import Seo from '@/core/components/Seo'; -import DelayRender from '@/core/components/elements/DelayRender/DelayRender'; + import { HeroBannerSkeleton } from '@/components/skeleton/BannerSkeleton'; import { PopularProductSkeleton } from '@/components/skeleton/PopularProductSkeleton'; -import PromotinProgram from '@/lib/promotinProgram/components/HomePage'; -import PreferredBrandSkeleton from '@/lib/home/components/Skeleton/PreferredBrandSkeleton'; +import Seo from '@/core/components/Seo'; +import DelayRender from '@/core/components/elements/DelayRender/DelayRender'; +import DesktopView from '@/core/components/views/DesktopView'; +import MobileView from '@/core/components/views/MobileView'; import { FlashSaleSkeleton } from '@/lib/flashSale/skeleton/FlashSaleSkeleton'; +import PreferredBrandSkeleton from '@/lib/home/components/Skeleton/PreferredBrandSkeleton'; +import PromotinProgram from '@/lib/promotinProgram/components/HomePage'; import PagePopupIformation from '~/modules/popup-information'; const BasicLayout = dynamic(() => @@ -97,7 +98,9 @@ export default function Home() {
- +
+ +
-- cgit v1.2.3 From 037c06c8c7ee0751105b5aea22819a5c4c41fdd8 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Thu, 29 Feb 2024 16:14:31 +0700 Subject: Add utm source function on product card --- src-migrate/constants/utm-source.ts | 8 ++ src-migrate/hooks/useUtmSource.ts | 20 +++ .../product-card/components/ProductCard.tsx | 8 +- src/lib/product/components/ProductCard.jsx | 134 ++++++++++++--------- 4 files changed, 112 insertions(+), 58 deletions(-) create mode 100644 src-migrate/constants/utm-source.ts create mode 100644 src-migrate/hooks/useUtmSource.ts diff --git a/src-migrate/constants/utm-source.ts b/src-migrate/constants/utm-source.ts new file mode 100644 index 00000000..95c03ed2 --- /dev/null +++ b/src-migrate/constants/utm-source.ts @@ -0,0 +1,8 @@ +export const UTM_SOURCE = { + '/': 'web.home', + '/shop/product/[slug]': 'web.other-product', + '/shop/search': 'web.search', + '/shop/brands/[slug]': 'web.brand', + '/shop/category/[slug]': 'web.category', + '/shop/cart': 'web.cart', +}; diff --git a/src-migrate/hooks/useUtmSource.ts b/src-migrate/hooks/useUtmSource.ts new file mode 100644 index 00000000..a72fae36 --- /dev/null +++ b/src-migrate/hooks/useUtmSource.ts @@ -0,0 +1,20 @@ +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import { UTM_SOURCE } from '~/constants/utm-source'; + +const useUtmSource = () => { + const router = useRouter(); + const [source, setSource] = useState(); + + useEffect(() => { + console.log(router.pathname); + + if (router.pathname) { + setSource(UTM_SOURCE[router.pathname as keyof typeof UTM_SOURCE]); + } + }, [router.pathname]); + + return source; +}; + +export default useUtmSource; diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx index 8487cd94..4ddebda5 100644 --- a/src-migrate/modules/product-card/components/ProductCard.tsx +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -1,8 +1,10 @@ import style from '../styles/product-card.module.css' +import clsx from 'clsx' import Link from 'next/link' -import React, { useMemo } from 'react' +import { useMemo } from 'react' import Image from '~/components/ui/image' +import useUtmSource from '~/hooks/useUtmSource' import clsxm from '~/libs/clsxm' import formatCurrency from '~/libs/formatCurrency' import { formatToShortText } from '~/libs/formatNumber' @@ -15,8 +17,10 @@ type Props = { } const ProductCard = ({ product, layout = 'vertical' }: Props) => { + const utmSource = useUtmSource() + const URL = { - product: createSlug('/shop/product/', product.name, product.id.toString()), + product: createSlug('/shop/product/', product.name, product.id.toString()) + `?utm_source=${utmSource}`, manufacture: createSlug('/shop/brands/', product.manufacture.name, product.manufacture.id.toString()), } diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index 1cec0804..9908a573 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -1,34 +1,46 @@ -import Image from '@/core/components/elements/Image/Image' -import Link from '@/core/components/elements/Link/Link' -import currencyFormat from '@/core/utils/currencyFormat' -import { sellingProductFormat } from '@/core/utils/formatValue' -import { createSlug } from '@/core/utils/slug' -import whatsappUrl from '@/core/utils/whatsappUrl' -import ImageNext from 'next/image' -import { useRouter } from 'next/router' -import { useMemo } from 'react' +import clsx from 'clsx'; +import ImageNext from 'next/image'; +import { useRouter } from 'next/router'; +import { useMemo } from 'react'; + +import Image from '@/core/components/elements/Image/Image'; +import Link from '@/core/components/elements/Link/Link'; +import currencyFormat from '@/core/utils/currencyFormat'; +import { sellingProductFormat } from '@/core/utils/formatValue'; +import { createSlug } from '@/core/utils/slug'; +import whatsappUrl from '@/core/utils/whatsappUrl'; +import useUtmSource from '~/hooks/useUtmSource'; const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { - const router = useRouter() + const router = useRouter(); + const utmSource = useUtmSource(); const callForPriceWhatsapp = whatsappUrl('product', { name: product.name, manufacture: product.manufacture?.name, - url: createSlug('/shop/product/', product.name, product.id, true) - }) + url: createSlug('/shop/product/', product.name, product.id, true), + }); const image = useMemo(() => { - if (product.image) return product.image + '?ratio=square' - return '/images/noimage.jpeg' - }, [product.image]) + if (product.image) return product.image + '?ratio=square'; + return '/images/noimage.jpeg'; + }, [product.image]); + + const URL = { + product: + createSlug('/shop/product/', product?.name, product?.id) + + `?utm_source=${utmSource}`, + manufacture: createSlug( + '/shop/brands/', + product?.manufacture?.name, + product?.manufacture.id + ), + }; if (variant == 'vertical') { return (
- + {product?.name} { height={5} /> - {product?.flashSale?.tag != 'false' || product?.flashSale?.tag + {product?.flashSale?.tag != 'false' || + product?.flashSale?.tag ? product?.flashSale?.tag : 'FLASH SALE'} @@ -75,27 +88,21 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
{product?.manufacture?.name ? ( - + {product.manufacture.name} ) : (
-
)} {product?.name} - {product?.flashSale?.id > 0 && product?.lowestPrice.discountPercentage > 0 ? ( + {product?.flashSale?.id > 0 && + product?.lowestPrice.discountPercentage > 0 ? ( <>
@@ -109,7 +116,11 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { {product?.lowestPrice.priceDiscount > 0 ? ( currencyFormat(product?.lowestPrice.priceDiscount) ) : ( - + Call for Inquiry )} @@ -122,11 +133,17 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { {currencyFormat(product?.lowestPrice.price)}
Inc. PPN:{' '} - {currencyFormat(product.lowestPrice.price * process.env.NEXT_PUBLIC_PPN)} + {currencyFormat( + product.lowestPrice.price * process.env.NEXT_PUBLIC_PPN + )}
) : ( -
+ Call for Inquiry )} @@ -134,7 +151,9 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { )}
- {product?.stockTotal > 0 &&
Ready Stock
} + {product?.stockTotal > 0 && ( +
Ready Stock
+ )} {/*
{product?.stockTotal > 5 ? '> 5' : '< 5'}
*/} {product?.qtySold > 0 && (
@@ -144,17 +163,14 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
- ) + ); } if (variant == 'horizontal') { return (
- + {product?.name} {
)} {product?.manufacture?.name ? ( - + {product.manufacture.name} ) : (
-
)} {product?.name} - {product?.flashSale?.id > 0 && product?.lowestPrice?.discountPercentage > 0 ? ( + {product?.flashSale?.id > 0 && + product?.lowestPrice?.discountPercentage > 0 ? ( <> {product?.lowestPrice.discountPercentage > 0 && (
@@ -220,7 +230,11 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { {product?.lowestPrice?.priceDiscount > 0 ? ( currencyFormat(product?.lowestPrice?.priceDiscount) ) : ( - + Call for Inquiry )} @@ -233,11 +247,17 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { {currencyFormat(product?.lowestPrice.price)}
Inc. PPN:{' '} - {currencyFormat(product.lowestPrice.price * process.env.NEXT_PUBLIC_PPN)} + {currencyFormat( + product.lowestPrice.price * process.env.NEXT_PUBLIC_PPN + )}
) : ( - + Call for Inquiry )} @@ -245,7 +265,9 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { )}
- {product?.stockTotal > 0 &&
Ready Stock
} + {product?.stockTotal > 0 && ( +
Ready Stock
+ )} {/*
{product?.stockTotal > 5 ? '> 5' : '< 5'}
*/} {product?.qtySold > 0 && (
@@ -255,8 +277,8 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
- ) + ); } -} +}; -export default ProductCard +export default ProductCard; -- cgit v1.2.3 From 07138ddc724233f688de9c16de59c1b61b885520 Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Fri, 1 Mar 2024 16:59:35 +0700 Subject: create component address dan expedisi --- src/lib/checkout/components/CheckoutSection.jsx | 207 +++++++++++++ src/lib/quotation/components/Quotation.jsx | 376 +++++++++++++++++++----- 2 files changed, 507 insertions(+), 76 deletions(-) create mode 100644 src/lib/checkout/components/CheckoutSection.jsx diff --git a/src/lib/checkout/components/CheckoutSection.jsx b/src/lib/checkout/components/CheckoutSection.jsx new file mode 100644 index 00000000..34fe19f7 --- /dev/null +++ b/src/lib/checkout/components/CheckoutSection.jsx @@ -0,0 +1,207 @@ +import Link from "next/link"; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import { AnimatePresence, motion } from "framer-motion"; +import { Divider, Spinner } from "@chakra-ui/react"; + +export const SectionAddress = ({ address, label, url }) => { + return ( +
+
+
{label}
+ + Pilih Alamat Lain + +
+ + {address && ( +
+
+ {address.type.charAt(0).toUpperCase() + + address.type.slice(1) + + ' Address'} +
+

{address.name}

+

{address.mobile}

+

+ {address.street}, {address?.city?.name} +

+
+ )} +
+ ) +} + +export const SectionValidation = ({ address }) => + address?.rajaongkirCityId == 0 && ( + +
+ Mohon untuk memperbarui alamat Anda dengan mengklik tombol di bawah ini.{' '} +
+
+ + Update Alamat + +
+
+ ); + +export const SectionExpedisi = ({ + address, + listExpedisi, + setSelectedExpedisi, + checkWeigth, + checkoutValidation, + expedisiValidation, + loadingRajaOngkir, +}) => + address?.rajaongkirCityId > 0 && ( +
+
+
Pilih Ekspedisi:
+
+
+ + + + {loadingRajaOngkir && ( + + + + )} + +
+ {checkoutValidation && ( + + *silahkan pilih expedisi + + )} +
+ +
+ {checkWeigth == true && ( +

+ Mohon maaf, pengiriman hanya tersedia untuk self pickup karena + terdapat barang yang belum diatur beratnya. Mohon atur berat barang + dengan menghubungi admin melalui{' '} + + tautan ini + +

+ )} +
+ ); + +export const SectionListService = ({ listserviceExpedisi, setSelectedServiceType }) => + listserviceExpedisi?.length > 0 && ( + <> +
+
+
Tipe Layanan Ekspedisi:
+
+ +
+
+
+ + + ); + + export const PickupAddress = ({ label }) => ( +
+
+
{label}
+
+
+

Indoteknik

+

+ Jl. Bandengan Utara Raya No.85, RT.3/RW.16, Penjaringan, Kec. + Penjaringan, Kota Jkt Utara, Daerah Khusus Ibukota Jakarta, Indonesia + Kodepos : 14440 +

+

Telp : 021-2933 8828/29

+

Mobile : 0813 9000 7430

+
+
+ ); + + const extractDuration = (text) => { + const matches = text.match(/\d+(?:-\d+)?/g); + + if (matches && matches.length === 1) { + const parts = matches[0].split('-'); + const min = parseInt(parts[0]); + const max = parseInt(parts[1]); + + if (min === max) { + return min.toString(); + } + + return matches[0]; + } + + return ''; + }; + \ No newline at end of file diff --git a/src/lib/quotation/components/Quotation.jsx b/src/lib/quotation/components/Quotation.jsx index 8c379ead..baf1492c 100644 --- a/src/lib/quotation/components/Quotation.jsx +++ b/src/lib/quotation/components/Quotation.jsx @@ -1,102 +1,265 @@ -import Alert from '@/core/components/elements/Alert/Alert' -import Divider from '@/core/components/elements/Divider/Divider' -import Link from '@/core/components/elements/Link/Link' -import useAuth from '@/core/hooks/useAuth' -import CartApi from '@/lib/cart/api/CartApi' -import { ExclamationCircleIcon } from '@heroicons/react/24/outline' -import { useEffect, useState } from 'react' -import _ from 'lodash' -import { deleteItemCart, getCart, getItemCart } from '@/core/utils/cart' -import currencyFormat from '@/core/utils/currencyFormat' -import { toast } from 'react-hot-toast' +import Alert from '@/core/components/elements/Alert/Alert'; +import Divider from '@/core/components/elements/Divider/Divider'; +import Link from '@/core/components/elements/Link/Link'; +import useAuth from '@/core/hooks/useAuth'; +import CartApi from '@/lib/cart/api/CartApi'; +import { ExclamationCircleIcon } from '@heroicons/react/24/outline'; +import { useEffect, useRef, useState } from 'react'; +import _ from 'lodash'; +import { deleteItemCart, getCart, getItemCart } from '@/core/utils/cart'; +import currencyFormat from '@/core/utils/currencyFormat'; +import { toast } from 'react-hot-toast'; // import checkoutApi from '@/lib/checkout/api/checkoutApi' -import { useRouter } from 'next/router' -import VariantGroupCard from '@/lib/variant/components/VariantGroupCard' -import MobileView from '@/core/components/views/MobileView' -import DesktopView from '@/core/components/views/DesktopView' -import Image from '@/core/components/elements/Image/Image' -import { useQuery } from 'react-query' -import CardProdcuctsList from '@/core/components/elements/Product/cartProductsList' +import { useRouter } from 'next/router'; +import VariantGroupCard from '@/lib/variant/components/VariantGroupCard'; +import MobileView from '@/core/components/views/MobileView'; +import DesktopView from '@/core/components/views/DesktopView'; +import Image from '@/core/components/elements/Image/Image'; +import { useQuery } from 'react-query'; +import CardProdcuctsList from '@/core/components/elements/Product/cartProductsList'; +import { Skeleton } from '@chakra-ui/react'; +import { + PickupAddress, + SectionAddress, + SectionExpedisi, + SectionListService, + SectionValidation, +} from '../../checkout/components/CheckoutSection'; +import addressesApi from '@/lib/address/api/addressesApi'; +import { getItemAddress } from '@/core/utils/address'; +import ExpedisiList from '../../checkout/api/ExpedisiList'; +import axios from 'axios'; -const { checkoutApi } = require('@/lib/checkout/api/checkoutApi') -const { getProductsCheckout } = require('@/lib/checkout/api/checkoutApi') +const { checkoutApi } = require('@/lib/checkout/api/checkoutApi'); +const { getProductsCheckout } = require('@/lib/checkout/api/checkoutApi'); const Quotation = () => { - const router = useRouter() - const auth = useAuth() + const router = useRouter(); + const auth = useAuth(); - const { data: cartCheckout } = useQuery('cartCheckout', () => getProductsCheckout()) + const { data: cartCheckout } = useQuery('cartCheckout', () => + getProductsCheckout() + ); - const [products, setProducts] = useState(null) - const [totalAmount, setTotalAmount] = useState(0) - const [totalDiscountAmount, setTotalDiscountAmount] = useState(0) + const SELF_PICKUP_ID = 32; + + const [products, setProducts] = useState(null); + const [totalAmount, setTotalAmount] = useState(0); + const [totalDiscountAmount, setTotalDiscountAmount] = useState(0); + + //start set up address and carrier + const [selectedCarrierId, setselectedCarrierId] = useState(0); + const [listExpedisi, setExpedisi] = useState([]); + const [selectedExpedisi, setSelectedExpedisi] = useState(0); + const [checkWeigth, setCheckWeight] = useState(false); + const [checkoutValidation, setCheckoutValidation] = useState(false); + const [loadingRajaOngkir, setLoadingRajaOngkir] = useState(false); + + const [listserviceExpedisi, setListServiceExpedisi] = useState([]); + const [selectedServiceType, setSelectedServiceType] = useState(null); + + const [selectedCarrier, setselectedCarrier] = useState(0); + const [totalWeight, setTotalWeight] = useState(0); + + const [biayaKirim, setBiayaKirim] = useState(0); + const [selectedExpedisiService, setselectedExpedisiService] = useState(null); + const [etd, setEtd] = useState(null); + + const expedisiValidation = useRef(null); + + const [selectedAddress, setSelectedAddress] = useState({ + shipping: null, + invoicing: null, + }); + + const [addresses, setAddresses] = useState(null); + + useEffect(() => { + if (!auth) return; + + const getAddresses = async () => { + const dataAddresses = await addressesApi(); + setAddresses(dataAddresses); + }; + + getAddresses(); + }, [auth]); + + useEffect(() => { + if (!addresses) return; + + const matchAddress = (key) => { + const addressToMatch = getItemAddress(key); + const foundAddress = addresses.filter( + (address) => address.id == addressToMatch + ); + if (foundAddress.length > 0) { + return foundAddress[0]; + } + return addresses[0]; + }; + + setSelectedAddress({ + shipping: matchAddress('shipping'), + invoicing: matchAddress('invoicing'), + }); + }, [addresses]); + + const loadExpedisi = async () => { + let dataExpedisi = await ExpedisiList(); + dataExpedisi = dataExpedisi.map((expedisi) => ({ + value: expedisi.id, + label: expedisi.name, + carrierId: expedisi.deliveryCarrierId, + })); + setExpedisi(dataExpedisi); + }; + + const loadServiceRajaOngkir = async () => { + setLoadingRajaOngkir(true); + const body = { + origin: 2127, + destination: selectedAddress.shipping.rajaongkirCityId, + weight: totalWeight, + courier: selectedCarrier, + originType: 'subdistrict', + destinationType: 'subdistrict', + }; + setBiayaKirim(0); + const dataService = await axios( + '/api/rajaongkir-service?body=' + JSON.stringify(body) + ); + setLoadingRajaOngkir(false); + setListServiceExpedisi(dataService.data[0].costs); + if (dataService.data[0].costs[0]) { + setBiayaKirim(dataService.data[0].costs[0]?.cost[0].value); + setselectedExpedisiService( + dataService.data[0].costs[0]?.description + + '-' + + dataService.data[0].costs[0]?.service + ); + setEtd(dataService.data[0].costs[0]?.cost[0].etd); + toast.success('Harap pilih tipe layanan pengiriman'); + } else { + toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.'); + } + }; + + useEffect(() => { + setCheckoutValidation(false); + + if (selectedCarrier != 0 && selectedCarrier != 1 && totalWeight > 0) { + console.log('masuk'); + loadServiceRajaOngkir(); + } else { + setListServiceExpedisi(); + setBiayaKirim(0); + setselectedExpedisiService(); + setEtd(); + } + }, [selectedCarrier, selectedAddress, totalWeight]); + + useEffect(() => { + if (selectedExpedisi) { + let serviceType = selectedExpedisi.split(','); + if (serviceType[0] === 0) return; + + setselectedCarrier(serviceType[0]); + setselectedCarrierId(serviceType[1]); + setListServiceExpedisi([]); + } + }, [selectedExpedisi]); + + // end set up address and carrier useEffect(() => { const loadProducts = async () => { - const cart = getCart() + const cart = getCart(); const variantIds = _.filter(cart, (o) => o.selected == true) .map((o) => o.productId) - .join(',') - const dataProducts = await CartApi({ variantIds }) + .join(','); + const dataProducts = await CartApi({ variantIds }); const productsWithQuantity = dataProducts?.map((product) => { return { ...product, - quantity: getItemCart({ productId: product.id }).quantity - } - }) + quantity: getItemCart({ productId: product.id }).quantity, + }; + }); if (productsWithQuantity) { Promise.all(productsWithQuantity).then((resolvedProducts) => { - setProducts(resolvedProducts) - }) + setProducts(resolvedProducts); + }); } - } + }; + loadExpedisi(); // loadProducts() - }, []) + }, []); useEffect(() => { - setProducts(cartCheckout?.products) - }, [cartCheckout]) + setProducts(cartCheckout?.products); + setCheckWeight(cartCheckout?.hasProductWithoutWeight); + setTotalWeight(cartCheckout?.totalWeight.g); + console.log(cartCheckout); + }, [cartCheckout]); useEffect(() => { if (products) { - let calculateTotalAmount = 0 - let calculateTotalDiscountAmount = 0 + let calculateTotalAmount = 0; + let calculateTotalDiscountAmount = 0; products.forEach((product) => { - calculateTotalAmount += product.price.price * product.quantity + calculateTotalAmount += product.price.price * product.quantity; calculateTotalDiscountAmount += - (product.price.price - product.price.priceDiscount) * product.quantity - }) - setTotalAmount(calculateTotalAmount) - setTotalDiscountAmount(calculateTotalDiscountAmount) + (product.price.price - product.price.priceDiscount) * + product.quantity; + }); + setTotalAmount(calculateTotalAmount); + setTotalDiscountAmount(calculateTotalDiscountAmount); } - }, [products]) + }, [products]); - const [isLoading, setIsLoading] = useState(false) + const [isLoading, setIsLoading] = useState(false); const checkout = async () => { - if (!products || products.length == 0) return - setIsLoading(true) + // validation checkout + if (selectedExpedisi === 0) { + setCheckoutValidation(true); + if (expedisiValidation.current) { + const position = expedisiValidation.current.getBoundingClientRect(); + window.scrollTo({ + top: position.top - 300 + window.pageYOffset, + behavior: 'smooth', + }); + } + return; + } + if (selectedCarrier != 1 && biayaKirim == 0) { + toast.error('Maaf, layanan tidak tersedia. Mohon pilih expedisi lain.'); + return; + } + + if (!products || products.length == 0) return; + setIsLoading(true); const productOrder = products.map((product) => ({ product_id: product.id, - quantity: product.quantity - })) + quantity: product.quantity, + })); let data = { partner_shipping_id: auth.partnerId, partner_invoice_id: auth.partnerId, user_id: auth.id, - order_line: JSON.stringify(productOrder) - } - const isSuccess = await checkoutApi({ data }) - setIsLoading(false) + order_line: JSON.stringify(productOrder), + }; + const isSuccess = await checkoutApi({ data }); + setIsLoading(false); if (isSuccess?.id) { - for (const product of products) deleteItemCart({ productId: product.id }) - router.push(`/shop/quotation/finish?id=${isSuccess.id}`) - return + for (const product of products) deleteItemCart({ productId: product.id }); + router.push(`/shop/quotation/finish?id=${isSuccess.id}`); + return; } - toast.error('Gagal melakukan transaksi, terjadi kesalahan internal') - } + toast.error('Gagal melakukan transaksi, terjadi kesalahan internal'); + }; - const taxTotal = (totalAmount - totalDiscountAmount) * 0.11 + const taxTotal = (totalAmount - totalDiscountAmount) * 0.11; return ( <> @@ -107,8 +270,8 @@ const Quotation = () => {
- Jika mengalami kesulitan dalam melakukan pembelian di website Indoteknik. Hubungi kami - disini + Jika mengalami kesulitan dalam melakukan pembelian di website + Indoteknik. Hubungi kami disini
@@ -116,7 +279,9 @@ const Quotation = () => {
- {products && } + {products && ( + + )}
@@ -124,7 +289,9 @@ const Quotation = () => {
Ringkasan Penawaran
-
{products?.length} Barang
+
+ {products?.length} Barang +

@@ -134,7 +301,9 @@ const Quotation = () => {
Diskon Produk
-
- {currencyFormat(cartCheckout?.totalDiscount)}
+
+ - {currencyFormat(cartCheckout?.totalDiscount)} +
Subtotal
@@ -152,9 +321,12 @@ const Quotation = () => { {currencyFormat(cartCheckout?.grandTotal)}
-

*) Belum termasuk biaya pengiriman

+

+ *) Belum termasuk biaya pengiriman +

- Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} + Dengan melakukan pembelian melalui website Indoteknik, saya + menyetujui{' '} Syarat & Ketentuan {' '} @@ -165,7 +337,11 @@ const Quotation = () => {

-
@@ -174,15 +350,62 @@ const Quotation = () => {
-
Detail Barang
- + {selectedCarrierId == SELF_PICKUP_ID && ( + + )} + {selectedCarrierId != SELF_PICKUP_ID && ( + + + + + + )} + + + + + + {/*
*/} +
Detail Barang
+ + {/*
*/}
Ringkasan Pesanan
-
{products?.length} Barang
+
+ {products?.length} Barang +

@@ -219,7 +442,8 @@ const Quotation = () => { *) Belum termasuk biaya pengiriman

- Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} + Dengan melakukan pembelian melalui website Indoteknik, saya + menyetujui{' '} Syarat & Ketentuan {' '} @@ -240,7 +464,7 @@ const Quotation = () => {

- ) -} + ); +}; -export default Quotation +export default Quotation; -- cgit v1.2.3 From df1f989fac16e99925b4d74472f329f9cdb97787 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 5 Mar 2024 09:16:34 +0700 Subject: Add id flashsale on mobile --- src/pages/_app.jsx | 4 ++-- src/pages/index.jsx | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx index 01dec611..bcb41dd6 100644 --- a/src/pages/_app.jsx +++ b/src/pages/_app.jsx @@ -89,9 +89,9 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }) { {animateLoader && ( + + -
- @@ -123,7 +123,9 @@ export default function Home() { - +
+ +
-- cgit v1.2.3 From 1113f6ab09ebec64d1f458d9177e6f2339ce554f Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 5 Mar 2024 11:09:18 +0700 Subject: Fix id flashsale position on home page --- src/pages/index.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/index.jsx b/src/pages/index.jsx index f0bf5843..c097530c 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -120,13 +120,13 @@ export default function Home() { - - -
- +
+ + + -- cgit v1.2.3 From 87bc6b410b875d6f811e21e1e1d6f974e7cac653 Mon Sep 17 00:00:00 2001 From: Rafi Zadanly Date: Tue, 5 Mar 2024 13:22:30 +0700 Subject: Update background flash sale --- public/images/BG-FLASH-SALE-DEFAULT.jpg | Bin 0 -> 21334 bytes public/images/BG-FLASH-SALE.jpg | Bin 0 -> 17079 bytes public/images/GAMBAR-BG-FLASH-SALE.jpg | Bin 21334 -> 0 bytes src-migrate/modules/product-detail/components/Image.tsx | 2 +- src/lib/product/components/Product/ProductDesktop.jsx | 2 +- src/lib/product/components/Product/ProductMobile.jsx | 2 +- src/lib/product/components/ProductCard.jsx | 2 +- 7 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 public/images/BG-FLASH-SALE-DEFAULT.jpg create mode 100644 public/images/BG-FLASH-SALE.jpg delete mode 100644 public/images/GAMBAR-BG-FLASH-SALE.jpg diff --git a/public/images/BG-FLASH-SALE-DEFAULT.jpg b/public/images/BG-FLASH-SALE-DEFAULT.jpg new file mode 100644 index 00000000..db26136a Binary files /dev/null and b/public/images/BG-FLASH-SALE-DEFAULT.jpg differ diff --git a/public/images/BG-FLASH-SALE.jpg b/public/images/BG-FLASH-SALE.jpg new file mode 100644 index 00000000..15b1f927 Binary files /dev/null and b/public/images/BG-FLASH-SALE.jpg differ diff --git a/public/images/GAMBAR-BG-FLASH-SALE.jpg b/public/images/GAMBAR-BG-FLASH-SALE.jpg deleted file mode 100644 index db26136a..00000000 Binary files a/public/images/GAMBAR-BG-FLASH-SALE.jpg and /dev/null differ diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx index b69cc87f..3d7777f8 100644 --- a/src-migrate/modules/product-detail/components/Image.tsx +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -67,7 +67,7 @@ const Image = ({ product }: Props) => {
{ {
{
Date: Tue, 5 Mar 2024 14:02:12 +0700 Subject: Update bg flash sale --- public/images/BG-FLASH-SALE.jpg | Bin 17079 -> 27230 bytes .../product/components/Product/ProductMobile.jsx | 4 +--- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/public/images/BG-FLASH-SALE.jpg b/public/images/BG-FLASH-SALE.jpg index 15b1f927..6f699ae7 100644 Binary files a/public/images/BG-FLASH-SALE.jpg and b/public/images/BG-FLASH-SALE.jpg differ diff --git a/src/lib/product/components/Product/ProductMobile.jsx b/src/lib/product/components/Product/ProductMobile.jsx index 50a59655..113a1e42 100644 --- a/src/lib/product/components/Product/ProductMobile.jsx +++ b/src/lib/product/components/Product/ProductMobile.jsx @@ -202,9 +202,7 @@ const ProductMobile = ({ product, wishlist, toggleWishlist }) => {
-- cgit v1.2.3 From 39b5e05a5fcc7ca26342f37e85c6585d1dacb3a5 Mon Sep 17 00:00:00 2001 From: "HATEC\\SPVDEV001" Date: Tue, 5 Mar 2024 14:37:44 +0700 Subject: add address & expedisi di page quotation - template stepper approval --- src/lib/checkout/components/CheckoutSection.jsx | 174 +++++++---- src/lib/quotation/components/Quotation.jsx | 116 ++++++- src/lib/transaction/components/Transaction.jsx | 396 +++++++++++++++--------- src/lib/transaction/components/stepper.jsx | 64 ++++ 4 files changed, 543 insertions(+), 207 deletions(-) create mode 100644 src/lib/transaction/components/stepper.jsx diff --git a/src/lib/checkout/components/CheckoutSection.jsx b/src/lib/checkout/components/CheckoutSection.jsx index 34fe19f7..7f9ea08a 100644 --- a/src/lib/checkout/components/CheckoutSection.jsx +++ b/src/lib/checkout/components/CheckoutSection.jsx @@ -1,35 +1,35 @@ -import Link from "next/link"; +import Link from 'next/link'; import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; -import { AnimatePresence, motion } from "framer-motion"; -import { Divider, Spinner } from "@chakra-ui/react"; +import { AnimatePresence, motion } from 'framer-motion'; +import { Divider, Spinner } from '@chakra-ui/react'; export const SectionAddress = ({ address, label, url }) => { - return ( -
-
-
{label}
- - Pilih Alamat Lain - -
- - {address && ( -
-
- {address.type.charAt(0).toUpperCase() + - address.type.slice(1) + - ' Address'} -
-

{address.name}

-

{address.mobile}

-

- {address.street}, {address?.city?.name} -

-
- )} + return ( +
+
+
{label}
+ + Pilih Alamat Lain +
- ) -} + + {address && ( +
+
+ {address.type.charAt(0).toUpperCase() + + address.type.slice(1) + + ' Address'} +
+

{address.name}

+

{address.mobile}

+

+ {address.street}, {address?.city?.name} +

+
+ )} +
+ ); +}; export const SectionValidation = ({ address }) => address?.rajaongkirCityId == 0 && ( @@ -129,7 +129,10 @@ export const SectionExpedisi = ({
); -export const SectionListService = ({ listserviceExpedisi, setSelectedServiceType }) => +export const SectionListService = ({ + listserviceExpedisi, + setSelectedServiceType, +}) => listserviceExpedisi?.length > 0 && ( <>
@@ -169,39 +172,86 @@ export const SectionListService = ({ listserviceExpedisi, setSelectedServiceType ); - export const PickupAddress = ({ label }) => ( -
-
-
{label}
-
-
-

Indoteknik

-

- Jl. Bandengan Utara Raya No.85, RT.3/RW.16, Penjaringan, Kec. - Penjaringan, Kota Jkt Utara, Daerah Khusus Ibukota Jakarta, Indonesia - Kodepos : 14440 -

-

Telp : 021-2933 8828/29

-

Mobile : 0813 9000 7430

-
+export const PickupAddress = ({ label }) => ( +
+
+
{label}
- ); +
+

Indoteknik

+

+ Jl. Bandengan Utara Raya No.85, RT.3/RW.16, Penjaringan, Kec. + Penjaringan, Kota Jkt Utara, Daerah Khusus Ibukota Jakarta, Indonesia + Kodepos : 14440 +

+

Telp : 021-2933 8828/29

+

Mobile : 0813 9000 7430

+
+
+); + +const extractDuration = (text) => { + const matches = text.match(/\d+(?:-\d+)?/g); - const extractDuration = (text) => { - const matches = text.match(/\d+(?:-\d+)?/g); - - if (matches && matches.length === 1) { - const parts = matches[0].split('-'); - const min = parseInt(parts[0]); - const max = parseInt(parts[1]); - - if (min === max) { - return min.toString(); - } - - return matches[0]; + if (matches && matches.length === 1) { + const parts = matches[0].split('-'); + const min = parseInt(parts[0]); + const max = parseInt(parts[1]); + + if (min === max) { + return min.toString(); } - - return ''; - }; - \ No newline at end of file + + return matches[0]; + } + + return ''; +}; + +export function calculateEstimatedArrival(duration) { + if (duration) { + let estimationDate = duration.split('-'); + estimationDate[0] = parseInt(estimationDate[0]); + estimationDate[1] = parseInt(estimationDate[1]); + const from = addDays(new Date(), estimationDate[0] + 3); + const to = addDays(new Date(), estimationDate[1] + 3); + + let etdText = `*Estimasi tiba ${formatDate(from)}`; + + if (estimationDate[1] > estimationDate[0]) { + etdText += ` - ${formatDate(to)}`; + } + + return etdText; + } + + return ''; +} + +function addDays(date, days) { + const result = new Date(date); + result.setDate(result.getDate() + days); + return result; +} + +function formatDate(date) { + const day = date.getDate(); + const month = date.toLocaleString('default', { month: 'short' }); + return `${day} ${month}`; +} + +export function splitDuration(duration) { + if (duration) { + let estimationDate = null; + if (duration.includes('-')) { + estimationDate = duration.split('-'); + estimationDate = parseInt(estimationDate[1]); + } else { + estimationDate = parseInt(duration); + } + + return estimationDate; + } + + return ''; +} \ No newline at end of file diff --git a/src/lib/quotation/components/Quotation.jsx b/src/lib/quotation/components/Quotation.jsx index baf1492c..fbb0627c 100644 --- a/src/lib/quotation/components/Quotation.jsx +++ b/src/lib/quotation/components/Quotation.jsx @@ -24,6 +24,8 @@ import { SectionExpedisi, SectionListService, SectionValidation, + calculateEstimatedArrival, + splitDuration, } from '../../checkout/components/CheckoutSection'; import addressesApi from '@/lib/address/api/addressesApi'; import { getItemAddress } from '@/core/utils/address'; @@ -64,6 +66,7 @@ const Quotation = () => { const [biayaKirim, setBiayaKirim] = useState(0); const [selectedExpedisiService, setselectedExpedisiService] = useState(null); const [etd, setEtd] = useState(null); + const [etdFix, setEtdFix] = useState(null); const expedisiValidation = useRef(null); @@ -170,6 +173,19 @@ const Quotation = () => { } }, [selectedExpedisi]); + useEffect(() => { + if (selectedServiceType) { + let serviceType = selectedServiceType.split(','); + setBiayaKirim(serviceType[0]); + setselectedExpedisiService(serviceType[1]); + setEtd(serviceType[2]); + } + }, [selectedServiceType]); + + useEffect(() => { + if (etd) setEtdFix(calculateEstimatedArrival(etd)); + }, [etd]); + // end set up address and carrier useEffect(() => { @@ -248,6 +264,10 @@ const Quotation = () => { partner_invoice_id: auth.partnerId, user_id: auth.id, order_line: JSON.stringify(productOrder), + delivery_amount: biayaKirim, + carrier_id: selectedCarrierId, + estimated_arrival_days: splitDuration(etd), + delivery_service_type: selectedExpedisiService, }; const isSuccess = await checkoutApi({ data }); setIsLoading(false); @@ -278,6 +298,68 @@ const Quotation = () => { + {selectedCarrierId == SELF_PICKUP_ID && ( +
+ +
+ )} + + {selectedCarrierId == SELF_PICKUP_ID && ( + + )} + {selectedCarrierId != SELF_PICKUP_ID && ( + + + + + + )} + + + + + +
{products && ( @@ -313,12 +395,25 @@ const Quotation = () => {
PPN 11%
{currencyFormat(cartCheckout?.tax)}
+
+
+ Biaya Kirim

{etdFix}

+
+
+ {currencyFormat( + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 + )} +
+

Grand Total
- {currencyFormat(cartCheckout?.grandTotal)} + {currencyFormat( + cartCheckout?.grandTotal + + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 + )}

@@ -428,6 +523,16 @@ const Quotation = () => {

PPN 11%
{currencyFormat(cartCheckout?.tax)}
+
+
+ Biaya Kirim

{etdFix}

+
+
+ {currencyFormat( + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 + )} +
+

@@ -435,12 +540,15 @@ const Quotation = () => {
Grand Total
- {currencyFormat(cartCheckout?.grandTotal)} + {currencyFormat( + cartCheckout?.grandTotal + + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 + )}
-

+ {/*

*) Belum termasuk biaya pengiriman -

+

*/}

Dengan melakukan pembelian melalui website Indoteknik, saya menyetujui{' '} diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index 82eb1775..d4b0f92c 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -1,83 +1,97 @@ -import Spinner from '@/core/components/elements/Spinner/Spinner' -import useTransaction from '../hooks/useTransaction' -import TransactionStatusBadge from './TransactionStatusBadge' -import Divider from '@/core/components/elements/Divider/Divider' -import { useMemo, useRef, useState } from 'react' -import { downloadPurchaseOrder, downloadQuotation } from '../utils/transactions' -import BottomPopup from '@/core/components/elements/Popup/BottomPopup' -import uploadPoApi from '../api/uploadPoApi' -import { toast } from 'react-hot-toast' -import getFileBase64 from '@/core/utils/getFileBase64' -import currencyFormat from '@/core/utils/currencyFormat' -import VariantGroupCard from '@/lib/variant/components/VariantGroupCard' -import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon } from '@heroicons/react/24/outline' -import Link from '@/core/components/elements/Link/Link' -import checkoutPoApi from '../api/checkoutPoApi' -import cancelTransactionApi from '../api/cancelTransactionApi' -import MobileView from '@/core/components/views/MobileView' -import DesktopView from '@/core/components/views/DesktopView' -import Menu from '@/lib/auth/components/Menu' -import Image from '@/core/components/elements/Image/Image' -import { createSlug } from '@/core/utils/slug' -import toTitleCase from '@/core/utils/toTitleCase' -import useAirwayBill from '../hooks/useAirwayBill' -import Manifest from '@/lib/treckingAwb/component/Manifest' +import Spinner from '@/core/components/elements/Spinner/Spinner'; +import useTransaction from '../hooks/useTransaction'; +import TransactionStatusBadge from './TransactionStatusBadge'; +import Divider from '@/core/components/elements/Divider/Divider'; +import { useMemo, useRef, useState } from 'react'; +import { + downloadPurchaseOrder, + downloadQuotation, +} from '../utils/transactions'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import uploadPoApi from '../api/uploadPoApi'; +import { toast } from 'react-hot-toast'; +import getFileBase64 from '@/core/utils/getFileBase64'; +import currencyFormat from '@/core/utils/currencyFormat'; +import VariantGroupCard from '@/lib/variant/components/VariantGroupCard'; +import { + ChevronDownIcon, + ChevronRightIcon, + ChevronUpIcon, +} from '@heroicons/react/24/outline'; +import Link from '@/core/components/elements/Link/Link'; +import checkoutPoApi from '../api/checkoutPoApi'; +import cancelTransactionApi from '../api/cancelTransactionApi'; +import MobileView from '@/core/components/views/MobileView'; +import DesktopView from '@/core/components/views/DesktopView'; +import Menu from '@/lib/auth/components/Menu'; +import Image from '@/core/components/elements/Image/Image'; +import { createSlug } from '@/core/utils/slug'; +import toTitleCase from '@/core/utils/toTitleCase'; +import useAirwayBill from '../hooks/useAirwayBill'; +import Manifest from '@/lib/treckingAwb/component/Manifest'; +import useAuth from '@/core/hooks/useAuth'; +import StepApproval from './stepper'; const Transaction = ({ id }) => { - const { transaction } = useTransaction({ id }) - const { queryAirwayBill } = useAirwayBill({ orderId: id }) - - const [airwayBillPopup, setAirwayBillPopup] = useState(null) - - const poNumber = useRef(null) - const poFile = useRef(null) - const [uploadPo, setUploadPo] = useState(false) - const [idAWB, setIdAWB] = useState(null) - const openUploadPo = () => setUploadPo(true) - const closeUploadPo = () => setUploadPo(false) + const auth = { ...useAuth(), isApprovalState: true }; + const { transaction } = useTransaction({ id }); + const { queryAirwayBill } = useAirwayBill({ orderId: id }); + + const [airwayBillPopup, setAirwayBillPopup] = useState(null); + + const poNumber = useRef(null); + const poFile = useRef(null); + const [uploadPo, setUploadPo] = useState(false); + const [idAWB, setIdAWB] = useState(null); + const openUploadPo = () => setUploadPo(true); + const closeUploadPo = () => setUploadPo(false); const submitUploadPo = async () => { - const file = poFile.current.files[0] - const name = poNumber.current.value + const file = poFile.current.files[0]; + const name = poNumber.current.value; if (typeof file === 'undefined' || !name) { - toast.error('Nomor dan Dokumen PO harus diisi') - return + toast.error('Nomor dan Dokumen PO harus diisi'); + return; } if (file.size > 5000000) { - toast.error('Maksimal ukuran file adalah 5MB') - return + toast.error('Maksimal ukuran file adalah 5MB'); + return; } - const data = { name, file: await getFileBase64(file) } - const isUploaded = await uploadPoApi({ id, data }) + const data = { name, file: await getFileBase64(file) }; + const isUploaded = await uploadPoApi({ id, data }); if (isUploaded) { - toast.success('Berhasil upload PO') - transaction.refetch() - closeUploadPo() - return + toast.success('Berhasil upload PO'); + transaction.refetch(); + closeUploadPo(); + return; } - toast.error('Terjadi kesalahan internal, coba lagi nanti atau hubungi kami') - } - - const [cancelTransaction, setCancelTransaction] = useState(false) - const openCancelTransaction = () => setCancelTransaction(true) - const closeCancelTransaction = () => setCancelTransaction(false) + toast.error( + 'Terjadi kesalahan internal, coba lagi nanti atau hubungi kami' + ); + }; + + const [cancelTransaction, setCancelTransaction] = useState(false); + const openCancelTransaction = () => setCancelTransaction(true); + const closeCancelTransaction = () => setCancelTransaction(false); const submitCancelTransaction = async () => { - const isCancelled = await cancelTransactionApi({ transaction: transaction.data }) + const isCancelled = await cancelTransactionApi({ + transaction: transaction.data, + }); if (isCancelled) { - toast.success('Berhasil batalkan transaksi') - transaction.refetch() + toast.success('Berhasil batalkan transaksi'); + transaction.refetch(); } - closeCancelTransaction() - } + closeCancelTransaction(); + }; const checkout = async () => { if (!transaction.data?.purchaseOrderFile) { - toast.error('Mohon upload dokumen PO anda sebelum melanjutkan pesanan') - return + toast.error('Mohon upload dokumen PO anda sebelum melanjutkan pesanan'); + return; } - await checkoutPoApi({ id }) - toast.success('Berhasil melanjutkan pesanan') - transaction.refetch() - } + await checkoutPoApi({ id }); + toast.success('Berhasil melanjutkan pesanan'); + transaction.refetch(); + }; const memoizeVariantGroupCard = useMemo( () => ( @@ -102,19 +116,19 @@ const Transaction = ({ id }) => {

), [transaction.data] - ) + ); if (transaction.isLoading) { return (
- ) + ); } const closePopup = () => { - setIdAWB(null) - } + setIdAWB(null); + }; return ( transaction.data?.name && ( @@ -156,10 +170,18 @@ const Transaction = ({ id }) => {
- -
@@ -167,18 +189,27 @@ const Transaction = ({ id }) => { +
+ +
- {transaction.data?.name} + + {transaction.data?.name} + {transaction.data?.paymentTerm} - {transaction.data?.sales} - {transaction.data?.dateOrder} + + {transaction.data?.sales} + + + {transaction.data?.dateOrder} +
@@ -214,25 +245,27 @@ const Transaction = ({ id }) => { -
- - {transaction.data?.purchaseOrderName || '-'} - -
-

Dokumen PO

- + {!auth.isApprovalState && ( +
+ + {transaction.data?.purchaseOrderName || '-'} + +
+

Dokumen PO

+ +
-
+ )} @@ -278,7 +311,29 @@ const Transaction = ({ id }) => {
- {transaction.data?.status == 'draft' && ( + {transaction.data?.status == 'draft' && auth.isApprovalState && ( +
+ + +
+ )} + {transaction.data?.status == 'draft' && !auth.isApprovalState && ( @@ -308,10 +363,17 @@ const Transaction = ({ id }) => {
-

Detail Transaksi

+
+

+ Detail Transaksi +

+ +
- {transaction?.data?.name} + + {transaction?.data?.name} +
@@ -322,11 +384,35 @@ const Transaction = ({ id }) => { > Download - {transaction.data?.status == 'draft' && ( - - )} + {transaction.data?.status == 'draft' && + auth.isApprovalState && ( +
+ + +
+ )} + {transaction.data?.status == 'draft' && + !auth.isApprovalState && ( + + )} {transaction.data?.status != 'draft' && ( -
+ {!auth.isApprovalState && ( + <> +
Purchase Order
+
+ : {transaction?.data?.purchaseOrderName}{' '} + +
+ ) + + )}
-
Informasi Pelanggan
+
+ Informasi Pelanggan +
Detail Pelanggan
- +
-
Pengiriman
+
+ Pengiriman +
{transaction?.data?.pickings?.map((airway) => (